夫天地者,万物之逆旅;光阴者,百代之过客。而浮生若梦,为欢几何?
.Net Core 项目实战:我是如何定义自己的日志记录组件(下篇)

前言——尴尬

笔者本想将内容全部放到一篇文章中,没想到内容太多出现了413错误,又懒得去修改配置,所以就将内容分为了上下两篇。

创建 RabbitLoggerProvider实现ILoggerProvider 接口

RabbitLoggerProvider 负责创建 RabbitLogger,笔者的日志组件需要支持 日志作用域,所以我们还需实现ISupportExternalScope接口。在RabbitLoggerProvider的构造函数中接收IOptionsMonitor<RabbitLoggerOptions> rabbitLoggerOptions 和 ILoggerPersistence loggerPersistence 两个参数,下面是核心代码:

/// <summary>
    /// 
    /// </summary>
    public class RabbitLoggerProvider : ILoggerProvider, ISupportExternalScope
    {
        /// <summary>
        /// 
        /// </summary>
        private readonly ConcurrentDictionary<string, RabbitLogger> _loggers;
        /// <summary>
        /// 
        /// </summary>
        private readonly IOptionsMonitor<RabbitLoggerOptions> _RabbitLoggerOptions;
        
        /// <summary>
        /// 
        /// </summary>
        private readonly ILoggerPersistence _LoggerPersistence;
        /// <summary>
        /// 
        /// </summary>
        private IExternalScopeProvider _scopeProvider;
        /// <summary>
        /// 
        /// </summary>
        /// <param name="rabbitLoggerOptions"></param>
        public RabbitLoggerProvider(IOptionsMonitor<RabbitLoggerOptions> rabbitLoggerOptions, ILoggerPersistence loggerPersistence)
        {
            _loggers = new ConcurrentDictionary<string, RabbitLogger>();
            _RabbitLoggerOptions = rabbitLoggerOptions;
            _LoggerPersistence = loggerPersistence;
            ReloadLoggerOptions(_RabbitLoggerOptions.CurrentValue);
        }
         
        /// <summary>
        /// 
        /// </summary>
        /// <param name="categoryName"></param>
        /// <returns></returns>
        public ILogger CreateLogger(string categoryName)
        {
            return _loggers.GetOrAdd(categoryName, loggerName => new RabbitLogger(categoryName, _LoggerPersistence)
            {
                Options = _RabbitLoggerOptions.CurrentValue,
                ScopeProvider = _scopeProvider
            });
        }
         
        /// <summary>
        /// 
        /// </summary>
        /// <param name="scopeProvider"></param>
        public void SetScopeProvider(IExternalScopeProvider scopeProvider)
        {
            _scopeProvider = scopeProvider;
        }
    }


创建 IServiceCollection 扩展方法 AddIMLogger

定义一个静态类,添加 IServiceCollection类的扩展方法 AddIMLogger,这样在Startup的ConfigureServices方法中就可以使用 services.AddIMLogger 的形式注册我们写的组件。核心代码如下:

/// <summary>
    /// 
    /// </summary>
    public static class LoggerServiceCollectionExtensions
    {
        /// <summary>
        /// 
        /// </summary>
        /// <param name="services"></param>
        /// <returns></returns>
        private static IServiceCollection AddIMLogger(this IServiceCollection services)
        {
            services.AddOptions();
            services.AddSingleton<ILoggerPersistence, RabbitLoggerPersistence>();
            services.AddLogging(t =>
            {
                t.AddIMLogger();
            });
            return services;
        }
        /// <summary>
        /// 所有的配置都从配置文件读取
        /// </summary>
        /// <param name="services"></param>
        /// <param name="configuration"></param>
        /// <returns></returns>
        public static IServiceCollection AddIMLogger(this IServiceCollection services, IConfiguration configuration)
        {
            services.Configure<RabbitLoggerOptions>(configuration);
            services.Configure<RabbitConnectOptions>(configuration.GetSection("RabbitMQ"));
            services.AddIMLogger();
            return services;
        }
      }

Startup 注册服务

 services.AddIMLogger(Configuration.GetSection("Logging"));

appsetting.json 配置

 "Logging": {
    "LogLevel": {
      "Default": "Information",
      "System": "Error",
      "Microsoft": "Error"
    },
    "IncludeScopes": true,
    "EnableAsync": true,
    "ApplicationName": "测试",
    "MinLevel": "Trace",
    "RabbitMQ": {
      "HostName": "127.0.0.1",
      "Port": 5672,
      "UserName": "guest",
      "Password": "guest",
      "ExChange": "log",
      "RoutingKey": "log",
      "Queue": "log",
      "IsDurable": true,
      "RetryInterval": 10,
      "EnableBuffer": true
    }
  },

我们组件的配置和Core内置的日记配置都放到 Logging Key下,这样方便管理和移植。

Controller 中使用

[Route("api/[controller]")]
    [ApiController]
    public class ValuesController : ControllerBase
    {
        private ILogger _Logger;
        public ValuesController(ILogger<ValuesController> logger)
        {
            this._Logger = logger;
        }
        // GET api/values
        [HttpGet]
        public ActionResult<IEnumerable<string>> Get()
        {
            // 具有作用域的使用示例
            using (_Logger.BeginScope("测试啦。。"))
            {
                _Logger.LogInformation("LogInformation233333333333");
                _Logger.LogDebug("Debug级别日志");
                _Logger.LogWarning("Warning级别日志。。。");
            }
            return new string[] { "value1", "value2" };
        }

组件支持功能及优势

到此我们的组件已经开发完成了,现在来讲讲该组件的优势:

1.支持appsetting.json 配置变更后自动应用新更改。如 将MinLevel改成Error后在程序不重启的情况下只会记录大于Error级别的日志。

2.支持日志类别的过滤,缩小输出日志的范围。比如在 Loglevel key下增加一项  "Glodon.IM.Logger.Test.Controllers" :"Trace",那么将输出该类下所有大于Trace级别的埋点日志,这对调试非常有用。

3.支持日志作用域。笔者暂时没体验到爽的地方。

4.支持同步和异步记录日志。异步时支持缓冲池。

5.使用方式和Core的方式一样。



作者:暗夜余晖

本文版权归作者所有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。

0

支持

0

反对

posted @2019-2-1  拜读(796)

评论列表

评论内容:



喜欢请打赏

支付宝 微信

请放心支付