mirror of
https://github.com/docker/compose.git
synced 2025-07-25 14:44:29 +02:00
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
|
package ecs
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
"github.com/docker/compose-cli/formatter"
|
||||||
"io"
|
"io"
|
||||||
"strconv"
|
|
||||||
"strings"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func (b *ecsAPIService) Logs(ctx context.Context, project string, w io.Writer) error {
|
func (b *ecsAPIService) Logs(ctx context.Context, project string, w io.Writer) error {
|
||||||
consumer := logConsumer{
|
consumer := formatter.NewLogConsumer(w)
|
||||||
colors: map[string]colorFunc{},
|
|
||||||
width: 0,
|
|
||||||
writer: w,
|
|
||||||
}
|
|
||||||
err := b.aws.GetLogs(ctx, project, consumer.Log)
|
err := b.aws.GetLogs(ctx, project, consumer.Log)
|
||||||
return err
|
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.
|
limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package ecs
|
package formatter
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
91
formatter/logs.go
Normal file
91
formatter/logs.go
Normal file
@ -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
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -25,6 +25,7 @@ import (
|
|||||||
"github.com/compose-spec/compose-go/types"
|
"github.com/compose-spec/compose-go/types"
|
||||||
"github.com/docker/compose-cli/api/compose"
|
"github.com/docker/compose-cli/api/compose"
|
||||||
"github.com/docker/compose-cli/api/containers"
|
"github.com/docker/compose-cli/api/containers"
|
||||||
|
"github.com/docker/compose-cli/formatter"
|
||||||
moby "github.com/docker/docker/api/types"
|
moby "github.com/docker/docker/api/types"
|
||||||
"github.com/docker/docker/api/types/container"
|
"github.com/docker/docker/api/types/container"
|
||||||
"github.com/docker/docker/api/types/filters"
|
"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 {
|
func (s *local) Up(ctx context.Context, project *types.Project, detach bool) error {
|
||||||
for k, network := range project.Networks {
|
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)
|
network.Name = fmt.Sprintf("%s_%s", project.Name, k)
|
||||||
project.Networks[k] = network
|
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 {
|
for _, service := range project.Services {
|
||||||
containerConfig, hostConfig, networkingConfig, err := getContainerCreateOptions(project, service)
|
containerConfig, hostConfig, networkingConfig, err := getContainerCreateOptions(project, service)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -104,11 +116,13 @@ func (s *local) Logs(ctx context.Context, projectName string, w io.Writer) error
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
var wg sync.WaitGroup
|
var wg sync.WaitGroup
|
||||||
|
consumer := formatter.NewLogConsumer(w)
|
||||||
for _, c := range list {
|
for _, c := range list {
|
||||||
|
service := c.Labels["com.docker.compose.service"]
|
||||||
go func() {
|
go func() {
|
||||||
s.containerService.Logs(ctx, c.ID, containers.LogsRequest{
|
s.containerService.Logs(ctx, c.ID, containers.LogsRequest{
|
||||||
Follow: true,
|
Follow: true,
|
||||||
Writer: w,
|
Writer: consumer.GetWriter(service, c.ID),
|
||||||
})
|
})
|
||||||
wg.Done()
|
wg.Done()
|
||||||
}()
|
}()
|
||||||
@ -418,3 +432,19 @@ func (s *local) connectContainerToNetwork(ctx context.Context, id string, servic
|
|||||||
}
|
}
|
||||||
return nil
|
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…
x
Reference in New Issue
Block a user