Testing Serilog Logging with MEL and TestCorrelator

 Logging as a cross-cutting concern might be implemented in one of plenty of ways. Recently I ran into an attribute-based implementation using passive attributes. What happened was at certain points in the attributed object's lifecycle it had to write messages to a logger. As the author did not want to cramp logging into the production code, they decided to use a passive attribute and a filter checking for said attribute to take care of logging.

However, the filter was not tested, lowering our test coverage.

What I had to do was injecting a mocked Microsoft.ExtensibilityLogging.ILogger<T> into the filter. Easy, right?

Well, sort of. The project uses Serilog for logging. Serilog does support testing through the rather useful TestCorrelator Sink. The canonical way of creating a TestCorrelator is as follows:

Serilog.ILogger logger = new LoggerConfiguration().WriteTo.TestCorrelator().CreateLogger();

Looks good, except for the tiny issue that my LoggingFilter expects a parameter of type Microsoft.Extensions.Logging.ILogger<LoggingFilter> in the constructor. There is a Serilog provider for MEL, that takes care of such conversions by creating a SeilogLogger that implements the ILogger interface from MEL. However, there is no explicit conversion offered, nothing like a FromLogger() factory method, or a constructor accepting an existing Serilog.ILogger.

Googling led me to the GitHub issues page for the said provider. Someone wrote a rather simple wrapper for the SerilogLogger that can be used for the sort of injection that I require:

using Microsoft.Extensions.Logging;
using Serilog;
using Serilog.Extensions.Logging;

public class SerilogTypedLogger<T> : Microsoft.Extensions.Logging.ILogger<T>
{
	Microsoft.Extensions.Logging.ILogger _logger;

	public SerilogTypedLogger(Serilog.ILogger logger) 
	{
		using (var logfactory = new SerilogLoggerFactory(logger))
			_logger = logfactory.CreateLogger(typeof(T).FullName);
	}

	IDisposable Microsoft.Extensions.Logging.ILogger.BeginScope<TState>(TState state) =>
		_logger.BeginScope<TState>(state);

	bool Microsoft.Extensions.Logging.ILogger.IsEnabled(LogLevel logLevel) => 
		_logger.IsEnabled(logLevel);

	void Microsoft.Extensions.Logging.ILogger.Log<TState>(LogLevel logLevel, EventId eventId, TState state, Exception exception, Func<TState, Exception, string> formatter) => 
		_logger.Log<TState>(logLevel, eventId, state, exception, formatter);
}

Comments

Popular Posts