Skip to content

Commit

Permalink
[feat] Add toggle to allow log lines to be JSON colorized
Browse files Browse the repository at this point in the history
  • Loading branch information
rm-hull committed Feb 2, 2023
1 parent 3794a61 commit 6eda4e9
Show file tree
Hide file tree
Showing 22 changed files with 114 additions and 42 deletions.
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -339,6 +339,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
Expand Down
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,7 @@ require (
github.com/prometheus/common v0.37.0 // indirect
github.com/prometheus/procfs v0.8.0 // indirect
github.com/rivo/uniseg v0.4.3 // 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/v2 v2.1.0 // indirect
github.com/shopspring/decimal v1.2.0 // indirect
Expand Down
2 changes: 2 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -544,6 +544,8 @@ github.com/rakyll/hey v0.1.4/go.mod h1:nAOTOo+L52KB9SZq/M6J18kxjto4yVtXQDjU2HgjU
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
github.com/rivo/uniseg v0.4.3 h1:utMvzDsuh3suAEnhH0RdHmoPbU648o6CvXxTx4SBMOw=
github.com/rivo/uniseg v0.4.3/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
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=
Expand Down
2 changes: 2 additions & 0 deletions internal/config/config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -294,6 +294,7 @@ var expectedConfig = `k9s:
fullScreenLogs: false
textWrap: false
showTime: false
showJSON: false
currentContext: blee
currentCluster: blee
clusters:
Expand Down Expand Up @@ -393,6 +394,7 @@ var resetConfig = `k9s:
fullScreenLogs: false
textWrap: false
showTime: false
showJSON: false
currentContext: blee
currentCluster: blee
clusters:
Expand Down
1 change: 1 addition & 0 deletions internal/config/logger.go
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
29 changes: 24 additions & 5 deletions internal/dao/log_item.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@ package dao

import (
"bytes"
"encoding/json"

"github.com/rm-hull/colorjson"
)

// LogChan represents a channel for logs.
Expand Down Expand Up @@ -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]")
Expand All @@ -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')
}
6 changes: 3 additions & 3 deletions internal/dao/log_item_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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())
})
}
Expand All @@ -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)
}
}

Expand All @@ -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)
}
}
26 changes: 13 additions & 13 deletions internal/dao/log_items.go
Original file line number Diff line number Diff line change
Expand Up @@ -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()

Expand All @@ -118,28 +118,28 @@ 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()
}

return ll
}

// 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()
Expand All @@ -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()
}
}
Expand All @@ -167,26 +167,26 @@ 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
}

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)
Expand All @@ -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
Expand All @@ -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 {
Expand Down
4 changes: 2 additions & 2 deletions internal/dao/log_items_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -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]))
})
}
Expand Down
1 change: 1 addition & 0 deletions internal/dao/log_options.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ type LogOptions struct {
SingleContainer bool
MultiPods bool
ShowTimestamp bool
ShowJSON bool
AllContainers bool
}

Expand Down
20 changes: 13 additions & 7 deletions internal/model/log.go
Original file line number Diff line number Diff line change
Expand Up @@ -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()
{
Expand Down Expand Up @@ -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)
}

Expand Down Expand Up @@ -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)
}

Expand All @@ -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)
}

Expand Down Expand Up @@ -347,15 +353,15 @@ 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
}

// 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
Expand All @@ -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))
}
Expand All @@ -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 {
Expand Down
6 changes: 3 additions & 3 deletions internal/model/log_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}

Expand All @@ -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()
Expand All @@ -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()
Expand Down
1 change: 1 addition & 0 deletions internal/view/container.go
Original file line number Diff line number Diff line change
Expand Up @@ -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,
}

Expand Down
1 change: 1 addition & 0 deletions internal/view/dp.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 == "" {
Expand Down
Loading

0 comments on commit 6eda4e9

Please sign in to comment.