From a0e0da1da9fbdaa8e46490f8e24e6d84959840e9 Mon Sep 17 00:00:00 2001 From: Peter Reisinger Date: Sat, 25 Nov 2017 18:30:50 +0000 Subject: [PATCH] Added 'view logs binding' --- README.md | 2 ++ grid.go | 4 +++ menus.go | 44 ++++++++++++++++++++++++ widgets/view.go | 91 +++++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 141 insertions(+) create mode 100644 widgets/view.go diff --git a/README.md b/README.md index 0e848b0..2a24d3e 100644 --- a/README.md +++ b/README.md @@ -76,6 +76,8 @@ H | Toggle ctop header h | Open help dialog s | Select container sort field r | Reverse container sort order +m | Manage container (start, stop and/or remove) +l | View container logs q | Quit ctop [build]: _docs/build.md diff --git a/grid.go b/grid.go index 7266e94..5b50691 100644 --- a/grid.go +++ b/grid.go @@ -97,6 +97,10 @@ func Display() bool { menu = ContainerMenu ui.StopLoop() }) + ui.Handle("/sys/kbd/l", func(ui.Event) { + menu = LogMenu + ui.StopLoop() + }) ui.Handle("/sys/kbd/", func(ui.Event) { single = true ui.StopLoop() diff --git a/menus.go b/menus.go index 88bda21..d6252b5 100644 --- a/menus.go +++ b/menus.go @@ -144,3 +144,47 @@ func ContainerMenu() { }) ui.Loop() } + +func LogMenu() { + + c := cursor.Selected() + if c == nil { + return + } + + ui.DefaultEvtStream.ResetHandlers() + defer ui.DefaultEvtStream.ResetHandlers() + + logs, quit := logReader(c) + m := widgets.NewTextView(logs) + m.BorderLabel = "Logs" + ui.Render(m) + + ui.Handle("/sys/kbd/", func(ui.Event) { + quit <- true + ui.StopLoop() + }) + ui.Loop() +} + +func logReader(container *container.Container) (logs chan string, quit chan bool) { + + logCollector := container.Logs() + stream := logCollector.Stream() + logs = make(chan string) + quit = make(chan bool) + + go func() { + for { + select { + case log := <- stream: + logs <- log.Message + case <- quit: + logCollector.Stop() + close(logs) + return + } + } + }() + return +} diff --git a/widgets/view.go b/widgets/view.go new file mode 100644 index 0000000..984e788 --- /dev/null +++ b/widgets/view.go @@ -0,0 +1,91 @@ +package widgets + +import ( + ui "github.com/gizak/termui" + "fmt" +) + +type TextView struct { + ui.Block + inputStream <- chan string + render chan bool + Text []string // all the text + TextOut []string // text to be displayed + TextFgColor ui.Attribute + TextBgColor ui.Attribute + padding Padding +} + +func NewTextView(lines <- chan string) *TextView { + i := &TextView{ + Block: *ui.NewBlock(), + inputStream: lines, + render: make(chan bool), + Text: []string{}, + TextOut: []string{}, + TextFgColor: ui.ThemeAttr("menu.text.fg"), + TextBgColor: ui.ThemeAttr("menu.text.bg"), + padding: Padding{4, 2}, + } + + i.BorderFg = ui.ThemeAttr("menu.border.fg") + i.BorderLabelFg = ui.ThemeAttr("menu.label.fg") + + ui.Clear() + i.Height = ui.TermHeight() + i.Width = ui.TermWidth() + + i.readInputLoop() + i.renderLoop() + return i +} + +func (i *TextView) Buffer() ui.Buffer { + + var cell ui.Cell + buf := i.Block.Buffer() + + x := i.Block.X + i.padding[0] + y := i.Block.Y + i.padding[1] + + for _, line := range i.TextOut { + for _, ch := range line { + cell = ui.Cell{Ch: ch, Fg: i.TextFgColor, Bg: i.TextBgColor} + buf.Set(x, y, cell) + x++ + } + x = i.Block.X + i.padding[0] + y++ + } + return buf +} + +func (i *TextView) renderLoop() { + go func() { + for range i.render { + size := i.Height - (i.padding[1] * 2) + if size > len(i.Text) { + size = len(i.Text) + } + i.TextOut = i.Text[len(i.Text) - size:] + + width := i.Width - (i.padding[0] * 2) + for n := range i.TextOut { + if len(i.TextOut[n]) > width { + i.TextOut[n] = fmt.Sprintf("%s...", i.TextOut[n][:width - 3]) + } + } + ui.Render(i) + } + }() +} + +func (i *TextView) readInputLoop() { + go func() { + for line := range i.inputStream { + i.Text = append(i.Text, line) + i.render <- true + } + close(i.render) + }() +}