【问题标题】:Custom implementation of ILoggerILogger 的自定义实现
【发布时间】:2019-04-28 20:53:43
【问题描述】:

在我的项目中,我经常为我的日志消息添加前缀。

目前我正在这样做

      logger.LogDebug(prefix + " some message");

我认为这是实现自定义记录器的好方法,在该记录器中我设置了前缀,记录器本身在每次记录某些内容时都会附加它。

所以我创建了我的自定义记录器类并实现了 ILogger 接口。但是我不明白如何使用

    public void Log<TState>(LogLevel logLevel, EventId eventId, TState state, Exception exception, Func<TState, Exception, string> formatter)

添加前缀的方法(它是自定义记录器类的成员)。

我的完整代码是:

      public class CustomLogger : ILogger
      {

        private readonly ILogger _logger;
        private string _logPrefix;

        public CustomLogger(ILogger logger)
        {
          _logger = logger ?? throw new ArgumentNullException(nameof(logger));
        _logPrefix = null;
        }

        public ILogger SetLogPrefix(string logPrefix)
        {
          _logPrefix = logPrefix;
          return this;
        }

        public IDisposable BeginScope<TState>(TState state)
        {
          return _logger.BeginScope(state);
        }

        public bool IsEnabled(LogLevel logLevel)
        {
          return _logger.IsEnabled(logLevel);
        }

        public void Log<TState>(LogLevel logLevel, EventId eventId, TState state, Exception exception, Func<TState, Exception, string> formatter)
        {
          _logger.Log(logLevel, eventId, state, exception, formatter);
        }

      }

【问题讨论】:

  • 类似Func&lt;TState, Exception, string&gt; custom = (s, e) =&gt; _logPrefix + formatter(s, e)?并将_logger.Log(...formatter) 替换为_logger.Log(...custom)

标签: asp.net-core .net-core asp.net-core-2.1


【解决方案1】:

我认为您不应该在自定义记录器中调用 _logger。

这将是运行时的循环调用,结果将是“前缀:前缀:前缀:前缀:前缀:前缀:前缀:前缀:...”

很简单,你可以创建一个简单的logger,实现一个log writter,比如Console,database writter,log4net,...

现在首先,您应该更改自定义记录器,如下所示:

    public class CustomLogger : ILogger
    {
        private readonly string CategoryName;
        private readonly string _logPrefix;

        public CustomLogger(string categoryName, string logPrefix)
        {
            CategoryName = categoryName;
            _logPrefix = logPrefix;
        }

        public IDisposable BeginScope<TState>(TState state)
        {
            return new NoopDisposable();
        }

        public bool IsEnabled(LogLevel logLevel)
        {
            return true;
        }

        public void Log<TState>(LogLevel logLevel, EventId eventId, TState state, Exception exception, Func<TState, Exception, string> formatter)
        {
            string message = _logPrefix;
            if (formatter != null)
            {
                message += formatter(state, exception);
            }
            // Implement log writter as you want. I am using Console
            Console.WriteLine($"{logLevel.ToString()} - {eventId.Id} - {CategoryName} - {message}");
        }

        private class NoopDisposable : IDisposable
        {
            public void Dispose()
            {
            }
        }
    }

第二步,创建logger提供者:

     public class LoggerProvider : ILoggerProvider
        {     
            public ILogger CreateLogger(string categoryName)
            {                
                return new CustomLogger(categoryName, "This is prefix: ");
            }

            public void Dispose()
            {
            }
        }

第三步,在Configure from Startup.cs:

    loggerFactory.AddProvider(new MicroserviceLoggerProvider());

【讨论】:

  • 那是有可能的。我只是认为日志前缀更像是一个扩展。我在我的控制器中注入了依赖项 ILogger 并且我的想法是编写这样的包装器/扩展来避免我当前在控制器中实现的日志方法,如 LogDebug(string msg)、LogInfo(string msg) 所有调用例如。 logger.LogDebug(前缀+味精)。我很惊讶在 ILogger 接口中没有找到 LogDebug 等方法。
  • 如果是扩展,代理类如何包装ILogger,那么我们调用proxyLogger.LogDebug(msg)。代理类在调用真正的 ILoger 实例之前添加前缀?
  • 是的,这听起来更接近我的初衷。只是不知道如何做到这一点,尽管实现 ILogger 是一种方式。
【解决方案2】:

我个人认为这不是一个好方法,“前缀”会重复很多。你为什么不改用Log Scopes 呢?

public IActionResult GetById(string id)
{
    TodoItem item;
    using (_logger.BeginScope("Message attached to logs created in the using block"))
    {
        _logger.LogInformation(LoggingEvents.GetItem, "Getting item {ID}", id);
        item = _todoRepository.Find(id);
        if (item == null)
        {
            _logger.LogWarning(LoggingEvents.GetItemNotFound, "GetById({ID}) NOT FOUND", id);
            return NotFound();
        }
    }
    return new ObjectResult(item);
}

输出

info: TodoApi.Controllers.TodoController[1002]
      => RequestId:0HKV9C49II9CK RequestPath:/api/todo/0 => TodoApi.Controllers.TodoController.GetById (TodoApi) => Message attached to logs created in the using block
      Getting item 0
warn: TodoApi.Controllers.TodoController[4000]
      => RequestId:0HKV9C49II9CK RequestPath:/api/todo/0 => TodoApi.Controllers.TodoController.GetById (TodoApi) => Message attached to logs created in the using block
      GetById(0) NOT FOUND

目前无法更改日志模板,这是 Asp.net Core 内置基本日志的限制。更强大的可以试试Serilog,继续使用ILogger接口,在program.cs类中修改几行代码

你也应该看看这个Benefits of Structured Logging vs basic logging

【讨论】:

    【解决方案3】:

    实现将prefix 添加到日志记录的扩展。

        public static class LogExtensions
        {
            public static void PrefixLogDebug(this ILogger logger, string message, string prefix = "Edward", params object[] args)
            {
                logger.LogDebug($"{prefix} {message}");
            }
        }
    

    用途:

            _log.PrefixLogDebug("Log From Prefix extension");
            _log.PrefixLogDebug("Log From Prefix extension", "New Prefix");
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-02-17
      • 2018-09-21
      • 2011-06-15
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多