【问题标题】:Is passive logging possible in .NET?.NET 中是否可以进行被动日志记录?
【发布时间】:2010-10-15 19:55:30
【问题描述】:

我经常对代码中必须包含的大量日志记录感到沮丧,这让我想知道是否有更好的处理方式。

我不知道这是否已经完成,或者是否有人提出了更好的主意,但我想知道是否有任何人知道将记录器“注入”到应用程序中,以便它被动地监视线程并在进程发生时安静地记录进程,而无需执行以下操作:

public void MyProcess(int a, string b, object c)
{
  log(
    String.Format(
      "Entering process MyProcess with arguments: [a] = [{0}]; [b] = [{1}]; [c] = [{2}]",
      a.ToString(),
      b,
      c.ToString()
  );

  try
  {
    int d = DoStuff(a)
    log(
      String.Format(
        "DoStuff({0}) returned value {1}",
        a.ToString(),
        d.ToString()
      )
    );
  }
  catch (Exception ex)
  {
    log(
      String.Format("An exception occurred during process DoStuff({0})\nException:\n{1}",
      a.ToString(),
      ex.ToString())
    )
  }
}

如果我可以对我的记录器说,那就太好了:

Monitor(MyClass.MyMethod)

然后它将监视该方法内部发生的所有事情,包括传入的参数以及传递给这些方法的方法调用和值、发生的异常等。

过去有没有人实现过类似的东西?它甚至可以实施吗?以这种方式登录只是一个白日梦吗?

我很想设计一些可以做到这一点的东西,但我什至不知道从哪里开始。当然,我也不想重新发明轮子,如果已经完成了,如果有人能指出我正确的方向,那就太好了。

如有任何建议,我们将不胜感激......

编辑:我想我会评论一个关于日志中所需详细程度的答案。经常需要提供可配置的日志记录级别,这样如果配置指定了详细的日志记录,那么所有内容都会被记录,而如果配置了关键日志记录,那么只会记录某些信息以及异常。如果配置了致命日志记录,则只会记录导致应用程序死亡的信息。这样的事情是可配置的,还是 AOP 需要 3 或 4 个不同的构建,具体取决于日志记录级别的数量?

我经常使用 4 个级别:致命、关键、信息、详细

【问题讨论】:

  • 这可以在运行时进行配置,但是在运行时运行的记录器仍然会接收所有信息,然后必须决定记录什么。如果您不希望将所有信息的开销传递给记录器,那么唯一的方法就是使用不同的构建。

标签: c# .net asp.net vb.net .net-3.5


【解决方案1】:

您可以使用PostSharp 记录“周围”该方法。这正是 AOP 擅长的事情。您可能想从 Log4PostSharp 开始 - 一个专门用于日志记录的插件。

【讨论】:

  • 过去我在使用 PostSharp 时遇到过问题 - 它与 Team Build 不能很好地融合。它实际上编辑了编译器生成的 IL,并且对在哪里编写编辑后的二进制文件感到困惑。也许现在已经修好了。无论如何都要重写我的 IL,这让我很紧张。
  • 出于兴趣,您对代码合同是否也有同样的保留?
  • 我不知道;它会重写 IL 吗?它是否使用了经过 Microsoft 充分测试的场景?我担心 PostSharp 是唯一一个或少数几段重写 IL 的代码,而 Team Build 等可能不会考虑到这一点。请问“代码合同”的网址?
  • 是的,它重写了 IL(无论如何都会使用该工具)。 research.microsoft.com/contracts
  • @Jon:看了一下,会有很大帮助。我会看到所有 DevDiv 都意识到 IL 重写的可能性,我希望看到开发出最佳实践。然后我会期望 PostSharp 遵守它们,我会很高兴的。
【解决方案2】:

这是面向方面编程的经典示例。请参阅 PostSharp 以获得非常好的基于 CLR 的库。

【讨论】:

    【解决方案3】:

    这是一本教科书(不确定哪本教科书有 AoP,但你明白了)AoP 示例 - 日志记录:你想在方法之前和之后粘贴一些东西。

    您可能想探索 AoP 路线,PostSharp 是最受欢迎的路线之一,还有 Microsoft Unity(也是 IoC)、Castle。

    AoP 的一个简单示例是在方法之前和之后添加代码,而不是在实际方法中添加方法调用。当您使用 C# 标记问题时,您可能只想考虑制作一个扩展方法来记录它,这已经在 this question 中。

    我会采取一种实用的方法:您实际进行了多少日志记录?你能不能只用一个扩展方法而不是让阅读你代码的人眼花缭乱。日志记录built into the .NET framework 已经不错了。

    【讨论】:

    • 好吧,我考虑过使用扩展方法,但客户端想要由配置文件定义的可变级别日志记录,因此取决于配置文件中指定的日志记录级别,取决于详细程度日志会。哪一种排除了这种情况。
    • 这一切都可以在配置文件、信息/警告/错误和类别中定义
    • 无论是 PostSharp 还是其他什么,您都应该查看 Enterprise Library 的日志 API。您可以配置记录的内容、记录的位置和格式。
    • @JS:不必在你的代码中到处输入大量的“记录这个”或“记录那个”代码?
    • 没有。您将不得不使用 PostSharp 或等效工具来生成代码来进行日志记录。我只是建议生成的代码应该调用 Enterprise Library Logging Application Block。
    【解决方案4】:

    除了 Jon 提到的日志记录方法之外,它可能还值得注意 VS 中另一个用于跟踪程序流的有用功能,那就是具有不间断断点的能力,它会在以下情况下简单地输出消息或运行宏hit(注意你也可以打印变量值)

    右击你的断点并选择When Hit...上下文菜单项。

    当然,另一个非常有用的功能是 System.Diagnostics 中的 Trace 对象和 Trace Listners。

    【讨论】:

    • 我确实使用了这个功能,在开发阶段它很棒,但是它不能帮助客户在他们的生产环境中独立调试应用程序,这通常是需要这个场景的地方。
    【解决方案5】:

    我最近编写了一个日志库,它使用 IDisposable 接口来包装带有日志上下文的区域。基本上,您可以像这样使用 LogSite 一次性对象:

    using(var logger = new LogSite("methodName", new object[] { p1, p2, p3 })
    {
        // code that does stuff goes here
    }
    

    LogSite 对象有一堆方便的构造函数重载,例如 MethodBase,因此您可以使用 MethodBase.GetCurrentMethod() 并使用反射来获取方法和参数的实际名称(而不是硬编码的字符串) .

    它的工作方式是这样的——在构造函数中,它将所有跟踪信息写入日志以表明它进入了块。在 Dispose 方法中,它写入一个退出条目。

    在处理时,它还会检查 Marshal.GetExceptionCode() 的非零值,以查看 using 中的代码是否抛出异常或正常退出。它不会给您异常,因此必须在 catch 处理程序中显式记录,但它确实指示该区域的“通过/失败”。这允许您的日志记录范围比方法更具体,因为您可以在一个方法中拥有很多这样的块,并且知道哪个确切地引发了异常。

    此外,由于现在有一个“记录器”对象可用,因此您的 catch 处理程序看起来像:

    try { ... } 
    catch (Exception ex)
    { 
        logger.LogException(ex);
    }
    

    记录器已经知道方法名称、参数信息等等,并且有用于格式化异常信息的内部方法。

    进入这个高级对象下面的架构,有一个“LogDisposition”的概念,它处理我们之前确定的“通过/失败”,还有一个“LogEntryType”的概念,它是一个过滤器(用 Flags 枚举实现) 指示正在传递哪种日志条目(错误、跟踪等)。

    实际进行日志记录的只是发布者/侦听器模式。发布者获取传入的日志条目,就像多播委托一样,保留 LogListener 实例的注册表(应该在程序开始时设置,或根据需要动态添加),并将日志条目传递给这些实例.

    LogListeners 依次过滤掉他们关心的日志条目类型。因此,如果您不希望非错误条件的方法入口和出口点,它们不必显示在日志中.这可以在运行时进行控制,以允许用户随意打开和关闭详细日志记录。由于发布者可以写入各种日志侦听器,因此您可以连接写入文件、写入数据库或在 GUI 中显示错误通知等内容。

    这是一个相当不错的系统,并且需要相对较少的编码来获得相对丰富的日志记录。

    如果您愿意,我可以给您一个代码示例...您可以通过我的(几乎完全不活跃的)博客与我联系(请参阅我的帐户资料)。

    希望对您有所帮助。

    【讨论】:

      【解决方案6】:

      我在所有项目中都使用开源 Apache log4net。这是一个非常简单的实现,带有各种扩展,允许您登录到数据库、zip 文件、滚动日志文件、RRS 提要、telnet 客户端等。日志记录基本上就像:

      'Will print stack trace after message'
      log.err(ex.message,ex)
      
      log.warn("warn")
      log.info("info")
      log.debug("debug")
      

      输出格式和要输出的日志级别等日志记录参数都是在您的应用程序运行时实时读取的。

      【讨论】:

        猜你喜欢
        • 2021-08-21
        • 2017-01-22
        • 1970-01-01
        • 2018-01-28
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多