package app import ( "context" "errors" "fmt" "io" "os" "sync" "coopcloud.tech/abra/cli/internal" "coopcloud.tech/abra/client" "coopcloud.tech/abra/config" "github.com/docker/docker/api/types" "github.com/docker/docker/api/types/filters" dockerClient "github.com/docker/docker/client" "github.com/sirupsen/logrus" "github.com/urfave/cli/v2" ) // stackLogs lists logs for all stack services func stackLogs(stackName string, client *dockerClient.Client) { ctx := context.Background() filters := filters.NewArgs() filters.Add("name", stackName) serviceOpts := types.ServiceListOptions{Filters: filters} services, err := client.ServiceList(ctx, serviceOpts) if err != nil { logrus.Fatal(err) } var wg sync.WaitGroup for _, service := range services { wg.Add(1) go func(s string) { logOpts := types.ContainerLogsOptions{ Details: true, Follow: true, ShowStderr: true, ShowStdout: true, Tail: "20", Timestamps: true, } logs, err := client.ServiceLogs(ctx, s, logOpts) if err != nil { logrus.Fatal(err) } // defer after err check as any err returns a nil io.ReadCloser defer logs.Close() _, err = io.Copy(os.Stdout, logs) if err != nil && err != io.EOF { logrus.Fatal(err) } }(service.ID) } wg.Wait() os.Exit(0) } var appLogsCommand = &cli.Command{ Name: "logs", ArgsUsage: "[]", Usage: "tail app logs", Action: func(c *cli.Context) error { appName := c.Args().First() if appName == "" { internal.ShowSubcommandHelpAndError(c, errors.New("no app name provided")) } appFiles, err := config.LoadAppFiles("") if err != nil { logrus.Fatal(err) } appEnv, err := config.GetApp(appFiles, appName) if err != nil { logrus.Fatal(err) } ctx := context.Background() server := appFiles[appName].Server cl, err := client.New(server) if err != nil { logrus.Fatal(err) } serviceName := c.Args().Get(1) if serviceName == "" { stackLogs(appEnv.StackName(), cl) } service := fmt.Sprintf("%s_%s", appEnv.StackName(), serviceName) filters := filters.NewArgs() filters.Add("name", service) serviceOpts := types.ServiceListOptions{Filters: filters} services, err := cl.ServiceList(ctx, serviceOpts) if err != nil { logrus.Fatal(err) } if len(services) != 1 { logrus.Fatalf("expected 1 service but got %v", len(services)) } logOpts := types.ContainerLogsOptions{ Details: true, Follow: true, ShowStderr: true, ShowStdout: true, Tail: "20", Timestamps: true, } logs, err := cl.ServiceLogs(ctx, services[0].ID, logOpts) if err != nil { logrus.Fatal(err) } // defer after err check as any err returns a nil io.ReadCloser defer logs.Close() _, err = io.Copy(os.Stdout, logs) if err != nil && err != io.EOF { logrus.Fatal(err) } return nil }, }