serilogserilog-aspnetcoreserilog-exceptionsserilog-expressions

How to have Single Line Exception in Serilog Expressions Format?


I am trying to make a customized json log output using Serilog Expressions Format. I am using below template : "template": "{{\"Time\": \"{@t:yyyy-MM-ddTHH:mm:ss.fffffffzzz}\", \"Level\": \"{@l}\", \"Event\": \"{@m}\" {#if TraceId is not null}(, \"TraceId\": \"{@tr}\" ){#end} \"context\": {@p} , \"exception\":\"{@x}\"}}\n"

In this template every runs fine except when I have an exception logged, In that case due to multiline exceptions it is breaking the json. I have fixed it with an enricher

using Serilog.Core;
using Serilog.Events;
using System;

namespace CatalogService.Common.Enricher
{
    public class CustomLogEnricher : ILogEventEnricher
    {
        public void Enrich(LogEvent logEvent, ILogEventPropertyFactory propertyFactory)
        {
            //Enrich Exception (single line)
            if (logEvent?.Exception != null)
            {
                string? formattedException =  logEvent.Exception.ToString().Replace(Environment.NewLine, "\\n");

                logEvent.RemovePropertyIfPresent("Exception");
                logEvent.AddPropertyIfAbsent(propertyFactory.CreateProperty("Exception", formattedException));
            }
        }
    }

}

Now this enricher pushes the exception in log @p, due to this either exception are repeated in both @p and @p['Exception'] Since I want this exception to be in root of the json. Please help I need a simple log in following format:

{
"time": Timestamp,
"level": Level,
"event": Message(Rendered),
"exception": exception if any,
"context": @p (Rest of the properties)
}

I am expecting the json in following format:

{
"time": Timestamp,
"level": Level,
"event": Message(Rendered),
"exception": exception if any,
"context": @p (Rest of the properties)
}

I have tried using JsonFormat, CompactJsonFormat, Rendered Json Format but they don't offer customization. I have tried above Expression approach along with Enricher, which mostly works except for the exception appearing twice once in @p and then in @p['Exception']


Solution

  • Serilog.Expressions has native JSON support that handles newlines correctly, so you shouldn't need to do anything special to the exception.

    { {
    time: @t,
    level: @l,
    event: @m,
    exception: @x,
    context: @p
    } }
    

    Assuming you remove your enricher in this case.

    However, if you do still need to do some kind of exception processing, you can keep the enricher but remove Exception from the properties using:

    { {
    time: @t,
    level: @l,
    event: @m,
    exception: Exception,
    context: {..@p, Exception: undefined()}
    } }
    

    More details of this Serilog.Expressions recipe.