gimelfarb / ProductionStackTrace

Without deploying PDBs, generate a .NET exception stack trace that can be processed to retrieve source file and line number info
Other
79 stars 8 forks source link

ProductionStackTrace

Don't want to deploy PDBs, but still get exception stack traces that map to original source line numbers? Want to have your cake and eat it too?

ProductionStackTrace is intended to run in Production, without any access to PDB symbol files. It produces a stack trace with enough information to be analyzed by the techies back on the base with a help of a Symbol Store to get original source code line numbers.

Usage

  1. Install the ProductionStackTrace NuGet package

    PM> Install-Package ProductionStackTrace
  2. In your code, where you log exceptions:

    
    using ProductionStackTrace;
    ...
    try
    {
        ...
    }
    catch (Exception ex)
    {
        var trace = ExceptionReporting.GetExceptionReport(ex);
        Console.WriteLine(trace);
    }
    

This will produce a stack trace similar to this, which is still very much similar to standard one, but with some extra info embedded:

System.Exception: Test exception
   at ProductionStackTrace.Test!0x0600000f!ProductionStackTrace.Test.TestExceptionReporting.TestSimpleException() +0xc
==========
MODULE: ProductionStackTrace.Test => ProductionStackTrace.Test, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null; G:4e6f400982514fc29d72d9928819aac0; A:6

Differences with original stack trace (i.e. ex.ToString()):

Symbol Store

Make sure you're using a Symbol Store and have a build that publishes your PDB symbols there. If you need help setting it up, see here (also has more in-depth info about PDB symbols).

The simplest way to do this is to setup a shared network location, and use TFS Build server, which has integrated symbol indexing & publishing via a simple property setting ("Path to Publish Symbols").

TFS Build Symbols Options

When analyzing the retrieved logs, it helps to have the Symbol Server path configured (in Visual Studio go to Tools > Options > Debugger > Symbols). That way the analyzer can automatically find the right symbols file, and generate line mappings.

Visual Studio Debugging Symbols Paths

Analyzing

Analyzing these stack traces is simple with an associated analyzer application.

  1. Install the ProductionStackTrace Analyze Tool NuGet package - it's a solution-level tools package:

    PM> Install-Package ProductionStackTrace.Analyze.Console
  2. This will add a PowerShell command to the Package Manager Console - to launch it:

    PM> Convert-ProductionStackTrace

Copy-paste the stack trace into the window, to get the converted stack trace with original source line mappings:

System.Exception: Test exception
   at ProductionStackTrace.Test.TestExceptionReporting.TestSimpleException() in ..\ProductionStackTrace.Test\TestExceptionReporting.cs:line 23

You can also convert the entire log file:

PM> Convert-ProductionStackTrace [logfile] [outfile]

Embedding

If you want to embed analyzing into your own automated process, you can just get the binary files under tools folder in the extracted package folder.

Alternatively, there is a ProductionStackTrace Analyze NuGet package, which contain the library which performs the conversion.

Use with a logging framework

If you are logging exceptions through a logging framework, such as log4net, chances are that framework by default renders Exceptions by using the built-in .ToString() method which produces the default stack trace.

To override it, you have several choices:

The 3rd choice is obviously better, if your logging framework supports it. Below is an example of how to do it with the widely popular log4net.


[assembly: log4net.Config.Plugin(
    typeof(ProductionStackTraceLog4NetPlugin))]

internal class ProductionStackTraceLog4NetPlugin : 
    log4net.Plugin.PluginSkeleton,
    log4net.ObjectRenderer.IObjectRenderer
{
    public ProductionStackTraceLog4NetPlugin() 
        : base("ProductionStackTrace") {}

    public override void Attach(
        log4net.Repository.ILoggerRepository repository)
    {
        base.Attach(repository);
        repository.RendererMap.Put(typeof(Exception), this);
    }

    public void RenderObject(
        log4net.ObjectRenderer.RendererMap rendererMap, 
        object obj, 
        System.IO.TextWriter writer)
    {
        writer.Write(ProductionStackTrace.ExceptionReporting
            .GetExceptionReport((Exception) obj));
    }    
}

This registers a plugin with log4net which in turn adds an IObjectRenderer for the Exception type, which overrides how Exceptions look in the output.

Troubleshooting

  1. Original source line information is not showing when analyzing the logs

    In order for source line info to show, analyzer must be able to locate the matching PDB symbol files (i.e. matching the GUID & Age parameters of the specific assembly). The best way to ensure that is to incorporate publishing to Symbol Store in your build process - this makes sure that any built assembly has its symbols stored in a discoverable location.

    If you start analyzer in interactive mode (e.g. Convert-ProductionStackTrace), it will print the current symbols paths at the start. If your symbols server is not showing, ensure that it is specified in the Visual Studio Debugger Symbol Paths (Tools > Options > Debugger > Symbols).

  2. Stack traces look the same and don't include the extra information

    Ensure that you are using ExceptionReporting.GetExceptionReport(ex) to produce the stack trace. If you are using a logging framework, such as log4net, then you might want to customize how the logging framework renders exception stack traces. See above section on how to set that up with log4net specifically.

Additional help

If you have any problems with using the library, you can create an issue on GitHub (or see if someone else already raised the same one). If it's just a quick question, you can send me a Tweet.

Finally, if you are using it and liking it - share it around. Also send me a Tweet @LevGimelfarb, I'd love to know it was useful!