Serilog EventId property type variance between Serilog.Context.LogContext and Microsoft.Extensions.Logging

Viewed 38

I am trying to use the EventId property in the output template for a console sink. When I add the event id using Serilog.Context.LogContext.PushProperty, it appears in the context as a Serilog.Events.ScalarValue. When I add the event id using the Microsoft.Extensions.Logging.ILogger.LogInformation overload that accepts an event id as the first parameter, it appears in the context as a Serilog.Events.StructureValue.

The following MRE produces the behavior:

using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Serilog;
using Serilog.Context;

internal class Program
{
    private readonly Microsoft.Extensions.Logging.ILogger logger;

    public Program(ILogger<Program> logger) => this.logger = logger;

    static void Main()
    {
        Log.Logger = new LoggerConfiguration()
            .MinimumLevel.Verbose()
            .Enrich.FromLogContext()
            .WriteTo.Console(
                outputTemplate: "[{Timestamp:yyyy-MM-dd HH:mm:ss.ffff}] [{Level}] [{EventId}] {Message:l}{NewLine}{Exception}")
            .WriteTo.EventLog(
                nameof(SerilogRepro),
                manageEventSource: true)
            .CreateLogger();

        var servicesProvider = new ServiceCollection()
            .AddSingleton<Program>()
            .AddLogging(logging => logging.AddSerilog())
            .BuildServiceProvider();

        var program = servicesProvider.GetRequiredService<Program>();
        program.Run();

        Log.CloseAndFlush();
    }

    public void Run()
    {
        using (LogContext.PushProperty("EventId", 42))
        {
            Log.Information("EventId added using Serilog.Context.LogContext.");
        }

        this.logger.LogInformation(52, "EventId added using Microsoft.Extensions.Logging.");
    }
}

The resulting console output is as follows:

[2022-08-21 16:19:38.8027] [Information] [42] EventId added using Serilog.Context.LogContext.
[2022-08-21 16:19:38.9256] [Information] [{ Id: 52 }] EventId added using Microsoft.Extensions.Logging.

Since the EventId is a structure and not a scalar value when using Microsoft.Extensions.Logging, it is rendered incorrectly.

In my actual configuration, I use a custom event id provider with the event log sink that distinguishes the two types and applies the correct value. However, the console sink obviously does not facilitate this.

I need to be able to support calls to both libraries, what is the optimal approach for this case?

1 Answers

In the absence of a better solution, I have addressed the issue using an expression template and the Serilog.Expressions package.

const string consoleTemplate =
    "[{@t:yyyy-MM-dd HH:mm:ss.fff}] " +
    "[{@l}] " +
    "[{Coalesce(EventId.Id, EventId)}] " +
    "{@m:l}\n" +
    "{@x}";

Log.Logger = new LoggerConfiguration()
    .MinimumLevel.Verbose()
    .Enrich.FromLogContext()
    .WriteTo.Console(
        new ExpressionTemplate(consoleTemplate))
    .WriteTo.EventLog(
        nameof(SerilogRepro),
        manageEventSource: true)
    .CreateLogger();
Related