I recently was developing a console application in .net core, where I had to use log4net logging.
In the standard asp.net core approach we can use:
public void Configure(IApplicationBuilder app, Microsoft.Extensions.Logging.ILoggerFactory loggerFactory) { loggerFactory.AddLog4Net(); }
But this is .net core console application, where I’m creating LoggerFactory on my own, so it would not work.
In order to solve it, I had to implement my own Log4NetProvider, that would implement ILoggerProvider.
public class Log4NetProvider : ILoggerProvider { private readonly string _log4NetConfigFile; private readonly bool _skipDiagnosticLogs; private readonly ConcurrentDictionary<string, ILogger> _loggers = new ConcurrentDictionary<string, ILogger>(); public Log4NetProvider(string log4NetConfigFile, bool skipDiagnosticLogs) { _log4NetConfigFile = log4NetConfigFile; _skipDiagnosticLogs = skipDiagnosticLogs; } public ILogger CreateLogger(string categoryName) { return _loggers.GetOrAdd(categoryName, CreateLoggerImplementation); } public void Dispose() { _loggers.Clear(); } private ILogger CreateLoggerImplementation(string name) { return new Log4NetLogger(name, new FileInfo(_log4NetConfigFile), _skipDiagnosticLogs); } }
And the implementation of an actual logger:
public class Log4NetLogger : ILogger { private readonly string _name; private readonly ILog _log; private readonly bool _skipDiagnosticLogs; private ILoggerRepository _loggerRepository; public Log4NetLogger(string name, FileInfo fileInfo, bool skipDiagnosticLogs) { _name = name; _loggerRepository = LogManager.GetRepository(Assembly.GetEntryAssembly()); _log = LogManager.GetLogger(_loggerRepository.Name, name); _skipDiagnosticLogs = skipDiagnosticLogs; log4net.Config.XmlConfigurator.Configure(_loggerRepository, fileInfo); } public IDisposable BeginScope<TState>(TState state) { return null; } public bool IsEnabled(LogLevel logLevel) { switch (logLevel) { case LogLevel.Critical: return _log.IsFatalEnabled; case LogLevel.Debug: case LogLevel.Trace: return _log.IsDebugEnabled && AllowDiagnostics(); case LogLevel.Error: return _log.IsErrorEnabled; case LogLevel.Information: return _log.IsInfoEnabled && AllowDiagnostics(); case LogLevel.Warning: return _log.IsWarnEnabled; default: throw new ArgumentOutOfRangeException(nameof(logLevel)); } } 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)); } string message = $"{formatter(state, exception)} {exception}"; if (!string.IsNullOrEmpty(message) || exception != null) { switch (logLevel) { case LogLevel.Critical: _log.Fatal(message); break; case LogLevel.Debug: case LogLevel.Trace: _log.Debug(message); break; case LogLevel.Error: _log.Error(message); break; case LogLevel.Information: _log.Info(message); break; case LogLevel.Warning: _log.Warn(message); break; default: _log.Warn($"Encountered unknown log level {logLevel}, writing out as Info."); _log.Info(message, exception); break; } } } private bool AllowDiagnostics() { if (!_skipDiagnosticLogs) { return true; } return !(_name.ToLower().StartsWith("microsoft") || _name == "IdentityServer4.AccessTokenValidation.Infrastructure.NopAuthenticationMiddleware"); } }
One last touch is adding an extension for ILoggerFactory to be able to use AddLog4Net.
public static class Log4netExtensions { public static ILoggerFactory AddLog4Net(this ILoggerFactory factory, bool skipDiagnosticLogs) { factory.AddProvider(new Log4NetProvider("log4net.config", skipDiagnosticLogs)); return factory; } }
In my DI container registration, I added code:
var loggerFactory = new Microsoft.Extensions.Logging.LoggerFactory(); loggerFactory.AddLog4Net(true); Container.RegisterInstance<Microsoft.Extensions.Logging.ILoggerFactory>(loggerFactory);
Now it will all works!
To see the whole code, go to my GitHub repository and check this commit: https://github.com/mikuam/console-app-net-core/commit/650ac5348886d3e0238dfec07076b959d62bd4ba
Hope that works for you!