DominikAmon
DominikAmon

Reputation: 1106

Migrating to .net Core Logging from Log4net

I want to migrate existing code from .NET 4.7.2/log4net to the most recent .NET Core version (currently 2.2, by September should be 3.0) and the "default" logging.

We have many lines of code of getting an instance of a log4net Logger for a class MyClass such as:

private static ILog _logger = LogManager.GetLogger(typeof(MyClass));

and using the logger like _logger.Info("Some message");

Most samples and documentations I've found are using Dependecy Injection in a class constructor, such as:

public class AboutModel : PageModel
{
    private readonly ILogger _logger;

    public AboutModel(ILogger<AboutModel> logger)
    {
        _logger = logger;
    }

and the logger is used as such:

_logger.LogInformation("Message displayed: {Message}", Message);

The biggest issue for me for migrating is that it is passend on by the DI in the constructor, which might be fine like in the example of an MVC controller, but an issue in several other classes I have, because of breaking the signature of the constructor.

I am looking for an implementation which does not break the constructor signature (like the DI in Xamarin, which would make the migration process easier) - something like:

private static ILogger _logger = DependencyService.Get<ILogger<MyClass>>();

What I've found so far, was an example at http://www.binaryintellect.net/articles/17ee0ba2-99bb-47f0-ab18-f4fc32f476f8.aspx where they use the HttpContext.RequestServices:

INotificationHelper helper = (INotificationHelper) HttpContext.RequestServices.GetService(typeof(INotificationHelper));

My code also runs in console applications, so I do not have an HttpContext in those cases. Any idea how to solve this issue with .NET Core? (Preferably without any 3rd party library). Note that it is not only one class, that I have to migrate, it might be up to a 1000 classes.

Upvotes: 5

Views: 7070

Answers (1)

Dave Barnett
Dave Barnett

Reputation: 2226

I know you say preferably without a 3rd party library but I think you would find this one very useful. It is Microsoft.Extensions.Logging.Log4net.AspNetCore. Here is an example of how you can put it to use

using Microsoft.AspNetCore;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Logging;
using System.IO;
using System.Reflection;

namespace TryLog4net
{
    public class Program
    {
        public static void Main(string[] args)
        {
            CreateWebHostBuilder(args).Build().Run();
        }

        public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
            WebHost.CreateDefaultBuilder(args)
            .ConfigureLogging((hostingContext, logging) =>
            {
                var assembly = Assembly.GetAssembly(typeof(Program));
                var pathToConfig = Path.Combine(
                          hostingContext.HostingEnvironment.ContentRootPath
                        , "log4net.config");
                 var logManager = new AppLogManager(pathToConfig, assembly);

                logging.AddLog4Net(new Log4NetProviderOptions
                {
                    ExternalConfigurationSetup = true
                });
            })
            .UseStartup<Startup>();
    }
}

in the root of your project place your log4net.config file. An example is

<?xml version="1.0" encoding="utf-8" ?>
<log4net>
  <appender name="DebugAppender" type="log4net.Appender.DebugAppender" >
    <layout type="log4net.Layout.PatternLayout">
      <conversionPattern value="%date [%thread] %-5level %logger - %message%newline" />
    </layout>
  </appender>
  <root>
    <level value="ALL"/>
    <appender-ref ref="DebugAppender" />
  </root>
</log4net>

And here is a way you can get a logger without using dependency injection as you requested.

using log4net;
using System;
using System.IO;
using System.Reflection;

namespace TryLog4net
{
    public class AppLogManager 
    {
        private static AppLogManager _logManager = null;

        private AppLogManager()
        {    
        }    

        public AppLogManager(string fullConfigFilePath, Assembly assembly)
        {
            var logRepo = log4net.LogManager.GetRepository(assembly);
            log4net.Config.XmlConfigurator.Configure(logRepo, new FileInfo(fullConfigFilePath));
            _logManager = new AppLogManager();

        }
        public static ILog GetLogger<T>()
        {
            return _logManager.GetLogger(typeof(T));
        }

        public ILog GetLogger(Type type)
        {
            var log = log4net.LogManager.GetLogger(type);
            return log;
        }
    }

    public static class GenericLoggingExtensions
    {
        public static ILog Log<T>(this T thing)
        {
            var log = AppLogManager.GetLogger<T>();
            return log;
        }
    }
}

Here is a very simple example startup file that creates a log message.

using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.DependencyInjection;

namespace TryLog4net
{
    public class Startup
    {
        public void ConfigureServices(IServiceCollection services)
        { 
        }

        public void Configure(IApplicationBuilder app, IHostingEnvironment env)
        {
            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
            }

            app.Run(async (context) =>
            {
                var testClass = new TestClass();
                testClass.LogSomething();
                await context.Response.WriteAsync("Hello World!");
            }); 
        }

        public class TestClass
        {
            public void LogSomething()
            {
                var logger = this.Log();
                logger.Info("my message");              
            }
        }
    }
}

Upvotes: 7

Related Questions