{
internal static string DebuggerToString(string name, ILogger logger)
{
+ LogLevel? minimumLevel = CalculateEnabledLogLevel(logger);
+
+ var debugText = $@"Name = ""{name}""";
+ if (minimumLevel != null)
+ {
+ debugText += $", MinLevel = {minimumLevel}";
+ }
+ else
+ {
+ // Display "Enabled = false". This makes it clear that the entire ILogger
+ // is disabled and nothing is written.
+ //
+ // If "MinLevel = None" was displayed then someone could think that the
+ // min level is disabled and everything is written.
+ debugText += $", Enabled = false";
+ }
+
+ return debugText;
+ }
+
+ internal static LogLevel? CalculateEnabledLogLevel(ILogger logger)
+ {
ReadOnlySpan<LogLevel> logLevels = stackalloc LogLevel[]
{
LogLevel.Critical,
LogLevel.Trace,
};
- LogLevel minimumLevel = LogLevel.None;
+ LogLevel? minimumLevel = null;
// Check log level from highest to lowest. Report the lowest log level.
foreach (LogLevel logLevel in logLevels)
minimumLevel = logLevel;
}
- var debugText = $@"Name = ""{name}""";
- if (minimumLevel != LogLevel.None)
- {
- debugText += $", MinLevel = {minimumLevel}";
- }
- else
- {
- // Display "Enabled = false". This makes it clear that the entire ILogger
- // is disabled and nothing is written.
- //
- // If "MinLevel = None" was displayed then someone could think that the
- // min level is disabled and everything is written.
- debugText += $", Enabled = false";
- }
-
- return debugText;
+ return minimumLevel;
}
}
}
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;
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 static void ThrowLoggingError(List<Exception> exceptions)
+ private sealed class LoggerDebugView(Logger logger)
{
- throw new AggregateException(
- message: "An error occurred while writing to logger(s).", innerExceptions: exceptions);
+ 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 = logger.MessageLoggers?.FirstOrDefault(messageLogger => messageLogger.Logger == loggerInfo.Logger);
+
+ providers.Add(new LoggerProviderDebugView(providerName, messageLogger));
+ }
+
+ return providers;
+ }
+ }
+
+ 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 = stackalloc LogLevel[]
+ {
+ 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
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
+using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
+using System.Linq;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options;
/// <summary>
/// Produces instances of <see cref="ILogger"/> classes based on the given providers.
/// </summary>
+ [DebuggerDisplay("{DebuggerToString(),nq}")]
+ [DebuggerTypeProxy(typeof(LoggerFactoryDebugView))]
public class LoggerFactory : ILoggerFactory
{
private readonly ConcurrentDictionary<string, Logger> _loggers = new ConcurrentDictionary<string, Logger>(StringComparer.Ordinal);
_loggerFactory.AddProvider(provider);
}
}
+
+ private string DebuggerToString()
+ {
+ return $"Providers = {_providerRegistrations.Count}, {_filterOptions.DebuggerToString()}";
+ }
+
+ private sealed class LoggerFactoryDebugView(LoggerFactory loggerFactory)
+ {
+ public List<ILoggerProvider> Providers => loggerFactory._providerRegistrations.Select(r => r.Provider).ToList();
+ public bool Disposed => loggerFactory._disposed;
+ public LoggerFilterOptions FilterOptions => loggerFactory._filterOptions;
+ }
}
}
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
-using System.Collections.Generic;
+using System.Diagnostics;
namespace Microsoft.Extensions.Logging
{
/// <summary>
/// The options for a LoggerFactory.
/// </summary>
+ [DebuggerDisplay("ActivityTrackingOptions = {ActivityTrackingOptions}")]
public class LoggerFactoryOptions
{
/// <summary>
// The .NET Foundation licenses this file to you under the MIT license.
using System.Collections.Generic;
+using System.Diagnostics;
namespace Microsoft.Extensions.Logging
{
/// <summary>
/// The options for a LoggerFilter.
/// </summary>
+ [DebuggerDisplay("{DebuggerToString(),nq}")]
public class LoggerFilterOptions
{
/// <summary>
// Concrete representation of the rule list
internal List<LoggerFilterRule> RulesInternal { get; } = new List<LoggerFilterRule>();
+
+ internal string DebuggerToString()
+ {
+ string debugText;
+ if (MinLevel != LogLevel.None)
+ {
+ debugText = $"MinLevel = {MinLevel}";
+ }
+ else
+ {
+ // Display "Enabled = false". This makes it clear that the entire ILogger
+ // is disabled and nothing is written.
+ //
+ // If "MinLevel = None" was displayed then someone could think that the
+ // min level is disabled and everything is written.
+ debugText = $"Enabled = false";
+ }
+
+ if (Rules.Count > 0)
+ {
+ debugText += $", Rules = {Rules.Count}";
+ }
+
+ return debugText;
+ }
}
}
{
internal readonly struct MessageLogger
{
- public MessageLogger(ILogger logger, string? category, string? providerTypeFullName, LogLevel? minLevel, Func<string?, string?, LogLevel, bool>? filter)
+ public MessageLogger(ILogger logger, string category, string? providerTypeFullName, LogLevel? minLevel, Func<string?, string?, LogLevel, bool>? filter)
{
Logger = logger;
Category = category;
public ILogger Logger { get; }
- public string? Category { get; }
+ public string Category { get; }
private string? ProviderTypeFullName { get; }