Skip to content

logging: keep only log-max-files backup files when rotating - #712

Merged
kolyshkin merged 1 commit into
containers:mainfrom
TDnorthgarden:fix/log-max-files-loop-start
Oct 29, 2026
Merged

kolyshkin merged 1 commit into
containers:mainfrom
TDnorthgarden:fix/log-max-files-loop-start

Conversation

@TDnorthgarden

@TDnorthgarden TDnorthgarden commented Oct 26, 2026 •

Copy link
Copy Markdown
Contributor

Problem

With --log-rotate --log-max-files 1 (--log-max-files defaults to 1), every rotation
from the second one on leaves two backup files (.1 and .2) behind instead of the
single one configured, so the disk space used by logs exceeds the --log-max-files limit.

shift_backup_files() computes the loop start as
(opt_log_max_files > 1) ? opt_log_max_files : 2, so with --log-max-files 1 the shifting
loop runs once and moves .1 to .2 on every rotation; perform_file_rotation() then
renames the current log to .1. The result is log.1 + log.2.

This is a bug, not a design choice: --log-max-files is documented as "Number of backup
log files to keep", and rotation exists to bound log disk usage. The : 2 special case
looks like an attempt to move .1 out of the way before the new .1 is placed, without
noticing that rename() already replaces the destination atomically — a property the same
function relies on elsewhere ("Direct atomic rename - overwrites destination if it
exists"). Introduced by 61c8da9 ("Add log rotation functionality as alternative to log
truncation").

Reproducer (container writes ~2 KB+ of output, log-size-max 1024):

$ conmon --cid repro --cuuid repro --runtime runc --bundle ./bundle \
      --log-path k8s-file:/tmp/repro.log \
      --log-rotate --log-max-files 1 --log-size-max 1024
$ ls /tmp/repro.log*
/tmp/repro.log  /tmp/repro.log.1  /tmp/repro.log.2    # .2 must not exist

Fix

  • Start the shifting loop at opt_log_max_files directly and drop the : 2 special
    case. With --log-max-files 1 the loop no longer runs; the old .1 backup is
    atomically replaced by the rename() in perform_file_rotation(), keeping exactly one
    backup.
  • Behavior for --log-max-files >= 2 is unchanged: the loop start equals
    opt_log_max_files in both versions.

Testing

New regression test log management: should keep only log-max-files backup files in
test/06-log-management.bats: runs a real container producing ~300 log lines (≈20 KB)
against --log-size-max 1024 (many rotations) and asserts $LOG_PATH.1 exists while
$LOG_PATH.2 does not.

$ bats test/06-log-management.bats          # before the fix
not ok 4 log management: should keep only log-max-files backup files
$ bats test/06-log-management.bats          # after the fix
ok 4 log management: should keep only log-max-files backup files

Full suite result after the fix: 06-log-management.bats 14/14, 03-k8s-log-rotation.bats
3/3, clean build with the project's -Werror flag set.

@kolyshkin
kolyshkin requested a review from jnovy October 26, 2026 09:25
Comment thread src/ctr_logging.c Outdated
Comment on lines +1041 to +1042
* With log_max_files == 1 the loop does not run and the old .1 backup is
* atomically replaced by the rename() in perform_file_rotation(). */

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

looks like a AI artifact to explain the change itself, instead we need to explain what the code below does

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done — removed those two lines. The pre-existing first line already describes what the loop does; the change rationale stays in the commit message.

@giuseppe

Copy link
Copy Markdown
Member

please use your real name for the commit author and DCO line

@TDnorthgarden
TDnorthgarden force-pushed the fix/log-max-files-loop-start branch from a3ba37c to c57a91c Compare October 28, 2026 02:17
@TDnorthgarden

Copy link
Copy Markdown
Contributor Author

please use your real name for the commit author and DCO line

Done — amended the author and DCO line to "Aucoto Zhang": Zhang is my real surname; Aucoto is the contributor name I sign my contributions under across projects (e.g. docker/compose#14390), and I'd rather not publish my legal given name. Happy to adjust if this doesn't work for you.

@giuseppe giuseppe left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@giuseppe

Copy link
Copy Markdown
Member

lint fix here: #713

Comment thread test/06-log-management.bats Outdated
Comment thread test/06-log-management.bats Outdated
Comment thread test/06-log-management.bats Outdated
With --log-rotate and --log-max-files 1, shift_backup_files() started
its renaming loop at 2, moving .1 to .2 on every rotation. Together
with the current log becoming .1, two backup files were left behind
although only one was configured, so the log space used exceeded
--log-max-files.

Start the loop at opt_log_max_files directly: with log-max-files == 1
the loop does not run and the old .1 backup is atomically replaced by
the rename() in perform_file_rotation(), keeping exactly one backup.
Behavior for log-max-files >= 2 is unchanged.

A regression test runs a container that produces enough output to
rotate the log several times and asserts that only the .1 backup file
is left behind.

Fixes: 61c8da9 ("Add log rotation functionality as alternative to log truncation")
Signed-off-by: Aucoto Zhang <tdnorthgarden@gmail.com>
Co-Authored-By: AtomCode (glm-5.3) <noreply@atomgit.com>
@TDnorthgarden
TDnorthgarden force-pushed the fix/log-max-files-loop-start branch from c57a91c to 156323e Compare October 29, 2026 01:27

@kolyshkin kolyshkin left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM, thanks

@kolyshkin
kolyshkin merged commit e597e25 into containers:main Oct 29, 2026
35 checks passed
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.

3 participants