package gui import ( "context" "fmt" "io" "os" "os/signal" "time" "github.com/docker/docker/api/types" "github.com/docker/docker/pkg/stdcopy" "github.com/fatih/color" "github.com/jesseduffield/lazydocker/pkg/commands" "github.com/jesseduffield/lazydocker/pkg/utils" ) func (gui *Gui) renderContainerLogsToMain(container *commands.Container) error { mainView := gui.getMainView() mainView.Autoscroll = true mainView.Wrap = gui.Config.UserConfig.Gui.WrapMainPanel return gui.T.NewTickerTask(time.Millisecond*200, nil, func(stop, notifyStopped chan struct{}) { gui.renderContainerLogsToMainAux(container, stop, notifyStopped) }) } func (gui *Gui) renderContainerLogsToMainAux(container *commands.Container, stop, notifyStopped chan struct{}) { gui.clearMainView() defer func() { notifyStopped <- struct{}{} }() ctx, ctxCancel := context.WithCancel(context.Background()) go func() { <-stop ctxCancel() }() mainView := gui.getMainView() if err := gui.writeContainerLogs(container, ctx, mainView); err != nil { gui.Log.Error(err) } // if we are here because the task has been stopped, we should return // if we are here then the container must have exited, meaning we should wait until it's back again before ticker := time.NewTicker(time.Millisecond * 100) defer ticker.Stop() for { select { case <-stop: return case <-ticker.C: result, err := container.Inspect() if err != nil { // if we get an error, then the container has probably been removed so we'll get out of here gui.Log.Error(err) return } if result.State.Running { return } } } } func (gui *Gui) renderLogsToStdout(container *commands.Container) { stop := make(chan os.Signal, 1) defer signal.Stop(stop) ctx, cancel := context.WithCancel(context.Background()) go func() { signal.Notify(stop, os.Interrupt) <-stop cancel() }() if err := gui.g.Suspend(); err != nil { gui.Log.Error(err) return } defer func() { if err := gui.g.Resume(); err != nil { gui.Log.Error(err) } }() if err := gui.writeContainerLogs(container, ctx, os.Stdout); err != nil { gui.Log.Error(err) return } gui.promptToReturn() } func (gui *Gui) promptToReturn() { if !gui.Config.UserConfig.Gui.ReturnImmediately { fmt.Fprintf(os.Stdout, "\n\n%s", utils.ColoredString(gui.Tr.PressEnterToReturn, color.FgGreen)) // wait for enter press if _, err := fmt.Scanln(); err != nil { gui.Log.Error(err) } } } func (gui *Gui) writeContainerLogs(container *commands.Container, ctx context.Context, writer io.Writer) error { readCloser, err := gui.DockerCommand.Client.ContainerLogs(ctx, container.ID, types.ContainerLogsOptions{ ShowStdout: true, ShowStderr: true, Timestamps: gui.Config.UserConfig.Logs.Timestamps, Since: gui.Config.UserConfig.Logs.Since, Follow: true, }) if err != nil { return err } if container.DetailsLoaded() && container.Details.Config.Tty { _, err = io.Copy(writer, readCloser) if err != nil { return err } } else { _, err = stdcopy.StdCopy(writer, writer, readCloser) if err != nil { return err } } return nil }