fix(logs): cap streamed log reads so dockerd does not scan the full log file

Closes #7693
This commit is contained in:
Andras Bacsai committed 2026-11-03 22:22:22 +01:00
1 parent dec776025e
commit 1b571f4509
3 files changed
+15 -5

No files matched your search

+12 -2
View File
@@ -172,7 +172,7 @@ class GetLogs extends Component
$since = is_string($since) ? $since : null;
if (is_string($since) && preg_match(self::DOCKER_TIMESTAMP_PATTERN, $since)) {
$options = "--since {$since} -t";
$options = $this->sinceOptions($since);
} else {
$options = "-n {$this->resolveLogTail()} -t";
}
@@ -281,7 +281,7 @@ class GetLogs extends Component
if ($all) {
$options = '-t';
} elseif ($sinceValue !== null) {
$options = "--since {$sinceValue} -t";
$options = $this->sinceOptions($sinceValue);
} else {
$options = "-n {$tail} -t";
}
@@ -441,6 +441,16 @@ class GetLogs extends Component
return collect();
}
/**
* `--since` without `-n` makes dockerd decode the complete log file of the container on each
* stream tick, which keeps it at full CPU for large logs. With `-n`, dockerd reads from the end.
* The viewer keeps at most MAX_LOG_LINES lines, so a stream tick never needs more.
*/
private function sinceOptions(string $since): string
{
return "--since {$since} -n ".self::MAX_LOG_LINES.' -t';
}
private function resolveLogTail(): int|string
{
if ($this->numberOfLines === -1) {
@@ -221,7 +221,7 @@ describe('GetLogs browser-rendered output', function () {
->call('getLogs', '2026-09-28T10:00:02.123456789Z')
->assertReturned('2026-09-28T10:00:03Z new');
Process::assertRan(fn ($process) => str_contains(remoteProcessScript($process), 'docker logs --since 2026-09-28T10:00:02.123456789Z -t test-container'));
Process::assertRan(fn ($process) => str_contains(remoteProcessScript($process), 'docker logs --since 2026-09-28T10:00:02.123456789Z -n 50000 -t test-container'));
});
test('getLogs prefixes the streaming command with sudo for non-root servers', function () {
@@ -234,7 +234,7 @@ describe('GetLogs browser-rendered output', function () {
'container' => 'test-container',
])->call('getLogs', '2026-09-28T10:00:02Z');
Process::assertRan(fn ($process) => str_contains(remoteProcessScript($process), '(sudo docker logs --since 2026-09-28T10:00:02Z -t test-container) 2>&1 | head -c'));
Process::assertRan(fn ($process) => str_contains(remoteProcessScript($process), '(sudo docker logs --since 2026-09-28T10:00:02Z -n 50000 -t test-container) 2>&1 | head -c'));
});
test('getLogs ignores a since value that is not a docker timestamp', function (string $since) {
+1 -1
View File
@@ -180,7 +180,7 @@ it('asks each source only for lines after its own timestamp', function () {
$frankfurt = collect($commands)->first(fn (string $command): bool => str_contains($command, 'web-frankfurt'));
$helsinki = collect($commands)->first(fn (string $command): bool => str_contains($command, 'web-helsinki'));
expect($frankfurt)->toContain('--since 2026-09-28T10:00:01.123456789Z')
expect($frankfurt)->toContain('--since 2026-09-28T10:00:01.123456789Z -n 50000 -t')
->and($helsinki)->toContain('docker logs -n ')
->and($helsinki)->not->toContain('--since');
});