Serilog vs log4net

Serilog and log4net are two very popular choices for logging messages from .NET applications. In this post, I'll show you how to configure and use each framework as well as list the advantages and disadvantages of each framework. I even dare to recommend you use one of the frameworks :)

Let's start with a bit of information about each framework.

Serilog

Launched in 2013, Serilog is one of the newest logging frameworks. This definitely isn't a bad thing, since the framework uses some of the newer and more advanced features of .NET. Structured logging probably being the most noticeable, but also the concept of enrichment is what makes Serilog unique compared to a lot of other logging frameworks.

Serilog is constantly growing and new log targets are being launched every month. There is a large community around Serilog. Compared to other frameworks, the people involved are genuinely nice and want to help out.

log4net

log4net is the old boy on the team of .NET logging frameworks. Originally launched as a port of log4j, it has evolved into a unique product run by the Apache Foundation. You will find a lot of existing projects out there that use log4net for their logging needs. With the latest release back in 2017, development of the main framework pretty much stood still during the last years. New appenders are being developed though, and log4net actually has pretty decent support for modern NoSQL databases as well.

Configuration

Both log4net and Serilog need configuration to start logging. Let's dive into each framework to see how easy the configuration is.

Serilog

Serilog supports both XML and C# based configuration. I've never actually heard of anyone using the XML approach, so that's why the rest of this section will show configuration code written in C#. Being able to configure Serilog in a strongly typed language is a great benefit.

To configure simple logging to a file we write configuration code like this:

Log.Logger = new LoggerConfiguration()
    .WriteTo.File("log-.txt", rollingInterval: RollingInterval.Day)
    .CreateLogger();

The LoggerConfiguration class is the key part of configuring and creating the Serilog ILogger object. In Serilog, log destinations are called Sinks and are added using extension methods on the WriteTo property. In the example above, I tell Serilog to log messages to a rolling file. Finally, I call the CreateLogger method and set the output on the static Logger property on the Log class. This ensures easy access to the ILogger object from your program.

log4net

Like Serilog, log4net can be configured in both XML and C#. The C# approach is clearly something that was added later and is not really intuitive (IMO). Since most of the examples you will find online are in XML, I will use that for the rest of the examples in this post.

log4net configuration is added through either app.config or a dedicated log4net.config file:

<log4net>
  <appender name="FileAppender" type="log4net.Appender.FileAppender">
    <file value="log-file.txt" />
    <appendToFile value="true" />
    <layout type="log4net.Layout.PatternLayout">
      <conversionPattern value="%date %level %message%newline" />
    </layout>
  </appender>
 
  <root>
    <level value="ALL" />
    <appender-ref ref="FileAppender" />
  </root>
</log4net>

Notice how the file appender has its own named appender element, which is then referenced inside the root element in the appender-ref sub-element.

To make the configuration visible for the rest of your program, add a bit of code somewhere (like in AssemblyInfo.cs):

[assembly: log4net.Config.XmlConfigurator(Watch=false)]

As mentioned earlier, configuration isn't really log4net's strong side. Luckily, there is some pretty good documentation and because of its long history, a lot of examples and blog posts online.

elmah.io supports both Serilog and log4net

➡️ Reduce errors by 90% with elmah.io error logging and uptime monitoring ⬅️

Sinks and Appenders

Unless you want to store everything in /dev/zero, you need some way of storing your log messages. Both examples above, showed how to store log messages to a file. Serilog has the concept of Sinks and log4net has appenders. They both cover the same need, just with different naming.

Sinks and appenders are available for a lot of different destinations like files, databases, and remote services. As an example, elmah.io provides both a sink for Serilog and an appender for log4net. They both do the same, store all messages logged through each framework to elmah.io. Both logging frameworks have sinks and appenders for the most common storage options. The quality of each implementation highly varies, which is why I would recommend you try out each sink/appender and see if it covers your needs.

Logging Messages

Why configure a logging framework if you don't want to log any messages? Let's do something about that.

Serilog

Logging in Serilog is dead simple:

Log.Information("Hello World");

Remember how we initialized the Log class in a previous section? Well, here we really see the benefit. The Log class has overloads for all available log levels through Serilog. In this example, I log an information message.

A great benefit of Serilog is the feature of structured logging:

Log.Information("Hello World from {FirstName}", "Thomas");

Rather than using normal string concatenation, I tell Serilog to replace {FirstName} with Thomas. This produces a nice log message, as well as tells Serilog to remember the FirstName key and its value. This can be used to log structured data in a service like elmah.io or in NoSQL databases like MongoDB or Elasticsearch.

This isn't a guide to structured logging with Serilog. If you don't know the concept, check out Structured Data on the Serilog wiki. You are in for a treat!

log4net

Logging through log4net is a bit more complex than Serilog, but it still does the job:

var log = LogManager.GetLogger(typeof(Bar));
log.Info("Hello World");

The main difference is the need for the LogManager. You typically get a reference to the logger one time per class and re-use the same log instance.

log4net doesn't support the concept of structured logging. Like shown in the conversionPattern element in the XML configuration, you have some variables to play with when writing to the storage. But including properties like FirstName in the Serilog example isn't available.

Conclusion

I don't really like to pick on log4net since it has served as a great (and default) logging choice for me for the past decade. But in the end, Serilog is superior in most ways. Structured logging, enrichments (I didn't even show you those, but check out the Serilog documentation for examples) and C# based configuration are all winners.

In most cases, I wouldn't recommend you to replace the logging framework on legacy systems, but in this case, I definitely see some benefits of jumping from log4net to Serilog.

Serilog

Advantages

  • Structured logging and enrichment
  • Great documentation and community
  • C# based configuration

Disadvantages

  • More features to learn

log4net

Advantages

  • A lot of documentation and blog posts
  • Easy to understand when coming from logging frameworks from other languages

Disadvantages

  • No structured logging
  • Hard to configure
  • Project seems dead