Skip to content

Formatting Output

Nicholas Blumhardt edited this page Mar 5, 2024 · 16 revisions

Serilog provides several output formatting mechanisms.

Formatting plain text

Sinks that write plain text output, such as the console and file-based sinks, generally accept output templates to control how log event data is formatted.

The format of events written by these sinks can be modified using the outputTemplate configuration parameter. For example, to control the console sink:

Log.Logger = new LoggerConfiguration()
    .WriteTo.Console(outputTemplate:
        "[{Timestamp:HH:mm:ss} {Level:u3}] {Message:lj}{NewLine}{Exception}")
    .CreateLogger();

A number of built-in properties can appear in output templates:

  • Exception - The full exception message and stack trace, formatted across multiple lines. Empty if no exception is associated with the event.
  • Level - The log event level, formatted as the full level name. For more compact level names, use a format such as {Level:u3} or {Level:w3} for three-character upper- or lowercase level names, respectively.
  • Message - The log event's message, rendered as plain text. The :l format specifier switches off quoting of strings, and :j uses JSON-style rendering for any embedded structured data.
  • NewLine - A property with the value of System.Environment.NewLine.
  • Properties - All event property values that don't appear elsewhere in the output. Use the :j format to use JSON rendering.
  • Timestamp - The event's timestamp, as a DateTimeOffset.
  • TraceId - The id of the trace that was active when the event was created, if any.
  • SpanId - The id of the span that was active when the event was created, if any.

Properties from events, including those attached using enrichers, can also appear in the output template.

Formatting JSON

Many sinks record log events as JSON, or can be configured to do so. To emit JSON, rather than plain text, a formatter can be specified. This example configures the file sink using the formatter from Serilog.Formatting.Compact.

Log.Logger = new LoggerConfiguration()
    .WriteTo.File(new CompactJsonFormatter(), "log.txt")
    .CreateLogger();

There are three JSON formatters provided by the Serilog project:

  • Serilog.Formatting.Json.JsonFormatter - This is the historical default shipped in the Serilog package. It produces a complete rendering of the log event and supports a few configuration options.
  • Serilog.Formatting.Compact.CompactJsonFormatter - A newer, more space-efficient JSON formatter shipped in Serilog.Formatting.Compact.
  • Serilog.Formatting.Compact.RenderedCompactJsonFormatter - Also shipped in Serilog.Formatting.Compact, this formatter pre-renders message templates into text.

Flexible formatting with ExpressionTemplate

The Serilog.Expressions package includes the ExpressionTemplate class for more sophisticated text and JSON formatting. Expression templates can include conditional blocks, repeated sections, computations over event properties, and custom formatting functions.

ExpressionTemplate implements ITextFormatter, so it works with any text-based Serilog sink, including Console (with ANSI color themes), File, Debug, and Email.

Custom formatters

Both plain text and JSON formatting are implemented using the ITextFormatter interface. Implementations of this interface can format log events into any text-based format.

Custom JSON formatters can be built around the JsonValueFormatter class included in Serilog. For some details see this blog post.

Format providers

There are a number of options available to formatting the output of individual types like dates. One example is the use of the format provider that is accepted by most sinks.

Below is a simple console sample using the Serilog.Sinks.Console sink. This is using the default behavior for rendering a date.

class User
{
    public int Id { get; set; }
    public string Name { get; set; }
    public DateTime Created { get; set; }
}

public class Program
{
    public static void Main(string[] args)
    {
        Log.Logger = new LoggerConfiguration()
            .WriteTo.Console()
            .CreateLogger();

        var exampleUser = new User { Id = 1, Name = "Adam", Created = DateTime.Now };
        Log.Information("Created {@User} on {Created}", exampleUser, DateTime.Now);

        Log.CloseAndFlush();
    }
}

This writes the following output to the console.

[18:46:45 INF] Created {"Id": 1, "Name": "Adam", "Created": "2018-05-17T18:46:45.9064879+10:00", "$type": "User"} on 05/17/2018 18:46:45

There may be scenarios where it is desirable to override or specify the way a DateTime is formatted. This can be done via the implementation of IFormatProvider. This strategy applies to any type that you pass to Serilog.

class User
{
    public int Id { get; set; }
    public string Name { get; set; }
    public DateTime Created { get; set; }
}

class CustomDateFormatter : IFormatProvider
{
    readonly IFormatProvider basedOn;
    readonly string shortDatePattern;
    public CustomDateFormatter(string shortDatePattern, IFormatProvider basedOn)
    {
        this.shortDatePattern = shortDatePattern;
        this.basedOn = basedOn;
    }
    public object GetFormat(Type formatType)
    {
        if (formatType == typeof(DateTimeFormatInfo))
        {
            var basedOnFormatInfo = (DateTimeFormatInfo)basedOn.GetFormat(formatType);
            var dateFormatInfo = (DateTimeFormatInfo)basedOnFormatInfo.Clone();
            dateFormatInfo.ShortDatePattern = this.shortDatePattern;
            return dateFormatInfo;
        }
        return this.basedOn.GetFormat(formatType);
    }
}

public class Program
{
    public static void Main(string[] args)
    {
        var formatter = new CustomDateFormatter("dd-MMM-yyyy", new CultureInfo("en-AU"));
        Log.Logger = new LoggerConfiguration() 
            .WriteTo.Console(formatProvider: new CultureInfo("en-AU")) // Console 1
            .WriteTo.Console(formatProvider: formatter)                // Console 2
            .CreateLogger();

        var exampleUser = new User { Id = 1, Name = "Adam", Created = DateTime.Now };
        Log.Information("Created {@User} on {Created}", exampleUser, DateTime.Now);

        Log.CloseAndFlush();
    }
}

The following is the output of the above example, with two consoles sinks configured.

[13:57:12 INF] Created {"Id": 1, "Name": "Adam", "Created": "2020-09-01T13:56:59.7803740-05:00", "$type": "User"} on 1/09/2020 1:57:12 PM
[13:57:12 INF] Created {"Id": 1, "Name": "Adam", "Created": "2020-09-01T13:56:59.7803740-05:00", "$type": "User"} on 01-Sep-2020 1:57:12 PM