Currently, I have two types of custom loggers that both have their own configuration section, ILoggerProvider, and Ilogger implementation. However, it seems there is an issue with how my configuration scope is being read as the last configuration to be added during DI to our ILoggingBuilder.
Appsetting configuration settings that will get pulled
{
"Logging": {
"LogLevel": {
"Default": "Warning"
},
"Logger1": {
"LogLevel": {
"Default": "Information"
},
"BenchmarkConfig": {
"Enable": true,
"LogThreshold": {
"Critical": "00:00:15",
"Information": "00:00:00"
}
}
},
"Logger2": {
"LogLevel": {
"Default": "Critical"
}
}
}
}
This is the DI layer that I add them in
protected override void LoggingHook(IServiceCollection services, IConfiguration configuration)
{
base.LoggingHook(services, configuration);
_ = services.AddLogging(config => _ = config.AddLogger1(configuration));
_ = services.AddLogging(config => _ = config.AddLogger2(configuration));
}
Logger1
public static class Logger1Extensions
{
public static ILoggingBuilder AddLogger1(this ILoggingBuilder builder, IConfiguration configuration)
{
var configurationSection = configuration.GetSection(Logger1.Section);
_ = builder.AddConfiguration(configurationSection);
builder.Services.AddTransient<InitializedLogMessage>();
builder.Services.AddSingleton<ILogger1Config>(configurationSection.Get<Logger1Config>());
builder.Services.TryAddEnumerable(ServiceDescriptor.Singleton<ILoggerProvider, Logger1Provider>());
return builder;
}
}
public interface ILogger1Config
{
LogLevel LogLevel { get; set; }
Logger1BenchmarkConfig BenchmarkConfig { get; set; }
}
public class Logger1 : ILogger
{
private readonly ILambdaLogger _lambdaLogger;
private readonly ILogger1Config _loggerConfig;
public const string Section = "Logging:Logger1";
public const string SectionLogLevel = Section + ":LogLevel";
public const string SectionLogLevelDefault = SectionLogLevel + ":Default";
public Logger1(ILambdaLogger logger, ILogger1Config loggerConfig, InitializedLogMessage logMessage)
{
_lambdaLogger = logger;
_loggerConfig = loggerConfig;
LogMessage = logMessage;
}
private InitializedLogMessage LogMessage { get; }
public void Log<TState>(LogLevel logLevel, EventId eventId, TState state, Exception exception, Func<TState, Exception, string> formatter)
{
if (IsEnabled(logLevel))
{
_lambdaLogger?.LogLine($"{GetScopePrefix()}[{logLevel}] {formatter(state, exception)}");
}
}
public bool IsEnabled(LogLevel logLevel) => logLevel != LogLevel.None;
}
Logger2
public static class Logger2Extensions
{
public static ILoggingBuilder AddLogger2Generic<T>(this ILoggingBuilder builder,
IConfiguration configuration) where T : class, ILoggerProvider
{
IConfigurationSection section = configuration.GetSection("Logging:Logger2");
builder.AddConfiguration((IConfiguration) section);
builder.Services.TryAddEnumerable(ServiceDescriptor.Singleton<ILoggerProvider, T>());
return builder;
}
public static ILoggingBuilder AddLogger2(this ILoggingBuilder builder,
IConfiguration configuration)
{
return builder.AddLogger2Generic<Logger2Provider>(configuration);
}
}
public class Logger2 : ILogger
{
private _rest { get; set; }
public Logger2(IRest rest)
{
_rest = rest
}
public void Log<TState>(LogLevel logLevel, EventId eventId, TState state, Exception exception, Func<TState, Exception, string> formatter)
{
if (IsEnabled(logLevel))
{
_rest.log()
}
}
public bool IsEnabled(LogLevel logLevel) => logLevel != LogLevel.None;
}
If I comment out adding the configuration inside the Logger2Extesions.AddLogger2Generic then everything in Logger1 works fine. But once Logger2 is added into the mix it not only overwrites the configuration for Logger1 but it also reads the log level incorrectly. Since I am using the loglevel enum from microsoft it results in a defaulted loglevel of trace since what it picks up to cast to the enum is invalid.
In your AddLogger1
and AddLogger2/AddLogger2Generic
methods, you are calling AddConfiguration
and providing the section for that specific logger. Instead, you should call AddConfiguration
once, and only once, and provide the "Logging"
section. You may also want to call ClearProviders
beforehand.
Try adding this before you add the other loggers (and remember to remove the AddConfiguration
calls from those other methods):
services.AddLogging(config =>
{
_ = config.ClearProviders();
_ = config.AddConfiguration(configuration.GetSection("Logging"));
});