Browse Source

allow to collect logs for a subset of project services

Signed-off-by: Nicolas De Loof <[email protected]>
Nicolas De Loof 5 years ago
parent
commit
056dfb95bc
8 changed files with 60 additions and 12 deletions
  1. 1 1
      aci/compose.go
  2. 1 1
      api/client/compose.go
  3. 6 1
      api/compose/api.go
  4. 7 5
      cli/cmd/compose/logs.go
  5. 1 1
      ecs/local/compose.go
  6. 29 1
      ecs/logs.go
  7. 1 1
      example/backend.go
  8. 14 1
      local/compose/logs.go

+ 1 - 1
aci/compose.go

@@ -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
 }
 

+ 1 - 1
api/client/compose.go

@@ -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
 }
 

+ 6 - 1
api/compose/api.go

@@ -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"

+ 7 - 5
cli/cmd/compose/logs.go

@@ -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,
+	})
 }

+ 1 - 1
ecs/local/compose.go

@@ -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)),
 	})

+ 29 - 1
ecs/logs.go

@@ -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)
+	}
+}

+ 1 - 1
example/backend.go

@@ -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
 }
 

+ 14 - 1
local/compose/logs.go

@@ -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