Skip to Main Menu

Exploring Serilog v2 - Lets Go!

Serilog has been around for some time and since then the ecosystem has grown and been developed behind a fantasic community and the leadership of Nick. I have been lucky enough to be involved for some time as a co-owner of Serilog however I still find myself forgetting key areas of the platform.

As as a result, I thought I start a series of posts addressing areas that are commonly asked about either in our Gitter Channel, on GitHub or elsewhere.

First up… a quick example. I am primarily using a Mac these days so here is how to get running with VSCode & .Net Core.

  • Create a console app
    • dotnet new console -n example-console
    • cd example-console
  • Add the sink package
    • dotnet add package Serilog.Sinks.Console
  • Open VSCode
    • code .

You will have something like the stub below.

using System;

namespace example_console
{
    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("Hello World!");
        }
    }
}

Add a using statement e.g. using Serilog;. One of the great features of Serilog is the fluent interface, in particular how via convention, sinks and other helpers are included in the root namespace making methods easily accesible.

Let start by setting up the static logger, while in the code, remove the stubbed Console.WriteLine above.

Rembember! Friends don’t let friends write to Console.Writeline…

Log.Logger = new LoggerConfiguration()
            .CreateLogger();

In Serilog the following logging levels exist, in order from most verbose to fatal. A quick description can be found over on the wiki.

  • Verbose
  • Debug
  • Information
  • Warning
  • Error
  • Fatal

Now let’s add a few log statements.

Log.Verbose("This is a debug statement");
Log.Debug("This is a debug statement");
Log.Information("This is a info statement");
Log.Warning("This is a warning statement");
Log.Error("This is an error statement");
Log.Fatal("This is a fatal statement");

If we run this project, there won’t be any output. Why?

Firstly, lets get a little background on the pipeline flow within Serilog itself. Initially Log.Logger is in instantiated with a SilentLogger, this is an internal logger that will efficently deal with events in the absence of any configuration. In fact, you don’t have to configure the Log.Logger as we have above to experience this behaviour.

Let’s add a sink, in other words, a destination for our events.

Update the configuration to use the console sink.

Log.Logger = new LoggerConfiguration()
        .WriteTo.Console()
        .CreateLogger();

If we run the app again, we get the following output.

[20:17:33 INF] This is a info statement
[20:17:33 WRN] This is an warning statement
[20:17:33 ERR] This is a error statement
[20:17:33 FTL] This is a fatal statement

Wait… Where are the verbose and debug events?

The reason the two initial events are missing, relates to the default configuration within Serilog. A key area to understanding the Serilog pipeline is the method by which events are enabled for a given log level. This applies to logging configuration, sinks and sub loggers. By default Serilog will enable the minimum log level as Information. This often seen overidden in examples using the MinimumLevel interface.

So let’s update the configuration to enable the Verbose level.

Log.Logger = new LoggerConfiguration()
    .....
    .MinimumLevel.Verbose()
    ....
    .CreateLogger();

As you would expect the events now appear.

[16:10:43 VRB] This is a verbose statement
[16:10:43 DBG] This is a debug statement
[16:10:43 INF] This is a info statement
[16:10:43 WRN] This is a warning statement
[16:10:43 ERR] This is an error statement
[16:10:43 FTL] This is a fatal statement

So what other options are available in the initial configuration?

  • .AuditTo(...)
    • An optional feature of Auditing events not generally used with async style sinks (HTTP destinations).
  • .Destructure(...)
    • The ability to destructure events via message templates. A great explaination of message templates can be found over at https://messagetemplates.org/.
  • .Enrich(...)
    • Used to add property data related to a specific event. Enrichers will implement ILogEventEnricher.
  • .Filter(...)
    • A filter will implement ILogEventFilter in order to filter events based on log event data.
  • .ReadFrom(...)
    • A way of applying configuration to the log configuration, common examples include from configuration files.
  • .WriteTo(...)
    • The way sinks are wired into the pipeline, as per the console example above. Sinks implement the ILogEventSink interface.

On the surface, it does not appear that much can be achieved with such a small API. In fact the eco-system built by the community shows quite the opposite with new sinks and extensions being added all the time.

Here are some future posts when Exploring Serilog v2:

Get Amongst It!!

comments powered by Disqus