System.Diagnostics.Trace 是否有任何性能下降?

Does System.Diagnostics.Trace have any performance degredation?

我正在寻找一个简单的本机 .NET 错误记录解决方案。我对使用 log4net、Elmah 或任何其他日志记录库不感兴趣。以下是我计划使用的内容。我的问题是我网站的性能是最重要的,这个流程是否会带来任何性能问题?

Global.asax

protected void Application_Start(object sender, EventArgs e)
        {
            GlobalFilters.Filters.Add(new HandleExceptionsAttribute());
        }

处理异常属性:

public sealed class HandleExceptionsAttribute : HandleErrorAttribute
    {
        private void TraceError(Exception _this)
        {
            Trace.TraceError("{0} Exception {1}", DateTime.Now.ToString("M/d/yyyy h:mmtt"), _this);
        }

        public override void OnException(ExceptionContext filterContext)
        {
            var ex = filterContext.Exception;
            TraceError(ex);
            Task.Run(() =>
            {
                using (var msg = new MailMessage(ConfigurationManager.AppSettings["SendErrorsFrom"], ConfigurationManager.AppSettings["SendErrorsTo"]))
                {
                    msg.Subject = ex.Message;
                    msg.Body = ex.StackTrace;
                    using (var smtp = new SmtpClient())
                        smtp.Send(msg);
                }
            });
        }
    }

web.config:

  <system.diagnostics>
    <trace autoflush="true" indentsize="4">
      <listeners>
        <add name="textWriterListener" type="System.Diagnostics.TextWriterTraceListener" initializeData="logs\log.txt"/>
        <add name="eventLogListener" type="System.Diagnostics.EventLogTraceListener" initializeData="Base Test" />
        <remove name="Default"/>
      </listeners>
    </trace>
  </system.diagnostics>

视情况而定。

TraceError 使用条件编译器属性,这意味着如果您在编译时将 TRACE 标志关闭 (如在 RELEASE 模式下),那么所有对 TraceError 的调用将从您的编译代码中删除。从表面上看,这将消除实时代码的大多数性能问题,因为性能如此重要,您肯定会在 RELEASE 模式下编译。

但是,在对 TraceError 的调用中未实际找到的任何补充代码仍将 运行。所以在你的情况下,对 HandleExceptionsAttribute.TraceError() 的调用仍然会被执行——它只会立即 return 而不做任何事情。

// This code sticks around, and could still have performance implications.
var additionalInfo = GoGetMoreInformation(); 
// This code disappears when compiled
Trace.TraceError("Something went wrong: {0}", additionalInfo); 

当然,另一个缺点是您无法从实时环境中的痕迹中获取任何信息,而在这些情况下它们可能会非常有用。在您的情况下,这意味着在您的实时系统中抛出的任何异常对您来说都是完全不可见的。如果用户不抱怨结果,您就不会发现有任何问题。

更正

显然我上面所说的 Debug 是正确的,但是 Visual Studio 默认情况下会在发布模式构建期间保持 TRACE 标志处于活动状态。

每次异常都收到一封电子邮件?祝你好运。

目前的代码将无法正常工作。这不是 Trace.TraceError 本身,而是你想做什么。我有时会看到错误循环会在几分钟内产生数百万个异常。您正在努力进行自我造成的拒绝服务攻击。让我们做一些数学运算。每分钟 100 万个异常,文本 blob 为 ca。 1500 字节的网络导致 1.5GB/分钟的邮件数据。这会吃大约。 25 MBit 的网络带宽。这是一个保守的估计。但如果发生这种情况,您将很快 运行 内存不足。异步任务方法将任务排队。由于发送电子邮件的速度比产生异常的速度慢,因此您很快就会收到 OutOfMemoryException,您也将尝试通过邮件发送该异常……至少在拒绝服务情况持续存在之前,您将很快获得免费重启。

更好的方法是编写一个自定义 MailTraceListener,它将聚合异常并限制每封邮件的发送异常率,例如至多 1/分钟。然后您可以添加一个 RollingFileTraceListener(不是 .NET 的一部分。存在外部日志记录库是有原因的),它将记录到一个平面文件,您可以通过邮件发送汇总摘要。 通过异常消息检测重复异常以仅记录摘要也是有意义的。例如。第一个总是记录下来,然后你再次检查最后一个异常是否与新异常相同。如果是,增加一个计数器并继续这样做,直到出现另一个计数器。然后你可以写一个很好的总结,刚刚发生了 100 亿个异常,你记录了 none 个异常,除了第一个。

异常本身很慢。如果您跟踪或不跟踪它们,则没有任何作用。您可以优化掉 Trace 调用,但这是您最不关心的问题。堆栈展开是异常处理中最昂贵的部分。跟踪的主要性能影响来自您配置的输出设备。如果你分析它,你会很快发现它。

正如我在上面概述的那样,一个好的错误处理策略并不容易实施,因为您要处理几乎任何事情都可能发生的异常情况。包括您对异常的处理成为问题的一部分。您需要彻底测试它,否则您会发现如果没有错误发生,它可以完美运行,但由于某些奇怪的原因,当出现某些特殊情况异常时它会崩溃。

对于 Web 应用程序,没有理由自行推出——该框架能够使用 ASP.NET 健康监控仅通过配置通知您任何未处理的异常。请参阅 https://msdn.microsoft.com/en-us/library/Bb398933.aspx 以获得良好的开端。

另外请记住,您获得的代码会很丑陋。当电子邮件服务器配置错误并在您的异常处理程序中抛出异常时会发生什么。在不影响性能的情况下崩溃时记录错误是一个非常重要的问题,强烈建议使用经过良好测试的库而不是尝试在此处推出自己的解决方案。