Skip to content

[containers] Docker executable invocations do not log stdout/stderr in production (NullLogger + Debug-only success path) #1373

Description

@JoshuaRowePhantom

Summary

Invocations of the docker executable do not log their standard output or standard error in production. Two independent gaps cause this: (1) the production Mongo path constructs the Docker engine with a NullLogger, so all docker stdout/stderr is silently discarded; and (2) even when a real ILogger is supplied, successful runs log only the combined output at Debug level, so at default log levels docker output is invisible. The requirement is that invocations of the docker executable log BOTH standard output and standard error, on success and on failure.

Docker is invoked via DockerCommandRunner.RunAsync -> ProcessRunner.RunAndLogAsync -> ProcessRunner.RunProcessAsync.

Root Cause

1. NullLogger in the production Mongo path

Phantom.Workspaces.Data.MongoDB\MongoDbConnectionBroker.cs (line ~35) constructs the engine with the PARAMETERLESS constructor:

_containerEngine = containerEngine ?? new WindowsDockerDesktopEngine();

Phantom.Workspaces.Containers\WindowsDockerDesktopEngine.cs (lines ~11-14): the parameterless ctor wires a NullLogger:

public WindowsDockerDesktopEngine()
    : this(new DockerCommandRunner(NullLogger<DockerCommandRunner>.Instance))
{
}

So EVERY docker command run by the broker (info, container inspect, create, start, stop, rm) logs to NullLogger -> all stdout/stderr is silently discarded. Phantom.Workspaces.Data.MongoDB.Tests\MongoDbTestDatabaseFixture.cs (line ~23) does the same:

private readonly ContainerEngine _containerEngine = new WindowsDockerDesktopEngine();

2. Success output only at Debug, combined stream only

Even when a real ILogger is supplied, Phantom.Workspaces.Data.Core\ProcessRunner.cs RunAndLogAsync (lines ~158-200) logs the COMBINED stdout+stderr blob only at LogDebug on success (exit 0), at LogWarning on non-zero exit, and LogError on timeout:

if (result.ExitCode != 0)
{
    logger.LogWarning(
        "Process '{Command}' exited with code {ExitCode}{Description}.\nOutput:\n{Output}",
        ...
        result.StandardOutAndError);
}
else if (!string.IsNullOrWhiteSpace(result.StandardOutAndError))
{
    logger.LogDebug(
        "Process '{Command}' completed successfully{Description}.\nOutput:\n{Output}",
        ...
        result.StandardOutAndError);
}

On a normal successful run at default log levels, docker output is invisible. stdout and stderr are captured SEPARATELY into ProcessResult (ProcessRunner.cs lines ~146-150), but only the combined StandardOutAndError blob is ever logged:

return new ProcessResult(
    process.ExitCode,
    string.Join(Environment.NewLine, stdoutLines),
    string.Join(Environment.NewLine, stderrLines),
    string.Join(Environment.NewLine, combinedLines));

3. Failure exception surfaces only stderr

WindowsDockerDesktopEngine.RunAndEnsureSuccessAsync (lines ~135-151) throws on failure but includes only result.StandardError in the exception message; stdout on failure is not surfaced there. (It IS in the Warning log, but only when a real logger is wired -- which it is not in the broker path.)

throw new InvalidOperationException(
    $"Docker command failed: docker {string.Join(' ', arguments)}{Environment.NewLine}" +
    $"{result.StandardError}".TrimEnd());

Impact

When the Mongo container fails to create/start (image pull failure, port conflict, mount error, Atlas Local startup error), docker's actual stdout/stderr -- the only useful diagnostic -- is thrown away because the broker uses NullLogger. Users and maintainers diagnosing container issues (e.g. related to the slow/flaky container-connect tests and first-run image pulls) get no docker output at all.

Affected Files

File Role
Phantom.Workspaces.Data.MongoDB\MongoDbConnectionBroker.cs Line ~35 uses parameterless WindowsDockerDesktopEngine() -> NullLogger; must pass a real logger
Phantom.Workspaces.Containers\WindowsDockerDesktopEngine.cs Parameterless ctor (line ~11) wires NullLogger; ctor selection
Phantom.Workspaces.Containers\DockerCommandRunner.cs Routes all docker commands through ProcessRunner.RunAndLogAsync with its ILogger
Phantom.Workspaces.Data.Core\ProcessRunner.cs RunAndLogAsync logs combined output only at Debug on success (lines ~181-197); should log stdout+stderr at a visible level
Phantom.Workspaces.Data.MongoDB.Tests\MongoDbTestDatabaseFixture.cs Line ~23 also uses parameterless ctor

Design / Fix

Make docker invocations always log stdout AND stderr through a real logger.

Recommended

  1. Wire a real logger into the broker's engine. Give MongoDbConnectionBroker access to an ILoggerFactory/ILogger<DockerCommandRunner> (constructor param, defaulting to something non-null) and construct new WindowsDockerDesktopEngine(logger) (the ILogger<DockerCommandRunner> ctor at WindowsDockerDesktopEngine.cs line ~16) instead of the parameterless ctor. Do the same for MongoDbTestDatabaseFixture (use a test logger). Avoid the NullLogger default silently swallowing output in production -- the parameterless ctor should be reserved for tests, or removed/guarded.

  2. Log stdout and stderr on every invocation, not just failures. In ProcessRunner.RunAndLogAsync, raise the success log level for external-command output above Debug (e.g. Information, or a caller-selectable level), and/or log stdout and stderr as distinct fields rather than only the combined blob, so both streams are always visible. Consider adding an option so callers (docker) opt into always-log-at-Information. The exact level is a design decision, but the requirement is that docker stdout AND stderr are logged on both success and failure.

Considered / Background

Current behavior (Debug-only combined output, NullLogger in broker) -- rejected because docker output is invisible exactly when it is needed (container create/start failures).

Expected Tests

Existing tests: Phantom.Workspaces.Data.Core.Tests\ProcessRunnerTests.cs (namespace Phantom.Workspaces.Data.Tests) already has a FakeLogger : ILogger capturing (LogLevel Level, string Message) and tests such as RunAndLogAsync_ProcessSucceeds_LogsStdoutAtDebugLevel (which currently asserts Debug and will need updating). Phantom.Workspaces.Containers.Tests\WindowsDockerDesktopEngineTests.cs uses a fake RecordingDockerCommandRunner. New tests should match these classes and styles.

Test Name Class What It Verifies
ProcessRunner_RunAndLogAsync_OnSuccess_LogsStandardOutputAndStandardError ProcessRunnerTests A FakeLogger receives both stdout and stderr content at a non-Debug (visible) level on exit 0
ProcessRunner_RunAndLogAsync_OnFailure_LogsStandardOutputAndStandardError ProcessRunnerTests Both stdout and stderr are logged on non-zero exit
MongoDbConnectionBroker_UsesLoggingDockerEngine_DoesNotUseNullLogger MongoDbConnectionBrokerTests (broker/engine test) The production path constructs an engine whose command runner is wired with a real, non-null logger, so docker output is no longer silently discarded

Note: existing RunAndLogAsync_ProcessSucceeds_LogsStdoutAtDebugLevel / RunAndLogAsync_ProcessSucceeds_LogsStderrAtDebugLevel / RunAndLogAsync_NoLogCall_WhenExitCodeIsZero will need to be updated to reflect the new visible success-log level.

Metadata

Metadata

Labels

bugSomething isn't workingdiagnosedRoot cause identifiedneeds-slow-testsRequires full test suite including slow Git tests at checkinverified-locallyImplementation has been verified locally

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions