As discussed in the previous article about structured logging, I planned to look into Serilog in detail, and I decided to tackle this from a performance point of view. This article was finished a while ago but I never found time to review it and actually release it.

I decided to focus these performance tests on two frameworks, Serilog (obviously) and NLog. You'll have to take my word for it, but two years ago, a colleague of mine compared NLog to Log4Net using the exact same test I will be using, and concluded that NLog performs better if configured using the BufferedWrapper Target. Therefore I have decided not to bother comparing Serilog with Log4Net.

The test is performed using a modified version of the application provided here. The aim is to compare NLog with Serilog without making use of log provider specific features (such as MessageTemplates) as in the current version of NLog, it does not support them. NLog plans to release MessageTemplates in the next major release (version 5.0.0), which is currently in beta.

All tests were performed on a laptop running Windows 10, Xeon E3-1505M @ 2.8Ghz, and 32GB RAM, writing to an NVMe PM961 Samsung SSD.

The modifications performed to the application mentioned above were to introduce LibLog, an abstraction layer for various logging providers, the aim is to facilitate the use of both log providers, and to make sure that the log providers behave well when used with this library. The application is running on .NET Core 1.1, and will be run multiple times to ensure that there is consistency to the results.

Setup

The test has been performed by writing to a File named Log.txt using a buffered logger (for improved performance). Both tests have a flush timeout of 1000ms and write 64,000 log lines. All configuration has been performed through code, and can be found below.

Serilog

var log = new LoggerConfiguration()
	.WriteTo.File("Log.txt", buffered: true, flushToDiskInterval: TimeSpan.FromMilliseconds(1000))
	.CreateLogger();

Log.Logger = log;

NLog

var config = new LoggingConfiguration();
var fileTarget = new FileTarget
{
	Name = "FileTarget",
	FileName = "Log.txt"
};
var target = new BufferingTargetWrapper()
{
	BufferSize = 100,
	SlidingTimeout = false,
	FlushTimeout = 1000,
	Name = "BufferedTarget",
	WrappedTarget = fileTarget
};

config.AddTarget("file", target);
config.LoggingRules.Add(new LoggingRule("*", NLog.LogLevel.Debug, target));
LogManager.Configuration = config;
LogManager.ReconfigExistingLoggers();
LogProvider.SetCurrentLogProvider(new NLogLogProvider());

Results

Below are the results for the tests for both NLog and Serilog writing to file. The 99% Latency field represents the maximum time in microseconds to log 99% of the events.

NLog

Nlog Benchmark Numbers

Serilog

Serilog Benchmark Numbers

Visualising results

Throughput

Mean Latency

99% Observations Threshold

Conclusion

From the results it is clear that even though both the Serilog log provider and the NLog log provider are similarly configured, the results for Serilog are much better than the ones for NLog in both throughput and latency. In most cases latency for Serilog is half as much as the latency for NLog, and throughput twice as much.

This gives us further incentive to change from NLog to Serilog - apart from the added features we get added performance. That said, we are not measuring the impact that MessageTemplates (and rendering them), the serialisation of objects and the impact of various enrichers would have to Serilog's performance.