How to get nicely formatted file logs with Serilog outputTemplate (fixed width and truncation of SourceContext)

Viewed 704

I'm switching from log4net to Serilog but miss some of the formatting possibilities I had in log4net. I don't find any documentation for what formatters I can use in the outputTemplate. Is there a way to accomplish what I describe below?

Using the outputTemplate

"outputTemplate": "[{Timestamp:yyyy-MM-dd HH:mm:ss.fff} {Level:u3}] ({SourceContext}) {Message:lj}{NewLine}{Exception}"

It looks like this

[2020-03-30 11:31:06.464 DBG] (DomainLogic.TCMessageHandler) >>>Poll
[2020-03-30 11:31:06.481 DBG] (AmqpReader.Reader)    >>>Read
[2020-03-30 11:31:06.485 INF] (AmqpReader.Reader)       Fetched a message from the queue.
[2020-03-30 11:31:06.487 DBG] (AmqpReader.Reader)    <<<Read - 00:00:00.0066941
[2020-03-30 11:31:06.504 DBG] (DomainLogic.TCMessageHandler) <<<Poll - 00:00:00.0399191

This is what I want

[2020-03-30 11:31:06.464 DBG] (DomainLogic.TCMessageHandler) >>>Poll
[2020-03-30 11:31:06.481 DBG] (AmqpReader.Reader           )    >>>Read
[2020-03-30 11:31:06.485 INF] (AmqpReader.Reader           )       Fetched a message from the queue.
[2020-03-30 11:31:06.487 DBG] (AmqpReader.Reader           )    <<<Read - 00:00:00.0066941
[2020-03-30 11:31:06.504 DBG] (DomainLogic.TCMessageHandler) <<<Poll - 00:00:00.0399191

And if I set a fixed width and the SourceContext is longer than that, I'd like it to truncate from the left. Like this

[2020-03-30 11:31:06.464 DBG] (ogic.TCMessageHandler) >>>Poll
[2020-03-30 11:31:06.481 DBG] (AmqpReader.Reader    )    >>>Read
[2020-03-30 11:31:06.485 INF] (AmqpReader.Reader    )       Fetched a message from the queue.
[2020-03-30 11:31:06.487 DBG] (AmqpReader.Reader    )    <<<Read - 00:00:00.0066941
[2020-03-30 11:31:06.504 DBG] (ogic.TCMessageHandler) <<<Poll - 00:00:00.0399191
2 Answers

Serilog output templates (and message templates) are based on .NET format strings, and these don't support truncation of substituted values. There's no way to do this directly in the output template, though you could write an ILogEventEnricher that substitutes the property for a truncated version of it, depending on how else you plan to consume the events.

Here's an enricher that does just what I asked for.

public class StaticWidthSourceContextEnricher : ILogEventEnricher
{
    private readonly int _width;

    public StaticWidthSourceContextEnricher(int width)
    {
        _width = width;
    }

    public void Enrich(LogEvent logEvent, ILogEventPropertyFactory propertyFactory)
    {
        var typeName = logEvent.Properties.GetValueOrDefault("SourceContext").ToString();

        if (typeName.Length > _width)
        {
            typeName = typeName.Substring(typeName.Length - _width);
        }
        else if (typeName.Length < _width)
        {
            typeName = typeName + new string(' ', _width - typeName.Length);
        }

        logEvent.AddOrUpdateProperty(propertyFactory.CreateProperty("SourceContext", typeName));
    }
}
Related