Skip to content

Commit

Permalink
distinguish stdout and stderr in up logs
Browse files Browse the repository at this point in the history
Signed-off-by: Nicolas De Loof <nicolas.deloof@gmail.com>
  • Loading branch information
ndeloof committed Dec 13, 2022
1 parent 3ee2ab8 commit fa026a3
Show file tree
Hide file tree
Showing 11 changed files with 70 additions and 10 deletions.
2 changes: 1 addition & 1 deletion cmd/compose/logs.go
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ func runLogs(ctx context.Context, backend api.Service, opts logsOptions, service
if err != nil {
return err
}
consumer := formatter.NewLogConsumer(ctx, os.Stdout, !opts.noColor, !opts.noPrefix)
consumer := formatter.NewLogConsumer(ctx, os.Stdout, os.Stderr, !opts.noColor, !opts.noPrefix)
return backend.Logs(ctx, name, consumer, api.LogOptions{
Project: project,
Services: services,
Expand Down
2 changes: 1 addition & 1 deletion cmd/compose/up.go
Original file line number Diff line number Diff line change
Expand Up @@ -176,7 +176,7 @@ func runUp(ctx context.Context, backend api.Service, createOptions createOptions

var consumer api.LogConsumer
if !upOptions.Detach {
consumer = formatter.NewLogConsumer(ctx, os.Stdout, !upOptions.noColor, !upOptions.noPrefix)
consumer = formatter.NewLogConsumer(ctx, os.Stdout, os.Stderr, !upOptions.noColor, !upOptions.noPrefix)
}

attachTo := services
Expand Down
21 changes: 16 additions & 5 deletions cmd/formatter/logs.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,18 +32,20 @@ type logConsumer struct {
ctx context.Context
presenters sync.Map // map[string]*presenter
width int
writer io.Writer
stdout io.Writer
stderr io.Writer
color bool
prefix bool
}

// NewLogConsumer creates a new LogConsumer
func NewLogConsumer(ctx context.Context, w io.Writer, color bool, prefix bool) api.LogConsumer {
func NewLogConsumer(ctx context.Context, stdout, stderr io.Writer, color bool, prefix bool) api.LogConsumer {
return &logConsumer{
ctx: ctx,
presenters: sync.Map{},
width: 0,
writer: w,
stdout: stdout,
stderr: stderr,
color: color,
prefix: prefix,
}
Expand Down Expand Up @@ -84,19 +86,28 @@ func (l *logConsumer) getPresenter(container string) *presenter {

// Log formats a log message as received from name/container
func (l *logConsumer) Log(container, service, message string) {
l.write(l.stdout, container, service, message)
}

// Log formats a log message as received from name/container
func (l *logConsumer) Err(container, service, message string) {
l.write(l.stderr, container, service, message)
}

func (l *logConsumer) write(w io.Writer, container, service, message string) {
if l.ctx.Err() != nil {
return
}
p := l.getPresenter(container)
for _, line := range strings.Split(message, "\n") {
fmt.Fprintf(l.writer, "%s%s\n", p.prefix, line)
fmt.Fprintf(w, "%s%s\n", p.prefix, line)
}
}

func (l *logConsumer) Status(container, msg string) {
p := l.getPresenter(container)
s := p.colors(fmt.Sprintf("%s %s\n", container, msg))
l.writer.Write([]byte(s)) //nolint:errcheck
l.stdout.Write([]byte(s)) //nolint:errcheck
}

func (l *logConsumer) computeWidth() {
Expand Down
5 changes: 4 additions & 1 deletion pkg/api/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -438,6 +438,7 @@ type Stack struct {
// LogConsumer is a callback to process log messages from services
type LogConsumer interface {
Log(containerName, service, message string)
Err(containerName, service, message string)
Status(container, msg string)
Register(container string)
}
Expand All @@ -461,8 +462,10 @@ type ContainerEvent struct {
}

const (
// ContainerEventLog is a ContainerEvent of type log. Line is set
// ContainerEventLog is a ContainerEvent of type log on stdout. Line is set
ContainerEventLog = iota
// ContainerEventErr is a ContainerEvent of type log on stderr. Line is set
ContainerEventErr = iota
// ContainerEventAttach is a ContainerEvent of type attach. First event sent about a container
ContainerEventAttach
// ContainerEventStopped is a ContainerEvent of type stopped.
Expand Down
12 changes: 10 additions & 2 deletions pkg/compose/attach.go
Original file line number Diff line number Diff line change
Expand Up @@ -69,21 +69,29 @@ func (s *composeService) attachContainer(ctx context.Context, container moby.Con
Service: serviceName,
})

w := utils.GetWriter(func(line string) {
wOut := utils.GetWriter(func(line string) {
listener(api.ContainerEvent{
Type: api.ContainerEventLog,
Container: containerName,
Service: serviceName,
Line: line,
})
})
wErr := utils.GetWriter(func(line string) {
listener(api.ContainerEvent{
Type: api.ContainerEventErr,
Container: containerName,
Service: serviceName,
Line: line,
})
})

inspect, err := s.dockerCli.Client().ContainerInspect(ctx, container.ID)
if err != nil {
return err
}

_, _, err = s.attachContainerStreams(ctx, container.ID, inspect.Config.Tty, nil, w, w)
_, _, err = s.attachContainerStreams(ctx, container.ID, inspect.Config.Tty, nil, wOut, wErr)
return err
}

Expand Down
4 changes: 4 additions & 0 deletions pkg/compose/logs_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -193,6 +193,10 @@ func (l *testLogConsumer) Log(containerName, service, message string) {
l.logs[service][containerName] = append(l.logs[service][containerName], message)
}

func (l *testLogConsumer) Err(containerName, service, message string) {
l.Log(containerName, service, message)
}

func (l *testLogConsumer) Status(containerName, msg string) {}

func (l *testLogConsumer) Register(containerName string) {}
Expand Down
4 changes: 4 additions & 0 deletions pkg/compose/printer.go
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,10 @@ func (p *printer) Run(ctx context.Context, cascadeStop bool, exitCodeFrom string
if !aborting {
p.consumer.Log(container, event.Service, event.Line)
}
case api.ContainerEventErr:
if !aborting {
p.consumer.Err(container, event.Service, event.Line)
}
}
}
}
Expand Down
10 changes: 10 additions & 0 deletions pkg/e2e/compose_up_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -66,3 +66,13 @@ func TestPortRange(t *testing.T) {

c.RunDockerComposeCmd(t, "--project-name", projectName, "down", "--remove-orphans")
}

func TestStdoutStderr(t *testing.T) {
c := NewParallelCLI(t)
const projectName = "e2e-stdout-stderr"

res := c.RunDockerComposeCmdNoCheck(t, "-f", "fixtures/stdout-stderr/compose.yaml", "--project-name", projectName, "up")
res.Assert(t, icmd.Expected{Out: "log to stdout", Err: "log to stderr"})

c.RunDockerComposeCmd(t, "--project-name", projectName, "down", "--remove-orphans")
}
6 changes: 6 additions & 0 deletions pkg/e2e/fixtures/stdout-stderr/compose.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
services:
stderr:
image: alpine
command: /bin/ash /log_to_stderr.sh
volumes:
- ./log_to_stderr.sh:/log_to_stderr.sh
2 changes: 2 additions & 0 deletions pkg/e2e/fixtures/stdout-stderr/log_to_stderr.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
>&2 echo "log to stderr"
echo "log to stdout"
12 changes: 12 additions & 0 deletions pkg/mocks/mock_docker_compose_api.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

0 comments on commit fa026a3

Please sign in to comment.