Skip to content

Bug: POWERTOOLS_LOG_LEVEL=Trace or Debug is ignored — Trace/Debug calls never emit #1205

@nCubed

Description

@nCubed

Expected Behaviour

Setting POWERTOOLS_LOG_LEVEL=Trace should cause every level (Trace, Debug, Information, Warning, Error, Critical) to emit.

Setting POWERTOOLS_LOG_LEVEL=Debug should cause every level except Trace to emit.

This should hold whether the logger is used via Logger.LogTrace(...) / Logger.LogDebug(...) or via Logger.Log(LogLevel.Trace, ...) / Logger.Log(LogLevel.Debug, ...).

Current Behaviour

Trace and Debug calls are silently dropped regardless of the POWERTOOLS_LOG_LEVEL value. Information and above always emit. Setting POWERTOOLS_LOG_LEVEL to Information / Warning / Error / Critical correctly raises the floor; setting it below Information has no effect.

Reproduced on AWS.Lambda.Powertools.Logging 3.2.1, dotnet8 Lambda, no AWS_LAMBDA_LOG_LEVEL set, no explicit Logger.Configure(...) call.

Test matrix output:

Level        Pass    Missing       Observed
UNSET        True                  critical,error,information,warning
Trace        False   debug,trace   critical,error,information,warning
Debug        False   debug         critical,error,information,warning
Information  True                  critical,error,information,warning
Warning      True                  critical,error,warning
Error        True                  critical,error
Critical     True                  critical

Code snippet

Handler used to reproduce:


[Logging(ClearState = true)]
async Task<string> Handler(string input, ILambdaContext context)
{
    Logger.LogTrace("LEVELTEST trace");
    Logger.LogDebug("LEVELTEST debug");
    Logger.LogInformation("LEVELTEST information");
    Logger.LogWarning("LEVELTEST warning");
    Logger.LogError("LEVELTEST error");
    Logger.LogCritical("LEVELTEST critical");
    return await Task.FromResult(input);
}


Root cause is in `Internal/Helpers/LoggerFactoryHelper.cs`:


internal static ILoggerFactory CreateAndConfigureFactory(PowertoolsLoggerConfiguration configuration)
{
    var factory = LoggerFactory.Create(builder =>
    {
        builder.AddPowertoolsLogger(config => { /* copies fields */ });

        if (configuration.SamplingRate > 0)
        {
            builder.AddFilter(null, LogLevel.Debug);
            builder.SetMinimumLevel(LogLevel.Debug);
        }
        else if (configuration.MinimumLogLevel != LogLevel.None)
        {
            builder.AddFilter(null, configuration.MinimumLogLevel);
            builder.SetMinimumLevel(configuration.MinimumLogLevel);
        }
        // else: no SetMinimumLevel call -> factory minimum stays at Information
    });
    ...
}


When the factory is built lazily via `LoggerFactoryHolder.GetOrCreateFactory()`, the `configuration` passed in is the default `new PowertoolsLoggerConfiguration()` with `MinimumLogLevel = LogLevel.None`. Both branches above are skipped, so `Microsoft.Extensions.Logging`'s default factory minimum (Information) takes effect.

`PowertoolsLoggerProvider.ConfigureFromEnvironment()` then reads `POWERTOOLS_LOG_LEVEL` and updates the *provider*'s `MinimumLogLevel`, but the *factory*'s level filter has already been frozen. `ILogger.IsEnabled(LogLevel.Trace|Debug)` returns false at the factory level, so the call short-circuits before reaching `PowertoolsLogger.IsEnabledForConfig`.

Possible Solution

In LoggerFactoryHelper.CreateAndConfigureFactory, when MinimumLogLevel == None, force the factory minimum to LogLevel.Trace so every level reaches the provider. Let PowertoolsLoggerProvider / PowertoolsLogger.IsEnabledForConfig do the actual filtering using the env-var-derived level (which it already does correctly).

else if (configuration.MinimumLogLevel != LogLevel.None)
{
    builder.AddFilter(null, configuration.MinimumLogLevel);
    builder.SetMinimumLevel(configuration.MinimumLogLevel);
}
else
{
    // Pass everything through; provider handles env-var-derived level
    builder.AddFilter(null, LogLevel.Trace);
    builder.SetMinimumLevel(LogLevel.Trace);
}

Alternative: read POWERTOOLS_LOG_LEVEL in LoggerFactoryHolder.GetOrCreateFactory() and apply it to config.MinimumLogLevel before calling CreateAndConfigureFactory, so the existing non-None branch runs.

Steps to Reproduce

  1. Deploy a Lambda using AWS.Lambda.Powertools.Logging 3.2.1 with the handler above.
  2. Ensure AWS_LAMBDA_LOG_LEVEL is unset on the function.
  3. Set POWERTOOLS_LOG_LEVEL=Debug and invoke. CloudWatch shows no LEVELTEST debug line.
  4. Set POWERTOOLS_LOG_LEVEL=Trace and invoke. CloudWatch shows neither LEVELTEST trace nor LEVELTEST debug.
  5. Set POWERTOOLS_LOG_LEVEL=Information (or higher) and invoke. Behavior is correct from Information up.

Powertools for AWS Lambda (.NET) version

latest

AWS Lambda function runtime

dotnet8

Debugging logs

Metadata

Metadata

Assignees

Labels

area/loggingCore logging utilitybugUnexpected, reproducible and unintended software behaviour

Type

No type

Projects

Status

No status

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions