abra/pkg/logs/logs.go
decentral1se 47045ca8f1
All checks were successful
continuous-integration/drone/push Build is passing
feat: improved deploy progress reporting
See #478
2025-03-23 09:13:36 +00:00

105 lines
2.1 KiB
Go

package logs
import (
"bufio"
"context"
"fmt"
"io"
"os"
"os/signal"
"sync"
"github.com/docker/docker/api/types"
containerTypes "github.com/docker/docker/api/types/container"
"github.com/docker/docker/api/types/filters"
dockerClient "github.com/docker/docker/client"
)
type TailOpts struct {
AppName string
Services []string
StdErr bool
Since string
Buffer *[]string
ToBuffer bool
Filters filters.Args
}
// TailLogs gathers logs for the given app with optional service names to be
// filtered on. These logs can be printed to os.Stdout or gathered to a buffer.
func TailLogs(
cl *dockerClient.Client,
opts TailOpts,
) error {
sigIntCh := make(chan os.Signal, 1)
signal.Notify(sigIntCh, os.Interrupt)
defer signal.Stop(sigIntCh)
services, err := cl.ServiceList(
context.Background(),
types.ServiceListOptions{Filters: opts.Filters},
)
if err != nil {
return err
}
errCh := make(chan error)
waitCh := make(chan struct{})
go func() {
var wg sync.WaitGroup
for _, service := range services {
wg.Add(1)
go func(serviceID string) {
tail := "50"
if opts.ToBuffer {
// NOTE(d1): more logs from before deployment when analysing via file
tail = "150"
}
logs, err := cl.ServiceLogs(context.Background(), serviceID, containerTypes.LogsOptions{
ShowStderr: true,
ShowStdout: !opts.StdErr,
Since: opts.Since,
Until: "",
Timestamps: true,
Follow: true,
Tail: tail,
Details: false,
})
if err == nil {
defer logs.Close()
if opts.ToBuffer {
buf := bufio.NewScanner(logs)
for buf.Scan() {
line := fmt.Sprintf("%s: %s", service.Spec.Name, buf.Text())
*opts.Buffer = append(*opts.Buffer, line)
}
logs.Close()
return
}
if _, err = io.Copy(os.Stdout, logs); err != nil && err != io.EOF {
errCh <- fmt.Errorf("tailLogs: unable to copy buffer: %s", err)
}
}
}(service.ID)
}
wg.Wait()
close(waitCh)
}()
select {
case <-waitCh:
return nil
case <-sigIntCh:
return nil
case err := <-errCh:
return err
}
return nil
}