依赖关注:在大型应用程序中实现EventSource进行语义日志记录

7
我正在处理一个大型产品,由三个Windows服务和几个常规Windows应用程序(.exe)组成。现在我们想要转移到ETW和语义日志记录,并使用Microsoft.Diagnostics.Tracing.EventSource。
我曾经在某个地方读到,所有逻辑上相关的应用程序部分都应该使用同一个事件源。这意味着,最好我们希望我们的服务基本上只有一个EventSource。但是,如何在不引入产品中几乎所有程序集之间的依赖关系的情况下实现这一点呢?
该应用程序当前由大约70个程序集组成。为了能够在EventSource中创建一个日志方法,例如接受一个枚举值,包含事件源的程序集必须引用定义该枚举的程序集,这意味着枚举定义需要从使用它的程序集(可能是.exe)移动到所有程序集都引用的内容。
是否有一些方法来在一个应用程序中拥有几个派生自EventSource的类,仍然使用相同的ETW EventSource?或者,在这种情况下,实现具有ETW的语义日志记录的好方法是什么,当引入整堆新的依赖关系以创建您的日志类是不可取的?

我通常使用一个唯一的GUID,一个唯一的日志类,一个唯一的枚举(定义为“日志组件”),加上TraceLevel(错误、警告、信息等)。然后,Log方法可以使用酷炫的[CallerMemberName],这样你就可以自动设置跟踪的方法名。这样只创建了一个对公共类的引用(如果您不想要硬程序集引用,甚至可以在Visual Studio中使用源链接共享)。 - Simon Mourier
@SimonMourier 那对于“通用”的日志来说可能管用,但是似乎不适合语义日志记录? - DeCaf
是的,那更像是一条注释 :) - Simon Mourier
3个回答

0

小心,EventSource类必须被封装! 如果你想使用依赖注入来使用EventSource,有一个解决方法...

定义一个简单的接口:

// A simple interface to log what you need ...
public interface ILog
{
    void Debug(string message);

    void Info(string message);

    void Warn(string message);

    void Error(string message);

    void Error(string message, Exception exception);
}

实现(您的接口实现必须使用NonEventAttribute进行装饰:

[EventSource(Name = "MyLogEventsource")]
public class Log : EventSource, ILog
{
    public Log()
    {
        EventSourceAnalyzer.InspectAll(this);
    }

    [NonEvent]
    public void Debug(string message)
    {
        DebugInternal(message);
    }

    [Event(1)]
    private void DebugInternal(string message)
    {
        WriteEvent(1, message);
    }

    [NonEvent]
    public void Info(string message)
    {
        InfoInternal(message);
    }

    [Event(2)]
    private void InfoInternal(string message)
    {
        WriteEvent(2, message);
    }

    [NonEvent]
    public void Warn(string message)
    {
        WarnInternal(message);
    }

    [Event(3)]
    private void WarnInternal(string message)
    {
        WriteEvent(3, message);
    }

    [NonEvent]
    public void Error(string message)
    {
        ErrorInternal(message, "", "");
    }

    [NonEvent]
    public void Error(string message, Exception exception)
    {
        ErrorInternal(message, exception.Message, exception.ToString());
    }

    [Event(4)]
    private void ErrorInternal(string message, string exceptionMessage, string exceptionDetails)
    {
        WriteEvent(4, message, exceptionMessage, exceptionDetails);
    }
}

现在你可以注入你的日志类 ^^


0

有三种策略:

  1. 创建一个仅包含 EventSource 派生类的程序集,该类为所有应用程序定义事件。将该程序集的引用添加到所有必需的项目中。为了简单起见,您可以将其封装到 nuget 包中。
  2. 创建一个仅包含一个 EventSource 派生类的测试项目。仅将其用于验证目的。将此类复制到所有必需的项目中。这基本上是相同的解决方案,但没有二进制依赖项。
  3. 为每个项目创建新的 EventSource 派生类,但为它们指定相同的 Guid 属性。在这种情况下,您需要确保所有这些事件源对于重叠的事件(具有相同 ID)具有相同的声明。在这种情况下,您必须编写一些清单合并工具来生成组合清单。

这些选项都不是很吸引人,但我想上面的第三个选项至少回答了我的问题;虽然需要付出很多努力,但是这是可能的。 :/ - DeCaf
虽然我本来希望实现上述的第三种解决方案,但看起来它并不起作用。因为你不能创建两个具有相同 GUID 的 EventSource 类的实例,否则会生成一个错误事件,显示“ERROR: Exception in Command Processing for EventSource XXXX: An instance of EventSource with Guid c64e6d39-ff1f-4620-8041-e3f9cca908c9 already exist. :(”。 - DeCaf

0
通常我这样做是为了接口的隔离,即使它们使用单个事件源实例。在我的IOC中,所有带有ISingletonDependency的代码都被注册为单例。因此,您可以调用具有非常特定方法的接口,但它们仍然是相同的EventSource。
希望这可以帮助到您。
public MyCompanyEventSource: IMyCompanyEventSource, ISingletonDependency{
}
public IMyCompanyEventSource: IComponentLogger1, IComponentLogger2, IComponentLogger3{
}
public Component1{
       public Component1(IComponentLogger logger){
       }
    }

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接