From 4aa4b55ce8cbbebcaab1696bc0bbe2752d6dc61a Mon Sep 17 00:00:00 2001 From: Richard Hull Date: Fri, 23 Sep 2022 18:38:21 +0100 Subject: [PATCH] [feat] Add toggle to allow log lines to be JSON colorized --- README.md | 2 ++ go.mod | 3 ++- go.sum | 4 ++++ internal/config/config_test.go | 2 ++ internal/config/logger.go | 1 + internal/dao/log_item.go | 29 ++++++++++++++++++++++++----- internal/dao/log_item_test.go | 6 +++--- internal/dao/log_items.go | 26 +++++++++++++------------- internal/dao/log_items_test.go | 4 ++-- internal/dao/log_options.go | 1 + internal/model/log.go | 20 +++++++++++++------- internal/model/log_test.go | 6 +++--- internal/view/container.go | 1 + internal/view/dp.go | 1 + internal/view/log.go | 14 ++++++++++++++ internal/view/log_indicator.go | 19 +++++++++++++++++++ internal/view/log_indicator_test.go | 4 ++-- internal/view/log_int_test.go | 6 +++--- internal/view/log_test.go | 8 ++++---- internal/view/logs_extender.go | 1 + internal/view/pod.go | 1 + internal/view/sts.go | 1 + 22 files changed, 117 insertions(+), 43 deletions(-) diff --git a/README.md b/README.md index c69e38f4b3..21b42dfb30 100644 --- a/README.md +++ b/README.md @@ -319,6 +319,8 @@ K9s uses aliases to navigate most K8s resources. textWrap: false # Toggles log line timestamp info. Default false showTime: false + # Toggles whether JSON log lines should be colorized. Default false + showJSON: false # Indicates the current kube context. Defaults to current context currentContext: minikube # Indicates the current kube cluster. Defaults to current context cluster diff --git a/go.mod b/go.mod index fa9e248771..8edd27281a 100644 --- a/go.mod +++ b/go.mod @@ -135,6 +135,7 @@ require ( github.com/prometheus/common v0.32.1 // indirect github.com/prometheus/procfs v0.7.3 // indirect github.com/rivo/uniseg v0.2.0 // indirect + github.com/rm-hull/colorjson v0.0.0-20220923220430-b50ee91dc6f6 // indirect github.com/rubenv/sql-migrate v1.1.2 // indirect github.com/russross/blackfriday v1.5.2 // indirect github.com/shopspring/decimal v1.2.0 // indirect @@ -150,7 +151,7 @@ require ( golang.org/x/net v0.0.0-20220722155237-a158d28d115b // indirect golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8 // indirect golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4 // indirect - golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab // indirect + golang.org/x/sys v0.0.0-20220919091848-fb04ddd9f9c8 // indirect golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 // indirect golang.org/x/time v0.0.0-20220210224613-90d013bbcef8 // indirect google.golang.org/appengine v1.6.7 // indirect diff --git a/go.sum b/go.sum index fe1e2a7339..42f3d7fc53 100644 --- a/go.sum +++ b/go.sum @@ -586,6 +586,8 @@ github.com/rakyll/hey v0.1.4/go.mod h1:nAOTOo+L52KB9SZq/M6J18kxjto4yVtXQDjU2HgjU github.com/rivo/uniseg v0.1.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY= github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= +github.com/rm-hull/colorjson v0.0.0-20220923220430-b50ee91dc6f6 h1:avrA8y9AJF9WtGipEvrM8I/7XoKcxEk30659rPHJlnM= +github.com/rm-hull/colorjson v0.0.0-20220923220430-b50ee91dc6f6/go.mod h1:tJTNxpJk1e15vd8WY5lsj9Tq5vjdnNz3YAbCwxYskBs= github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= github.com/rogpeppe/go-internal v1.8.0/go.mod h1:WmiCO8CzOY8rg0OYDC4/i/2WRWAB6poM+XZ2dLUbcbE= @@ -883,6 +885,8 @@ golang.org/x/sys v0.0.0-20220114195835-da31bd327af9/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab h1:2QkjZIsXupsJbJIdSjjUOgWK3aEtzyuh2mPt3l/CkeU= golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220919091848-fb04ddd9f9c8 h1:h+EGohizhe9XlX18rfpa8k8RAc5XyaeamM+0VHRd4lc= +golang.org/x/sys v0.0.0-20220919091848-fb04ddd9f9c8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20201210144234-2321bbc49cbf/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210406210042-72f3dc4e9b72/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= diff --git a/internal/config/config_test.go b/internal/config/config_test.go index d3109b17d8..b5d0538893 100644 --- a/internal/config/config_test.go +++ b/internal/config/config_test.go @@ -293,6 +293,7 @@ var expectedConfig = `k9s: fullScreenLogs: false textWrap: false showTime: false + showJSON: false currentContext: blee currentCluster: blee clusters: @@ -388,6 +389,7 @@ var resetConfig = `k9s: fullScreenLogs: false textWrap: false showTime: false + showJSON: false currentContext: blee currentCluster: blee clusters: diff --git a/internal/config/logger.go b/internal/config/logger.go index 11a59f5231..1722e0ac65 100644 --- a/internal/config/logger.go +++ b/internal/config/logger.go @@ -21,6 +21,7 @@ type Logger struct { FullScreenLogs bool `yaml:"fullScreenLogs"` TextWrap bool `yaml:"textWrap"` ShowTime bool `yaml:"showTime"` + ShowJSON bool `yaml:"showJSON"` } // NewLogger returns a new instance. diff --git a/internal/dao/log_item.go b/internal/dao/log_item.go index a66e1748d0..ddf7cc7cb9 100644 --- a/internal/dao/log_item.go +++ b/internal/dao/log_item.go @@ -2,6 +2,9 @@ package dao import ( "bytes" + "encoding/json" + + "github.com/rm-hull/colorjson" ) // LogChan represents a channel for logs. @@ -64,7 +67,7 @@ func (l *LogItem) Size() int { } // Render returns a log line as string. -func (l *LogItem) Render(paint string, showTime bool, bb *bytes.Buffer) { +func (l *LogItem) Render(paint string, showTime bool, showJson bool, bb *bytes.Buffer) { index := bytes.Index(l.Bytes, []byte{' '}) if showTime && index > 0 { bb.WriteString("[gray::b]") @@ -89,9 +92,25 @@ func (l *LogItem) Render(paint string, showTime bool, bb *bytes.Buffer) { bb.WriteString("[-::] ") } - if index > 0 { - bb.Write(l.Bytes[index+1:]) - } else { - bb.Write(l.Bytes) + bb.Write(colorizeJSON(l.Bytes[index+1:], showJson)) +} + +func colorizeJSON(text []byte, showJson bool) []byte { + if !showJson { + return text + } + var obj map[string]interface{} + err := json.Unmarshal(text, &obj) + if err != nil { + return text + } + f := colorjson.NewFormatter() + f.Indent = 0 + f.ObjectSeparator = false + + s, err := f.Marshal(obj) + if err != nil { + return text } + return append(s, '\n') } diff --git a/internal/dao/log_item_test.go b/internal/dao/log_item_test.go index 1eeacc548d..a8a81bea20 100644 --- a/internal/dao/log_item_test.go +++ b/internal/dao/log_item_test.go @@ -85,7 +85,7 @@ func TestLogItemRender(t *testing.T) { i.Pod, i.Container = n, u.opts.Container bb := bytes.NewBuffer(make([]byte, 0, i.Size())) - i.Render("yellow", u.opts.ShowTimestamp, bb) + i.Render("yellow", u.opts.ShowTimestamp, u.opts.ShowJSON, bb) assert.Equal(t, u.e, bb.String()) }) } @@ -100,7 +100,7 @@ func BenchmarkLogItemRender(b *testing.B) { b.ReportAllocs() for n := 0; n < b.N; n++ { bb := bytes.NewBuffer(make([]byte, 0, i.Size())) - i.Render("yellow", true, bb) + i.Render("yellow", true, false, bb) } } @@ -113,6 +113,6 @@ func BenchmarkLogItemRenderNoTS(b *testing.B) { b.ReportAllocs() for n := 0; n < b.N; n++ { bb := bytes.NewBuffer(make([]byte, 0, i.Size())) - i.Render("yellow", false, bb) + i.Render("yellow", false, false, bb) } } diff --git a/internal/dao/log_items.go b/internal/dao/log_items.go index 888c3db395..4b8590724d 100644 --- a/internal/dao/log_items.go +++ b/internal/dao/log_items.go @@ -101,7 +101,7 @@ func (l *LogItems) Add(ii ...*LogItem) { } // Lines returns a collection of log lines. -func (l *LogItems) Lines(index int, showTime bool, ll [][]byte) { +func (l *LogItems) Lines(index int, showTime bool, showJson bool, ll [][]byte) { l.mx.Lock() defer l.mx.Unlock() @@ -118,20 +118,20 @@ func (l *LogItems) Lines(index int, showTime bool, ll [][]byte) { colorIndex++ } bb := bytes.NewBuffer(make([]byte, 0, item.Size())) - item.Render(color, showTime, bb) + item.Render(color, showTime, showJson, bb) ll[i] = bb.Bytes() } } // StrLines returns a collection of log lines. -func (l *LogItems) StrLines(index int, showTime bool) []string { +func (l *LogItems) StrLines(index int, showTime bool, showJson bool) []string { l.mx.Lock() defer l.mx.Unlock() ll := make([]string, len(l.items[index:])) for i, item := range l.items[index:] { bb := bytes.NewBuffer(make([]byte, 0, item.Size())) - item.Render("white", showTime, bb) + item.Render("white", showTime, showJson, bb) ll[i] = bb.String() } @@ -139,7 +139,7 @@ func (l *LogItems) StrLines(index int, showTime bool) []string { } // Render returns logs as a collection of strings. -func (l *LogItems) Render(index int, showTime bool, ll [][]byte) { +func (l *LogItems) Render(index int, showTime bool, showJson bool, ll [][]byte) { var colorIndex int for i, item := range l.items[index:] { id := item.ID() @@ -153,7 +153,7 @@ func (l *LogItems) Render(index int, showTime bool, ll [][]byte) { colorIndex++ } bb := bytes.NewBuffer(make([]byte, 0, item.Size())) - item.Render(color, showTime, bb) + item.Render(color, showTime, showJson, bb) ll[i] = bb.Bytes() } } @@ -167,15 +167,15 @@ func (l *LogItems) DumpDebug(m string) { } // Filter filters out log items based on given filter. -func (l *LogItems) Filter(index int, q string, showTime bool) ([]int, [][]int, error) { +func (l *LogItems) Filter(index int, q string, showTime bool, showJson bool) ([]int, [][]int, error) { if q == "" { return nil, nil, nil } if IsFuzzySelector(q) { - mm, ii := l.fuzzyFilter(index, strings.TrimSpace(q[2:]), showTime) + mm, ii := l.fuzzyFilter(index, strings.TrimSpace(q[2:]), showTime, showJson) return mm, ii, nil } - matches, indices, err := l.filterLogs(index, q, showTime) + matches, indices, err := l.filterLogs(index, q, showTime, showJson) if err != nil { return nil, nil, err } @@ -183,10 +183,10 @@ func (l *LogItems) Filter(index int, q string, showTime bool) ([]int, [][]int, e return matches, indices, nil } -func (l *LogItems) fuzzyFilter(index int, q string, showTime bool) ([]int, [][]int) { +func (l *LogItems) fuzzyFilter(index int, q string, showTime bool, showJson bool) ([]int, [][]int) { q = strings.TrimSpace(q) matches, indices := make([]int, 0, len(l.items)), make([][]int, 0, 10) - mm := fuzzy.Find(q, l.StrLines(index, showTime)) + mm := fuzzy.Find(q, l.StrLines(index, showTime, showJson)) for _, m := range mm { matches = append(matches, m.Index) indices = append(indices, m.MatchedIndexes) @@ -195,7 +195,7 @@ func (l *LogItems) fuzzyFilter(index int, q string, showTime bool) ([]int, [][]i return matches, indices } -func (l *LogItems) filterLogs(index int, q string, showTime bool) ([]int, [][]int, error) { +func (l *LogItems) filterLogs(index int, q string, showTime bool, showJson bool) ([]int, [][]int, error) { var invert bool if IsInverseSelector(q) { invert = true @@ -207,7 +207,7 @@ func (l *LogItems) filterLogs(index int, q string, showTime bool) ([]int, [][]in } matches, indices := make([]int, 0, len(l.items)), make([][]int, 0, 10) ll := make([][]byte, len(l.items[index:])) - l.Lines(index, showTime, ll) + l.Lines(index, showTime, showJson, ll) for i, line := range ll { locs := rx.FindIndex(line) if locs != nil && invert { diff --git a/internal/dao/log_items_test.go b/internal/dao/log_items_test.go index e4e2742ea6..63839893b4 100644 --- a/internal/dao/log_items_test.go +++ b/internal/dao/log_items_test.go @@ -71,7 +71,7 @@ func TestLogItemsFilter(t *testing.T) { for _, i := range ii.Items() { i.Pod, i.Container = n, u.opts.Container } - res, _, err := ii.Filter(0, u.q, false) + res, _, err := ii.Filter(0, u.q, false, false) assert.Equal(t, u.err, err) if err == nil { assert.Equal(t, u.e, res) @@ -121,7 +121,7 @@ func TestLogItemsRender(t *testing.T) { ii.Items()[0].Pod, ii.Items()[0].Container = n, u.opts.Container t.Run(k, func(t *testing.T) { res := make([][]byte, 1) - ii.Render(0, u.opts.ShowTimestamp, res) + ii.Render(0, u.opts.ShowTimestamp, u.opts.ShowJSON, res) assert.Equal(t, u.e, string(res[0])) }) } diff --git a/internal/dao/log_options.go b/internal/dao/log_options.go index bd3fd03d6f..c83e9e5657 100644 --- a/internal/dao/log_options.go +++ b/internal/dao/log_options.go @@ -23,6 +23,7 @@ type LogOptions struct { SingleContainer bool MultiPods bool ShowTimestamp bool + ShowJSON bool AllContainers bool } diff --git a/internal/model/log.go b/internal/model/log.go index 52290a73cb..d0e7d88438 100644 --- a/internal/model/log.go +++ b/internal/model/log.go @@ -89,6 +89,12 @@ func (l *Log) ToggleShowTimestamp(b bool) { l.Refresh() } +// ToggleShowJSON toggles to colorize JSON logs. +func (l *Log) ToggleShowJSON(b bool) { + l.logOptions.ShowJSON = b + l.Refresh() +} + func (l *Log) Head(ctx context.Context) { l.mx.Lock() { @@ -146,7 +152,7 @@ func (l *Log) Clear() { func (l *Log) Refresh() { l.fireLogCleared() ll := make([][]byte, l.lines.Len()) - l.lines.Render(0, l.logOptions.ShowTimestamp, ll) + l.lines.Render(0, l.logOptions.ShowTimestamp, l.logOptions.ShowJSON, ll) l.fireLogChanged(ll) } @@ -181,7 +187,7 @@ func (l *Log) Set(lines *dao.LogItems) { l.fireLogCleared() ll := make([][]byte, l.lines.Len()) - l.lines.Render(0, l.logOptions.ShowTimestamp, ll) + l.lines.Render(0, l.logOptions.ShowTimestamp, l.logOptions.ShowJSON, ll) l.fireLogChanged(ll) } @@ -195,7 +201,7 @@ func (l *Log) ClearFilter() { l.fireLogCleared() ll := make([][]byte, l.lines.Len()) - l.lines.Render(0, l.logOptions.ShowTimestamp, ll) + l.lines.Render(0, l.logOptions.ShowTimestamp, l.logOptions.ShowJSON, ll) l.fireLogChanged(ll) } @@ -347,7 +353,7 @@ func (l *Log) applyFilter(index int, q string) ([][]byte, error) { if q == "" { return nil, nil } - matches, indices, err := l.lines.Filter(index, q, l.logOptions.ShowTimestamp) + matches, indices, err := l.lines.Filter(index, q, l.logOptions.ShowTimestamp, l.logOptions.ShowJSON) if err != nil { return nil, err } @@ -355,7 +361,7 @@ func (l *Log) applyFilter(index int, q string) ([][]byte, error) { // No filter! if matches == nil { ll := make([][]byte, l.lines.Len()) - l.lines.Render(index, l.logOptions.ShowTimestamp, ll) + l.lines.Render(index, l.logOptions.ShowTimestamp, l.logOptions.ShowJSON, ll) return ll, nil } // Blank filter @@ -364,7 +370,7 @@ func (l *Log) applyFilter(index int, q string) ([][]byte, error) { } filtered := make([][]byte, 0, len(matches)) ll := make([][]byte, l.lines.Len()) - l.lines.Lines(index, l.logOptions.ShowTimestamp, ll) + l.lines.Lines(index, l.logOptions.ShowTimestamp, l.logOptions.ShowJSON, ll) for i, idx := range matches { filtered = append(filtered, color.Highlight(ll[idx], indices[i], 209)) } @@ -375,7 +381,7 @@ func (l *Log) applyFilter(index int, q string) ([][]byte, error) { func (l *Log) fireLogBuffChanged(index int) { ll := make([][]byte, l.lines.Len()-index) if l.filter == "" { - l.lines.Render(index, l.logOptions.ShowTimestamp, ll) + l.lines.Render(index, l.logOptions.ShowTimestamp, l.logOptions.ShowJSON, ll) } else { ff, err := l.applyFilter(index, l.filter) if err != nil { diff --git a/internal/model/log_test.go b/internal/model/log_test.go index 09d00c6927..a0432fb0f0 100644 --- a/internal/model/log_test.go +++ b/internal/model/log_test.go @@ -154,7 +154,7 @@ func TestLogBasic(t *testing.T) { assert.Equal(t, 1, v.clearCalled) assert.Equal(t, 0, v.errCalled) ll := make([][]byte, data.Len()) - data.Lines(0, false, ll) + data.Lines(0, false, false, ll) assert.Equal(t, ll, v.data) } @@ -168,7 +168,7 @@ func TestLogAppend(t *testing.T) { items.Add(dao.NewLogItemFromString("blah blah")) m.Set(items) ll := make([][]byte, items.Len()) - items.Lines(0, false, ll) + items.Lines(0, false, false, ll) assert.Equal(t, ll, v.data) data := dao.NewLogItems() @@ -181,7 +181,7 @@ func TestLogAppend(t *testing.T) { } assert.Equal(t, 1, v.dataCalled) ll = make([][]byte, items.Len()) - items.Lines(0, false, ll) + items.Lines(0, false, false, ll) assert.Equal(t, ll, v.data) m.Notify() diff --git a/internal/view/container.go b/internal/view/container.go index a17d1bfaed..090509b5ce 100644 --- a/internal/view/container.go +++ b/internal/view/container.go @@ -101,6 +101,7 @@ func (c *Container) logOptions(prev bool) (*dao.LogOptions, error) { SinceSeconds: cfg.SinceSeconds, SingleContainer: true, ShowTimestamp: cfg.ShowTime, + ShowJSON: cfg.ShowJSON, Previous: prev, } diff --git a/internal/view/dp.go b/internal/view/dp.go index 8bbfcfde46..2c135ef4a3 100644 --- a/internal/view/dp.go +++ b/internal/view/dp.go @@ -76,6 +76,7 @@ func (d *Deploy) logOptions(prev bool) (*dao.LogOptions, error) { SingleContainer: len(cc) == 1, AllContainers: allCos, ShowTimestamp: cfg.ShowTime, + ShowJSON: cfg.ShowJSON, Previous: prev, } if co == "" { diff --git a/internal/view/log.go b/internal/view/log.go index 5406232732..e964377640 100644 --- a/internal/view/log.go +++ b/internal/view/log.go @@ -94,6 +94,7 @@ func (l *Log) Init(ctx context.Context) (err error) { l.updateTitle() l.model.ToggleShowTimestamp(l.app.Config.K9s.Logger.ShowTime) + l.model.ToggleShowJSON((l.app.Config.K9s.Logger.ShowJSON)) return nil } @@ -248,6 +249,7 @@ func (l *Log) bindKeys() { ui.KeyM: ui.NewKeyAction("Mark", l.markCmd, true), ui.KeyS: ui.NewKeyAction("Toggle AutoScroll", l.toggleAutoScrollCmd, true), ui.KeyF: ui.NewKeyAction("Toggle FullScreen", l.toggleFullScreenCmd, true), + ui.KeyJ: ui.NewKeyAction("Toggle JSON", l.toggleJSONCmd, true), ui.KeyT: ui.NewKeyAction("Toggle Timestamp", l.toggleTimestampCmd, true), ui.KeyW: ui.NewKeyAction("Toggle Wrap", l.toggleTextWrapCmd, true), tcell.KeyCtrlS: ui.NewKeyAction("Save", l.SaveCmd, true), @@ -345,6 +347,7 @@ func (l *Log) Flush(lines [][]byte) { if l.cancelUpdates { break } + _, _ = l.ansiWriter.Write(lines[i]) } if l.follow { @@ -508,6 +511,17 @@ func (l *Log) toggleFullScreen() { } } +func (l *Log) toggleJSONCmd(evt *tcell.EventKey) *tcell.EventKey { + if l.app.InCmdMode() { + return evt + } + l.indicator.ToggleJSON() + l.model.ToggleShowJSON(l.indicator.showJson) + l.indicator.Refresh() + + return nil +} + func (l *Log) isContainerLogView() bool { return l.model.HasDefaultContainer() } diff --git a/internal/view/log_indicator.go b/internal/view/log_indicator.go index ff360b589f..c36ac14c92 100644 --- a/internal/view/log_indicator.go +++ b/internal/view/log_indicator.go @@ -19,6 +19,7 @@ type LogIndicator struct { fullScreen bool textWrap bool showTime bool + showJson bool allContainers bool shouldDisplayAllContainers bool } @@ -33,6 +34,7 @@ func NewLogIndicator(cfg *config.Config, styles *config.Styles, allContainers bo fullScreen: cfg.K9s.Logger.FullScreenLogs, textWrap: cfg.K9s.Logger.TextWrap, showTime: cfg.K9s.Logger.ShowTime, + showJson: cfg.K9s.Logger.ShowJSON, shouldDisplayAllContainers: allContainers, } l.StylesChanged(styles) @@ -69,6 +71,11 @@ func (l *LogIndicator) FullScreen() bool { return l.fullScreen } +// Json reports the whether the logs are JSON colorized. +func (l *LogIndicator) Json() bool { + return l.showJson +} + // ToggleTimestamp toggles the current timestamp mode. func (l *LogIndicator) ToggleTimestamp() { l.showTime = !l.showTime @@ -80,6 +87,12 @@ func (l *LogIndicator) ToggleFullScreen() { l.Refresh() } +// ToggleJSON toggles the whether JSON logging is colorized. +func (l *LogIndicator) ToggleJSON() { + l.showJson = !l.showJson + l.Refresh() +} + // ToggleTextWrap toggles the wrap mode. func (l *LogIndicator) ToggleTextWrap() { l.textWrap = !l.textWrap @@ -131,6 +144,12 @@ func (l *LogIndicator) Refresh() { l.indicator = append(l.indicator, "[::b]FullScreen:[gray::d]Off[-::]"+spacer...) } + if l.Json() { + l.indicator = append(l.indicator, "[::b]JSON:[limegreen::b]On[-::] "+spacer...) + } else { + l.indicator = append(l.indicator, "[::b]JSON:[gray::d]Off[-::]"+spacer...) + } + if l.Timestamp() { l.indicator = append(l.indicator, "[::b]Timestamps:[limegreen::b]On[-::] "+spacer...) } else { diff --git a/internal/view/log_indicator_test.go b/internal/view/log_indicator_test.go index 2432a938ca..48db3eb156 100644 --- a/internal/view/log_indicator_test.go +++ b/internal/view/log_indicator_test.go @@ -15,10 +15,10 @@ func TestLogIndicatorRefresh(t *testing.T) { e string }{ "all-containers": { - view.NewLogIndicator(config.NewConfig(nil), defaults, true), "[::b]AllContainers:[gray::d]Off[-::] [::b]Autoscroll:[limegreen::b]On[-::] [::b]FullScreen:[gray::d]Off[-::] [::b]Timestamps:[gray::d]Off[-::] [::b]Wrap:[gray::d]Off[-::]\n", + view.NewLogIndicator(config.NewConfig(nil), defaults, true), "[::b]AllContainers:[gray::d]Off[-::] [::b]Autoscroll:[limegreen::b]On[-::] [::b]FullScreen:[gray::d]Off[-::] [::b]JSON:[gray::d]Off[-::] [::b]Timestamps:[gray::d]Off[-::] [::b]Wrap:[gray::d]Off[-::]\n", }, "plain": { - view.NewLogIndicator(config.NewConfig(nil), defaults, false), "[::b]Autoscroll:[limegreen::b]On[-::] [::b]FullScreen:[gray::d]Off[-::] [::b]Timestamps:[gray::d]Off[-::] [::b]Wrap:[gray::d]Off[-::]\n", + view.NewLogIndicator(config.NewConfig(nil), defaults, false), "[::b]Autoscroll:[limegreen::b]On[-::] [::b]FullScreen:[gray::d]Off[-::] [::b]JSON:[gray::d]Off[-::] [::b]Timestamps:[gray::d]Off[-::] [::b]Wrap:[gray::d]Off[-::]\n", }, } diff --git a/internal/view/log_int_test.go b/internal/view/log_int_test.go index 026a877eb2..bf15e07676 100644 --- a/internal/view/log_int_test.go +++ b/internal/view/log_int_test.go @@ -23,10 +23,10 @@ func TestLogAutoScroll(t *testing.T) { v.GetModel().Set(ii) v.GetModel().Notify() - assert.Equal(t, 16, len(v.Hints())) + assert.Equal(t, 17, len(v.Hints())) v.toggleAutoScrollCmd(nil) - assert.Equal(t, "Autoscroll:Off FullScreen:Off Timestamps:Off Wrap:Off", v.Indicator().GetText(true)) + assert.Equal(t, "Autoscroll:Off FullScreen:Off JSON:Off Timestamps:Off Wrap:Off", v.Indicator().GetText(true)) } func TestLogViewNav(t *testing.T) { @@ -84,7 +84,7 @@ func TestLogTimestamp(t *testing.T) { l.SendKeys(ui.KeyT) l.Logs().Clear() ll := make([][]byte, ii.Len()) - ii.Lines(0, true, ll) + ii.Lines(0, true, false, ll) l.Flush(ll) assert.Equal(t, fmt.Sprintf("%-30s %s", "ttt", "fred/blee c1 Testing 1, 2, 3\n"), l.Logs().GetText(true)) diff --git a/internal/view/log_test.go b/internal/view/log_test.go index 7e966b80bd..41908b79c1 100644 --- a/internal/view/log_test.go +++ b/internal/view/log_test.go @@ -27,7 +27,7 @@ func TestLog(t *testing.T) { ii := dao.NewLogItems() ii.Add(dao.NewLogItemFromString("blee\n"), dao.NewLogItemFromString("bozo\n")) ll := make([][]byte, ii.Len()) - ii.Lines(0, false, ll) + ii.Lines(0, false, false, ll) v.Flush(ll) assert.Equal(t, "Waiting for logs...\nblee\nbozo\n", v.Logs().GetText(true)) @@ -47,7 +47,7 @@ func TestLogFlush(t *testing.T) { dao.NewLogItemFromString("\033[0;32mBozo\n"), ) ll := make([][]byte, items.Len()) - items.Lines(0, false, ll) + items.Lines(0, false, false, ll) v.Flush(ll) assert.Equal(t, "[orange::d]Waiting for logs...\n[black::]blee\n[green::]Bozo\n\n", v.Logs().GetText(false)) @@ -68,7 +68,7 @@ func BenchmarkLogFlush(b *testing.B) { dao.NewLogItemFromString("\033[0;101mBozo\n"), ) ll := make([][]byte, items.Len()) - items.Lines(0, false, ll) + items.Lines(0, false, false, ll) b.ReportAllocs() b.ResetTimer() @@ -103,7 +103,7 @@ func TestLogViewSave(t *testing.T) { ii := dao.NewLogItems() ii.Add(dao.NewLogItemFromString("blee"), dao.NewLogItemFromString("bozo")) ll := make([][]byte, ii.Len()) - ii.Lines(0, false, ll) + ii.Lines(0, false, false, ll) v.Flush(ll) dir := filepath.Join(app.Config.K9s.GetScreenDumpDir(), app.Config.K9s.CurrentCluster) diff --git a/internal/view/logs_extender.go b/internal/view/logs_extender.go index d7260cc369..d355123243 100644 --- a/internal/view/logs_extender.go +++ b/internal/view/logs_extender.go @@ -81,6 +81,7 @@ func (l *LogsExtender) buildLogOpts(path, co string, prevLogs bool) *dao.LogOpti Lines: int64(cfg.TailCount), Previous: prevLogs, ShowTimestamp: cfg.ShowTime, + ShowJSON: cfg.ShowJSON, } if opts.Container == "" { opts.AllContainers = true diff --git a/internal/view/pod.go b/internal/view/pod.go index 7f6f9a788b..4cd1bfe080 100644 --- a/internal/view/pod.go +++ b/internal/view/pod.go @@ -102,6 +102,7 @@ func (p *Pod) logOptions(prev bool) (*dao.LogOptions, error) { SinceSeconds: cfg.SinceSeconds, SingleContainer: len(cc) == 1, ShowTimestamp: cfg.ShowTime, + ShowJSON: cfg.ShowJSON, Previous: prev, } if c, ok := dao.GetDefaultLogContainer(pod.ObjectMeta, pod.Spec); ok { diff --git a/internal/view/sts.go b/internal/view/sts.go index 5ddc4ef3e6..2f779b01bb 100644 --- a/internal/view/sts.go +++ b/internal/view/sts.go @@ -65,6 +65,7 @@ func (s *StatefulSet) logOptions(prev bool) (*dao.LogOptions, error) { SinceSeconds: cfg.SinceSeconds, AllContainers: allCos, ShowTimestamp: cfg.ShowTime, + ShowJSON: cfg.ShowJSON, Previous: prev, } if co == "" {