diff --git a/local/compose/logs.go b/local/compose/logs.go index 1949ad609..8847679a3 100644 --- a/local/compose/logs.go +++ b/local/compose/logs.go @@ -88,15 +88,17 @@ func (s *composeService) Logs(ctx context.Context, projectName string, consumer } type splitBuffer struct { + buffer bytes.Buffer name string + service string container string consumer compose.ContainerEventListener - service string } // getWriter creates a io.Writer that will actually split by line and format by LogConsumer func getWriter(name, service, container string, events compose.ContainerEventListener) io.Writer { - return splitBuffer{ + return &splitBuffer{ + buffer: bytes.Buffer{}, name: name, service: service, container: container, @@ -104,18 +106,26 @@ func getWriter(name, service, container string, events compose.ContainerEventLis } } -func (s splitBuffer) Write(b []byte) (n int, err error) { - split := bytes.Split(b, []byte{'\n'}) - for _, line := range split { - if len(line) != 0 { - s.consumer(compose.ContainerEvent{ - Type: compose.ContainerEventLog, - Name: s.name, - Service: s.service, - Source: s.container, - Line: string(line), - }) - } +// Write implements io.Writer. joins all input, splits on the separator and yields each chunk +func (s *splitBuffer) Write(b []byte) (int, error) { + n, err := s.buffer.Write(b) + if err != nil { + return n, err } - return len(b), nil + for { + b = s.buffer.Bytes() + index := bytes.Index(b, []byte{'\n'}) + if index < 0 { + break + } + line := s.buffer.Next(index + 1) + s.consumer(compose.ContainerEvent{ + Type: compose.ContainerEventLog, + Name: s.name, + Service: s.service, + Source: s.container, + Line: string(line[:len(line)-1]), + }) + } + return n, nil } diff --git a/local/compose/logs_test.go b/local/compose/logs_test.go new file mode 100644 index 000000000..9f62572c4 --- /dev/null +++ b/local/compose/logs_test.go @@ -0,0 +1,41 @@ +/* + Copyright 2020 Docker Compose CLI authors + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +package compose + +import ( + "testing" + + "gotest.tools/v3/assert" + + "github.com/docker/compose-cli/api/compose" +) + +func TestSplitWriter(t *testing.T) { + var lines []string + w := getWriter("service", "container", func(event compose.ContainerEvent) { + lines = append(lines, event.Line) + }) + w.Write([]byte("h")) //nolint: errcheck + w.Write([]byte("e")) //nolint: errcheck + w.Write([]byte("l")) //nolint: errcheck + w.Write([]byte("l")) //nolint: errcheck + w.Write([]byte("o")) //nolint: errcheck + w.Write([]byte("\n")) //nolint: errcheck + w.Write([]byte("world!\n")) //nolint: errcheck + assert.DeepEqual(t, lines, []string{"hello", "world!"}) + +}