前言

鲁迅都说:没有日志的系统不能上线(鲁迅说:这句我没说过,但是在理)!日志对于一个系统而言,特别重要,不管是用于事务审计,还是用于系统排错,还是用于安全追踪.....都扮演了很重要的角色;之前有很多第三方的日志框架也很给力,如Log4Net、NLog和Serilog等,在.NetCore中也集成了日志模型,使用便捷,同时很方便的与第三方日志框架进行集成扩展;

正文

实例演示之前,先了解一下日志级别,后续如果不想输出全部日志,可以通过日志级别进行过滤,同时通过日志级别可以标注日志内容的重要程度:

namespace Microsoft.Extensions.Logging
{// 日志级别从下往上递增,所以根据级别可以过滤掉低级别的日志信息public enum LogLevel{Trace,Debug,Information,Warning,Error,Critical,None}
}

来一个控制台程序实例演示:

运行结果:

咋样,使用还是依旧简单,这里是控制台程序,还需要写配置框架和依赖注入相关的代码逻辑,如果在WebAPI项目,直接就可以使用日志记录了,如下:

对于WebAPI项目而言,在项目启动流程分析的时候,就提到内部已经注册了相关服务了,所以才能这样如此简单的使用;

难道日志就这样结束了吗?猜想看到这的小伙伴也不甘心,是的,得进一步了解,不需要特别深入,但至少得知道关键嘛,对不对?

老规矩,程序中能看到日志相关点,当然就从这开始,看看是如何注册日志啊相关服务的:

对应代码:

namespace Microsoft.Extensions.DependencyInjection
{// IServiceCollection的扩展方法,用于注册日志相关服务public static class LoggingServiceCollectionExtensions{public static IServiceCollection AddLogging(this IServiceCollection services){return services.AddLogging(delegate{});}// 核心方法,上面的方法就是调用下面这个public static IServiceCollection AddLogging(this IServiceCollection services, Action<ILoggingBuilder> configure){if (services == null){throw new ArgumentNullException("services");}// 为了支持Options选项,得注册Options相关服务,上篇讲过services.AddOptions();// 注册ILoggerFactoryservices.TryAdd(ServiceDescriptor.Singleton<ILoggerFactory, LoggerFactory>());// 注册ILoggerservices.TryAdd(ServiceDescriptor.Singleton(typeof(ILogger<>), typeof(Logger<>)));// 注册日志级别过滤,并默认设置级别为Informationservices.TryAddEnumerable(ServiceDescriptor.Singleton((IConfigureOptions<LoggerFilterOptions>)new DefaultLoggerLevelConfigureOptions(LogLevel.Information)));// 执行传入的委托方法configure(new LoggingBuilder(services));return services;}}
}

日志相关服务注册了解了,那接着看看关键实现,其实日志记录有三个核心类型:ILogger、ILoggerFactory和ILoggerProvider,对应的实现分别是Logger、LoggerFactory、xxxLoggerProvider;

  • xxxLoggerProvider:针对于不同的目的地创建对应的xxxLogger,这里的xxxLogger负责在目的地(文件、数据库、控制台等)写入内容;

  • LoggerFactory:负责创建Logger,其中包含所有注册的xxxLoggerProvider对应Logger;

  • Logger:以上两种;

