/// <param>The <see cref="ILoggingBuilder"/> to use.</param> public static ILoggingBuilder AddConsole(this ILoggingBuilder builder) { builder.AddConfiguration(); builder.AddConsoleFormatter<JsonConsoleFormatter, JsonConsoleFormatterOptions>(); builder.AddConsoleFormatter<SystemdConsoleFormatter, ConsoleFormatterOptions>(); builder.AddConsoleFormatter<SimpleConsoleFormatter, SimpleConsoleFormatterOptions>(); builder.Services.TryAddEnumerable(ServiceDescriptor.Singleton<ILoggerProvider, ConsoleLoggerProvider>()); LoggerProviderOptions.RegisterProviderOptions<ConsoleLoggerOptions, ConsoleLoggerProvider>(builder.Services); return builder; }
builder.Services.TryAddEnumerable(ServiceDescriptor.Singleton<ILoggerProvider, ConsoleLoggerProvider>()) 里面给我们ILoggerProvider 增加了一个ConsoleLoggerProvider,故而我们多了一个打印的功能。
LoggerProviderOptions.RegisterProviderOptions<ConsoleLoggerOptions, ConsoleLoggerProvider>(builder.Services) 给我们加上了ConsoleLoggerOptions 绑定为ConsoleLoggerProvider的配置。
RegisterProviderOptions 如下:
public static void RegisterProviderOptions<TOptions, TProvider>(IServiceCollection services) where TOptions : class { services.TryAddEnumerable(ServiceDescriptor.Singleton<IConfigureOptions<TOptions>, LoggerProviderConfigureOptions<TOptions, TProvider>>()); services.TryAddEnumerable(ServiceDescriptor.Singleton<IOptionsChangeTokenSource<TOptions>, LoggerProviderOptionsChangeTokenSource<TOptions, TProvider>>()); }
接下来就是调用服务:
var loggerObj = loggerFactory.CreateLogger("Default"); loggerObj.LogInformation(2021, "Default,now that is 2021");
看下LoggerFactory的CreateLogger:
public ILogger CreateLogger(string categoryName) { if (CheckDisposed()) { throw new ObjectDisposedException(nameof(LoggerFactory)); } lock (_sync) { if (!_loggers.TryGetValue(categoryName, out Logger logger)) { logger = new Logger { Loggers = CreateLoggers(categoryName), }; (logger.MessageLoggers, logger.ScopeLoggers) = ApplyFilters(logger.Loggers); _loggers[categoryName] = logger; } return logger; } }
里面做了缓存,如果categoryName有缓存的话直接使用缓存,如果没有那么调用CreateLoggers创建。
查看CreateLoggers:
private LoggerInformation[] CreateLoggers(string categoryName) { var loggers = new LoggerInformation[_providerRegistrations.Count]; for (int i = 0; i < _providerRegistrations.Count; i++) { loggers[i] = new LoggerInformation(_providerRegistrations[i].Provider, categoryName); } return loggers; }
这里面就用我们前面注册过的全部logger的provider,封装进LoggerInformation。
查看LoggerInformation:
internal readonly struct LoggerInformation { public LoggerInformation(ILoggerProvider provider, string category) : this() { ProviderType = provider.GetType(); Logger = provider.CreateLogger(category); Category = category; ExternalScope = provider is ISupportExternalScope; } public ILogger Logger { get; } public string Category { get; } public Type ProviderType { get; } public bool ExternalScope { get; } }
里面调用了我们,每个provider的CreateLogger。
那么这个时候我们就找一个provider 看下CreateLogger到底做了什么,这里就找一下ConsoleLoggerProvider,因为我们添加了这个。
[ProviderAlias("Console")] public class ConsoleLoggerProvider : ILoggerProvider, ISupportExternalScope { private readonly IOptionsMonitor<ConsoleLoggerOptions> _options; public ILogger CreateLogger(string name) { if (_options.CurrentValue.FormatterName == null || !_formatters.TryGetValue(_options.CurrentValue.FormatterName, out ConsoleFormatter logFormatter)) { #pragma warning disable CS0618 logFormatter = _options.CurrentValue.Format switch { ConsoleLoggerFormat.Systemd => _formatters[ConsoleFormatterNames.Systemd], _ => _formatters[ConsoleFormatterNames.Simple], }; if (_options.CurrentValue.FormatterName == null) { UpdateFormatterOptions(logFormatter, _options.CurrentValue); } #pragma warning disable CS0618 } return _loggers.GetOrAdd(name, loggerName => new ConsoleLogger(name, _messageQueue) { Options = _options.CurrentValue, ScopeProvider = _scopeProvider, Formatter = logFormatter, }); } }
看到这个IOptionsMonitor,就知道console 配置是支持热更新的,里面创建了ConsoleLogger,这个ConsoleLogger就是用来打log正在的调用类。
值得注意的是_messageQueue这个,看了打印log还是有一个队列的,按照先进先出原则。
那么最后来看一下loggerObj.LogInformation(2021, "Default,now that is 2021");: