【问题标题】:What is the most performant way to log the name of class and method?记录类和方法名称的最高效方式是什么?
【发布时间】:2009-08-28 10:39:03
【问题描述】:

我基本上想编写一个客户记录器/跟踪器,它还记录调用记录器/跟踪器的方法的类和方法名称。这应该很快,这样它就不会影响应用程序的性能,强类型和干净。有没有人有好的想法?

以下是我拥有的几个,但我担心由于反射而导致的性能:

  • StackTrace/StackFrame(开销太大?)

  • MethodInfo.GetCurrentMethod()(还是太慢了?而且不是很干净)

  • 将方法作为委托传递(C# 可以隐式地做到这一点,我可以访问 MethodInfo,但不是很干净)

我感谢任何人的 cmets。

更新:

我喜欢 StackFrame 的整洁,所以我问了一个更具体的问题 here 关于 StackFrame 的性能,我得到了一些非常好的回应,包括性能测试。事实证明,MethodInfo.GetCurrentMethod() 是最快的(在我的计算机上大约需要 1 微秒),new StackFrame(1) 可以按需请求(在我的计算机上大约需要 15-20 微秒)。我把方法作为委托选项扔掉了,因为当方法有多个参数时它太乱了。

结论:

我已经查看了所有选项,最好的方法是为 PostSharp 编写一个自定义插件,该插件在编译期间将方法名称作为字符串注入 MSIL 中,并在其中应用 [Trace] 之类的自定义属性。在我看来,这是最快和最可维护的选择。这甚至可以实现更多的事情,比如在没有任何反射的情况下传递参数名称和参数,以及自动捕获和记录异常。此my related question 了解更多信息。

【问题讨论】:

  • 请问,你为什么不想使用log4net?
  • 我不喜欢太依赖其他组件。而且在 log4net 的情况下,它也太复杂了。
  • Hermann,这在我们的行业中通常被称为“非此处发明综合症”,最好避免。这是一个维基百科链接,其中包含有关这种痛苦的更多信息:en.wikipedia.org/wiki/Not_Invented_Here
  • Joel Spolsky 的公司还发明了他们自己的编程语言。我不确定这是一个很好的例子。你真的应该考虑使用书面库。日志记录是一个已解决的问题,我认为重新发明轮子可能不是您时间的最佳利用方式。只是我的 2 美分...花它的价值。
  • 另外,您会注意到他的论点是,如果这是您业务的核心,您应该这样做。如果您的企业正在进行日志记录(例如,如果您的公司名称是“We Log Good, Inc.”),我会说去吧。

标签: c# .net reflection logging


【解决方案1】:

最快的方法是在编译时这样做,这是真的,但在 .NET 中最方便的方法是通过使用 .NET 中包含的CallerMemberNameCallerFilePath 和其他编译器服务属性4.5:

public void TraceMethodCall([CallerMemberName]string methodName)
{
}

【讨论】:

    【解决方案2】:

    简单地说,最快的方法是在编译时静态地做:

    public void DoSomething()
    {
        TraceCall("DoSomething");
    } 
    
    public void TraceCall(string methodName)
    {
        if (!tracingEnabled) { return; }
        // Log the call
    }
    

    那么这个问题与你之前的问题基本相同;以可维护和准确的方式做到这一点的最佳方法是什么?我们之前讨论的运行时选项对性能造成了相对严重的影响,但代价是它们很容易实现。

    正如在之前的帖子中提到的,这开始倾向于面向方面的编程; PostSharp 就是这样一个库,它可以帮助找到静态编译性能与简单维护之间的平衡。

    【讨论】:

    • 你是对的。我希望用 PostSharp 实现这一点不需要太多努力。我想这取决于每个人的需求,但我不会将 20 微秒称为严重的性能损失。
    • @Hermann:除非您编写的东西必须处理 Google 的吞吐量,否则 20 毫秒完全可以忽略不计。我们在另一个问题中所做的测试表明,如果您需要为 100,000 个方法启动日志记录调用,它将添加 - 最多 - 1.5 秒。尽管有些方法比其他方法快得多,但最终所有方法都是边缘化的。
    【解决方案3】:

    当您已经在方法中(或者如果您正在拦截该方法),我认为您不会比 MethodInfo.GetCurrentMethod 做得更好 - 这不是太快 - 如果你想保持输入 -安全。

    既然您想为日志记录执行此操作,您是否考虑过在异步操作中执行此操作,以免拖慢主线程?

    根据您的应用程序上已有的负载,它可能有帮助,也可能没有帮助。

    【讨论】:

    • 是的,我也在考虑这个问题。但是当应用程序繁忙时会发生什么?这不会推迟日志记录,当应用程序崩溃时它不会丢失吗?
    • 取决于你如何实现异步操作。如果你只是把它扔到 ThreadPool 上,是的,你是对的,并且不能保证排序。但是,您也可以将其写入 MSMQ,它可以为您提供有序、持久的日志记录。
    • 异步选项只会减轻对日志的实际写入(这仍然是有益的)。实际的“GetCurrentMethod”调用仍会对主线程造成性能损失。
    • @Yooder:是的,这就是我所说的“取决于您的应用程序上已经存在的负载”的意思。 IIRC,MethodInfo.GetCurrentMethod 主要使用 CPU,因此如果整体瓶颈在其他地方(例如,磁盘 I/O),那么它会有所帮助。如果 CPU 是瓶颈,它不会 - 但我想不出任何会......
    【解决方案4】:

    执行此操作的最佳方法是在编译时:C++ 传统上在宏中使用 ____FILE____ 和 ____LINE____ 预处理器符号来执行此操作。不幸的是,C# 编译器目前不提供宏或与 ____FILE____ 和 ____LINE____ 的等效项。

    这实际上意味着您唯一的选择是在运行时记录此信息,这意味着要么繁琐地向要记录的每个方法添加手动跟踪,要么按照您的描述使用反射。

    【讨论】:

      【解决方案5】:

      我认为Log4PostSharp 会做得很好。

      【讨论】:

      • 我不想使用其他记录器,尤其不是基于 log4net 的记录器,但我_真的)喜欢注入 MSIL 代码的想法。如果我能以某种方式使用它,我将不得不研究。
      • 阅读 Stu 的回答——大约需要使用 PostSharp(或 TYPEMETHOD)开发等价的 FILELINE 需要一小时。好吧,对我来说是 1 小时。
      • 我会看看 PostSharp。谢谢盖尔!
      • 如果配置正确,它不会那么慢。但是如果你有大量的小项目,你可能会受到影响。 PostSharp 更适合大型项目(因为每个项目的开销很大)。请务必使用通过安装程序安装的 1.5 RTM。
      • 查看所有选项后,我将使用 PostSharp 的自定义插件。谢谢大家!
      【解决方案6】:

      如果您关心速度,则应使用Event Tracing for Windows 创建实际日志消息。 ETW 是一个高速日志库,实现为可从内核模式和用户模式访问的驱动程序。跟踪可以动态打开和关闭。日志记录给应用程序增加了很少的开销。我已经使用 ETW 在高吞吐量服务器应用程序中实现跟踪。查看NTrace

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2013-06-18
        • 2013-03-09
        • 2015-04-04
        • 2012-03-26
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2014-04-13
        相关资源
        最近更新 更多