From d81c407dd2cf8617c4610229d63ef444393cde99 Mon Sep 17 00:00:00 2001 From: Jesse Duffield Date: Sun, 9 Jun 2019 12:24:22 +1000 Subject: [PATCH] allow for viewing logs in both status and containers panel --- pkg/commands/container.go | 11 ++++++++++ pkg/commands/docker.go | 9 +++++++++ pkg/commands/os.go | 7 +++++++ pkg/commands/service.go | 6 +----- pkg/config/app_config.go | 40 +++++++++++++++++++++---------------- pkg/gui/containers_panel.go | 27 +++++++++++++++++++------ pkg/gui/keybindings.go | 14 +++++++++++++ pkg/gui/status_panel.go | 14 +++++++++++-- 8 files changed, 98 insertions(+), 30 deletions(-) diff --git a/pkg/commands/container.go b/pkg/commands/container.go index 216af724..71661292 100644 --- a/pkg/commands/container.go +++ b/pkg/commands/container.go @@ -389,3 +389,14 @@ func (c *Container) EraseOldHistory() { } } } + +// ViewLogs attaches to a subprocess viewing the container's logs +func (c *Container) ViewLogs() (*exec.Cmd, error) { + templateString := c.OSCommand.Config.UserConfig.CommandTemplates.ViewContainerLogs + command := utils.ApplyTemplate(templateString, c) + + cmd := c.OSCommand.ExecutableFromString(command) + c.OSCommand.PrepareForChildren(cmd) + + return cmd, nil +} diff --git a/pkg/commands/docker.go b/pkg/commands/docker.go index 217b0068..279d1245 100644 --- a/pkg/commands/docker.go +++ b/pkg/commands/docker.go @@ -5,6 +5,7 @@ import ( "context" "encoding/json" "fmt" + "os/exec" "sort" "strings" "sync" @@ -392,3 +393,11 @@ func (c *DockerCommand) PruneContainers() error { _, err := c.Client.ContainersPrune(context.Background(), filters.Args{}) return err } + +// ViewAllLogs attaches to a subprocess viewing all the logs from docker-compose +func (c *DockerCommand) ViewAllLogs() (*exec.Cmd, error) { + cmd := c.OSCommand.ExecutableFromString(c.OSCommand.Config.UserConfig.CommandTemplates.ViewAllLogs) + c.OSCommand.PrepareForChildren(cmd) + + return cmd, nil +} diff --git a/pkg/commands/os.go b/pkg/commands/os.go index 0e79fe84..1e9d8118 100644 --- a/pkg/commands/os.go +++ b/pkg/commands/os.go @@ -377,3 +377,10 @@ func (c *OSCommand) Kill(cmd *exec.Cmd) error { return cmd.Process.Kill() } + +// PrepareForChildren sets Setpgid to true on the cmd, so that when we run it as a sideproject, we can kill its group rather than the process itself. This is because some commands, like `docker-compose logs` spawn multiple children processes, and killing the parent process isn't sufficient for killing those child processes. We set the group id here, and then in subprocess.go we check if the group id is set and if so, we kill the whole group rather than just the one process. +func (c *OSCommand) PrepareForChildren(cmd *exec.Cmd) { + cmd.SysProcAttr = &syscall.SysProcAttr{ + Setpgid: true, + } +} diff --git a/pkg/commands/service.go b/pkg/commands/service.go index f04a20d9..079cb279 100644 --- a/pkg/commands/service.go +++ b/pkg/commands/service.go @@ -2,7 +2,6 @@ package commands import ( "os/exec" - "syscall" "github.com/docker/docker/api/types" "github.com/fatih/color" @@ -65,10 +64,7 @@ func (s *Service) ViewLogs() (*exec.Cmd, error) { command := utils.ApplyTemplate(templateString, s) cmd := s.OSCommand.ExecutableFromString(command) - // so long as this is commented in, the child process does not receive the interrupt - cmd.SysProcAttr = &syscall.SysProcAttr{ - Setpgid: true, - } + s.OSCommand.PrepareForChildren(cmd) return cmd, nil } diff --git a/pkg/config/app_config.go b/pkg/config/app_config.go index 629e42a2..bee3ab02 100644 --- a/pkg/config/app_config.go +++ b/pkg/config/app_config.go @@ -102,15 +102,19 @@ type GuiConfig struct { } type CommandTemplatesConfig struct { - RestartService string `yaml:"restartService,omitempty"` - DockerCompose string `yaml:"dockerCompose,omitempty"` - StopService string `yaml:"stopService,omitempty"` - ServiceLogs string `yaml:"serviceLogs,omitempty"` - ViewServiceLogs string `yaml:"viewServiceLogs,omitempty"` - RebuildService string `yaml:"rebuildService,omitempty"` - ContainerLogs string `yaml:"containerLogs,omitempty"` - ContainerTTYLogs string `yaml:"containerTTYLogs,omitempty"` - AllLogs string `yaml:"allLogs,omitempty"` + RestartService string `yaml:"restartService,omitempty"` + DockerCompose string `yaml:"dockerCompose,omitempty"` + StopService string `yaml:"stopService,omitempty"` + ServiceLogs string `yaml:"serviceLogs,omitempty"` + ViewServiceLogs string `yaml:"viewServiceLogs,omitempty"` + RebuildService string `yaml:"rebuildService,omitempty"` + + // ViewContainerLogs is for viewing the container logs in a subprocess. We have this as a separate command in case you want to show all the logs rather than just tail them for the sake of reducing CPU load when in the lazydocker GUI + ViewContainerLogs string `yaml:"viewContainerLogs,omitempty"` + ContainerLogs string `yaml:"containerLogs,omitempty"` + ContainerTTYLogs string `yaml:"containerTTYLogs,omitempty"` + AllLogs string `yaml:"allLogs,omitempty"` + ViewAllLogs string `yaml:"viewAlLogs,omitempty"` } type OSConfig struct { @@ -158,14 +162,16 @@ func GetDefaultConfig() UserConfig { Reporting: "undetermined", ConfirmOnQuit: false, CommandTemplates: CommandTemplatesConfig{ - RestartService: "docker-compose restart {{ .Name }}", - RebuildService: "docker-compose up -d --build {{ .Name }}", - DockerCompose: "apdev compose", - StopService: "apdev stop {{ .Name }}", - ServiceLogs: "apdev logs {{ .Name }}", - ContainerLogs: "docker logs --timestamps --follow --tail=100 {{ .ID }}", - ContainerTTYLogs: "docker logs --follow --tail=100 {{ .ID }}", - AllLogs: "apdev logs --tail=100", + RestartService: "docker-compose restart {{ .Name }}", + RebuildService: "docker-compose up -d --build {{ .Name }}", + DockerCompose: "apdev compose", + StopService: "apdev stop {{ .Name }}", + ServiceLogs: "apdev logs {{ .Name }}", + ContainerLogs: "docker logs --timestamps --follow --tail=100 {{ .ID }}", + ViewContainerLogs: "docker logs --timestamps --follow --tail=100 {{ .ID }}", + ContainerTTYLogs: "docker logs --follow --tail=100 {{ .ID }}", + AllLogs: "apdev logs --tail=100", + ViewAllLogs: "apdev logs", }, OS: GetPlatformDefaultConfig(), Update: UpdateConfig{ diff --git a/pkg/gui/containers_panel.go b/pkg/gui/containers_panel.go index eea774e9..3035a032 100644 --- a/pkg/gui/containers_panel.go +++ b/pkg/gui/containers_panel.go @@ -26,7 +26,7 @@ func (gui *Gui) getContainerContextTitles() []string { return []string{gui.Tr.LogsTitle, gui.Tr.StatsTitle, gui.Tr.ConfigTitle} } -func (gui *Gui) getSelectedContainer(g *gocui.Gui) (*commands.Container, error) { +func (gui *Gui) getSelectedContainer() (*commands.Container, error) { selectedLine := gui.State.Panels.Containers.SelectedLine if selectedLine == -1 { return &commands.Container{}, gui.Errors.ErrNoContainers @@ -64,7 +64,7 @@ func (gui *Gui) handleContainerSelect(g *gocui.Gui, v *gocui.View) error { return err } - container, err := gui.getSelectedContainer(g) + container, err := gui.getSelectedContainer() if err != nil { if err != gui.Errors.ErrNoContainers { return err @@ -379,7 +379,7 @@ func (r *removeContainerOption) GetDisplayStrings(isFocused bool) []string { } func (gui *Gui) handleContainersRemoveMenu(g *gocui.Gui, v *gocui.View) error { - container, err := gui.getSelectedContainer(g) + container, err := gui.getSelectedContainer() if err != nil { return nil } @@ -430,7 +430,7 @@ func (gui *Gui) handleContainersRemoveMenu(g *gocui.Gui, v *gocui.View) error { } func (gui *Gui) handleContainerStop(g *gocui.Gui, v *gocui.View) error { - container, err := gui.getSelectedContainer(g) + container, err := gui.getSelectedContainer() if err != nil { return nil } @@ -448,7 +448,7 @@ func (gui *Gui) handleContainerStop(g *gocui.Gui, v *gocui.View) error { } func (gui *Gui) handleContainerRestart(g *gocui.Gui, v *gocui.View) error { - container, err := gui.getSelectedContainer(g) + container, err := gui.getSelectedContainer() if err != nil { return nil } @@ -463,7 +463,7 @@ func (gui *Gui) handleContainerRestart(g *gocui.Gui, v *gocui.View) error { } func (gui *Gui) handleContainerAttach(g *gocui.Gui, v *gocui.View) error { - container, err := gui.getSelectedContainer(g) + container, err := gui.getSelectedContainer() if err != nil { return nil } @@ -488,3 +488,18 @@ func (gui *Gui) handlePruneContainers(g *gocui.Gui, v *gocui.View) error { }) }, nil) } + +func (gui *Gui) handleContainerViewLogs(g *gocui.Gui, v *gocui.View) error { + container, err := gui.getSelectedContainer() + if err != nil { + return nil + } + + c, err := container.ViewLogs() + if err != nil { + return gui.createErrorPanel(gui.g, err.Error()) + } + + gui.SubProcess = c + return gui.Errors.ErrSubProcess +} diff --git a/pkg/gui/keybindings.go b/pkg/gui/keybindings.go index 2718effe..7166d6a0 100644 --- a/pkg/gui/keybindings.go +++ b/pkg/gui/keybindings.go @@ -147,6 +147,13 @@ func (gui *Gui) GetInitialKeybindings() []*Binding { Handler: gui.handleStatusNextContext, Description: gui.Tr.NextContext, }, + { + ViewName: "status", + Key: 'm', + Modifier: gocui.ModNone, + Handler: gui.handleViewAllLogs, + Description: gui.Tr.ViewLogs, + }, { ViewName: "menu", Key: gocui.KeyEsc, @@ -214,6 +221,13 @@ func (gui *Gui) GetInitialKeybindings() []*Binding { Handler: gui.handlePruneContainers, Description: gui.Tr.PruneContainers, }, + { + ViewName: "containers", + Key: 'm', + Modifier: gocui.ModNone, + Handler: gui.handleContainerViewLogs, + Description: gui.Tr.ViewLogs, + }, { ViewName: "services", Key: 'd', diff --git a/pkg/gui/status_panel.go b/pkg/gui/status_panel.go index d4dd7c4a..fbe78e13 100644 --- a/pkg/gui/status_panel.go +++ b/pkg/gui/status_panel.go @@ -3,7 +3,6 @@ package gui import ( "fmt" "strings" - "syscall" "github.com/go-errors/errors" "github.com/jesseduffield/gocui" @@ -101,7 +100,7 @@ func (gui *Gui) renderAllLogs() error { cmd.Stdout = mainView cmd.Stderr = mainView - cmd.SysProcAttr = &syscall.SysProcAttr{Setpgid: true} + gui.OSCommand.PrepareForChildren(cmd) cmd.Start() go func() { @@ -163,3 +162,14 @@ func (gui *Gui) handleStatusPrevContext(g *gocui.Gui, v *gocui.View) error { return nil } + +// handleViewAllLogs switches to a subprocess viewing all the logs from docker-compose +func (gui *Gui) handleViewAllLogs(g *gocui.Gui, v *gocui.View) error { + c, err := gui.DockerCommand.ViewAllLogs() + if err != nil { + return gui.createErrorPanel(gui.g, err.Error()) + } + + gui.SubProcess = c + return gui.Errors.ErrSubProcess +}