【问题标题】:How can I organize EventSources for the Semantic Logging Application Block?如何为语义日志记录应用程序块组织事件源?
【发布时间】:2014-04-30 04:49:28
【问题描述】:

语义记录应用程序块 (SLAB) 对我非常有吸引力,我希望在我正在编写的大型复合应用程序中使用它。要使用它,需要编写一个派生自“EventSource”的类,并在类中为他们想要记录为类型事件的每个事件包含一个方法,而不是简单的字符串。

像我这样的应用程序可能有数百个这样的事件。我可以有一个基于“EventSource”的类,其中只有一个事件“SomethingHappened”,并通过它记录所有内容,在努力和准确度范围的一个极端,我可以为我执行的每个操作都有一个事件。

让我觉得为不同的功能领域提供 EventSource 衍生产品是个好主意。该应用程序本身对业务逻辑知之甚少;这些都是由 MEF 插件模块提供的,所以我可以有用于引导、安全、配置更改等的事件源,并且任何插件模块都可以为它想要记录的任何事件定义一个事件源。

这是一个好的策略,还是许多EventSource 派生记录器是不受欢迎的应用程序功能?

【问题讨论】:

    标签: c# logging etw semantic-logging


    【解决方案1】:

    从你的问题

    ...我希望在我正在编写的大型复合应用程序中使用它...

    我可以推断,大是指单个开发人员的上下文。在这种情况下,您可以从 EventSource 派生并将您可能想要的所有事件添加到该类中。 为复合应用程序的每个部分创建一个额外的 EventSource 派生类没有多大意义,因为它会污染已经注册了 2K 提供程序的事件源注册数据库。除此之外,如果您需要记住 20 个您需要启用的 guid,以便通过多个层遵循您的应用程序逻辑,那么为您的应用程序启用日志记录会变得很困难。

    折衷方案是在您的 EventSource 类中定义一些通用事件,例如

    public void WriteViolation(string Subsystem, string Message, string Context)
    

    您在组件中的每个组件都有一个记录器类

    public static class NetworkLogger
    {
       public static void Violation(string message)
       {
          GenericSource.Instance.Violation("Network", message, NetworkContext.Current);
       }
    }
    
    public static class DatabaseLogger
    {
      public static void Violation(string message)
      {
          GenericSource.Instance.Violation("Database", message, DBContext.Current);
      }
    }
    

    这样您可以保持记录器组件的特定性,并且可以添加例如必要时自动为通用事件提供上下文信息。 另一种方法是在您的应用程序跟踪中使用您的跟踪方法进入/离开、信息、警告、错误,而您的 EventSource 派生类只知道这些事件。当您为每个跟踪条目添加类型名称 + 方法名称时,您可以在 WPA 中按命名空间和按类进行过滤,以查看您在做什么。 Semantic Tracing For .NET 4.0 中显示了一个示例。 对于大型应用程序,您可以在您的机器上签出文件

    C:\Windows\Microsoft.NET\Framework\v4.0.30319\CLR-ETW.man
    

    您可以使用 Windows SDK 中的 ecmangen.exe 打开它,以获得漂亮的 GUI 来查看事件的结构。 .NET 只定义了两个事件提供程序。许多事件通过关键字进行分组,以启用 .NET 的特定方面,例如。 GC,加载程序,异常,.... 这很重要,因为您可以在启用提供程序特定关键字时传递给它以仅启用大型提供程序的某些事件。

    您还可以查看 Microsoft.Windows.ApplicationServer.Applications.45.man 以了解 Workflow 人员如何看待 ETW 事件。这应该有助于找到自己的方式。这与您如何准确地构建事件无关,因为真正的测试是在客户站点发现生产错误。您很有可能需要进行多次迭代,直到找到正确的平衡点来记录/跟踪相关信息,从而帮助您诊断现场故障。

    【讨论】:

    • 不,团队中总共有大约五名开发人员。我现在是唯一一个编写 WPF 的人,但另一个人正在将 200 多个 Delphi 模块包装成插件组件,然后我们三个人将开始将这些模块的集合转换为更合适的 WPF 组件。
    【解决方案2】:

    这有点挥手,因为它太长了,无法发表评论。但是模板和工厂服务怎么样?

    这不会改变,您在应用程序启动和加载插件后绑定所有内容。

    interface IReportable
    {
        void Report(object param);
    }
    
    interface IKernel
    {
        T Get<T>();
    }
    
    class EventSource2 : EventSource
    {
        private IKernel _factory;
    
        public EventSource2(IKernel factory)
        {
            _factory = factory;
        }
    
        public void Report<TReportable>(object param = null) where TReportable : IReportable
        {
            var reportable = _factory.Get<TReportable>();
    
            reportable.Report(param);
    
            //... Do what you want to do with EventSource
        }
    }
    

    【讨论】:

      【解决方案3】:

      在逻辑上将事件分组到不同的较小提供程序(EventSource 类)中,而不是到 1 个大文件中。

      这样做的好处是您可以仅在特殊情况下为您关心的提供者启用事件。

      【讨论】:

        【解决方案4】:

        不要将 EventSource 视为您可能在应用程序中执行的所有可能日志事件的列表。请记住,有一些方法可以使用关键字和详细程度/事件级别来过滤您的事件。您甚至可以进一步深入研究并使用操作码和任务。 SLAB 1.1 版支持 ActivityID 和 RelatedActivityID。本周早些时候发布的 2.0 版 (https://slab.codeplex.com/wikipage?title=SLAB2.0ReleaseNotes&version=2) 现在支持进程和线程 ID。

        举个例子,我有一个非常小的 EventSource 派生类,并且有 StartLog、LogStatus、StopLogging、LogError、LogDebug 和 CreateDump 的方法,前三个使用相同的事件级别,但由于格式不同,事件 ID 不同其余的使用不同的事件级别,因此除非我使用配置文件设置动态启用它,否则我不会调试或创建转储。关键是我可以使用来自 asp.net 站点以及类库或控制台应用程序的相同方法。不要忘记这仅定义了日志记录事件。您仍然必须让接收器订阅该事件,从而为您提供更多可能性。您可以将调试消息发送到文件,将错误消息发送到数据库和/或电子邮件。无限可能。

        最后一件事。当我进行测试时,我以为我把自己画到了一个角落,发现多个程序集正在记录到同一个文件,因为它们使用相同的事件方法(因此相同的事件 ID、关键字、事件级别等)。我修改了我的代码以传递调用程序集名称,该名称现在在确定是否应写入日志消息(来自配置文件设置)以及写入位置(基于程序集名称的日志文件)时用于过滤进程。希望这会有所帮助!

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2014-10-13
          • 2011-09-24
          • 1970-01-01
          • 2012-11-04
          • 2018-03-23
          • 2010-12-30
          相关资源
          最近更新 更多