扒开这三个类型的定义,简单看看都定义了什么....

  • ILogger/Logger

    namespace Microsoft.Extensions.Logging
    {public interface ILogger{// 记录日志方法,其中包含日志级别、事件ID、写入的内容、格式化内容等void Log<TState>(LogLevel logLevel, EventId eventId, TState state, Exception exception, Func<TState, Exception, string> formatter);// 判断对应的日志级别是否可用bool IsEnabled(LogLevel logLevel);// 日志作用域IDisposable BeginScope<TState>(TState state);}
    }
    

    Logger中挑了比较关键的属性和方法简单说说

    internal class Logger : ILogger
    {// 用于缓存真正Logger记录器的public LoggerInformation[] Loggers { get; set; }public MessageLogger[] MessageLoggers { get; set; }// 这个用于缓存日志作用域Loggers    public ScopeLogger[] ScopeLoggers { get; set; }// Log日志记录方法    public void Log<TState>(LogLevel logLevel, EventId eventId, TState state, Exception exception, Func<TState, Exception, string> formatter){var loggers = MessageLoggers;if (loggers == null){return;}List<Exception> exceptions = null;// 遍历对应的Loggers        for (var i = 0; i < loggers.Length; i++){ref readonly var loggerInfo = ref loggers[i];if (!loggerInfo.IsEnabled(logLevel)){continue;}// 执行内部方法LoggerLog(logLevel, eventId, loggerInfo.Logger, exception, formatter, ref exceptions, state);}if (exceptions != null && exceptions.Count > 0){ThrowLoggingError(exceptions);}static void LoggerLog(LogLevel logLevel, EventId eventId, ILogger logger, Exception exception, Func<TState, Exception, string> formatter, ref List<Exception> exceptions, in TState state){try{// 记录日志内容            logger.Log(logLevel, eventId, state, exception, formatter);}catch (Exception ex){if (exceptions == null){exceptions = new List<Exception>();}exceptions.Add(ex);}}}
    }
    

    ILoggerFactory/LoggerFactory

    namespace Microsoft.Extensions.Logging
    {// 创建 ILogger和注册LoggerProviderpublic interface ILoggerFactory : IDisposable{// 根据名称创建ILogger    ILogger CreateLogger(string categoryName);// 注册ILoggerProvidervoid AddProvider(ILoggerProvider provider);}
    }
    ........省略方法-私下研究......
    
    // LoggerFactory挑了几个关键方法进行说明
    // 创建Logger
    public ILogger CreateLogger(string categoryName)
    {if (CheckDisposed()){throw new ObjectDisposedException(nameof(LoggerFactory));}lock (_sync){if (!_loggers.TryGetValue(categoryName, out var logger)){// new一个Logger,这是LoggerFactory管理的Logger        logger = new Logger{// 根据注册的xxxLoggerProvider创建具体的xxxLogger// 并将其缓存到LoggerFactory创建的Logger对应的Loggers属性中                            Loggers = CreateLoggers(categoryName),};// 根据消息级别和作用域范围,赋值对应的MessageLoggers、ScopeLoggers(logger.MessageLoggers, logger.ScopeLoggers) = ApplyFilters(logger.Loggers);// 同时将创建出来的logger缓存在字典中_loggers[categoryName] = logger;}return logger;}
    }
    // 这个用于注册具体的xxxLoggerProvider
    public void AddProvider(ILoggerProvider provider)
    {if (CheckDisposed()){throw new ObjectDisposedException(nameof(LoggerFactory));}lock (_sync){// 将传入的provider封装了结构体进行缓存   AddProviderRegistration(provider, dispose: true);// 同时创建对应的logger,创建过程和上面一样foreach (var existingLogger in _loggers){var logger = existingLogger.Value;var loggerInformation = logger.Loggers;// 在原来基础上增加具体的xxxLoggervar newLoggerIndex = loggerInformation.Length;Array.Resize(ref loggerInformation, loggerInformation.Length + 1);loggerInformation[newLoggerIndex] = new LoggerInformation(provider, existingLogger.Key);logger.Loggers = loggerInformation;(logger.MessageLoggers, logger.ScopeLoggers) = ApplyFilters(logger.Loggers);}}
    }
    // 封装对应的xxxLoggerProvider,然后进行缓存
    private void AddProviderRegistration(ILoggerProvider provider, bool dispose)
    {// 先封装成结构体,然后在缓存,方便后续生命周期管理_providerRegistrations.Add(new ProviderRegistration{Provider = provider,ShouldDispose = dispose});// 判断是否继承了ISupportExternalScope if (provider is ISupportExternalScope supportsExternalScope){if (_scopeProvider == null){_scopeProvider = new LoggerExternalScopeProvider();}supportsExternalScope.SetScopeProvider(_scopeProvider);}
    }
    // 创建具体的xxxLogger
    private LoggerInformation[] CreateLoggers(string categoryName)
    {// 根据注册的xxxLoggerProvider个数初始化一个数组var loggers = new LoggerInformation[_providerRegistrations.Count];// 遍历注册的xxxLoggerProvider,创建具体的xxxLogger   for (var i = 0; i < _providerRegistrations.Count; i++){// 创建具体的xxxLogger    loggers[i] = new LoggerInformation(_providerRegistrations[i].Provider, categoryName);}return loggers;
    }
    ........省略方法-私下研究......
    
  • ILoggerProvider/xxxLoggerProvider

    namespace Microsoft.Extensions.Logging
    {public interface ILoggerProvider : IDisposable{// 根据名称创建对应的LoggerILogger CreateLogger(string categoryName);}
    }
    namespace Microsoft.Extensions.Logging
    {public interface ILoggerProvider : IDisposable{// 根据名称创建对应的LoggerILogger CreateLogger(string categoryName);}
    }
    
    namespace Microsoft.Extensions.Logging.Console
    {[ProviderAlias("Console")]public class ConsoleLoggerProvider : ILoggerProvider, ISupportExternalScope{// 支持Options动态监听  private readonly IOptionsMonitor<ConsoleLoggerOptions> _options;// 缓存对应的xxxLogger        private readonly ConcurrentDictionary<string, ConsoleLogger> _loggers;// 日志处理        private readonly ConsoleLoggerProcessor _messageQueue;private IDisposable _optionsReloadToken;private IExternalScopeProvider _scopeProvider = NullExternalScopeProvider.Instance;// 构造函数,初始化public ConsoleLoggerProvider(IOptionsMonitor<ConsoleLoggerOptions> options){_options = options;_loggers = new ConcurrentDictionary<string, ConsoleLogger>();ReloadLoggerOptions(options.CurrentValue);_optionsReloadToken = _options.OnChange(ReloadLoggerOptions);_messageQueue = new ConsoleLoggerProcessor();// 判断是否是Windows系统,因为即日至的方式不一样            if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)){  // 如果是windows_messageQueue.Console = new WindowsLogConsole();_messageQueue.ErrorConsole = new WindowsLogConsole(stdErr: true);}else{  // 如果是其他平台_messageQueue.Console = new AnsiLogConsole(new AnsiSystemConsole());_messageQueue.ErrorConsole = new AnsiLogConsole(new AnsiSystemConsole(stdErr: true));}}private void ReloadLoggerOptions(ConsoleLoggerOptions options){foreach (var logger in _loggers){logger.Value.Options = options;}}// 根据名称获取或创建对应xxxLoggerpublic ILogger CreateLogger(string name){// 根据名称获取,如果没有,则根据传入的委托方法进行创建        return _loggers.GetOrAdd(name, loggerName => new ConsoleLogger(name, _messageQueue){Options = _options.CurrentValue,ScopeProvider = _scopeProvider});}......省略一些方法,私下研究.......}
    }
    

想了想,这里就不一一针对不同目的地(比如Trace、EventLog)扒代码看了,不然说着说着就变成了代码解读了,如果有兴趣,可以私下照着以下思路去看看代码:

每一个目的地日志记录都会有一个实现xxxLoggerProvider和对应的记录器xxxLogger(真实记录日志内容),LoggerFactory创建的Logger(暴露给程序员使用的)包含了对应的具体的记录器,比如以写入日志控制台为例:

有一个ConsoleLoggerProvider的实现和对应的ConsoleLogger,ConsoleLoggerProvider负责通过名称创建对应的ConsoleLogger,而LoggerFactory创建出来的Logger就是包含已注册ConsoleLoggerProvider创建出来的ConsoleLogger;从而我们调用记录日志方法的时候,其实最终是调用ConsoleLoggerProvider创建的ConsoleLogger对象方法;

总结

本来想着日志应该用的很频繁了,直接举例演示就OK了,但是写着写着,用的多不一定清除关键步骤,于是又扒了下代码,挑出了几个关键方法简单的说说,希望使用的小伙伴不困惑,深入研究就靠私下好好瞅瞅代码了;

下一节实例演示日志的使用、日志的作用域、集成第三方日志框架进行日志扩展.....

------------------------------------------------

一个被程序搞丑的帅小伙,关注"Code综艺圈",识别关注跟我一起学~~~

跟我一起学.NetCore之日志(Log)模型核心相关推荐

  1. 跟我一起学.NetCore之文件系统应用及核心浅析

    前言 在开发过程中,肯定避免不了读取文件操作,比如读取配置文件.上传和下载文件.Web中html.js.css.图片等静态资源的访问:在配置文件读取章节中有说到,针对不同配置源数据读取由对应的ICon ...

  2. 跟我一起学.NetCore之MVC过滤器,这篇看完走路可以仰着头走

    前言 MVC过滤器在之前Asp.Net的时候就已经广泛使用啦,不管是面试还是工作,总有一个考点或是需求涉及到,可以毫不疑问的说,这个技术点是非常重要的: 在之前参与的面试中,得知很多小伙伴只知道有一两 ...

  3. 跟我一起学.NetCore之Options实例演示及分析

    前言 来啦!来啦!上一节一堆代码,是不是感觉甚是无味啊?没关系,这里结合上一节内容专注举例演示,绝不废话!走起~~~~~ 正文 老规矩,一个WebApi项目走起,项目结构如下: 上一节中提到,Opti ...

  4. Python统计网站访问日志log中的IP信息,并排序。。

    Python统计网站访问日志log中的IP信息,并排序,打印排名靠前的IP及访问量.示例代码如下: #!/usr/bin/env pythonipdict = {}file = open(" ...

  5. 跟我一起学.NetCore之熟悉的接口权限验证不能少(Jwt)

    前言 权限管控对于一个系统来说是非常重要的,最熟悉不过的是菜单权限和数据权限,上一节通过Jwt实现了认证,接下来用它实现接口权限的验证,为什么不是菜单权限呢?对于前后端分离而言,称其为接口权限感觉比较 ...

  6. 跟我一起学.NetCore之WebApi接口裸奔有风险(Jwt)

    前言 撸码需谨慎,裸奔有风险.经常在一些技术交流群中了解到,还有很多小伙伴的项目中Api接口没有做任何安全机制验证,直接就裸奔了,对于一些临时项目或是个人小项目还好,其余的话,建议小伙伴们酌情考虑都加 ...

  7. 跟我一起学.NetCore之静态文件处理的那些事

    前言 如今前后端分离开发模式如火如荼,开发职责更加分明(当然前后端一起搞的模式也没有完全褪去):而对于每个公司产品实施来说,部署模式会稍有差别,有的会单独将前端文件部署为一个站点,有的会将前端文件和后 ...

  8. 跟我一起学.NetCore之选项(Options)核心类型简介

    前言 .NetCore中提供的选项框架,我把其理解为配置组,主要是将服务中可供配置的项提取出来,封装成一个类型:从而服务可根据应用场景进行相关配置项的设置来满足需求,其中使用了依赖注入的形式,使得更加 ...

  9. 如何正确使用日志Log

    如何正确使用日志Log title: 如何正确使用日志Log date: 2015-01-08 12:54:46 categories: [Python] tags: [Python,log] --- ...

最新文章

  1. python编程基础知识体系_Python 编程核心知识体系-基础|数据类型|控制流(一)...
  2. hibernate的异常 Session was already closed
  3. native react 集成微信登录_react native 接入微信登陆(Android)
  4. iOS开源项目周报0316
  5. 华为手机在开发Android调试时logcat不显示输出信息的解决办法
  6. Homebrew软件包管理器中发现RCE漏洞,小心你的Mac和Linux
  7. 有条件忽略测试的JUnit规则
  8. Beaglebone Back学习五(PWM测试)
  9. 【Python CheckiO 题解】Fizz Buzz
  10. java有没有number数据类型_Java基本数据类型之Number
  11. EXCEL下载功能在XP系统中运行是好好的,到windows2003系统上,就报错
  12. 又一尴尬问题!不少用户反馈iPhone 12屏幕特别“黄”
  13. 关于冒泡、快排、二分排序算法分析
  14. 完全背包问题(Java)
  15. 获取某一天的开始时间
  16. SSO单点登录之用户认证实践
  17. java获取用户地理位置_Java获取用户访问IP及地理位置的方法详解
  18. linux 目录防篡改,一种基于Linux虚拟文件系统的防篡改方法及系统的制作方法
  19. Echarts 柏拉图
  20. 短视频系统源代码,加载本地图片和加载网络图片

热门文章

  1. 【Luogu3931】SAC E#1 - 一道难题 Tree
  2. js最全的十种跨域解决方案
  3. ASP.NET2.0_多语言本地化应用程序
  4. 梅原对justin wang 真人版
  5. linkButton与Button 的click事件与onclientClick事件
  6. esp32 cam工作电流_我如何在家工作:Cam的生产力之痛
  7. Qtum 量子链跨链原子激活 实现 QTUM-BTC 交换
  8. 【leetcode】521. Longest Uncommon Subsequence I
  9. 10 个有关 String 的面试问题
  10. 微信小程序 --- 页面跳转