Log4net for .Net Core 2.0

Log4net is a an excellent library that allows developers to output log statements to a variety of output targets through what is called Appenders. However, it’s not compatible with .Net Core 2.0 yet, and all the online log4net extension libraries available today don’t provide a thorough solution to rectify all log4net appenders specifically the ADOAppender; which logs to a database.  In this article I am going to introduce a solution to this problem.

I have uploaded the solution to GitHub at this URL:  https://github.com/rizksobhi/Log4net.NetCore

1- AdoNetAppender

I have created an appender that is compatible with .Net Core that can be used to log to a database. The code is straight forward and I have imported most of it from the log4net library. https://github.com/rizksobhi/Log4net.NetCore/blob/master/Log4net.NetCore.Lib/Appenders/AdoNetAppender.cs

2- Configuration file replacements

I have substituted the configuration file with a static configuration class which provides several ways of creating a variety of appenders. This configuration class uses the AdoNetAppender which is described above.

public static IAppender CreateAdoNetAppender(string connectionString)
{
    AdoNetAppender appender = new AdoNetAppender()
    {
        Name = "AdoNetAppender",
        BufferSize = 1,
        ConnectionType = "System.Data.SqlClient.SqlConnection, System.Data, Version = 1.0.3300.0, Culture = neutral, PublicKeyToken = b77a5c561934e089",
        ConnectionString = connectionString,
        CommandText = "INSERT INTO Log ([Date],[Thread],[Level],[Logger],[Message],[Exception]) VALUES (@log_date, @thread, @log_level, @logger, @message, @exception)"
    };

    AddDateTimeParameterToAppender(appender, "@log_date");
    AddStringParameterToAppender(appender, "@thread", 255, "%thread");
    AddStringParameterToAppender(appender, "@log_level", 50, "%level");
    AddStringParameterToAppender(appender, "@logger", 255, "%logger");
    AddStringParameterToAppender(appender, "@message", 4000, "%message");
    AddErrorParameterToAppender(appender, "@exception", 2000);

    appender.ActivateOptions();
    return appender;
}

3- ASP.Net Core integration

The following extension method helps to register log4net in the logging factory

public static ILoggerFactory AddLog4Net(this ILoggerFactory factory, string connectionString, string logFilePath)
{
    factory.AddProvider(new Log4NetProvider(connectionString, logFilePath));
    return factory;
}

Now we can register our log4net provider through using the extension method at the Configure function in Startup.cs

public void Configure(IApplicationBuilder app, IHostingEnvironment env, Log4netDBContext context, ILoggerFactory loggerFactory)
{
    loggerFactory.AddLog4Net(Configuration["Logging:ConnectionString"], Configuration["Logging:LogFilePath"]);

    DBInitializer.Initialize(context);

    if (env.IsDevelopment())
    {
        app.UseBrowserLink();
        app.UseDeveloperExceptionPage();
    }
    else
    {
        app.UseExceptionHandler("/Home/Error");
    }
            
    app.UseStaticFiles();

    app.UseMvc(routes =>
    {
        routes.MapRoute(
            name: "default",
            template: "{controller=Home}/{action=Index}/{id?}");
    });
}

Here is the implementation of Log4NetProvider https://github.com/rizksobhi/Log4net.NetCore/blob/master/Log4net.NetCore.Lib/Log4NetProvider.cs

And finally using in the HomeController

public HomeController(ILogger logger)
{
    _Logger = logger;
}
public IActionResult Index()
{
    _Logger.LogDebug("Index has been requested");
    return View();
}

References

  1. https://github.com/apache/logging-log4net
  2. https://dotnetthoughts.net/how-to-use-log4net-with-aspnetcore-for-logging/

6 thoughts on “Log4net for .Net Core 2.0

  1. Very nice post Rizk Hanna. I was struggling with log4net in .net core from long time and you made my work easy. I just have a small doubt. Where do we specify the minimum logging level?

    Like

  2. Thank you Ramya for your comment. I think you can set the log level in the Log4NetLogger constructor as the following

    ((log4net.Repository.Hierarchy.Hierarchy)_LoggerRepository).Root.Level = log4net.Core.Level.Debug

    and definitely you can parameterize the level and maybe store it in any setting.json file and pass it through the AddLog4Net extension method to the Log4NetLogger.

    Like

  3. Hi Rizk Hanna
    I love your implementation of Log4Net Ado.Net provide. I am all ready using it in an internally application I am developing and plan to deploy internally in my company.
    However I cannot mock the logger for running unit test as it do not implement an interface (like ILog4Net I believe).
    Code snippet:
    var loggerMock = new Mock<ILogger>(MockBehavior.Strict);
    loggerMock.Setup(x => x.LogInformation(“LogInformation failed”)).Verifiable(“LogInformation not called”);

    When attempting to execute xUnit test with Moq I get this error:
    System.NotSupportedException : Invalid setup on an extension method: x => x.LogInformation(“LogInformation failed”, new[] { })
    at Moq.Mock.ThrowIfSetupExpressionInvolvesUnsupportedMember(Expression setup, MethodInfo method) in C:\projects\moq4\Source\Mock.cs:line 883

    Any chance you could look into this and make it mockable or advice how I could unit test method that have ILogger injected?

    Like

Leave a comment