6

When using LibLog, is it possible to assert calls to the logger? Given the wiki lists the following example for usage:

public class MyClass
{
    private static readonly ILog Logger = LogProvider.For<MyClass>(); 
}

Here the logger is an implementation detail hidden from the consumer, which is most of the benefit of using this library. Such that the library consumer does not have to worry about how loggers are instantiated. Looking at this blog post:

http://dhickey.ie/2015/06/capturing-log-output-in-tests-with-xunit2/

It seems that a lot of boiler plate is added to capture the log output, I'm not entirely sure about the approach, given that it also uses a redirected Serilog output in the unit test, something that seems odd given the library should only rely on the logging abstraction?

The only options I can currently think of are:

Inject the logger - This probably would be odd for the consumer of the library, and each library then would carry it's own ILogger definition that needs to be injected, defeating the advantages of the abstraction.

Wire up to a real logging framework - Set the current LogProvider for LibLog to use Log4Net or similar, and then somehow try and inject a mock / stub Logger into Log4Net, and assert calls via proxy.

Any relatively simple way to assert calls to the logger would be appreciated, but I suspect parallel test execution would cause problems even if it was possible to assert calls on the above logger?

Hux
  • 3,102
  • 1
  • 26
  • 33

2 Answers2

0

What I've done in my project:

I've created my LoggerFactory. It exposes same static methods as NLogger.

public class LoggerFactory
{
    private static ILoggerFactoryStrategy _loggerFactoryStrategy = new DummyLoggerFactoryStrategy();

    public static void Initialize(ILoggerFactoryStrategy loggerFactoryStrategy)
    {
        _loggerFactoryStrategy = loggerFactoryStrategy;
    }

    public ILogger GetLogger<T>()
    {
        return _loggerFactoryStrategy.GetLogger<T>();
    }

    ....
}

Dummy strategy can write just to debug output or do nothing. Another strategy could look smth like:

public class LoggerFactoryStrategy : ILoggerFactoryStrategy
{
    public ILogger GetLogger<T>()
    {
        //create LibLog instance instead with LogProvider.For<T>()
        var nlogger = LogManager.GetLogger(typeof(T).Name); //create instance of NLogger
        return new NLogLogger(nlogger);
    }
}

And NlogLogger wrapper could be smth like

internal class NLogLogger : ILogger
{
    private readonly Logger _logger;

    public NLogLogger(Logger logger)
    {
        _logger = logger;
    }

    public void Debug(string message)
    {
        _logger.Debug(message);
    }

    public void Warn(string message, params object[] args)
    {
        _logger.Warn(message, args);
    }

    public void Info(Exception exception)
    {
        _logger.Info(exception);
    }

    ......
}

When application starts I initialize it with proper strategy what uses NLogger under the hood.

If I want to test calls to logger I can use mocked strategy. This approach lets you to remove references to logger library across your solution, except your root projects and lets you switch from one to another if you need in the future.

Also, this allowed us to use NLogger in PCL projects.

Artiom
  • 7,694
  • 3
  • 38
  • 45
0

In the logging config for almost all loggers you can configure then to throw exception when log fail.

Sample from nlog

<nlog throwExceptions="true">
 ... your nlog config
</nlog>

But in the abstraction created by LibLog you lost this features