本篇将进一步剖析.net core webapi中对日志的处理逻辑,方便在项目中灵活使用。
.net core中关于日志的接口都定义在Microsoft.Extensions.Logging这个名称空间中,
(所在的程序集是Microsoft.Extensions.Logging.Abstractions.dll)
它包含3个核心接口:
》ILoggerFactory 日志记录器工厂
》ILoggerProvider 日志记录器提供者
》ILogger 日志记录器
一、先看这三个接口是如何定义的。
1. ILoggerFactory的定义,方法名即表示方法作用:
1 public interface ILoggerFactory : IDisposable 2 { 3 //将 ILoggerProvider 添加到日志记录系统 。 4 void AddProvider (ILoggerProvider provider); 5 6 //创建一个新的 ILogger 实例, categoryName:记录器(Logger)生成的消息的类别名称 7 ILogger CreateLogger (string categoryName); 8 }
ILoggerFactory还有2个扩展方法用于创建 ILogger 的实例,定义如下:
1 public static class LoggerFactoryExtensions 2 { 3 public static ILogger CreateLogger (this ILoggerFactory factory, Type type); 4 public static ILogger<T> CreateLogger<T> (this ILoggerFactory factory); 5 }
2. ILoggerProvider的定义:
1 public interface ILoggerProvider : IDisposable 2 { 3 //创建一个新的 ILogger 实例 4 //categoryName 记录器生成的消息的类别名称。 5 public ILogger CreateLogger (string categoryName); 6 }
ILoggerProvider的派生类,分别向Console、Debug窗口等具体的终端输出日志。
1 Microsoft.Extensions.Logging.Abstractions.NullLoggerProvider 2 3 Microsoft.Extensions.Logging.AzureAppServices.BatchingLoggerProvider 4 5 Microsoft.Extensions.Logging.Console.ConsoleLoggerProvider 6 7 Microsoft.Extensions.Logging.Debug.DebugLoggerProvider 8 9 Microsoft.Extensions.Logging.EventLog.EventLogLoggerProvider 10 11 Microsoft.Extensions.Logging.EventSource.EventSourceLoggerProvider 12 13 Microsoft.Extensions.Logging.TraceSource.TraceSourceLoggerProvider
3.ILogger的定义:
1 public interface ILogger 2 { 3 //TState:要开始范围的状态的类型,state:范围的标识符。 4 public IDisposable BeginScope<TState> (TState state); 5 6 public bool IsEnabled (LogLevel logLevel); 7 8 //TState:要写入的对象的类型, state:要写入的项。 也可以是对象。 9 //formatter Func<TState,Exception,String> 函数,用于创建 state 和 exception 的 String 消息。 10 public void Log<TState> (LogLevel logLevel, EventId eventId, TState state, Exception exception, Func<TState,Exception,string> formatter); 11 }
泛型的ILogger
1 public interface ILogger<out TCategoryName> : ILogger 2 {
3 4 }
ILogger的扩展方法如下,其中LogXxx( )系列方法提供更友好的编程接口:
序号 | 扩展方法 | 描述 |
1 | BeginScope(ILogger, String, Object[]) | 设置消息格式并创建范围。 |
2 | Log(ILogger, LogLevel, EventId, Exception, String, Object[]) | 在指定的日志级别设置日志消息格式并写入该消息。 |
3 | Log(ILogger, LogLevel, EventId, String, Object[]) | (同上) |
4 | Log(ILogger, LogLevel, Exception, String, Object[]) | (同上) |
5 | Log(ILogger, LogLevel, String, Object[]) | (同上) |
6 | LogCritical(ILogger, EventId, Exception, String, Object[]) | 设置关键日志消息格式并写入该消息。 |
7 | LogCritical(ILogger, EventId, String, Object[]) | (同上) |
8 | LogCritical(ILogger, Exception, String, Object[]) | (同上) |
9 | LogCritical(ILogger, String, Object[]) | (同上) |
10 | LogDebug(ILogger, EventId, Exception, String, Object[]) | 设置调试日志消息格式并写入该消息。 |
11 | LogDebug(ILogger, EventId, String, Object[]) | (同上) |
12 | LogDebug(ILogger, Exception, String, Object[]) | (同上) |
13 | LogDebug(ILogger, String, Object[]) | (同上) |
14 | LogError(ILogger, EventId, Exception, String, Object[]) | 设置错误日志消息格式并写入该消息。 |
15 | LogError(ILogger, EventId, String, Object[]) | (同上) |
16 | LogError(ILogger, Exception, String, Object[]) | (同上) |
17 | LogError(ILogger, String, Object[]) | (同上) |
18 | LogInformation(ILogger, EventId, Exception, String, Object[]) | 设置信息日志消息格式并写入该消息。 |
19 | LogInformation(ILogger, EventId, String, Object[]) | (同上) |
20 | LogInformation(ILogger, Exception, String, Object[]) | (同上) |
21 | LogInformation(ILogger, String, Object[]) | (同上) |
22 | LogTrace(ILogger, EventId, Exception, String, Object[]) | 设置跟踪日志消息格式并写入该消息 |
23 | LogTrace(ILogger, EventId, String, Object[]) | (同上) |
24 | LogTrace(ILogger, Exception, String, Object[]) | (同上) |
25 | LogTrace(ILogger, String, Object[]) | (同上) |
26 | LogWarning(ILogger, EventId, Exception, String, Object[]) | 设置警告日志消息格式并写入该消息。 |
27 | LogWarning(ILogger, EventId, String, Object[]) | (同上) |
28 | LogWarning(ILogger, Exception, String, Object[]) | (同上) |
29 | LogWarning(ILogger, String, Object[]) | (同上) |
从上面的接口定义中我们要弄清2个问题:
1 . ILoggerFactory、ILoggerProvider、ILogger这3者的关系及其各自实例是怎样创建和调用的?
2 . ILoggerFactory.CreateLogger() 和 ILoggerProvider.CreateLogger()创建的ILogger是不是一回事?
二、要回答上面的2个问题,我们有必要从源码的角度来分析一下日志功能的三个核心组件的调用关系和运作流程。
2.1 以Project的方式运行项目,如下:
可以看到日志除了被记录到文件之外,在VS2019的调试控制台也有一条相同的日志输出,画面如下:
在UsersController.cs中调用
_logger.LogInformation("====== {p1} ====== {p2} ======", DateTime.Now.ToString("yyyy/MM/dd HH:mm:dd:fff"), list.Count.ToString());
后日志信息是如何被创建并分别输出到VS2019的调试控制台和文件中的呢?
2.2 UserController中构造函数public UsersController(ILogger<UsersController> logger, IUserDao userDao) 对 Ilogger的注入
来自于LoggingServiceCollectionExtensions类的AddLogging( )方法,如下:
( 此类存在于Microsoft.Extensions.Loggin.dll程序集,命名空间是Microsoft.Extensions.DependencyInjection )
可以看到 ILoggerFactory 对应的实例是 LoggerFactory,ILogger<> 对应的实例是泛型 Logger<>:
1 public static class LoggingServiceCollectionExtensions 2 { 3 public static IServiceCollection AddLogging(this IServiceCollection services) 4 { 5 return services.AddLogging(delegate 6 { 7 }); 8 } 9 10 public static IServiceCollection AddLogging(this IServiceCollection services, Action<ILoggingBuilder> configure) 11 { 12 if (services == null) 13 { 14 throw new ArgumentNullException("services"); 15 } 16 OptionsServiceCollectionExtensions.AddOptions(services); 17 services.TryAdd(ServiceDescriptor.Singleton<ILoggerFactory, LoggerFactory>()); 18 services.TryAdd(ServiceDescriptor.Singleton(typeof(ILogger<>), typeof(Logger<>))); 19 services.TryAddEnumerable(ServiceDescriptor.Singleton<IConfigureOptions<LoggerFilterOptions>>((IConfigureOptions<LoggerFilterOptions>)(object)new DefaultLoggerLevelConfigureOptions(LogLevel.Information))); 20 configure(new LoggingBuilder(services)); 21 return services; 22 } 23 }
泛型的Logger<>构造函数如下:
1 public class Logger<T> : ILogger<T>, ILogger 2 { 3 private readonly ILogger _logger; 4 5 public Logger(ILoggerFactory factory) 6 { 7 if (factory == null) 8 { 9 throw new ArgumentNullException("factory"); 10 } 11 _logger = factory.CreateLogger(TypeNameHelper.GetTypeDisplayName(typeof(T), fullName: true, includeGenericParameterNames: false, includeGenericParameters: false, ‘.‘)); 12 } 13 14 IDisposable ILogger.BeginScope<TState>(TState state) 15 { 16 return _logger.BeginScope(state); 17 } 18 19 bool ILogger.IsEnabled(LogLevel logLevel) 20 { 21 return _logger.IsEnabled(logLevel); 22 } 23 24 void ILogger.Log<TState>(LogLevel logLevel, EventId eventId, TState state, Exception exception, Func<TState, Exception, string> formatter) 25 { 26 _logger.Log(logLevel, eventId, state, exception, formatter); 27 } 28 }
继续看 ILoggerFactory 的实现类 LoggerFactory 的构造函数及 CreateLogger() 方法:
( LoggerFactory所在的程序集是Microsoft.Extensions.Logging.dll,命名空间:Microsoft.Extensions.Logging)
1 public class LoggerFactory : ILoggerFactory, IDisposable 2 { 3 private struct ProviderRegistration 4 { 5 public ILoggerProvider Provider; 6 7 public bool ShouldDispose; 8 } 9 10 private class DisposingLoggerFactory : ILoggerFactory, IDisposable 11 { 12 private readonly ILoggerFactory _loggerFactory; 13 14 private readonly ServiceProvider _serviceProvider; 15 16 public DisposingLoggerFactory(ILoggerFactory loggerFactory, ServiceProvider serviceProvider) 17 { 18 _loggerFactory = loggerFactory; 19 _serviceProvider = serviceProvider; 20 } 21 22 public void Dispose() 23 { 24 _serviceProvider.Dispose(); 25 } 26 27 public ILogger CreateLogger(string categoryName) 28 { 29 return _loggerFactory.CreateLogger(categoryName); 30 } 31 32 public void AddProvider(ILoggerProvider provider) 33 { 34 _loggerFactory.AddProvider(provider); 35 } 36 } 37 38 private static readonly LoggerRuleSelector RuleSelector = new LoggerRuleSelector(); 39 40 private readonly Dictionary<string, Logger> _loggers = new Dictionary<string, Logger>(StringComparer.Ordinal); 41 42 private readonly List<ProviderRegistration> _providerRegistrations = new List<ProviderRegistration>(); 43 44 private readonly object _sync = new object(); 45 46 private volatile bool _disposed; 47 48 private IDisposable _changeTokenRegistration; 49 50 private LoggerFilterOptions _filterOptions; 51 52 private LoggerFactoryScopeProvider _scopeProvider; 53 54 private LoggerFactoryOptions _factoryOptions; 55 56 public LoggerFactory() 57 : this(Enumerable.Empty<ILoggerProvider>()) 58 { 59 } 60 61 public LoggerFactory(IEnumerable<ILoggerProvider> providers) 62 : this(providers, new StaticFilterOptionsMonitor(new LoggerFilterOptions())) 63 { 64 } 65 66 public LoggerFactory(IEnumerable<ILoggerProvider> providers, LoggerFilterOptions filterOptions) 67 : this(providers, new StaticFilterOptionsMonitor(filterOptions)) 68 { 69 } 70 71 public LoggerFactory(IEnumerable<ILoggerProvider> providers, IOptionsMonitor<LoggerFilterOptions> filterOption) 72 : this(providers, filterOption, null) 73 { 74 } 75 76 public LoggerFactory(IEnumerable<ILoggerProvider> providers, IOptionsMonitor<LoggerFilterOptions> filterOption, IOptions<LoggerFactoryOptions> options = null) 77 { 78 _factoryOptions = ((options == null || options.get_Value() == null) ? new LoggerFactoryOptions() : options.get_Value()); 79 if (((uint)_factoryOptions.ActivityTrackingOptions & 0xFFFFFFE0u) != 0) 80 { 81 throw new ArgumentException(System.SR.Format(System.SR.InvalidActivityTrackingOptions, _factoryOptions.ActivityTrackingOptions), "options"); 82 } 83 foreach (ILoggerProvider provider in providers) 84 { 85 AddProviderRegistration(provider, dispose: false); 86 } 87 _changeTokenRegistration = OptionsMonitorExtensions.OnChange<LoggerFilterOptions>(filterOption, (Action<LoggerFilterOptions>)RefreshFilters); 88 RefreshFilters(filterOption.get_CurrentValue()); 89 } 90 91 public static ILoggerFactory Create(Action<ILoggingBuilder> configure) 92 { 93 ServiceCollection services = new ServiceCollection(); 94 services.AddLogging(configure); 95 ServiceProvider serviceProvider = services.BuildServiceProvider(); 96 ILoggerFactory service = serviceProvider.GetService<ILoggerFactory>(); 97 return new DisposingLoggerFactory(service, serviceProvider); 98 } 99 100 private void RefreshFilters(LoggerFilterOptions filterOptions) 101 { 102 lock (_sync) 103 { 104 _filterOptions = filterOptions; 105 foreach (KeyValuePair<string, Logger> logger2 in _loggers) 106 { 107 Logger value = logger2.Value; 108 (value.MessageLoggers, value.ScopeLoggers) = ApplyFilters(value.Loggers); 109 } 110 } 111 } 112 113 public ILogger CreateLogger(string categoryName) 114 { 115 if (CheckDisposed()) 116 { 117 throw new ObjectDisposedException("LoggerFactory"); 118 } 119 lock (_sync) 120 { 121 if (!_loggers.TryGetValue(categoryName, out var value)) 122 { 123 value = new Logger 124 { 125 Loggers = CreateLoggers(categoryName) 126 }; 127 (value.MessageLoggers, value.ScopeLoggers) = ApplyFilters(value.Loggers); 128 _loggers[categoryName] = value; 129 } 130 return value; 131 } 132 } 133 134 public void AddProvider(ILoggerProvider provider) 135 { 136 if (CheckDisposed()) 137 { 138 throw new ObjectDisposedException("LoggerFactory"); 139 } 140 lock (_sync) 141 { 142 AddProviderRegistration(provider, dispose: true); 143 foreach (KeyValuePair<string, Logger> logger2 in _loggers) 144 { 145 Logger value = logger2.Value; 146 LoggerInformation[] array = value.Loggers; 147 int num = array.Length; 148 Array.Resize(ref array, array.Length + 1); 149 array[num] = new LoggerInformation(provider, logger2.Key); 150 value.Loggers = array; 151 (value.MessageLoggers, value.ScopeLoggers) = ApplyFilters(value.Loggers); 152 } 153 } 154 } 155 156 private void AddProviderRegistration(ILoggerProvider provider, bool dispose) 157 { 158 _providerRegistrations.Add(new ProviderRegistration 159 { 160 Provider = provider, 161 ShouldDispose = dispose 162 }); 163 ISupportExternalScope supportExternalScope = provider as ISupportExternalScope; 164 if (supportExternalScope != null) 165 { 166 if (_scopeProvider == null) 167 { 168 _scopeProvider = new LoggerFactoryScopeProvider(_factoryOptions.ActivityTrackingOptions); 169 } 170 supportExternalScope.SetScopeProvider(_scopeProvider); 171 } 172 } 173 174 private LoggerInformation[] CreateLoggers(string categoryName) 175 { 176 LoggerInformation[] array = new LoggerInformation[_providerRegistrations.Count]; 177 for (int i = 0; i < _providerRegistrations.Count; i++) 178 { 179 array[i] = new LoggerInformation(_providerRegistrations[i].Provider, categoryName); 180 } 181 return array; 182 } 183 184 private (MessageLogger[] MessageLoggers, ScopeLogger[] ScopeLoggers) ApplyFilters(LoggerInformation[] loggers) 185 { 186 List<MessageLogger> list = new List<MessageLogger>(); 187 List<ScopeLogger> list2 = (_filterOptions.CaptureScopes ? new List<ScopeLogger>() : null); 188 for (int i = 0; i < loggers.Length; i++) 189 { 190 LoggerInformation loggerInformation = loggers[i]; 191 RuleSelector.Select(_filterOptions, loggerInformation.ProviderType, loggerInformation.Category, out var minLevel, out var filter); 192 if (!minLevel.HasValue || !(minLevel > LogLevel.Critical)) 193 { 194 list.Add(new MessageLogger(loggerInformation.Logger, loggerInformation.Category, loggerInformation.ProviderType.FullName, minLevel, filter)); 195 if (!loggerInformation.ExternalScope) 196 { 197 list2?.Add(new ScopeLogger(loggerInformation.Logger, null)); 198 } 199 } 200 } 201 if (_scopeProvider != null) 202 { 203 list2?.Add(new ScopeLogger(null, _scopeProvider)); 204 } 205 return (list.ToArray(), list2?.ToArray()); 206 } 207 208 protected virtual bool CheckDisposed() 209 { 210 return _disposed; 211 } 212 213 public void Dispose() 214 { 215 if (_disposed) 216 { 217 return; 218 } 219 _disposed = true; 220 _changeTokenRegistration?.Dispose(); 221 foreach (ProviderRegistration providerRegistration in _providerRegistrations) 222 { 223 try 224 { 225 if (providerRegistration.ShouldDispose) 226 { 227 providerRegistration.Provider.Dispose(); 228 } 229 } 230 catch 231 { 232 } 233 } 234 } 235 }
(1)通过上面的源码可以看到在 LoggerFactory 的构造函数中,添加了所有已注册到依赖注入容器的 ILoggerProvider 的实例;
(2)在LoggerFactory.CreateLogger( )函数中执行了如下操作:
(2.1)New了一个新的 Logger 实例 (且称为Factory Logger);
(2.2) 遍历注册到容器的ILoggerProvide实例并调用ILoggerProvide.CreateLogger( )
生成一组对应的 Logger 实例 (且称为Provider Logger) ;
(2.3)将(2.2)中的这组Logger 实例存入(2.1) 的属性 public LoggerInformation[ ] Loggers { get; set; } 中;
(2.4)将(2.2)中的这组Logger 实例应用过滤规则后存入 (2.1)的属性 public MessageLogger[ ] MessageLoggers { get; set; }中;
2.3 结论:泛型 Logger<T>的成员变量 private readonly ILogger _logger ; 的值是
LoggerFactory.CreateLogger( )函数生成的 新的 Logger 实例(Factory Logger),
该实例提供给程序员编程使用,不执行具体的日志写入操作。
2.4 最后来看 Logger 类、LoggerInformation 类和 MessageLogger 类,这3个类是内部类,源码如下:
1 internal class Logger : ILogger 2 { 3 private class Scope : IDisposable 4 { 5 private bool _isDisposed; 6 7 private IDisposable _disposable0; 8 9 private IDisposable _disposable1; 10 11 private readonly IDisposable[] _disposable; 12 13 public Scope(int count) 14 { 15 if (count > 2) 16 { 17 _disposable = new IDisposable[count - 2]; 18 } 19 } 20 21 public void SetDisposable(int index, IDisposable disposable) 22 { 23 switch (index) 24 { 25 case 0: 26 _disposable0 = disposable; 27 break; 28 case 1: 29 _disposable1 = disposable; 30 break; 31 default: 32 _disposable[index - 2] = disposable; 33 break; 34 } 35 } 36 37 public void Dispose() 38 { 39 if (_isDisposed) 40 { 41 return; 42 } 43 _disposable0?.Dispose(); 44 _disposable1?.Dispose(); 45 if (_disposable != null) 46 { 47 int num = _disposable.Length; 48 for (int i = 0; i != num; i++) 49 { 50 if (_disposable[i] != null) 51 { 52 _disposable[i].Dispose(); 53 } 54 } 55 } 56 _isDisposed = true; 57 } 58 } 59 60 public LoggerInformation[] Loggers { get; set; } 61 62 public MessageLogger[] MessageLoggers { get; set; } 63 64 public ScopeLogger[] ScopeLoggers { get; set; } 65 66 public void Log<TState>(LogLevel logLevel, EventId eventId, TState state, Exception exception, Func<TState, Exception, string> formatter) 67 { 68 MessageLogger[] messageLoggers = MessageLoggers; 69 if (messageLoggers == null) 70 { 71 return; 72 } 73 List<Exception> exceptions2 = null; 74 for (int i = 0; i < messageLoggers.Length; i++) 75 { 76 ref MessageLogger reference = ref messageLoggers[i]; 77 if (reference.IsEnabled(logLevel)) 78 { 79 LoggerLog(logLevel, eventId, reference.Logger, exception, formatter, ref exceptions2, in state); 80 } 81 } 82 if (exceptions2 != null && exceptions2.Count > 0) 83 { 84 ThrowLoggingError(exceptions2); 85 } 86 static void LoggerLog(LogLevel logLevel, EventId eventId, ILogger logger, Exception exception, Func<TState, Exception, string> formatter, ref List<Exception> exceptions, in TState state) 87 { 88 try 89 { 90 logger.Log(logLevel, eventId, state, exception, formatter); 91 } 92 catch (Exception item) 93 { 94 if (exceptions == null) 95 { 96 exceptions = new List<Exception>(); 97 } 98 exceptions.Add(item); 99 } 100 } 101 } 102 103 public bool IsEnabled(LogLevel logLevel) 104 { 105 MessageLogger[] messageLoggers = MessageLoggers; 106 if (messageLoggers == null) 107 { 108 return false; 109 } 110 List<Exception> exceptions2 = null; 111 int i; 112 for (i = 0; i < messageLoggers.Length; i++) 113 { 114 ref MessageLogger reference = ref messageLoggers[i]; 115 if (reference.IsEnabled(logLevel) && LoggerIsEnabled(logLevel, reference.Logger, ref exceptions2)) 116 { 117 break; 118 } 119 } 120 if (exceptions2 != null && exceptions2.Count > 0) 121 { 122 ThrowLoggingError(exceptions2); 123 } 124 if (i >= messageLoggers.Length) 125 { 126 return false; 127 } 128 return true; 129 static bool LoggerIsEnabled(LogLevel logLevel, ILogger logger, ref List<Exception> exceptions) 130 { 131 try 132 { 133 if (logger.IsEnabled(logLevel)) 134 { 135 return true; 136 } 137 } 138 catch (Exception item) 139 { 140 if (exceptions == null) 141 { 142 exceptions = new List<Exception>(); 143 } 144 exceptions.Add(item); 145 } 146 return false; 147 } 148 } 149 150 public IDisposable BeginScope<TState>(TState state) 151 { 152 ScopeLogger[] scopeLoggers = ScopeLoggers; 153 if (scopeLoggers == null) 154 { 155 return Microsoft.Extensions.Logging.NullScope.Instance; 156 } 157 if (scopeLoggers.Length == 1) 158 { 159 return scopeLoggers[0].CreateScope(state); 160 } 161 Scope scope = new Scope(scopeLoggers.Length); 162 List<Exception> list = null; 163 for (int i = 0; i < scopeLoggers.Length; i++) 164 { 165 ref ScopeLogger reference = ref scopeLoggers[i]; 166 try 167 { 168 scope.SetDisposable(i, reference.CreateScope(state)); 169 } 170 catch (Exception item) 171 { 172 if (list == null) 173 { 174 list = new List<Exception>(); 175 } 176 list.Add(item); 177 } 178 } 179 if (list != null && list.Count > 0) 180 { 181 ThrowLoggingError(list); 182 } 183 return scope; 184 } 185 186 private static void ThrowLoggingError(List<Exception> exceptions) 187 { 188 throw new AggregateException("An error occurred while writing to logger(s).", exceptions); 189 } 190 }
在Logger类中有定义属性 public LoggerInformation[ ] Loggers { get; set; } 和 public MessageLogger[ ] MessageLoggers { get; set; }
它的Log<TState>( )方法中遍历了 MessageLogger[ ](非 LoggerInformation[ ])并调用Logger实例(Provider Logger)
的 logger.Log(logLevel, eventId, state, exception, formatter); 方法执行具体的写入操作。
LoggerInformation类源码如下:
1 internal readonly struct LoggerInformation
2 {
3 public ILogger Logger { get; }
4
5 public string Category { get; }
6
7 public Type ProviderType { get; }
8
9 public bool ExternalScope { get; }
10
11 public LoggerInformation(ILoggerProvider provider, string category)
12 {
13 this = default(LoggerInformation);
14 ProviderType = provider.GetType();
15 Logger = provider.CreateLogger(category);
16 Category = category;
17 ExternalScope = provider is ISupportExternalScope;
18 }
19 }
LoggerInformation是对Category、Logger、ProviderType等的封装,
可以看到这里的 Logger是由Provider的CreateLogger( )函数生成的。
MessageLogger类的源码如下:
1 internal readonly struct MessageLogger 2 { 3 public ILogger Logger { get; } 4 5 public string Category { get; } 6 7 private string ProviderTypeFullName { get; } 8 9 public LogLevel? MinLevel { get; } 10 11 public Func<string, string, LogLevel, bool> Filter { get; } 12 13 public MessageLogger(ILogger logger, string category, string providerTypeFullName, LogLevel? minLevel, Func<string, string, LogLevel, bool> filter) 14 { 15 Logger = logger; 16 Category = category; 17 ProviderTypeFullName = providerTypeFullName; 18 MinLevel = minLevel; 19 Filter = filter; 20 } 21 22 public bool IsEnabled(LogLevel level) 23 { 24 if (MinLevel.HasValue && level < MinLevel) 25 { 26 return false; 27 } 28 if (Filter != null) 29 { 30 return Filter(ProviderTypeFullName, Category, level); 31 } 32 return true; 33 } 34 }
MessageLogger是对Category、Logger、Filter等的封装,
这里的 Logger(Provider Logger)是从LoggerInformation[ ]中传递来的。
2.5 根据上面的源码分析可以得到下面的一张关系图:
2.6 最后看一下 ILoggerProvider 的实例是何时被注入容器的。
2.6.1 Console日志中的 ILoggerProvider实例
用反编译工具查看Program.cs文件的Host.CreateDefaultBuilder(args)的源码如下:
(Host.CreateDefaultBuilder(args)在Microsoft.Extensions.Hosting.dll这个程序集中)
1 public static IHostBuilder CreateDefaultBuilder(string[] args) 2 { 3 HostBuilder hostBuilder = new HostBuilder(); 4 hostBuilder.UseContentRoot(Directory.GetCurrentDirectory()); 5 hostBuilder.ConfigureHostConfiguration(delegate(IConfigurationBuilder config) 6 { 7 config.AddEnvironmentVariables("DOTNET_"); 8 if (args != null) 9 { 10 config.AddCommandLine(args); 11 } 12 }); 13 hostBuilder.ConfigureAppConfiguration(delegate(HostBuilderContext hostingContext, IConfigurationBuilder config) 14 { 15 IHostEnvironment hostingEnvironment = hostingContext.HostingEnvironment; 16 bool value = hostingContext.Configuration.GetValue("hostBuilder:reloadConfigOnChange", defaultValue: true); 17 config.AddJsonFile("appsettings.json", optional: true, value).AddJsonFile("appsettings." + hostingEnvironment.EnvironmentName + ".json", optional: true, value); 18 if (hostingEnvironment.IsDevelopment() && !string.IsNullOrEmpty(hostingEnvironment.ApplicationName)) 19 { 20 Assembly assembly = Assembly.Load(new AssemblyName(hostingEnvironment.ApplicationName)); 21 if (assembly != null) 22 { 23 config.AddUserSecrets(assembly, optional: true); 24 } 25 } 26 config.AddEnvironmentVariables(); 27 if (args != null) 28 { 29 config.AddCommandLine(args); 30 } 31 }).ConfigureLogging(delegate(HostBuilderContext hostingContext, ILoggingBuilder logging) 32 { 33 bool flag2 = RuntimeInformation.IsOSPlatform(OSPlatform.Windows); 34 if (flag2) 35 { 36 logging.AddFilter<EventLogLoggerProvider>((LogLevel level) => level >= LogLevel.Warning); 37 } 38 logging.AddConfiguration(hostingContext.Configuration.GetSection("Logging")); 39 logging.AddConsole(); 40 logging.AddDebug(); 41 logging.AddEventSourceLogger(); 42 if (flag2) 43 { 44 logging.AddEventLog(); 45 } 46 LoggingBuilderExtensions.Configure(logging, (Action<LoggerFactoryOptions>)delegate(LoggerFactoryOptions options) 47 { 48 options.set_ActivityTrackingOptions((ActivityTrackingOptions)7); 49 }); 50 }).UseDefaultServiceProvider(delegate(HostBuilderContext context, ServiceProviderOptions options) 51 { 52 bool validateOnBuild = (options.ValidateScopes = context.HostingEnvironment.IsDevelopment()); 53 options.ValidateOnBuild = validateOnBuild; 54 }); 55 return hostBuilder; 56 }
在第39行可以看到系统在启动的时候加入了Console类型的日志输出终端。
ILoggingBuilder的定义如下,该接口只包含一个服务(Service)集合 :
1 public interface ILoggingBuilder 2 { 3 IServiceCollection Services { get; } 4 }
ILoggingBuilder.AddConsole()的第7行实现将 ConsoleLoggerProvider 注入到容器 IServiceCollection 中,源码如下:
1 public static ILoggingBuilder AddConsole(this ILoggingBuilder builder) 2 { 3 builder.AddConfiguration(); 4 builder.AddConsoleFormatter<JsonConsoleFormatter, JsonConsoleFormatterOptions>(); 5 builder.AddConsoleFormatter<SystemdConsoleFormatter, ConsoleFormatterOptions>(); 6 builder.AddConsoleFormatter<SimpleConsoleFormatter, SimpleConsoleFormatterOptions>(); 7 builder.Services.TryAddEnumerable(ServiceDescriptor.Singleton<ILoggerProvider, ConsoleLoggerProvider>()); 8 LoggerProviderOptions.RegisterProviderOptions<ConsoleLoggerOptions, ConsoleLoggerProvider>(builder.Services); 9 return builder; 10 }
ConsoleLoggerProvider是日志输出到控制台的具体提供者。
2.5.2 文件日志输出的 ILoggerProvider 实例
用反编译工具打开 Serilog.Extensions.Logging.File.dll 程序集,查找ILoggerFactory的AddFile( )这个扩展方法,找到的源码如下:
1 public static ILoggerFactory AddFile(this ILoggerFactory loggerFactory, string pathFormat,
LogLevel minimumLevel = LogLevel.Information,
IDictionary<string, LogLevel> levelOverrides = null,
bool isJson = false,
long? fileSizeLimitBytes = 1073741824L,
int? retainedFileCountLimit = 31,
string outputTemplate = "{Timestamp:o} {RequestId,13} [{Level:u3}] {Message} ({EventId:x8}){NewLine}{Exception}") 2 { 3 Logger logger = CreateLogger(pathFormat, minimumLevel, levelOverrides, isJson, fileSizeLimitBytes, retainedFileCountLimit, outputTemplate); 4 return loggerFactory.AddSerilog((ILogger)(object)logger, dispose: true); 5 }
继续查看loggerFactory.AddSerilog()方法,源码如下:
1 public static ILoggerFactory AddSerilog(this ILoggerFactory factory, ILogger logger = null, bool dispose = false) 2 { 3 if (factory == null) 4 { 5 throw new ArgumentNullException("factory"); 6 } 7 factory.AddProvider(new SerilogLoggerProvider(logger, dispose)); 8 return factory; 9 }
在第7行中ILoggerFactory添加了SerilogLoggerProvider这个日志记录器提供者(这里的实例不是依赖注入容器提供的),用于将日志写入文件中。
.net5 core webapi项目实战之十四:Microsoft.Extensions.Logging简介(番外篇)