Reduce the number of stack trace frames when logging an exception in Serilog

74 Views Asked by At

When logging an exception in ASP.NET, the stack trace usually runs as long as your arm. I don't need the complete stack trace when e.g. an entity isn't found in the database. I'd like to limit it - either to just the first frame of the stack trace, or if that isn't possible then just the exception message.

I'm using Serilog with ASP.NET (Core) 8.0. Currently we're logging to a plain text file.

My logger is configured as follows:

builder.Host.UseSerilog((context, services, configuration) => configuration
    .ReadFrom.Configuration(context.Configuration)
    .ReadFrom.Services(services)
    .Enrich.FromLogContext());

And in application.json:

"Serilog": {
  "Using":  [ "Serilog.Sinks.Console", "Serilog.Sinks.File" ],
  "MinimumLevel": { 
    "Default" : "Information",
    "Override": {
      "Microsoft": "Warning",
      "System": "Warning",
      "Microsoft.EntityFrameworkCore.Database.Command": "Fatal"
    } 
  },
  "WriteTo": [
    { "Name": "Console" },
    { "Name": "File", "Args": { "path":  "./logs/log-.txt", "rollingInterval": "Day" } }
  ],
  "Enrich": [ "FromLogContext", "WithMachineName", "WithThreadId" ],
  "Destructure": [
    { "Name": "ToMaximumDepth", "Args": { "maximumDestructuringDepth": 4 } },
    { "Name": "ToMaximumStringLength", "Args": { "maximumStringLength": 100 } },
    { "Name": "ToMaximumCollectionCount", "Args": { "maximumCollectionCount": 10 } }
  ]
},

I was hoping maximumDestructuringDepth would cover it, but that doesn't seem to be the case.

1

There are 1 best solutions below

1
Ivan On

This should works for you, config the serilog in your program.cs

builder.Host.UseSerilog((context, services, configuration) => {
    configuration.ReadFrom.Configuration(context.Configuration)
                 .ReadFrom.Services(services)
                 .Enrich.FromLogContext();
    Log.Logger = new LoggerConfiguration()
        .ReadFrom.Configuration(builder.Configuration)
        .Enrich.FromLogContext()
        .CreateLogger();
});

Create a customize exception formatter

public static class CustomExceptionFormatter
{
    public static string FormatException(Exception exception)
    {
        if (exception == null) return string.Empty;

        var stackTrace = new StackTrace(exception, fNeedFileInfo: true);
        var firstFrame = stackTrace.FrameCount > 0 ? stackTrace.GetFrame(0) : null;

        string formattedMessage = $"{exception.Message}";
        if (firstFrame != null)
        {
            formattedMessage += $" at {firstFrame.GetMethod()} in {firstFrame.GetFileName()}:line {firstFrame.GetFileLineNumber()}";
        }
        return formattedMessage;
    }
}

And the tester

    [HttpGet]
    public ActionResult Get()
    {
        try
        {
            
            throw new InvalidOperationException("Test exception");
        }
        catch (Exception ex)
        {
            var formattedException = CustomExceptionFormatter.FormatException(ex);
            Log.Error(ex, "{FormattedException}", formattedException);
            return Problem(detail: formattedException);
        }
    }