提纲
- 构建 ILoggerFactory 的实例
- 此实例中, 添加 ILoggerProvider 接口的实例到 ILoggerProvider的枚举
- 通过 ILoggerProvider的枚举创建 ILogger的实例的枚举,并通过 Composite 模式将日志写入到各个 ILogger 中
- 各个 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); ... }
网友评论