Skip to content

Bug: AddPowertoolsLogger leaks pipe file descriptors on every log write in warm Lambda containers #1143

@nicokaariainen

Description

@nicokaariainen

Expected Behaviour

Pipe file descriptors to stdout should remain stable regardless of the number of log writes, as they do with AddLambdaLogger from Amazon.Lambda.Logging.AspNetCore.

Current Behaviour

Pipe FDs to the stdout pipe grow linearly with each ILogger log call and are never released. Under sustained load this exhausts the Lambda 1024 FD limit and crashes the function with "Too many open files".

Code snippet

// Registration in Startup.cs — this alone triggers the leak
services.AddLogging(logging =>
{
    logging.ClearProviders();
    logging.AddPowertoolsLogger(config =>
    {
        config.MinimumLogLevel = LogLevel.Information;
        config.Service = "test-service";
    });
});

// Middleware that logs per request and monitors FDs
public async Task InvokeAsync(HttpContext context)
{
    _logger.LogInformation("Request: {Path}", context.Request.Path);

    var entries = Directory.GetFiles("/proc/self/fd");
    var targets = entries.Select(fd =>
    {
        try
        {
            var target = new FileInfo(fd).ResolveLinkTarget(true)?.FullName ?? "unknown";
            return target;
        }
        catch { return "unresolvable"; }
    }).ToList();

    var pipeCount = targets.Count(t => t.Contains("pipe:"));
    _logger.LogInformation("Open FDs: {Total}, Pipes: {Pipes}", entries.Length, pipeCount);

    await _next(context);
}

Possible Solution

The leak appears to be somehow related to how Powertools writes to Console.Out. The writes seem to duplicate the stdout pipe FD without closing it. The other tested logger Amazon.Lambda.Logging.AspNetCore writes to the same stdout pipe without leaks, so the issue is most likely in this implementation.

Workaround: replace AddPowertoolsLogger with AddLambdaLogger:

services.AddLogging(logging =>
{
    logging.ClearProviders();
    logging.AddLambdaLogger(new LambdaLoggerOptions
    {
        IncludeCategory = true,
        IncludeLogLevel = true,
        IncludeNewline = true,
    });
});

Steps to Reproduce

Steps to reproduce:

  1. Create an ASP.NET Core Lambda using APIGatewayProxyFunction base class
  2. Register AddPowertoolsLogger as the only logging provider to DI container (with ClearProviders() first)
  3. Inject ILogger into an API controller from the DI container
  4. Add middleware that logs on every request and counts /proc/self/fd entries (see code snippet above)
  5. Deploy to Lambda or run with SAM CLI local (--warm-containers EAGER to simulate warm containers)
  6. Send sustained traffic — e.g., 10 concurrent clients for 30+ seconds
  7. Observe pipe FD count growing linearly in the logs until it hits the 1024 limit
    Environment:
  • AWS.Lambda.Powertools.Logging: 3.1.0
  • .NET 8 (dotnet8 Lambda runtime)
  • Amazon.Lambda.AspNetCoreServer: 9.2.1
  • SAM CLI with --warm-containers EAGER (Originally identified while running in a cloud Lambda, replicated in SAM CLI)
  • The [Logging] attribute on the handler is NOT required to trigger the leak

Powertools for AWS Lambda (.NET) version

3.1.0

AWS Lambda function runtime

dotnet8

Debugging logs

FD snapshot after a 30s of concurrent requests in SAM (abbreviated, showing the leaking pipe):

Open file descriptors: 709, sources:
  /proc/self/fd/pipe:[1625223] (328)    <-- grows with every log write
  /proc/self/fd/pipe:[1625281] (2)       <-- stable (runtime pipes)
  /proc/self/fd/pipe:[1625332] (2)       <-- stable
  /proc/self/fd/socket:[1625283] (1)     <-- stable
  ... ~150 DLL mappings at 2 FDs each (stable)

Same workload with AddLambdaLogger:
Open file descriptors: 389, sources:
  /proc/self/fd/pipe:[1630353] (6)       <-- stable, does not grow
  /proc/self/fd/pipe:[1617742] (2)       <-- stable
  ... ~150 DLL mappings at 2 FDs each (stable)

Metadata

Metadata

Assignees

Labels

bugUnexpected, reproducible and unintended software behaviourreleasedFix or implementation already in main and released

Type

Projects

Status

✅ Done

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions