Merge pull request #1073 from docker/logs_service

allow to collect logs for a subset of project services
This commit is contained in:
Nicolas De loof 2020-12-17 10:43:16 +01:00 committed by GitHub
commit 76ba85fe5d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 60 additions and 12 deletions

View File

@ -194,7 +194,7 @@ func (cs *aciComposeService) List(ctx context.Context, project string) ([]compos
return stacks, nil
}
func (cs *aciComposeService) Logs(ctx context.Context, projectName string, consumer compose.LogConsumer) error {
func (cs *aciComposeService) Logs(ctx context.Context, projectName string, consumer compose.LogConsumer, options compose.LogOptions) error {
return errdefs.ErrNotImplemented
}

View File

@ -56,7 +56,7 @@ func (c *composeService) Down(context.Context, string) error {
return errdefs.ErrNotImplemented
}
func (c *composeService) Logs(context.Context, string, compose.LogConsumer) error {
func (c *composeService) Logs(context.Context, string, compose.LogConsumer, compose.LogOptions) error {
return errdefs.ErrNotImplemented
}

View File

@ -39,7 +39,7 @@ type Service interface {
// Down executes the equivalent to a `compose down`
Down(ctx context.Context, projectName string) error
// Logs executes the equivalent to a `compose logs`
Logs(ctx context.Context, projectName string, consumer LogConsumer) error
Logs(ctx context.Context, projectName string, consumer LogConsumer, options LogOptions) error
// Ps executes the equivalent to a `compose ps`
Ps(ctx context.Context, projectName string) ([]ContainerSummary, error)
// List executes the equivalent to a `docker stack ls`
@ -76,6 +76,11 @@ type ServiceStatus struct {
Publishers []PortPublisher
}
// LogOptions defines optional parameters for the `Log` API
type LogOptions struct {
Services []string
}
const (
// STARTING indicates that stack is being deployed
STARTING string = "Starting"

View File

@ -21,18 +21,18 @@ import (
"os"
"github.com/docker/compose-cli/api/client"
"github.com/docker/compose-cli/api/compose"
"github.com/docker/compose-cli/formatter"
"github.com/spf13/cobra"
)
func logsCommand() *cobra.Command {
opts := composeOptions{}
logsCmd := &cobra.Command{
Use: "logs",
Use: "logs [service...]",
Short: "View output from containers",
RunE: func(cmd *cobra.Command, args []string) error {
return runLogs(cmd.Context(), opts)
return runLogs(cmd.Context(), opts, args)
},
}
logsCmd.Flags().StringVarP(&opts.Name, "project-name", "p", "", "Project name")
@ -42,7 +42,7 @@ func logsCommand() *cobra.Command {
return logsCmd
}
func runLogs(ctx context.Context, opts composeOptions) error {
func runLogs(ctx context.Context, opts composeOptions, services []string) error {
c, err := client.NewWithDefaultLocalBackend(ctx)
if err != nil {
return err
@ -53,5 +53,7 @@ func runLogs(ctx context.Context, opts composeOptions) error {
return err
}
consumer := formatter.NewLogConsumer(ctx, os.Stdout)
return c.ComposeService().Logs(ctx, projectName, consumer)
return c.ComposeService().Logs(ctx, projectName, consumer, compose.LogOptions{
Services: services,
})
}

View File

@ -180,7 +180,7 @@ services:
return cmd.Run()
}
func (e ecsLocalSimulation) Logs(ctx context.Context, projectName string, consumer compose.LogConsumer) error {
func (e ecsLocalSimulation) Logs(ctx context.Context, projectName string, consumer compose.LogConsumer, options compose.LogOptions) error {
list, err := e.moby.ContainerList(ctx, types2.ContainerListOptions{
Filters: filters.NewArgs(filters.Arg("label", "com.docker.compose.project="+projectName)),
})

View File

@ -22,7 +22,35 @@ import (
"github.com/docker/compose-cli/api/compose"
)
func (b *ecsAPIService) Logs(ctx context.Context, projectName string, consumer compose.LogConsumer) error {
func (b *ecsAPIService) Logs(ctx context.Context, projectName string, consumer compose.LogConsumer, options compose.LogOptions) error {
if len(options.Services) > 0 {
consumer = filteredLogConsumer(consumer, options.Services)
}
err := b.aws.GetLogs(ctx, projectName, consumer.Log)
return err
}
func filteredLogConsumer(consumer compose.LogConsumer, services []string) compose.LogConsumer {
if len(services) == 0 {
return consumer
}
allowed := map[string]bool{}
for _, s := range services {
allowed[s] = true
}
return &allowListLogConsumer{
allowList: allowed,
delegate: consumer,
}
}
type allowListLogConsumer struct {
allowList map[string]bool
delegate compose.LogConsumer
}
func (a *allowListLogConsumer) Log(service, container, message string) {
if a.allowList[service] {
a.delegate.Log(service, container, message)
}
}

View File

@ -175,7 +175,7 @@ func (cs *composeService) Ps(ctx context.Context, projectName string) ([]compose
func (cs *composeService) List(ctx context.Context, project string) ([]compose.Stack, error) {
return nil, errdefs.ErrNotImplemented
}
func (cs *composeService) Logs(ctx context.Context, projectName string, consumer compose.LogConsumer) error {
func (cs *composeService) Logs(ctx context.Context, projectName string, consumer compose.LogConsumer, options compose.LogOptions) error {
return errdefs.ErrNotImplemented
}

View File

@ -29,18 +29,31 @@ import (
"golang.org/x/sync/errgroup"
)
func (s *composeService) Logs(ctx context.Context, projectName string, consumer compose.LogConsumer) error {
func (s *composeService) Logs(ctx context.Context, projectName string, consumer compose.LogConsumer, options compose.LogOptions) error {
list, err := s.apiClient.ContainerList(ctx, types.ContainerListOptions{
Filters: filters.NewArgs(
projectFilter(projectName),
),
})
ignore := func(string) bool {
return false
}
if len(options.Services) > 0 {
ignore = func(s string) bool {
return !contains(options.Services, s)
}
}
if err != nil {
return err
}
eg, ctx := errgroup.WithContext(ctx)
for _, c := range list {
service := c.Labels[serviceLabel]
if ignore(service) {
continue
}
container, err := s.apiClient.ContainerInspect(ctx, c.ID)
if err != nil {
return err