美文网首页
2020-03-29 09:00 EFCore的日志系统

2020-03-29 09:00 EFCore的日志系统

作者: daiwei_9b9c | 来源:发表于2020-03-29 16:40 被阅读0次

    提纲

    1. 构建 ILoggerFactory 的实例
    2. 此实例中, 添加 ILoggerProvider 接口的实例到 ILoggerProvider的枚举
    3. 通过 ILoggerProvider的枚举创建 ILogger的实例的枚举,并通过 Composite 模式将日志写入到各个 ILogger 中
    4. 各个 ILogger 实例根据日志级别判断是否应该写入日志, 准备写入日志(返回 Dispose实例),写入日志,释放 Dispose实例;

    EFCore的Logging项目

    [https://github.com/dotnet/EntityFramework.Docs/tree/master/samples/core/Miscellaneous/Logging]

    注意这个项目不能使用 add-migration InitiateContext 方式去创建数据库和表,

    因为其在构造函数中调用了 OnConfigure 方法,而此方法会创建 日志工厂的实例,但貌似在 NPM中创建日志工厂的实例将失败,因为NPM并没有加载所有的库而抛出一个未找到方法的异常;
    所以,这儿有一行代码

    创建数据库(如果数据库不存在,则创建数据库和所有的表,如果数据库存在,则不继续处理,也不会根据当前类型变动表结构
    db.Database.EnsureCreated();  
    

    主要代码如下

     public static readonly ILoggerFactory MyLoggerFactory
                = LoggerFactory.Create(builder => { builder.AddConsole();
                });
    protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
                => optionsBuilder
                    .UseLoggerFactory(MyLoggerFactory) // Warning: 应该使用静态实例
                    .UseSqlServer(
                        @"Server=(localdb)\mssqllocaldb;Database=EFLogging;Trusted_Connection=True;ConnectRetryCount=0");
    

    主要逻辑就是, 使用静态方法构建 Microsoft.Extensions.Logging.LoggerFactory.Create 构架 ILoggerFactory 的实例,
    使用包 Microsoft.Extensions.Logging.Console 内的扩展方法 (AddConsole) 使得日志消息输出到 Console
    扩展方法定义

    估计,这个方法是再 ILoggingBuider中添加了一个  ConsoleLoggerProvider 的实例,
    并且为了支持链式调用,又返回了 传入ILoggingBuilder 实例
    public static ILoggingBuilder AddConsole(this ILoggingBuilder builder);
    public static ILoggingBuilder AddConsole(this ILoggingBuilder builder, Action<ConsoleLoggerOptions> configure);
    

    从上面,至少我们看到了2个类, ILoggingBuilder, ILoggerFactory,

    LoggerFactory 类

    public class LoggerFactory : ILoggerFactory, IDisposable
    {
            public LoggerFactory();
            public LoggerFactory(IEnumerable<ILoggerProvider> providers);
            public LoggerFactory(IEnumerable<ILoggerProvider> providers, LoggerFilterOptions filterOptions);
            public LoggerFactory(IEnumerable<ILoggerProvider> providers, IOptionsMonitor<LoggerFilterOptions> filterOption);
            public static ILoggerFactory Create(Action<ILoggingBuilder> configure);
            public void AddProvider(ILoggerProvider provider);
            下面这个创建 Microsoft.Extensions.Logging.ILogger 的实例,  并指定此实例记录的消息的分类名称
            public ILogger CreateLogger(string categoryName);
    

    上面代码又引入了更多的类, ILoggerProvider, ILogger
    如果继续往下理,可能会更多更复杂的类出来,我们只看最基础的,也就是 Microsoft.Extensions.Logging
    这个命名空间内的内容;

    Microsoft.Extensions.Logging.LogLevel 定义日志级别,

    更有7个日志级别,从低到高,越低日志记录就越详细

            // 摘要:记录最详细的信息
           Trace = 0,
            // 摘要:记录调试的信息
            Debug = 1,
            // 摘要:记录调用工作流信息,
            Information = 2,
            // 摘要:记录不正常或者非预期的事件
            Warning = 3,
            // 摘要:记录异常
            Error = 4,
            // 摘要:记录应用奔溃
            Critical = 5,
            // 摘要:
            None = 6
    

    直接使用日志级别的是 ILogger 接口, 实现 ILogger 接口的类是 Logger<T> 的类,但是这个类的构造函数需要传入 ILoggerFactory 接口的实例,

    ILoggerFactory 接口

    代表一个配置日志系统和根据已注册的 ILoggerProvider 创建ILogger实例的类
    ILoggerFactory 很简单

        public interface ILoggerFactory : IDisposable
        {
            void AddProvider(ILoggerProvider provider);
            ILogger CreateLogger(string categoryName);
        }
    

    这儿很明显会根据已注册的 ILoggerProvider 列表,
    创建一个 ILogger的实例,当然对于这个 ILogger的实例, 里面应该会遍历调用已注册的 ILoggerProvider列表的里面的方法(所以 ILoggerProvider 应该也有 CreateLogger 的方法) ,然后写日志时,再遍历调用 ILogger 实例里面的日志记录方法

    ILoggerProvider接口

        public interface ILoggerProvider : IDisposable
       {
            ILogger CreateLogger(string categoryName);
       }
    

    ILogger 接口

    这个接口应该是真正将日志进行处理的接口(例如写文件\写控制台\或者啥都不干,例如 NullLogger)
    表示一个处理日志的类型

             /**开始记录一个逻辑操作区间(基本上就是写日志之前做点事情,写完日志之后再调用这个东东返回的 Dispose 方法**/
            IDisposable BeginScope<TState>(TState state);
          
           /** 摘要:对于给定的日志级别,是否记录日志**/
            bool IsEnabled(LogLevel logLevel);
    
          /**写日志的方法
          logLevel 日志级别
          eventId 事件Id
          state 被写入的对象
          exception 和对象相关的异常 
          formatter 转换 state 和 exception 转换为字符串的方法,以便写入日志内容 **/
            void Log<TState>(LogLevel logLevel, EventId eventId, TState state, Exception exception, Func<TState, Exception, string> formatter);
        }
    

    写日志时还会写入日志的事件Id

    EventId 接口

        // 日志事件Id ( 结构 ),主要有 Id 和 Name属性
        public readonly struct EventId
        {    
           public EventId(int id, string name = null);
           public int Id { get; }
           public string Name { get; }
           public static implicit operator EventId(int i);
         ...
      }
    

    相关文章

      网友评论

          本文标题:2020-03-29 09:00 EFCore的日志系统

          本文链接:https://www.haomeiwen.com/subject/knuyuhtx.html