Search code examples
asp.net-core.net-coreserilogserilog-sinks-consoleserilog-enricher

Serilog console sink that logs exception message without stacktrace


Some of Serilog's standard template properties can be customised, e.g. {Timestamp:HH:mm:ss}, {Level:u3} and {Message:lj}. Not so for {Exception}, which prints both message and stack trace, on multiple lines.

For the human-readable console sink, a stack trace adds significant noise and friction, especially when tailing a remote docker container which is already ugly (no colour, for example). If I need the stack trace, I would consult a durable sink, e.g. file, Seq.

How can I log just the exception message to the console sink?

(I'm aware of Serilog.Expressions, but for something so simple I hope there's a solution without another dependency.)


Solution

  • As per a comment on the repo, here's a solution using a custom enricher.

    Create the enricher ExceptionMessageEnricher.cs:

    using Serilog.Core;
    using Serilog.Events;
    
    namespace Serilog.Enrichers;
    
    public sealed class ExceptionMessageEnricher : ILogEventEnricher
    {
      public const string PROPERTY_NAME = "ExceptionMessage";
    
      public void Enrich(LogEvent logEvent, ILogEventPropertyFactory propertyFactory)
      {
        ArgumentNullException.ThrowIfNull(logEvent, nameof(logEvent));
        ArgumentNullException.ThrowIfNull(propertyFactory, nameof(propertyFactory));
    
        if (logEvent.Exception == null) return;
    
        var property = propertyFactory.CreateProperty(PROPERTY_NAME, $"\"{logEvent.Exception.Message}\"");
        logEvent.AddPropertyIfAbsent(property);
      }
    
    }
    

    Then use it in the Serilog config:

    var outputTemplate = "[{Timestamp:HH:mm:ss} {Level:u3}] {Message:lj} {ExceptionMessage}{NewLine}";
    
    services.AddSerilog((services, config) => config
      // ...
      .WriteTo.Logger(x => x
        .Enrich.With<ExceptionMessageEnricher>()
        .WriteTo.Console(outputTemplate:outputTemplate)
      )
    );
    

    A rendered message would look something like this:

    [13:04:44 ERR] Something bad happened "The exception message"
    

    Notes:

    • The custom enricher adds an ExceptionMessage property, which is then referenced in the log template
    • The ExceptionMessage property is not needed in durable sinks (e.g. file, Seq) as they receive the full exception anyway; thus the console sink and custom enricher are defined in a sub-logger - I'm unaware whether there are perf considerations, but I suspect it should be fine
    • If needed, the console sink can be wrapped in an async sink

    An alternative approach, using Serilog.Expressions: {Inspect(@x).Message}.