提纲

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

执行数据库操作任务

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

主要代码如下:

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

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

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

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

LoggerFactory 类

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

上面代码又引入了更多的类, ILoggerProvider, ILogger
如果继续往下理,可能会更多更复杂的类出来,我们只看最基础的,也就是 Microsoft.Extensions.Logging
这个命名空间内的内容;
Microsoft.Extensions.Logging.LogLevel 定义日志级别,
更有7个日志级别,从低到高,越低日志记录就越详细

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

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

ILoggerFactory 接口

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

  1. public interface ILoggerFactory : IDisposable
  2. {
  3. void AddProvider(ILoggerProvider provider);
  4. ILogger CreateLogger(string categoryName);
  5. }

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

ILoggerProvider接口

  1. public interface ILoggerProvider : IDisposable
  2. {
  3. ILogger CreateLogger(string categoryName);
  4. }

ILogger 接口

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

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

写日志时还会写入日志的事件Id
EventId接口

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