Skip to content

feat(logging): add --log-file to persist nerdctl's own log - #5147

Open
ekalinin wants to merge 1 commit into
containerd:mainfrom
ekalinin:feat/log-file
Open

feat(logging): add --log-file to persist nerdctl's own log#5147
ekalinin wants to merge 1 commit into
containerd:mainfrom
ekalinin:feat/log-file

Conversation

@ekalinin

Copy link
Copy Markdown
Contributor

Fixes #4872

What

Adds a global --log-file (also log_file in nerdctl.toml, and $NERDCTL_LOG_FILE) that appends nerdctl's own log to a file, in addition to the standard error. Empty by default, so nothing changes unless it is set.

$ nerdctl --debug --log-file /var/log/nerdctl.log run --rm alpine true

Why

#4872 asks for a way to keep a record of a failing nerdctl invocation: today the diagnostics only go to the standard error and are gone once the process exits, and containerd does not record the client side of a failed container creation either.

Everything nerdctl logs funnels through the standard logger, including the error that terminates the command: SilenceErrors: true on the root command means cobra prints nothing itself, the error returns to main(), and main() calls log.L.Fatal(err). Capturing that stream is therefore enough to capture the failure the issue is about, together with everything logged on the way there.

Implementation notes

  • The file output is attached as a logrus hook, not by replacing Logger.Out with an io.MultiWriter. The text formatter picks its output style by type-asserting Logger.Out to *os.File, so a MultiWriter would silently downgrade the console output from INFO[0000] msg to time="..." level=info msg="..." for everyone enabling the flag. With a hook the console is left untouched and the file gets a stable, TTY-independent format.
  • No new dependency: the hook is written against the type aliases exported by containerd/log, so nerdctl still does not import logrus directly.
  • Record format is <RFC3339NanoFixed> <LEVEL> <message> [key="value" ...], fields sorted for stable output.
  • Opened with O_CREATE|O_WRONLY|O_APPEND, mode 0600, so concurrent invocations can share one file and a previous invocation is never truncated away. Rotation is deliberately left to logrotate or an equivalent - pulling in a rotation dependency for a CLI seemed disproportionate.
  • The file is left open on purpose: log.L.Fatal calls os.Exit, so a deferred Close would not run, and the hook writes are unbuffered.
  • The level keeps following the existing --debug / --debug-full, so no new --log-level flag is introduced.

Not covered

Testing

  • pkg/logging/file_hook_test.go: record format and field ordering, append semantics, file mode, and that Logger.Out is left alone.
  • cmd/nerdctl/main_test.go TestLogFile: a failing invocation writes the error to the file, and two invocations both end up in it.

Example output:

$ nerdctl --debug --log-file /tmp/nerdctl.log version
$ cat /tmp/nerdctl.log
2026-08-19T22:57:03.776364000+03:00 WARNING unable to determine buildctl version error="exec: \"buildctl\": executable file not found in $PATH"
2026-08-19T22:57:03.776581000+03:00 FATAL cannot access containerd socket "/var/run/containerd/containerd.sock": no such file or directory

nerdctl only reports its diagnostics on the standard error, so nothing
survives the process. When a container fails to be created there is no
record left to look at, and containerd does not log the client side of
the failure either.

Add a global --log-file (also log_file in nerdctl.toml and
$NERDCTL_LOG_FILE) that appends nerdctl's own log to a file, in addition
to the standard error. Every terminal error funnels through log.L.Fatal
in main(), so the failure that ends the command is recorded together
with everything logged on the way there. Combine with --debug for a full
trace.

The output is attached as a logrus hook rather than by replacing
Logger.Out with an io.MultiWriter: the formatter picks its output style
by type-asserting Logger.Out to *os.File, so a MultiWriter would
silently change the console format whenever the flag is used.

The file is opened in append mode so concurrent invocations can share
it, and it is left open on purpose since log.L.Fatal exits the process.

Fixes containerd#4872

Signed-off-by: Eugene Kalinin <e.v.kalinin@gmail.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

can nerdctl support to save log ?

1 participant