4
votes

I am consuming a Class Library which is built using .NET Core. The Library has only one public class with a constructor accepting ILoggerFactory and another overloaded constructor accepting ILogger. But I am consuming this library in a console app built using .NET Framework 4.7. My console app uses Log4Net for logging. But I need to inject the instance of ILogger or ILoggerFactory so that the class library log's error based on my log.config settings.

I tried as below

ILogger logger = new LoggerFactory().CreateLogger(); var contentManager = new ContentManager(logger);

But I don't know how to pass the Log4Net instance to the LoggerFactory.

private static ILog _log = LogManager.GetLogger(typeof(Program));
static void Main(string[] args)
{
   ILogger logger = new LoggerFactory().CreateLogger<ContentManager>();
   var contentManager = new ContentManager(logger);
   contentManager.Run();
}

I don't find anything getting logged and ContentManager doesn't throw any exceptions. I know that the ILogger instance doesn't know anything about Log4Net from my console app, how I need to pass the Log4Net instance? Need help on this.

2
Does the library also use Log4Net?Dan S
Yes. The Library uses Log4Net.s-atv
Is there a working instance of ILogger you can pass to the Library object?Dan S
No. I don't have a working instance of ILogger because ILooger comes with Microsoft.Extensions.Logging (.NET Core). But I think I need to implement the ILogger on my .NET Framework console app. Looks like there is no straightforward way.s-atv

2 Answers

2
votes

Try the Adapter Pattern to facilitate delegation to Log4Net. I'm not familiar with Log4Net but I think you'll understand this example:

class MyLoggerAdapter : Library.ILogger 
{
    private readonly ILog _delegation;

    public MyLoggerAdapter(ILog delegation)
    {
        _delegation = delegation;
    }

    public void ILogger.Debug(string msg, params object[] p)
    {
        _delegation.Debug(msg, p); 
    }
}

Then in your code you can do this:

private static ILog _log = LogManager.GetLogger(typeof(Program));
static void Main(string[] args)
{
   MyLoggerAdapter logAdapter = new MyLoggerAdapter(_log);
   var contentManager = new ContentManager(logAdapter);
   contentManager.Run();
}
0
votes

The answer from Dan S is a great general answer with good example. To implement this specifically for log4net you could use a class like this:

public class Log4netAdapter<TCategoryName> : ILogger<TCategoryName>
{
    private readonly ILog _logger = LogManager.GetLogger(typeof(TCategoryName));

    public IDisposable BeginScope<TState>(TState state)
    {
        return null; // log4net does not have scope capability
    }

    public bool IsEnabled(LogLevel logLevel)
    {
        switch (logLevel)
        {
            case LogLevel.Trace:
            case LogLevel.Debug:
                return _logger.IsDebugEnabled;
            case LogLevel.Information:
                return _logger.IsInfoEnabled;
            case LogLevel.Warning:
                return _logger.IsWarnEnabled;
            case LogLevel.Error:
                return _logger.IsErrorEnabled;
            case LogLevel.Critical:
                return _logger.IsFatalEnabled;
            case LogLevel.None:
            default:
                return false;
        }
    }

    public void Log<TState>(LogLevel logLevel, EventId eventId, TState state, Exception exception, Func<TState, Exception, string> formatter)
    {
        if (!IsEnabled(logLevel)) return;
        if (formatter == null) throw new ArgumentNullException(nameof(formatter));

        var message = formatter(state, exception);
        if (!string.IsNullOrEmpty(message) || exception != null)
        {
            switch (logLevel)
            {
                case LogLevel.None:
                    break;
                case LogLevel.Trace:
                case LogLevel.Debug:
                    _logger.Debug(message, exception);
                    break;
                case LogLevel.Information:
                    _logger.Info(message, exception);
                    break;
                case LogLevel.Warning:
                    _logger.Warn(message, exception);
                    break;
                case LogLevel.Error:
                    _logger.Error(message, exception);
                    break;
                case LogLevel.Critical:
                    _logger.Fatal(message, exception);
                    break;
            }
        }
    }
}

Then reference the packages log4net and Microsoft.Extensions.Logging (or Microsoft.Extensions.Logging.Abstractions if you are creating this in a library).

To use the adapter when instantiating a class using Microsoft.Extensions.Logging (and your project is not using dependency injection):

var logger = new Log4netAdapter<NewClassLibrary>();
var newClassLibrary = new NewClassLibrary(logger);