Skip to content

Commit 6204fb1

Browse files
authored
logs: fix for missing output on container exit (docker#10925)
We can't assume we receive container logs line by line. Some framework won't buffer output and will send char by char, and we also can receive looong lines which get buffered to 32kb and then cut into multiple logs. This assumes we will catch container streams being closed before we receive a die event for container, which could be subject to race condition, but at least the impact here is minimal and the fix works for reproduction examples provided in linked issues. Signed-off-by: Nicolas De Loof <[email protected]>
1 parent c79f67f commit 6204fb1

File tree

4 files changed

+16
-28
lines changed

4 files changed

+16
-28
lines changed

pkg/compose/attach.go

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -98,7 +98,7 @@ func (s *composeService) attachContainer(ctx context.Context, container moby.Con
9898
return err
9999
}
100100

101-
func (s *composeService) attachContainerStreams(ctx context.Context, container string, tty bool, stdin io.ReadCloser, stdout, stderr io.Writer) (func(), chan bool, error) {
101+
func (s *composeService) attachContainerStreams(ctx context.Context, container string, tty bool, stdin io.ReadCloser, stdout, stderr io.WriteCloser) (func(), chan bool, error) {
102102
detached := make(chan bool)
103103
var (
104104
restore = func() { /* noop */ }
@@ -140,6 +140,8 @@ func (s *composeService) attachContainerStreams(ctx context.Context, container s
140140

141141
if stdout != nil {
142142
go func() {
143+
defer stdout.Close() //nolint:errcheck
144+
defer stderr.Close() //nolint:errcheck
143145
if tty {
144146
io.Copy(stdout, streamOut) //nolint:errcheck
145147
} else {

pkg/compose/logs_test.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -71,9 +71,9 @@ func TestComposeService_Logs_Demux(t *testing.T) {
7171
c1Stdout := stdcopy.NewStdWriter(c1Writer, stdcopy.Stdout)
7272
c1Stderr := stdcopy.NewStdWriter(c1Writer, stdcopy.Stderr)
7373
go func() {
74-
_, err := c1Stdout.Write([]byte("hello\n stdout"))
74+
_, err := c1Stdout.Write([]byte("hello stdout\n"))
7575
assert.NoError(t, err, "Writing to fake stdout")
76-
_, err = c1Stderr.Write([]byte("hello\n stderr"))
76+
_, err = c1Stderr.Write([]byte("hello stderr\n"))
7777
assert.NoError(t, err, "Writing to fake stderr")
7878
_ = c1Writer.Close()
7979
}()
@@ -94,7 +94,7 @@ func TestComposeService_Logs_Demux(t *testing.T) {
9494

9595
require.Equal(
9696
t,
97-
[]string{"hello", " stdout", "hello", " stderr"},
97+
[]string{"hello stdout", "hello stderr"},
9898
consumer.LogsForContainer("c"),
9999
)
100100
}

pkg/utils/writer.go

Lines changed: 3 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -43,17 +43,11 @@ func (s *splitWriter) Write(b []byte) (int, error) {
4343
for {
4444
b = s.buffer.Bytes()
4545
index := bytes.Index(b, []byte{'\n'})
46-
if index > 0 {
47-
line := s.buffer.Next(index + 1)
48-
s.consumer(string(line[:len(line)-1]))
49-
} else {
50-
line := s.buffer.String()
51-
s.buffer.Reset()
52-
if len(line) > 0 {
53-
s.consumer(line)
54-
}
46+
if index < 0 {
5547
break
5648
}
49+
line := s.buffer.Next(index + 1)
50+
s.consumer(string(line[:len(line)-1]))
5751
}
5852
return n, nil
5953
}

pkg/utils/writer_test.go

Lines changed: 7 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -28,21 +28,13 @@ func TestSplitWriter(t *testing.T) {
2828
w := GetWriter(func(line string) {
2929
lines = append(lines, line)
3030
})
31-
w.Write([]byte("hello\n"))
32-
w.Write([]byte("world\n"))
33-
w.Write([]byte("!"))
34-
assert.DeepEqual(t, lines, []string{"hello", "world", "!"})
35-
36-
}
37-
38-
//nolint:errcheck
39-
func TestSplitWriterNoEOL(t *testing.T) {
40-
var lines []string
41-
w := GetWriter(func(line string) {
42-
lines = append(lines, line)
43-
})
44-
w.Write([]byte("hello\n"))
45-
w.Write([]byte("world!"))
31+
w.Write([]byte("h"))
32+
w.Write([]byte("e"))
33+
w.Write([]byte("l"))
34+
w.Write([]byte("l"))
35+
w.Write([]byte("o"))
36+
w.Write([]byte("\n"))
37+
w.Write([]byte("world!\n"))
4638
assert.DeepEqual(t, lines, []string{"hello", "world!"})
4739

4840
}

0 commit comments

Comments
 (0)