如何组织语义记录应用程序块的EventSource?

时间:2014-03-22 19:04:17

标签: c# logging etw semantic-logging

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

像我这样的应用程序可能会有数百个此类事件。我可以拥有一个' EventSource'只有一个事件的基础类," SomethingHappened"并记录所有内容,在努力和准确度谱的一个极端,我可以为我执行的每个操作都有一个事件。

让我觉得将EventSource衍生产品用于不同的功能领域是一个好主意。该应用程序几乎不了解业务逻辑本身;这些都是由MEF插件模块提供的,所以我可以为bootsrapping,安全性,配置更改等提供事件源。任何插件模块都可以为它想要记录的任何事件定义事件源。

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

4 个答案:

答案 0 :(得分:5)

从你的问题

  

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

我可以推断出大型是指单个开发人员的背景。在这种情况下,您可以从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,Loader,Exceptions,.... 这很重要,因为您可以在启用提供程序特定关键字时传递,以便仅启用大型提供程序的某些事件。

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

答案 1 :(得分:1)

由于评论时间过长,这有点过时了。但是如何模板化,然后是工厂服务呢?

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

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
    }
}

答案 2 :(得分:0)

将事件逻辑地分组到不同的较小提供者(EventSource类)而不是1个大文件。

这样做的好处是,您只能为特殊情况下您关心的提供商启用事件。

答案 3 :(得分:0)

不要将EventSource视为您可能在应用程序中执行的每个可能的日志事件的列表。请记住,有一些方法可以使用关键字和详细程度/事件级别来过滤您的事件。您甚至可以进一步深入查看并使用OpCodes和Tasks。 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,关键字,事件级别等)。我修改了我的代码以传递调用程序集名称,该名称现在在确定是否应该写入日志消息(来自配置文件设置)和where(基于程序集名称的日志文件)时使用过滤进程。希望这有帮助!