Implementation and usage of logger wrapper for Microsoft.Extensions.Logging
Asked Answered
P

2

6

This question is related to Steven’s answer - here. He proposed a very good logger wrapper. I will paste his code below:

public interface ILogger
{
    void Log(LogEntry entry);
}

public static class LoggerExtensions
{
    public static void Log(this ILogger logger, string message)
    {
        logger.Log(new LogEntry(LoggingEventType.Information,
            message, null));
    }

    public static void Log(this ILogger logger, Exception exception)
    {
        logger.Log(new LogEntry(LoggingEventType.Error, 
            exception.Message, exception));
    }

    // More methods here.
}

So, my question is what is the proper way to create implementation that proxies to Microsoft.Extensions.Logging and what is the best way to use it later in the code?

Note: this question is a copy of this question about log4net but now specific to Microsoft.Extensions.Logging.

Plantaineater answered 21/9, 2016 at 7:27 Comment(0)
P
9

So, my question is what is the proper way to create implementation that proxies to Microsoft.Extensions.ILogger?

you should create something like:

public sealed class MicrosoftLoggingAdapter  : ILogger
{
    private readonly Microsoft.Extensions.ILogger adaptee;

    public MicrosoftLoggingAdapter (Microsoft.Extensions.ILogger adaptee) =>
        this.adaptee = adaptee;

    public void Log(LogEntry e) =>
        adaptee.Log(ToLevel(e.Severity), 0, e.Message, e.Exception, (s, _) => s);       

    private static LogLevel ToLevel(LoggingEventType s) =>
        s == LoggingEventType.Debug ? LogLevel.Debug  :
        s == LoggingEventType.Information ? LogLevel.Information :
        s == LoggingEventType.Warning ? LogLevel.Warning :
        s == LoggingEventType.Error ? LogLevel.Error :
        LogLevel.Critical;      
}

what is the best way to use it later in the code?

If you are using a DI container, then just use the DI container to map ILogger to MicrosoftLoggingAdapter. You also need to register Microsoft.Extensions.ILogger, or just give an instance of MS logger to the DI container to inject it to the MicrosoftLoggingAdapter constructor.

If you don't use a DI container, i.e., you use Pure DI, then you do something like this:

var logger = loggerFactory.CreateLogger("Application");

ILogger logging_adapter = new MicrosoftLoggingAdapter(logger);

var myobject = new MyClass(other_dependencies_here, logging_adapter);
Plantaineater answered 21/9, 2016 at 7:27 Comment(2)
So, the ILogger this Adapter inherits from is the one from: #5647320 ? Sorry, there are three different posts re: this topic and I'm trying to get my head around it all.Bessette
You're everywhere Steven!!! This is the (best) accepted answer........my answer just shows what I used.......maybe helps, maybe not.Aponte
A
1

Here is my solution. Not too unlike Steven's. But not exactly like it. And mine leans toward dotNetCore, but the same thing can be accomplished in dotnetFW.

DotNetCoreLogger is the concrete of "MY" ILogger. And I inject the "microsoft" ILogger (Microsoft.Extensions.Logging.ILogger) into "My" concrete logger.

There is another SOF answer that "inspired" "my" logging abstraction....and after going from DotNetFramework (classic) to DotNotCore, I am so glad I did a "my" ILogger abstraction.

using System;

namespace MyApplication.Infrastructure.Logging.LoggingAbstractBase
{
    public interface ILogger
    {
        void Log(LogEntry entry);
        void Log(string message);
        void Log(Exception exception);
    }
}


namespace MyApplication.Infrastructure.Logging.LoggingAbstractBase
{
    public enum LoggingEventTypeEnum
    {
        Debug,
        Information,
        Warning,
        Error,
        Fatal
    };
}



using System;

namespace MyApplication.Infrastructure.Logging.LoggingAbstractBase
{
    public class LogEntry
    {
        public readonly LoggingEventTypeEnum Severity;
        public readonly string Message;
        public readonly Exception Exception;

