I have done the following
Steps to reproduce
Starting state: stock installed release (version in Environment below), container
system running, no custom ~/.config/container/config.toml. No prior commands required.
Reproducibility: always, deterministic whenever the first log line exceeds the
tail reader's 1024-byte read chunk.
-
Start the container system:
-
Create a container whose first log line is 2000 characters, followed by two short lines:
container run -d --name logtest debian:bookworm sh -c 'head -c 2000 /dev/zero | tr "\0" "a"; echo; echo second; echo third'
-
Confirm it has exited:
-
Print the full log and measure the first line, it is correct:
container logs logtest | head -1 | wc -c
Output: 2001 (2000 characters plus newline).
-
Request the last three lines of that same file:
container logs -n 3 logtest | head -1 | wc -c
Output: 1011. The first line is silently truncated to its own tail.
Expected 2001, identical to step 4.
Steps 4 and 5 read the same file and disagree, so the defect is isolated to the -n
path. container machine logs -n has a byte-for-byte identical implementation
(MachineLogs.swift:70-93 vs ContainerLogs.swift:60-88) and is affected the same way.
Problem description
Current behavior
1. container logs -n truncates the first line it prints. Measured above: the same
log file reports 2001 bytes without -n and 1011 bytes with -n 3.
The truncation point is a direct function of the read chunk size, not the environment.
The log is 2014 bytes (2000 + \n + second\n + third\n). The reader seeks back
1024 bytes to offset 990, so the first line in its window is bytes 990–1999, 1010
characters plus a newline, giving 1011.
Cause, in ContainerLogs.swift:60-88: the loop reads backwards in 1 KiB chunks and
stops as soon as it holds n non-empty lines. Here the very first chunk already yields
three (a fragment, second, third), so it stops mid-line and prints the fragment as
though it were complete. The same loop also prepends each chunk to a Data buffer and
re-decodes and re-splits the entire accumulated buffer every iteration, making the cost
quadratic in bytes read.
The following four were identified by code inspection, not measured. They are
inefficiencies rather than incorrect output:
2. Redundant regex compilation
ContainerBuild/Globber.swift:78-91. Each call
compiles the same pattern twice: once via try Regex(...) purely to validate and then
discard, and again inside range(of:options:.regularExpression). This runs for every
file and directory visited during a build-context walk.
3. Global lock held across a filesystem walk
Services/ContainerAPIService/Server/Containers/ContainersService.swift:231-252 and
.../Volumes/VolumesService.swift:183-199. A single un-keyed AsyncLock guards every
operation (create/delete/exec/list/disk-usage) and is held for the duration of a
synchronous recursive allocatedSize walk, so container system df blocks unrelated
container operations for the length of the scan.
4. Linear scan against a Set
ContainerBuild/BuildFSSync.swift:220.
items.contains { item.relativePath == rel } resolves to Sequence.contains(where:)
rather than the Set's O(1) contains(_:), despite DirEntry hashing and comparing
solely on relativePath. O(k²) per directory during build-context tar creation.
5. Serial per-container stats
ContainerCommands/Container/ContainerStats.swift:166-193.
Two samples per container are fetched one round trip at a time in a for loop, so wall
time scales with container count.
Expected behavior
container logs -n N returns the last N complete lines, byte-identical to the last
N lines of container logs. Cost linear in bytes read.
- Each distinct glob pattern compiled once.
- Disk-usage accounting snapshots state under the lock and sizes bundles outside it, so
container system df does not block unrelated commands.
- O(1) set membership.
- Concurrent fan-out; wall time set by the slowest container, not the sum.
Relevant logs
No error messages, stack traces, or non-zero exit codes are involved, the commands
succeed and return wrong output. The wc -c counts in the reproduction steps are the
complete evidence.
Environment
- OS: macOS 26.4 (25E246)
- Xcode: Version 26.5 (17F42)
- Container: Container CLI version 1.1.0 (build: release, commit: 5973b9c)
Code of Conduct
I have done the following
Steps to reproduce
Starting state: stock installed release (version in Environment below), container
system running, no custom
~/.config/container/config.toml. No prior commands required.Reproducibility: always, deterministic whenever the first log line exceeds the
tail reader's 1024-byte read chunk.
Start the container system:
Create a container whose first log line is 2000 characters, followed by two short lines:
container run -d --name logtest debian:bookworm sh -c 'head -c 2000 /dev/zero | tr "\0" "a"; echo; echo second; echo third'Confirm it has exited:
Print the full log and measure the first line, it is correct:
Output:
2001(2000 characters plus newline).Request the last three lines of that same file:
Output:
1011. The first line is silently truncated to its own tail.Expected
2001, identical to step 4.Steps 4 and 5 read the same file and disagree, so the defect is isolated to the
-npath.
container machine logs -nhas a byte-for-byte identical implementation(
MachineLogs.swift:70-93vsContainerLogs.swift:60-88) and is affected the same way.Problem description
Current behavior
1.
container logs -ntruncates the first line it prints. Measured above: the samelog file reports 2001 bytes without
-nand 1011 bytes with-n 3.The truncation point is a direct function of the read chunk size, not the environment.
The log is 2014 bytes (2000 +
\n+second\n+third\n). The reader seeks back1024 bytes to offset 990, so the first line in its window is bytes 990–1999, 1010
characters plus a newline, giving 1011.
Cause, in
ContainerLogs.swift:60-88: the loop reads backwards in 1 KiB chunks andstops as soon as it holds
nnon-empty lines. Here the very first chunk already yieldsthree (a fragment,
second,third), so it stops mid-line and prints the fragment asthough it were complete. The same loop also prepends each chunk to a
Databuffer andre-decodes and re-splits the entire accumulated buffer every iteration, making the cost
quadratic in bytes read.
The following four were identified by code inspection, not measured. They are
inefficiencies rather than incorrect output:
2. Redundant regex compilation
ContainerBuild/Globber.swift:78-91. Each callcompiles the same pattern twice: once via
try Regex(...)purely to validate and thendiscard, and again inside
range(of:options:.regularExpression). This runs for everyfile and directory visited during a build-context walk.
3. Global lock held across a filesystem walk
Services/ContainerAPIService/Server/Containers/ContainersService.swift:231-252and.../Volumes/VolumesService.swift:183-199. A single un-keyedAsyncLockguards everyoperation (create/delete/exec/list/disk-usage) and is held for the duration of a
synchronous recursive
allocatedSizewalk, socontainer system dfblocks unrelatedcontainer operations for the length of the scan.
4. Linear scan against a
SetContainerBuild/BuildFSSync.swift:220.items.contains { item.relativePath == rel }resolves toSequence.contains(where:)rather than the
Set's O(1)contains(_:), despiteDirEntryhashing and comparingsolely on
relativePath. O(k²) per directory during build-context tar creation.5. Serial per-container stats
ContainerCommands/Container/ContainerStats.swift:166-193.Two samples per container are fetched one round trip at a time in a
forloop, so walltime scales with container count.
Expected behavior
container logs -n Nreturns the last N complete lines, byte-identical to the lastN lines of
container logs. Cost linear in bytes read.container system dfdoes not block unrelated commands.Relevant logs
No error messages, stack traces, or non-zero exit codes are involved, the commands
succeed and return wrong output. The
wc -ccounts in the reproduction steps are thecomplete evidence.
Environment
Code of Conduct