| File: Logger.cs | Web Access |
| Project: src\runtime\src\libraries\Microsoft.Extensions.Logging\src\Microsoft.Extensions.Logging.csproj (Microsoft.Extensions.Logging) |
// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. using System; using System.Collections.Generic; using System.Diagnostics; using System.Linq; namespace Microsoft.Extensions.Logging { [DebuggerDisplay("{DebuggerToString(),nq}")] [DebuggerTypeProxy(typeof(LoggerDebugView))] internal sealed class Logger : ILogger { private readonly string _categoryName; public Logger(string categoryName, LoggerInformation[] loggers) { _categoryName = categoryName; Loggers = loggers; } public LoggerInformation[] Loggers { get; set; } public MessageLogger[]? MessageLoggers { get; set; } public ScopeLogger[]? ScopeLoggers { get; set; } public void Log<TState>(LogLevel logLevel, EventId eventId, TState state, Exception? exception, Func<TState, Exception?, string> formatter) { MessageLogger[]? loggers = MessageLoggers; if (loggers == null) { return; } List<Exception>? exceptions = null; for (int i = 0; i < loggers.Length; i++) { ref readonly MessageLogger loggerInfo = ref loggers[i]; if (!loggerInfo.IsEnabled(logLevel)) { continue; } LoggerLog(logLevel, eventId, loggerInfo.Logger, exception, formatter, ref exceptions, state); } if (exceptions != null && exceptions.Count > 0) { ThrowLoggingError(exceptions); } static void LoggerLog(LogLevel logLevel, EventId eventId, ILogger logger, Exception? exception, Func<TState, Exception?, string> formatter, ref List<Exception>? exceptions, in TState state) { try { logger.Log(logLevel, eventId, state, exception, formatter); } catch (Exception ex) { exceptions ??= new List<Exception>(); exceptions.Add(ex); } } } public bool IsEnabled(LogLevel logLevel) { MessageLogger[]? loggers = MessageLoggers; if (loggers == null) { return false; } List<Exception>? exceptions = null; int i = 0; for (; i < loggers.Length; i++) { ref readonly MessageLogger loggerInfo = ref loggers[i]; if (!loggerInfo.IsEnabled(logLevel)) { continue; } if (LoggerIsEnabled(logLevel, loggerInfo.Logger, ref exceptions)) { break; } } if (exceptions != null && exceptions.Count > 0) { ThrowLoggingError(exceptions); } return i < loggers.Length ? true : false; static bool LoggerIsEnabled(LogLevel logLevel, ILogger logger, ref List<Exception>? exceptions) { try { if (logger.IsEnabled(logLevel)) { return true; } } catch (Exception ex) { exceptions ??= new List<Exception>(); exceptions.Add(ex); } return false; } } public IDisposable? BeginScope<TState>(TState state) where TState : notnull { ScopeLogger[]? loggers = ScopeLoggers; if (loggers == null) { return NullScope.Instance; } if (loggers.Length == 1) { return loggers[0].CreateScope(state); } var scope = new Scope(loggers.Length); List<Exception>? exceptions = null; for (int i = 0; i < loggers.Length; i++) { ref readonly ScopeLogger scopeLogger = ref loggers[i]; try { scope.SetDisposable(i, scopeLogger.CreateScope(state)); } catch (Exception ex) { exceptions ??= new List<Exception>(); exceptions.Add(ex); } } if (exceptions != null && exceptions.Count > 0) { ThrowLoggingError(exceptions); } return scope; } private static void ThrowLoggingError(List<Exception> exceptions) { throw new AggregateException( message: "An error occurred while writing to logger(s).", innerExceptions: exceptions); } internal string DebuggerToString() { return DebuggerDisplayFormatting.DebuggerToString(_categoryName, this); } private sealed class LoggerDebugView(Logger logger) { public string Name => logger._categoryName; // The list of providers includes the full list of configured providers from the logger factory. // It then mentions the min level and enable status of each provider for this logger. public List<LoggerProviderDebugView> Providers { get { List<LoggerProviderDebugView> providers = new List<LoggerProviderDebugView>(); for (int i = 0; i < logger.Loggers.Length; i++) { LoggerInformation loggerInfo = logger.Loggers[i]; string providerName = ProviderAliasUtilities.GetAlias(loggerInfo.ProviderType) ?? loggerInfo.ProviderType.Name; MessageLogger? messageLogger = FirstOrNull(logger.MessageLoggers, loggerInfo.Logger); providers.Add(new LoggerProviderDebugView(providerName, messageLogger)); } return providers; // Find message logger or return null if there is no match. FirstOrDefault isn't used because MessageLogger is a struct. static MessageLogger? FirstOrNull(MessageLogger[]? messageLoggers, ILogger logger) { if (messageLoggers is null || messageLoggers.Length == 0) { return null; } foreach (MessageLogger item in messageLoggers) { if (item.Logger == logger) { return item; } } return null; } } } public List<object?>? Scopes { get { var scopeProvider = logger.ScopeLoggers?.FirstOrDefault().ExternalScopeProvider; if (scopeProvider == null) { return null; } List<object?> scopes = new List<object?>(); scopeProvider.ForEachScope((scope, scopes) => scopes!.Add(scope), scopes); return scopes; } } public LogLevel? MinLevel => DebuggerDisplayFormatting.CalculateEnabledLogLevel(logger); public bool Enabled => DebuggerDisplayFormatting.CalculateEnabledLogLevel(logger) != null; } [DebuggerDisplay("{DebuggerToString(),nq}")] private sealed class LoggerProviderDebugView(string providerName, MessageLogger? messageLogger) { public string Name => providerName; public LogLevel LogLevel => CalculateEnabledLogLevel(messageLogger) ?? LogLevel.None; private static LogLevel? CalculateEnabledLogLevel(MessageLogger? logger) { if (logger == null) { return null; } ReadOnlySpan<LogLevel> logLevels = [ LogLevel.Critical, LogLevel.Error, LogLevel.Warning, LogLevel.Information, LogLevel.Debug, LogLevel.Trace, ]; LogLevel? minimumLevel = null; // Check log level from highest to lowest. Report the lowest log level. foreach (LogLevel logLevel in logLevels) { if (!logger.Value.IsEnabled(logLevel)) { break; } minimumLevel = logLevel; } return minimumLevel; } private string DebuggerToString() { return $@"Name = ""{providerName}"", LogLevel = {LogLevel}"; } } private sealed class Scope : IDisposable { private bool _isDisposed; private IDisposable? _disposable0; private IDisposable? _disposable1; private readonly IDisposable?[]? _disposable; public Scope(int count) { if (count > 2) { _disposable = new IDisposable[count - 2]; } } public void SetDisposable(int index, IDisposable? disposable) { switch (index) { case 0: _disposable0 = disposable; break; case 1: _disposable1 = disposable; break; default: _disposable![index - 2] = disposable; break; } } public void Dispose() { if (!_isDisposed) { _disposable0?.Dispose(); _disposable1?.Dispose(); if (_disposable != null) { int count = _disposable.Length; for (int index = 0; index != count; ++index) { _disposable[index]?.Dispose(); } } _isDisposed = true; } } } } }