.net5 core webapi项目实战之十四:Microsoft.Extensions.Logging简介(番外篇)

本篇将进一步剖析.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的方式运行项目,如下:

.net5 core webapi项目实战之十四:Microsoft.Extensions.Logging简介(番外篇)

可以看到日志除了被记录到文件之外,在VS2019的调试控制台也有一条相同的日志输出,画面如下:

.net5 core webapi项目实战之十四:Microsoft.Extensions.Logging简介(番外篇)

在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 对应的实例是 LoggerFactoryILogger<> 对应的实例是泛型 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 根据上面的源码分析可以得到下面的一张关系图:

.net5 core webapi项目实战之十四:Microsoft.Extensions.Logging简介(番外篇)

 

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简介(番外篇)

上一篇:C# 实现语音聊天


下一篇:附_1_Windows 常用消息及含义.md