Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Unit test serilog configuration

I have a piece of code for setting up serilog based on some custom configuration combined with hosting environment. E.g. the application writes to one sink in development and another sink in production.

I'm trying to figure out how to write tests for this piece of code. Basically, I want to write a test that checks that a sink only is added if the environment name is set to a given value, and that the sink configuration, like log file path, respects the custom configuration that I provide.

But I haven't had any luck finding any way of getting values out of the LoggingConfiguration...

Anyone knows if this is possible?

like image 769
Vegar Avatar asked Sep 28 '18 11:09

Vegar


People also ask

Can you stop logging with Serilog?

For example, if you are using Serilog you should configure Log. Logger and LibLog will use it. Also you can disable logging by setting LogProvider. IsDisabled to true . "

How mock ILogger?

To mock an ILogger<T> object, we can use Moq library to instantiate a new Mock<ILogger<T>>() object. Make sure you have installed the latest Moq package (v4. 15.1 as of Nov. 16, 2020, which contains an update to support “nested” type matchers).


1 Answers

Unfortunately Serilog does not expose the list of Sinks that have been configured, so your only option at the moment would be to use Reflection.

If you poke around Serilog's source code, you'll see that it groups all configured sinks into an instance of an internal class SafeAggregateSink which is responsible emitting the logs to the different sinks setup, and holds an array with all the configured sinks in a private field called _sinks.

Here is a simple example:

var log = new LoggerConfiguration()
    .WriteTo.Console(restrictedToMinimumLevel: LogEventLevel.Verbose)
    .WriteTo.File(path: "log.txt", restrictedToMinimumLevel: LogEventLevel.Verbose)
    .CreateLogger();

var aggregateSinkFieldInfo = log.GetType()
    .GetField("_sink", BindingFlags.Instance | BindingFlags.NonPublic);

var aggregateSink = (ILogEventSink)aggregateSinkFieldInfo?.GetValue(log);

var sinkEnumerableFieldInfo = aggregateSink?.GetType()
    .GetField("_sinks", BindingFlags.Instance | BindingFlags.NonPublic);

var sinks = (ILogEventSink[])sinkEnumerableFieldInfo?
    .GetValue(aggregateSink);

if (sinks != null)
{
    foreach (var sink in sinks)
    {
        Console.WriteLine(sink.GetType().FullName);
    }
}

This should output:

Serilog.Sinks.SystemConsole.ConsoleSink
Serilog.Sinks.File.FileSink

N.B.: Keep in mind that Serilog wraps sinks in some cases, so you might need to handle that, before you can find the sink you're looking for. For example, if you restrict the minimum level of a sink, your sink will be wrapped into a RestrictedSink, so you'll have to get a hold of its _sink field to get the "real" sink you're looking for.

like image 117
C. Augusto Proiete Avatar answered Oct 20 '22 00:10

C. Augusto Proiete