added screen dumps
parent
d369c0694a
commit
6aa9e18e12
|
|
@ -3,7 +3,10 @@ package views
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
"strings"
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/derailed/k9s/internal/config"
|
"github.com/derailed/k9s/internal/config"
|
||||||
"github.com/derailed/tview"
|
"github.com/derailed/tview"
|
||||||
|
|
@ -21,6 +24,7 @@ type logView struct {
|
||||||
ansiWriter io.Writer
|
ansiWriter io.Writer
|
||||||
autoScroll bool
|
autoScroll bool
|
||||||
actions keyActions
|
actions keyActions
|
||||||
|
path string
|
||||||
}
|
}
|
||||||
|
|
||||||
func newLogView(title string, parent masterView) *logView {
|
func newLogView(title string, parent masterView) *logView {
|
||||||
|
|
@ -47,13 +51,14 @@ func newLogView(title string, parent masterView) *logView {
|
||||||
v.AddItem(v.logs, 0, 1, true)
|
v.AddItem(v.logs, 0, 1, true)
|
||||||
|
|
||||||
v.actions = keyActions{
|
v.actions = keyActions{
|
||||||
tcell.KeyEscape: {description: "Back", action: v.backCmd, visible: true},
|
tcell.KeyEscape: newKeyAction("Back", v.backCmd, true),
|
||||||
KeyC: {description: "Clear", action: v.clearCmd, visible: true},
|
KeyC: newKeyAction("Clear", v.clearCmd, true),
|
||||||
KeyS: {description: "Toggle AutoScroll", action: v.toggleScrollCmd, visible: true},
|
KeyS: newKeyAction("Toggle AutoScroll", v.toggleScrollCmd, true),
|
||||||
KeyG: {description: "Top", action: v.topCmd, visible: false},
|
KeyG: newKeyAction("Top", v.topCmd, false),
|
||||||
KeyShiftG: {description: "Bottom", action: v.bottomCmd, visible: false},
|
KeyShiftG: newKeyAction("Bottom", v.bottomCmd, false),
|
||||||
KeyF: {description: "Up", action: v.pageUpCmd, visible: false},
|
KeyF: newKeyAction("Up", v.pageUpCmd, false),
|
||||||
KeyB: {description: "Down", action: v.pageDownCmd, visible: false},
|
KeyB: newKeyAction("Down", v.pageDownCmd, false),
|
||||||
|
tcell.KeyCtrlS: newKeyAction("Save", v.saveCmd, true),
|
||||||
}
|
}
|
||||||
v.logs.SetInputCapture(v.keyboard)
|
v.logs.SetInputCapture(v.keyboard)
|
||||||
|
|
||||||
|
|
@ -111,6 +116,37 @@ func (v *logView) update() {
|
||||||
// ----------------------------------------------------------------------------
|
// ----------------------------------------------------------------------------
|
||||||
// Actions...
|
// Actions...
|
||||||
|
|
||||||
|
func (v *logView) saveCmd(evt *tcell.EventKey) *tcell.EventKey {
|
||||||
|
if err := os.MkdirAll(K9sDump, 0744); err != nil {
|
||||||
|
log.Error().Err(err).Msgf("Mkdir K9s dump")
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
now := time.Now().UnixNano()
|
||||||
|
fName := fmt.Sprintf("%s-%d.log", strings.Replace(v.path, "/", "-", -1), now)
|
||||||
|
|
||||||
|
path := filepath.Join(K9sDump, fName)
|
||||||
|
mod := os.O_CREATE | os.O_APPEND | os.O_WRONLY
|
||||||
|
file, err := os.OpenFile(path, mod, 0644)
|
||||||
|
defer func() {
|
||||||
|
if file != nil {
|
||||||
|
file.Close()
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
if err != nil {
|
||||||
|
log.Error().Err(err).Msgf("LogFile create %s", path)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, err := fmt.Fprintf(file, v.logs.GetText(true)); err != nil {
|
||||||
|
log.Error().Err(err).Msgf("Log dump %s", v.path)
|
||||||
|
}
|
||||||
|
v.app.flash().infof("Log %s saved successfully!", path)
|
||||||
|
log.Debug().Msgf("Log %s saved successfully!", path)
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func (v *logView) toggleScrollCmd(evt *tcell.EventKey) *tcell.EventKey {
|
func (v *logView) toggleScrollCmd(evt *tcell.EventKey) *tcell.EventKey {
|
||||||
v.autoScroll = !v.autoScroll
|
v.autoScroll = !v.autoScroll
|
||||||
if v.autoScroll {
|
if v.autoScroll {
|
||||||
|
|
|
||||||
|
|
@ -121,6 +121,7 @@ func (v *logsView) doLoad(path, co string) error {
|
||||||
l.logs.Clear()
|
l.logs.Clear()
|
||||||
fmat := skinTitle(fmt.Sprintf(logFmt, path, co), v.parent.appView().styles.Style)
|
fmat := skinTitle(fmt.Sprintf(logFmt, path, co), v.parent.appView().styles.Style)
|
||||||
l.SetTitle(fmat)
|
l.SetTitle(fmat)
|
||||||
|
l.path = path
|
||||||
|
|
||||||
c := make(chan string, 10)
|
c := make(chan string, 10)
|
||||||
go func(l *logView) {
|
go func(l *logView) {
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,15 @@
|
||||||
package views
|
package views
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"encoding/csv"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
"regexp"
|
"regexp"
|
||||||
"sort"
|
"sort"
|
||||||
"strings"
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/derailed/k9s/internal/config"
|
"github.com/derailed/k9s/internal/config"
|
||||||
"github.com/derailed/k9s/internal/resource"
|
"github.com/derailed/k9s/internal/resource"
|
||||||
|
|
@ -86,8 +90,7 @@ func newTableView(app *appView, title string) *tableView {
|
||||||
|
|
||||||
func (v *tableView) bindKeys() {
|
func (v *tableView) bindKeys() {
|
||||||
v.actions[KeyShiftI] = newKeyAction("Invert", v.sortInvertCmd, false)
|
v.actions[KeyShiftI] = newKeyAction("Invert", v.sortInvertCmd, false)
|
||||||
v.actions[KeyShiftN] = newKeyAction("Sort Name", v.sortColCmd(0), true)
|
v.actions[tcell.KeyCtrlS] = newKeyAction("Save", v.saveCmd, true)
|
||||||
v.actions[KeyShiftA] = newKeyAction("Sort Age", v.sortColCmd(-1), true)
|
|
||||||
|
|
||||||
v.actions[KeySlash] = newKeyAction("Filter Mode", v.activateCmd, false)
|
v.actions[KeySlash] = newKeyAction("Filter Mode", v.activateCmd, false)
|
||||||
v.actions[tcell.KeyEscape] = newKeyAction("Filter Reset", v.resetCmd, false)
|
v.actions[tcell.KeyEscape] = newKeyAction("Filter Reset", v.resetCmd, false)
|
||||||
|
|
@ -96,6 +99,9 @@ func (v *tableView) bindKeys() {
|
||||||
v.actions[tcell.KeyBackspace2] = newKeyAction("Erase", v.eraseCmd, false)
|
v.actions[tcell.KeyBackspace2] = newKeyAction("Erase", v.eraseCmd, false)
|
||||||
v.actions[tcell.KeyBackspace] = newKeyAction("Erase", v.eraseCmd, false)
|
v.actions[tcell.KeyBackspace] = newKeyAction("Erase", v.eraseCmd, false)
|
||||||
v.actions[tcell.KeyDelete] = newKeyAction("Erase", v.eraseCmd, false)
|
v.actions[tcell.KeyDelete] = newKeyAction("Erase", v.eraseCmd, false)
|
||||||
|
|
||||||
|
v.actions[KeyShiftN] = newKeyAction("Sort Name", v.sortColCmd(0), true)
|
||||||
|
v.actions[KeyShiftA] = newKeyAction("Sort Age", v.sortColCmd(-1), true)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (v *tableView) clearSelection() {
|
func (v *tableView) clearSelection() {
|
||||||
|
|
@ -133,6 +139,57 @@ func (v *tableView) setSelection() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// K9sDump represents a directory where K9s artifacts will be persisted.
|
||||||
|
var K9sDump = filepath.Join(os.TempDir(), fmt.Sprintf("k9s-screens-%s", config.MustK9sUser()))
|
||||||
|
|
||||||
|
const (
|
||||||
|
fullFmat = "%s-%s-%d.csv"
|
||||||
|
noNSFmat = "%s-%d.csv"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (v *tableView) saveCmd(evt *tcell.EventKey) *tcell.EventKey {
|
||||||
|
if err := os.MkdirAll(K9sDump, 0744); err != nil {
|
||||||
|
log.Error().Err(err).Msgf("Mkdir K9s dump")
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
ns, now := v.data.Namespace, time.Now().UnixNano()
|
||||||
|
if ns == resource.AllNamespaces {
|
||||||
|
ns = resource.AllNamespace
|
||||||
|
}
|
||||||
|
fName := fmt.Sprintf(fullFmat, v.baseTitle, ns, now)
|
||||||
|
if ns == resource.NotNamespaced {
|
||||||
|
fName = fmt.Sprintf(noNSFmat, v.baseTitle, now)
|
||||||
|
}
|
||||||
|
|
||||||
|
path := filepath.Join(K9sDump, fName)
|
||||||
|
mod := os.O_CREATE | os.O_APPEND | os.O_WRONLY
|
||||||
|
file, err := os.OpenFile(path, mod, 0644)
|
||||||
|
defer func() {
|
||||||
|
if file != nil {
|
||||||
|
file.Close()
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
if err != nil {
|
||||||
|
log.Error().Err(err).Msgf("CSV create %s", path)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
w := csv.NewWriter(file)
|
||||||
|
w.Write(v.data.Header)
|
||||||
|
for _, r := range v.data.Rows {
|
||||||
|
w.Write(r.Fields)
|
||||||
|
}
|
||||||
|
w.Flush()
|
||||||
|
if err := w.Error(); err != nil {
|
||||||
|
log.Error().Err(err).Msgf("Screen dump %s", v.baseTitle)
|
||||||
|
}
|
||||||
|
v.app.flash().infof("File %s saved successfully!", path)
|
||||||
|
log.Debug().Msgf("File %s saved successfully!", path)
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func (v *tableView) filterCmd(evt *tcell.EventKey) *tcell.EventKey {
|
func (v *tableView) filterCmd(evt *tcell.EventKey) *tcell.EventKey {
|
||||||
if v.cmdBuff.isActive() {
|
if v.cmdBuff.isActive() {
|
||||||
v.cmdBuff.setActive(false)
|
v.cmdBuff.setActive(false)
|
||||||
|
|
|
||||||
20
main.go
20
main.go
|
|
@ -12,15 +12,21 @@ import (
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
config.EnsurePath(config.K9sLogs, config.DefaultDirMod)
|
config.EnsurePath(config.K9sLogs, config.DefaultDirMod)
|
||||||
|
|
||||||
mod := os.O_CREATE | os.O_APPEND | os.O_WRONLY
|
|
||||||
if file, err := os.OpenFile(config.K9sLogs, mod, config.DefaultFileMod); err == nil {
|
|
||||||
log.Logger = log.Output(zerolog.ConsoleWriter{Out: file})
|
|
||||||
} else {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
|
mod := os.O_CREATE | os.O_APPEND | os.O_WRONLY
|
||||||
|
file, err := os.OpenFile(config.K9sLogs, mod, config.DefaultFileMod)
|
||||||
|
defer func() {
|
||||||
|
if file != nil {
|
||||||
|
file.Close()
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Logger = log.Output(zerolog.ConsoleWriter{Out: file})
|
||||||
|
|
||||||
cmd.Execute()
|
cmd.Execute()
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue