Microsoft.Extensions.Logging 的记录器包装器的实现和使用

Implementation and usage of logger wrapper for Microsoft.Extensions.Logging

这个问题与Steven’s answer - here有关。他提出了一个非常好的记录器包装器。我将在下面粘贴他的代码:

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.
}

所以,我的问题是 创建代理到 Microsoft.Extensions.Logging 的实现的正确方法是什么,使用它的最佳方法是什么稍后在代码中?

注意:此问题是 的副本,但现在特定于 Microsoft.Extensions.Logging。

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

您应该创建如下内容:

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?

如果您使用的是 DI 容器,那么只需使用 DI 容器将 ILogger 映射到 MicrosoftLoggingAdapter。您还需要注册 Microsoft.Extensions.ILogger,或者只是将 MS 记录器的实例提供给 DI 容器以将其注入 MicrosoftLoggingAdapter 构造函数。

如果您不使用 DI 容器,即您使用 Pure DI,那么您可以这样做:

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

ILogger logging_adapter = new MicrosoftLoggingAdapter(logger);

var myobject = new MyClass(other_dependencies_here, logging_adapter);

这是我的解决方案。和史蒂文的不太一样。但并不完全像。我的倾向于 dotNetCore,但同样的事情可以在 dotnetFW 中完成。

DotNetCoreLogger 是"MY" ILogger 的具体化。然后我将 "microsoft" ILogger (Microsoft.Extensions.Logging.ILogger) 注入到 "My" 具体记录器中。

还有另一个 SOF 答案 "inspired" "my" 日志抽象....在从 DotNetFramework(经典)到 DotNotCore 之后,我很高兴我做了 "my" ILogger 抽象。

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;
        }