        public LogEntry(LoggingEventTypeEnum severity, string message, Exception exception = null)
        {
            if (message == null) throw new ArgumentNullException("message");
            if (message == string.Empty) throw new ArgumentException("empty", "message");

            this.Severity = severity;
            this.Message = message;
            this.Exception = exception;
        }
    }
}



using System;
using Microsoft.Extensions.Logging;

namespace MyApplication.Infrastructure.Logging.LoggingCoreConcrete
{
    public class DotNetCoreLogger<T> : MyApplication.Infrastructure.Logging.LoggingAbstractBase.ILogger
    {
        private readonly ILogger<T> concreteLogger;

        public DotNetCoreLogger(Microsoft.Extensions.Logging.ILogger<T> concreteLgr)
        {
            this.concreteLogger = concreteLgr ?? throw new ArgumentNullException("Microsoft.Extensions.Logging.ILogger is null");
        }

        public void Log(MyApplication.Infrastructure.Logging.LoggingAbstractBase.LogEntry entry)
        {
            if (null == entry)
            {
                throw new ArgumentNullException("LogEntry is null");
            }
            else
            {
                switch (entry.Severity)
                {
                    case LoggingAbstractBase.LoggingEventTypeEnum.Debug:
                        this.concreteLogger.LogDebug(entry.Message);
                        break;
                    case LoggingAbstractBase.LoggingEventTypeEnum.Information:
                        this.concreteLogger.LogInformation(entry.Message);
                        break;
                    case LoggingAbstractBase.LoggingEventTypeEnum.Warning:
                        this.concreteLogger.LogWarning(entry.Message);
                        break;
                    case LoggingAbstractBase.LoggingEventTypeEnum.Error:
                        this.concreteLogger.LogError(entry.Message, entry.Exception);
                        break;
                    case LoggingAbstractBase.LoggingEventTypeEnum.Fatal:
                        this.concreteLogger.LogCritical(entry.Message, entry.Exception);
                        break;
                    default:
                        throw new ArgumentOutOfRangeException(string.Format("LogEntry.Severity out of range. (Severity='{0}')", entry.Severity));
                }
            }
        }

        public void Log(string message)
        {
            this.concreteLogger.LogInformation(message);
        }

        public void Log(Exception exception)
        {
        /* "Always pass exception as first parameter" from https://blog.rsuter.com/logging-with-ilogger-recommendations-and-best-practices/ */


        /* there is an issue with https://github.com/aspnet/Logging/blob/master/src/Microsoft.Extensions.Logging.Abstractions/LoggerExtensions.cs
         * the default MessageFormatter (for the extension methods) is not doing anything with the "error".  this plays out as not getting full exception information when using extension methods.  :(
         * 
         *         private static string MessageFormatter(FormattedLogValues state, Exception error)
         *         {
         *                  return state.ToString();
         *         }
         *          
         * Below code/implementation is purposely NOT using any extension method(s) to bypass the above MessageFormatter mishap.
         * 
         * */

            this.concreteLogger.Log(LogLevel.Error, exception, exception.Message);
        }
    }
}



/* IoC/DI below */


        private static System.IServiceProvider BuildDi(Microsoft.Extensions.Configuration.IConfiguration config)
        {
            //setup our DI
            IServiceProvider serviceProvider = new ServiceCollection()
                .AddLogging()
                .AddSingleton<IConfiguration>(config)
                .AddSingleton<MyApplication.Infrastructure.Logging.LoggingAbstractBase.ILogger, MyApplication.Infrastructure.Logging.LoggingCoreConcrete.DotNetCoreLogger<Program>>()
                .BuildServiceProvider();

            //configure console logging
            serviceProvider
                .GetService<ILoggerFactory>()
                .AddConsole(LogLevel.Debug);

            return serviceProvider;
        }
Aponte answered 20/11, 2018 at 14:18 Comment(1)
Here is the "inspired me" SOF answer : #5647320Aponte

© 2022 - 2024 — McMap. All rights reserved.