mirror of https://github.com/docker/compose.git
reuse ECS logConsumer to implement formatted compose log output
Signed-off-by: Nicolas De Loof <nicolas.deloof@gmail.com>
This commit is contained in:
parent
3f52508efe
commit
74de423cc3
41
ecs/logs.go
41
ecs/logs.go
|
@ -17,51 +17,14 @@
|
|||
package ecs
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"fmt"
|
||||
"github.com/docker/compose-cli/formatter"
|
||||
"io"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func (b *ecsAPIService) Logs(ctx context.Context, project string, w io.Writer) error {
|
||||
consumer := logConsumer{
|
||||
colors: map[string]colorFunc{},
|
||||
width: 0,
|
||||
writer: w,
|
||||
}
|
||||
consumer := formatter.NewLogConsumer(w)
|
||||
err := b.aws.GetLogs(ctx, project, consumer.Log)
|
||||
return err
|
||||
}
|
||||
|
||||
func (l *logConsumer) Log(service, container, message string) {
|
||||
cf, ok := l.colors[service]
|
||||
if !ok {
|
||||
cf = <-loop
|
||||
l.colors[service] = cf
|
||||
l.computeWidth()
|
||||
}
|
||||
prefix := fmt.Sprintf("%-"+strconv.Itoa(l.width)+"s |", service)
|
||||
|
||||
for _, line := range strings.Split(message, "\n") {
|
||||
buf := bytes.NewBufferString(fmt.Sprintf("%s %s\n", cf(prefix), line))
|
||||
l.writer.Write(buf.Bytes()) // nolint:errcheck
|
||||
}
|
||||
}
|
||||
|
||||
func (l *logConsumer) computeWidth() {
|
||||
width := 0
|
||||
for n := range l.colors {
|
||||
if len(n) > width {
|
||||
width = len(n)
|
||||
}
|
||||
}
|
||||
l.width = width + 3
|
||||
}
|
||||
|
||||
type logConsumer struct {
|
||||
colors map[string]colorFunc
|
||||
width int
|
||||
writer io.Writer
|
||||
}
|
||||
|
|
|
@ -14,7 +14,7 @@
|
|||
limitations under the License.
|
||||
*/
|
||||
|
||||
package ecs
|
||||
package formatter
|
||||
|
||||
import (
|
||||
"fmt"
|
|
@ -0,0 +1,91 @@
|
|||
/*
|
||||
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 formatter
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
|
||||
func NewLogConsumer(w io.Writer) LogConsumer {
|
||||
return LogConsumer{
|
||||
colors: map[string]colorFunc{},
|
||||
width: 0,
|
||||
writer: w,
|
||||
}
|
||||
}
|
||||
|
||||
func (l *LogConsumer) Log(service, container, message string) {
|
||||
cf, ok := l.colors[service]
|
||||
if !ok {
|
||||
cf = <-loop
|
||||
l.colors[service] = cf
|
||||
l.computeWidth()
|
||||
}
|
||||
prefix := fmt.Sprintf("%-"+strconv.Itoa(l.width)+"s |", service)
|
||||
|
||||
for _, line := range strings.Split(message, "\n") {
|
||||
buf := bytes.NewBufferString(fmt.Sprintf("%s %s\n", cf(prefix), line))
|
||||
l.writer.Write(buf.Bytes()) // nolint:errcheck
|
||||
}
|
||||
}
|
||||
|
||||
func (l *LogConsumer) GetWriter(service, container string) io.Writer {
|
||||
return splitBuffer{
|
||||
service: service,
|
||||
container: container,
|
||||
consumer: l,
|
||||
}
|
||||
}
|
||||
|
||||
func (l *LogConsumer) computeWidth() {
|
||||
width := 0
|
||||
for n := range l.colors {
|
||||
if len(n) > width {
|
||||
width = len(n)
|
||||
}
|
||||
}
|
||||
l.width = width + 3
|
||||
}
|
||||
|
||||
type LogConsumer struct {
|
||||
colors map[string]colorFunc
|
||||
width int
|
||||
writer io.Writer
|
||||
}
|
||||
|
||||
|
||||
type splitBuffer struct {
|
||||
service string
|
||||
container string
|
||||
consumer *LogConsumer
|
||||
}
|
||||
|
||||
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.Log(s.service, s.container, string(line))
|
||||
}
|
||||
}
|
||||
return len(b), nil
|
||||
}
|
||||
|
|
@ -72,3 +72,4 @@ func (s *local) ResourceService() resources.Service {
|
|||
return nil
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -25,6 +25,7 @@ import (
|
|||
"github.com/compose-spec/compose-go/types"
|
||||
"github.com/docker/compose-cli/api/compose"
|
||||
"github.com/docker/compose-cli/api/containers"
|
||||
"github.com/docker/compose-cli/formatter"
|
||||
moby "github.com/docker/docker/api/types"
|
||||
"github.com/docker/docker/api/types/container"
|
||||
"github.com/docker/docker/api/types/filters"
|
||||
|
@ -43,7 +44,7 @@ import (
|
|||
|
||||
func (s *local) Up(ctx context.Context, project *types.Project, detach bool) error {
|
||||
for k, network := range project.Networks {
|
||||
if !network.External.External {
|
||||
if !network.External.External && network.Name != "" {
|
||||
network.Name = fmt.Sprintf("%s_%s", project.Name, k)
|
||||
project.Networks[k] = network
|
||||
}
|
||||
|
@ -53,6 +54,17 @@ func (s *local) Up(ctx context.Context, project *types.Project, detach bool) err
|
|||
}
|
||||
}
|
||||
|
||||
for k, volume := range project.Volumes {
|
||||
if !volume.External.External && volume.Name != "" {
|
||||
volume.Name = fmt.Sprintf("%s_%s", project.Name, k)
|
||||
project.Volumes[k] = volume
|
||||
}
|
||||
err := s.ensureVolume(ctx, volume)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
for _, service := range project.Services {
|
||||
containerConfig, hostConfig, networkingConfig, err := getContainerCreateOptions(project, service)
|
||||
if err != nil {
|
||||
|
@ -104,11 +116,13 @@ func (s *local) Logs(ctx context.Context, projectName string, w io.Writer) error
|
|||
return err
|
||||
}
|
||||
var wg sync.WaitGroup
|
||||
consumer := formatter.NewLogConsumer(w)
|
||||
for _, c := range list {
|
||||
service := c.Labels["com.docker.compose.service"]
|
||||
go func() {
|
||||
s.containerService.Logs(ctx, c.ID, containers.LogsRequest{
|
||||
Follow: true,
|
||||
Writer: w,
|
||||
Writer: consumer.GetWriter(service, c.ID),
|
||||
})
|
||||
wg.Done()
|
||||
}()
|
||||
|
@ -418,3 +432,19 @@ func (s *local) connectContainerToNetwork(ctx context.Context, id string, servic
|
|||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *local) ensureVolume(ctx context.Context, volume types.VolumeConfig) error {
|
||||
// TODO could identify volume by label vs name
|
||||
_, err := s.volumeService.Inspect(ctx, volume.Name)
|
||||
if err != nil {
|
||||
if errdefs.IsNotFound(err) {
|
||||
// TODO we miss support for driver_opts and labels
|
||||
_, err := s.volumeService.Create(ctx, volume.Name, nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue