Does System.Diagnostics.Trace have any performance degredation?
Asked Answered
A

3

7

I am looking for a simple native .NET error logging solution. I am not interested in using log4net, Elmah or any other logging library. Below is what I am planning on using. My question is performance on my site is paramount and does this flow introduce any performance concerns?

Global.asax

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

Handle Exceptions Attribute:

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>
Ali answered 28/8, 2015 at 20:14 Comment(0)
K
4

It depends.

TraceError uses a conditional compiler attribute, which means that if you compile with the TRACE flag turned off (as in RELEASE mode), then all calls to TraceError will be removed from your compiled code. Ostensibly this would remove most performance concerns for live code, because with performance being so paramount, you are certainly going to compile in RELEASE mode.

However, any supplemental code that's not actually found inside the call to TraceError will still run. So in your case, the call to HandleExceptionsAttribute.TraceError() will still be executed--it will just return immediately without doing anything.

// 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); 

And, of course, the other down-side is that you're not going to get any information from your traces in live environments, where they may prove to be pretty useful. In your case, that means that any exceptions thrown in your live system will be completely invisible to you. You'll have no indication that anything is wrong if users don't complain about the results.

Correction

Apparently what I said above is true about Debug, but Visual Studio will leave the TRACE flag active during Release mode builds by default.

Knp answered 28/8, 2015 at 20:41 Comment(9)
Thanks for the concise answer, seems logical. Is there another way of doing this without a third party library?Ali
What I want to accomplish is getting emailed on the error as well as logging it to a file somewhere without hurting performance.Ali
@Cyberdrew: There are lots of ways. Most involve writing your own logging library. The third-party libraries have already worked out a lot of the hairy issues around doing it right. If your application is more than a small hobby site, I'd recommend using a third-party library. They can be easily configured to send information to a log file, database, and/or email, with custom formatting and thresholds for each destination. They can do it asynchronously, and they can do it with error handling so you don't drop your server with an unhandled exception on a background thread.Knp
You should be using a proper error handling module try Elmah. It has simple integration into MVC with database, file, email logging and so on.Horrendous
Thanks for the answer and the feedback, I will look into it.Ali
Just a FYI, by default VS leaves the TRACE constant on in Release modes, not off.Jaehne
@ScottChamberlain: Really? I assumed it turned off both DEBUG and TRACE. Can you point me to an article that describes which flags visual studio enables by default in the different modes?Knp
I don't have a article, all I have is visual studio itself. This screenshot was taken by creating a new console project in VS 2015 using .NET 4.5.2 as the framework and changing no settings other than changing the configuration dropdown from "Active (Debug)" to "Release".Jaehne
@ScottChamberlain: That's what I was looking for. Thanks for clarifying that.Knp
F
6

Getting an EMail for every exception? I wish you good luck with that.

The code as it is will not work well. It is not about Trace.TraceError itself but what you want to do. I have seen sometimes error loops which produce millions of exceptions within minutes. You are working towards a self inflicted denial of service attack. Lets do some math. 1 million exceptions per minute with a text blob of ca. 1500 bytes over the network leads to 1,5GB/min of mail data. This will eat ca. 25 MBit of your network bandwidth. And this is a conservative estimate. But if that happens you will run out of memory very quickly. The async task approach will queue the tasks. Since you will send emails slower than you can produce exceptions you will quite soon get an OutOfMemoryException which you will also try to send via mail .... At least you will get a free restart quite quickly before the denial of service situation will persist.

A better way would be to write a custom MailTraceListener which will aggregate the exceptions and throttle the rate of sent exceptions per mail e.g. to at most 1/minute. Then you can add a RollingFileTraceListener (not part of .NET. There is a reason why external logging libraries exist) which will log to a flat file and you can send an aggregated summary via mail. It also makes sense to detect duplicate exceptions by their exception message to log only a summary. E.g. the first one is logged always and then you check againts the last exception is identical with the new one. If yes increment a counter and keep doing that until a different one shows up. Then you can write a nice summary that 10 billion exceptions did just occur and you did log none of them except the first one.

Exceptions in itself are slow. If you trace them or not plays no role. You can optimize away the Trace call but this is your least concern. Stack unwinding is the most expensive part of exception handling. The main performance hit of tracing comes your configured output device. You will find that out quite quickly if you profile it.

As I have outlined above a good error handling strategy is not easy to implement since you are dealing with exceptional cases where pretty much anything can happen. Including that your handling of exceptions becomes part of the problem. You need to test that thoroughly or you will find that it works perfectly if no error happens but for some strange reason it crashes when some corner case exception occurs.

Fimble answered 28/8, 2015 at 21:5 Comment(0)
K
4

It depends.

TraceError uses a conditional compiler attribute, which means that if you compile with the TRACE flag turned off (as in RELEASE mode), then all calls to TraceError will be removed from your compiled code. Ostensibly this would remove most performance concerns for live code, because with performance being so paramount, you are certainly going to compile in RELEASE mode.

However, any supplemental code that's not actually found inside the call to TraceError will still run. So in your case, the call to HandleExceptionsAttribute.TraceError() will still be executed--it will just return immediately without doing anything.

// 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); 

And, of course, the other down-side is that you're not going to get any information from your traces in live environments, where they may prove to be pretty useful. In your case, that means that any exceptions thrown in your live system will be completely invisible to you. You'll have no indication that anything is wrong if users don't complain about the results.

Correction

Apparently what I said above is true about Debug, but Visual Studio will leave the TRACE flag active during Release mode builds by default.

Knp answered 28/8, 2015 at 20:41 Comment(9)
Thanks for the concise answer, seems logical. Is there another way of doing this without a third party library?Ali
What I want to accomplish is getting emailed on the error as well as logging it to a file somewhere without hurting performance.Ali
@Cyberdrew: There are lots of ways. Most involve writing your own logging library. The third-party libraries have already worked out a lot of the hairy issues around doing it right. If your application is more than a small hobby site, I'd recommend using a third-party library. They can be easily configured to send information to a log file, database, and/or email, with custom formatting and thresholds for each destination. They can do it asynchronously, and they can do it with error handling so you don't drop your server with an unhandled exception on a background thread.Knp
You should be using a proper error handling module try Elmah. It has simple integration into MVC with database, file, email logging and so on.Horrendous
Thanks for the answer and the feedback, I will look into it.Ali
Just a FYI, by default VS leaves the TRACE constant on in Release modes, not off.Jaehne
@ScottChamberlain: Really? I assumed it turned off both DEBUG and TRACE. Can you point me to an article that describes which flags visual studio enables by default in the different modes?Knp
I don't have a article, all I have is visual studio itself. This screenshot was taken by creating a new console project in VS 2015 using .NET 4.5.2 as the framework and changing no settings other than changing the configuration dropdown from "Active (Debug)" to "Release".Jaehne
@ScottChamberlain: That's what I was looking for. Thanks for clarifying that.Knp
B
0

For web apps there is no reason to roll your own -- the framework has the ability to notify you of any unhanded exception through configuration alone using ASP.NET health monitoring. See https://msdn.microsoft.com/en-us/library/Bb398933.aspx for a good start.

Also keep in mind the code you've got is going to be ugly. What happens when the email server is misconfigured throwing exceptions inside your exception handlers. Logging errors when things are crashing without effecting performance is a non-trivial problem, it is highly advisable to use a well tested library rather than trying to roll your own solution here.

Burglar answered 28/8, 2015 at 21:46 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.