package main import ( "bufio" "context" "fmt" "io" "log" "log/slog" "time" "github.com/k0kubun/pp/v3" "github.com/docker/docker/api/types/container" "github.com/docker/docker/client" "github.com/docker/docker/pkg/stdcopy" "github.com/docker/docker/api/types/events" "github.com/docker/docker/api/types/filters" ) var containers = make(map[string]int) type LogType struct { Log string Source string ContainerName string } func main() { ctx := context.Background() pp.Println("Running1") ticker := time.NewTicker(1 * time.Second) defer ticker.Stop() logschan := make(chan LogType) go listContainersAndLogType(ctx, logschan) go func() { time.Sleep(3 * time.Second) slog.Info("Slept") }() for i := range logschan { slog.Info(i.ContainerName + ": " + i.Log) } } func eventListen(cli *client.Client, logschan chan<- LogType) { ctx := context.Background() options := events.ListOptions{ Filters: filters.NewArgs( filters.Arg("type", "container"), ), } eventChan, errChan := cli.Events(ctx, options) slog.Info("Waiting for events") for { select { case event := <-eventChan: switch event.Action { case "create": fmt.Println("Container created:", event.Actor.Attributes["image"]) case "start": fmt.Println("Container started:", event.Actor.Attributes["image"]) streamContainerLogs(ctx, cli, event.Actor.ID, logschan) case "die": fmt.Println("Container stopped:", event.Actor.Attributes["image"]) case "destroy": fmt.Println("Container destroyed:", event.Actor.Attributes["image"]) } case err := <-errChan: if err != nil { fmt.Println("Error listening to Docker events:", err) return } } } } func listContainersAndLogType(ctx context.Context, logschan chan<- LogType) { cli, err := client.NewClientWithOpts(client.FromEnv, client.WithAPIVersionNegotiation()) if err != nil { log.Fatalf("Error creating Docker client: %v", err) } containers, err := cli.ContainerList(ctx, container.ListOptions{All: true}) if err != nil { log.Fatalf("Error listing containers: %v", err) } pp.Println(len(containers)) for _, curcont := range containers { go func() { defer streamContainerLogs(ctx, cli, curcont.ID, logschan) }() } fmt.Println("Ending") eventListen(cli, logschan) } func streamContainerLogs(ctx context.Context, cli *client.Client, containerID string, logschan chan<- LogType) { inspect, err := cli.ContainerInspect(ctx, containerID) if err != nil { log.Printf("Error inspecting container %s: %v", containerID, err) return } if len(inspect.Config.Cmd) == 0 { slog.Debug("Skipping container as cmd empty", "container-id", containerID, "container-image", inspect.Image) return } if inspect.Config.Cmd[0] == "/lilog" || inspect.Config.Cmd[0] == "sh" { slog.Debug("Skipping own container logs") return } logType := inspect.HostConfig.LogConfig.Type if inspect.State.Status != "running" { slog.Debug("Skipping container which isn't running", "container-id", containerID) return } fmt.Printf("Container: %s (%-20s) ID: %.12s LogDriver: %s\n", inspect.Config.Cmd[0], inspect.Image, inspect.ID, logType) reader, err := cli.ContainerLogs(ctx, containerID, container.LogsOptions{ ShowStdout: true, ShowStderr: true, Follow: true, Tail: "0", }) if err != nil { panic(err) } defer reader.Close() if inspect.Config.Tty { scanner := bufio.NewScanner(reader) for scanner.Scan() { logschan <- LogType{ContainerName: inspect.Name, Source: "tty", Log: scanner.Text()} } } else { stdoutPR, stdoutPW := io.Pipe() stderrPR, stderrPW := io.Pipe() defer stdoutPW.Close() defer stderrPW.Close() go func() { scanner := bufio.NewScanner(stdoutPR) for scanner.Scan() { logschan <- LogType{ ContainerName: inspect.Name, Source: "tty", Log: scanner.Text(), } } }() go func() { scanner := bufio.NewScanner(stderrPR) for scanner.Scan() { logschan <- LogType{ ContainerName: inspect.Name, Source: "tty", Log: scanner.Text(), } } }() _, err := stdcopy.StdCopy(stdoutPW, stderrPW, reader) if err != nil { panic(err) } } }