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
.
- Used to add property data related to a specific event. Enrichers will implement
.Filter(...)
- A filter will implement
ILogEventFilter
in order to filter events based on log event data.
- A filter will implement
.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.
- The way sinks are wired into the pipeline, as per the console example above. Sinks implement the
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.
Some #Serilog stats for your Friday arvo… pic.twitter.com/6X1knCbbZr
— Matthew Erbs (@MatthewErbs) February 9, 2018
Here are some future posts when Exploring Serilog v2:
- Exploring Serilog v2 - The Console Sink
- Exploring Serilog v2 - Configuration
- Exploring Serilog v2 - ASP.Net Core