allow for viewing logs in both status and containers panel

This commit is contained in:
Jesse Duffield 2019-06-09 12:24:22 +10:00
parent da645cf173
commit d81c407dd2
8 changed files with 98 additions and 30 deletions

View file

@ -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
}

View file

@ -5,6 +5,7 @@ import (
"context" "context"
"encoding/json" "encoding/json"
"fmt" "fmt"
"os/exec"
"sort" "sort"
"strings" "strings"
"sync" "sync"
@ -392,3 +393,11 @@ func (c *DockerCommand) PruneContainers() error {
_, err := c.Client.ContainersPrune(context.Background(), filters.Args{}) _, err := c.Client.ContainersPrune(context.Background(), filters.Args{})
return err 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
}

View file

@ -377,3 +377,10 @@ func (c *OSCommand) Kill(cmd *exec.Cmd) error {
return cmd.Process.Kill() 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,
}
}

View file

@ -2,7 +2,6 @@ package commands
import ( import (
"os/exec" "os/exec"
"syscall"
"github.com/docker/docker/api/types" "github.com/docker/docker/api/types"
"github.com/fatih/color" "github.com/fatih/color"
@ -65,10 +64,7 @@ func (s *Service) ViewLogs() (*exec.Cmd, error) {
command := utils.ApplyTemplate(templateString, s) command := utils.ApplyTemplate(templateString, s)
cmd := s.OSCommand.ExecutableFromString(command) cmd := s.OSCommand.ExecutableFromString(command)
// so long as this is commented in, the child process does not receive the interrupt s.OSCommand.PrepareForChildren(cmd)
cmd.SysProcAttr = &syscall.SysProcAttr{
Setpgid: true,
}
return cmd, nil return cmd, nil
} }

View file

@ -102,15 +102,19 @@ type GuiConfig struct {
} }
type CommandTemplatesConfig struct { type CommandTemplatesConfig struct {
RestartService string `yaml:"restartService,omitempty"` RestartService string `yaml:"restartService,omitempty"`
DockerCompose string `yaml:"dockerCompose,omitempty"` DockerCompose string `yaml:"dockerCompose,omitempty"`
StopService string `yaml:"stopService,omitempty"` StopService string `yaml:"stopService,omitempty"`
ServiceLogs string `yaml:"serviceLogs,omitempty"` ServiceLogs string `yaml:"serviceLogs,omitempty"`
ViewServiceLogs string `yaml:"viewServiceLogs,omitempty"` ViewServiceLogs string `yaml:"viewServiceLogs,omitempty"`
RebuildService string `yaml:"rebuildService,omitempty"` RebuildService string `yaml:"rebuildService,omitempty"`
ContainerLogs string `yaml:"containerLogs,omitempty"`
ContainerTTYLogs string `yaml:"containerTTYLogs,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
AllLogs string `yaml:"allLogs,omitempty"` 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 { type OSConfig struct {
@ -158,14 +162,16 @@ func GetDefaultConfig() UserConfig {
Reporting: "undetermined", Reporting: "undetermined",
ConfirmOnQuit: false, ConfirmOnQuit: false,
CommandTemplates: CommandTemplatesConfig{ CommandTemplates: CommandTemplatesConfig{
RestartService: "docker-compose restart {{ .Name }}", RestartService: "docker-compose restart {{ .Name }}",
RebuildService: "docker-compose up -d --build {{ .Name }}", RebuildService: "docker-compose up -d --build {{ .Name }}",
DockerCompose: "apdev compose", DockerCompose: "apdev compose",
StopService: "apdev stop {{ .Name }}", StopService: "apdev stop {{ .Name }}",
ServiceLogs: "apdev logs {{ .Name }}", ServiceLogs: "apdev logs {{ .Name }}",
ContainerLogs: "docker logs --timestamps --follow --tail=100 {{ .ID }}", ContainerLogs: "docker logs --timestamps --follow --tail=100 {{ .ID }}",
ContainerTTYLogs: "docker logs --follow --tail=100 {{ .ID }}", ViewContainerLogs: "docker logs --timestamps --follow --tail=100 {{ .ID }}",
AllLogs: "apdev logs --tail=100", ContainerTTYLogs: "docker logs --follow --tail=100 {{ .ID }}",
AllLogs: "apdev logs --tail=100",
ViewAllLogs: "apdev logs",
}, },
OS: GetPlatformDefaultConfig(), OS: GetPlatformDefaultConfig(),
Update: UpdateConfig{ Update: UpdateConfig{

View file

@ -26,7 +26,7 @@ func (gui *Gui) getContainerContextTitles() []string {
return []string{gui.Tr.LogsTitle, gui.Tr.StatsTitle, gui.Tr.ConfigTitle} 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 selectedLine := gui.State.Panels.Containers.SelectedLine
if selectedLine == -1 { if selectedLine == -1 {
return &commands.Container{}, gui.Errors.ErrNoContainers return &commands.Container{}, gui.Errors.ErrNoContainers
@ -64,7 +64,7 @@ func (gui *Gui) handleContainerSelect(g *gocui.Gui, v *gocui.View) error {
return err return err
} }
container, err := gui.getSelectedContainer(g) container, err := gui.getSelectedContainer()
if err != nil { if err != nil {
if err != gui.Errors.ErrNoContainers { if err != gui.Errors.ErrNoContainers {
return err return err
@ -379,7 +379,7 @@ func (r *removeContainerOption) GetDisplayStrings(isFocused bool) []string {
} }
func (gui *Gui) handleContainersRemoveMenu(g *gocui.Gui, v *gocui.View) error { func (gui *Gui) handleContainersRemoveMenu(g *gocui.Gui, v *gocui.View) error {
container, err := gui.getSelectedContainer(g) container, err := gui.getSelectedContainer()
if err != nil { if err != nil {
return 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 { func (gui *Gui) handleContainerStop(g *gocui.Gui, v *gocui.View) error {
container, err := gui.getSelectedContainer(g) container, err := gui.getSelectedContainer()
if err != nil { if err != nil {
return 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 { func (gui *Gui) handleContainerRestart(g *gocui.Gui, v *gocui.View) error {
container, err := gui.getSelectedContainer(g) container, err := gui.getSelectedContainer()
if err != nil { if err != nil {
return 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 { func (gui *Gui) handleContainerAttach(g *gocui.Gui, v *gocui.View) error {
container, err := gui.getSelectedContainer(g) container, err := gui.getSelectedContainer()
if err != nil { if err != nil {
return nil return nil
} }
@ -488,3 +488,18 @@ func (gui *Gui) handlePruneContainers(g *gocui.Gui, v *gocui.View) error {
}) })
}, nil) }, 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
}

View file

@ -147,6 +147,13 @@ func (gui *Gui) GetInitialKeybindings() []*Binding {
Handler: gui.handleStatusNextContext, Handler: gui.handleStatusNextContext,
Description: gui.Tr.NextContext, Description: gui.Tr.NextContext,
}, },
{
ViewName: "status",
Key: 'm',
Modifier: gocui.ModNone,
Handler: gui.handleViewAllLogs,
Description: gui.Tr.ViewLogs,
},
{ {
ViewName: "menu", ViewName: "menu",
Key: gocui.KeyEsc, Key: gocui.KeyEsc,
@ -214,6 +221,13 @@ func (gui *Gui) GetInitialKeybindings() []*Binding {
Handler: gui.handlePruneContainers, Handler: gui.handlePruneContainers,
Description: gui.Tr.PruneContainers, Description: gui.Tr.PruneContainers,
}, },
{
ViewName: "containers",
Key: 'm',
Modifier: gocui.ModNone,
Handler: gui.handleContainerViewLogs,
Description: gui.Tr.ViewLogs,
},
{ {
ViewName: "services", ViewName: "services",
Key: 'd', Key: 'd',

View file

@ -3,7 +3,6 @@ package gui
import ( import (
"fmt" "fmt"
"strings" "strings"
"syscall"
"github.com/go-errors/errors" "github.com/go-errors/errors"
"github.com/jesseduffield/gocui" "github.com/jesseduffield/gocui"
@ -101,7 +100,7 @@ func (gui *Gui) renderAllLogs() error {
cmd.Stdout = mainView cmd.Stdout = mainView
cmd.Stderr = mainView cmd.Stderr = mainView
cmd.SysProcAttr = &syscall.SysProcAttr{Setpgid: true} gui.OSCommand.PrepareForChildren(cmd)
cmd.Start() cmd.Start()
go func() { go func() {
@ -163,3 +162,14 @@ func (gui *Gui) handleStatusPrevContext(g *gocui.Gui, v *gocui.View) error {
return nil 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
}