From 5da7a21119fa9a930a201c890b593929220cb285 Mon Sep 17 00:00:00 2001 From: henrygd Date: Thu, 8 Jan 2026 13:57:56 -0500 Subject: [PATCH] agent: fix container logs decoding for raw streams (#1535) --- agent/docker.go | 9 +++++++-- agent/docker_test.go | 17 ++++++++++++++--- 2 files changed, 21 insertions(+), 5 deletions(-) diff --git a/agent/docker.go b/agent/docker.go index 3b83ed59..44558b24 100644 --- a/agent/docker.go +++ b/agent/docker.go @@ -694,7 +694,8 @@ func (dm *dockerManager) getLogs(ctx context.Context, containerID string) (strin } var builder strings.Builder - if err := decodeDockerLogStream(resp.Body, &builder); err != nil { + multiplexed := resp.Header.Get("Content-Type") == "application/vnd.docker.multiplexed-stream" + if err := decodeDockerLogStream(resp.Body, &builder, multiplexed); err != nil { return "", err } @@ -706,7 +707,11 @@ func (dm *dockerManager) getLogs(ctx context.Context, containerID string) (strin return logs, nil } -func decodeDockerLogStream(reader io.Reader, builder *strings.Builder) error { +func decodeDockerLogStream(reader io.Reader, builder *strings.Builder, multiplexed bool) error { + if !multiplexed { + _, err := io.Copy(builder, io.LimitReader(reader, maxTotalLogSize)) + return err + } const headerSize = 8 var header [headerSize]byte totalBytesRead := 0 diff --git a/agent/docker_test.go b/agent/docker_test.go index 601e6b39..972ae56f 100644 --- a/agent/docker_test.go +++ b/agent/docker_test.go @@ -950,6 +950,7 @@ func TestDecodeDockerLogStream(t *testing.T) { input []byte expected string expectError bool + multiplexed bool }{ { name: "simple log entry", @@ -960,6 +961,7 @@ func TestDecodeDockerLogStream(t *testing.T) { }, expected: "Hello World", expectError: false, + multiplexed: true, }, { name: "multiple frames", @@ -973,6 +975,7 @@ func TestDecodeDockerLogStream(t *testing.T) { }, expected: "HelloWorld", expectError: false, + multiplexed: true, }, { name: "zero length frame", @@ -985,12 +988,20 @@ func TestDecodeDockerLogStream(t *testing.T) { }, expected: "Hello", expectError: false, + multiplexed: true, }, { name: "empty input", input: []byte{}, expected: "", expectError: false, + multiplexed: true, + }, + { + name: "raw stream (not multiplexed)", + input: []byte("raw log content"), + expected: "raw log content", + multiplexed: false, }, } @@ -998,7 +1009,7 @@ func TestDecodeDockerLogStream(t *testing.T) { t.Run(tt.name, func(t *testing.T) { reader := bytes.NewReader(tt.input) var builder strings.Builder - err := decodeDockerLogStream(reader, &builder) + err := decodeDockerLogStream(reader, &builder, tt.multiplexed) if tt.expectError { assert.Error(t, err) @@ -1022,7 +1033,7 @@ func TestDecodeDockerLogStreamMemoryProtection(t *testing.T) { reader := bytes.NewReader(input) var builder strings.Builder - err := decodeDockerLogStream(reader, &builder) + err := decodeDockerLogStream(reader, &builder, true) assert.Error(t, err) assert.Contains(t, err.Error(), "log frame size") @@ -1056,7 +1067,7 @@ func TestDecodeDockerLogStreamMemoryProtection(t *testing.T) { reader := bytes.NewReader(input) var builder strings.Builder - err := decodeDockerLogStream(reader, &builder) + err := decodeDockerLogStream(reader, &builder, true) // Should complete without error (graceful truncation) assert.NoError(t, err)