Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[feat] Add toggle to allow log lines to be JSON colorized #1778

Open
wants to merge 7 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -427,6 +427,8 @@ You can now override the context portForward default address configuration by se
textWrap: false
# Toggles log line timestamp info. Default false
showTime: false
# Toggles whether JSON log lines should be colorized. Default false
showJSON: false
# Provide shell pod customization when nodeShell feature gate is enabled!
shellPod:
# The shell pod image to use.
Expand Down
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -246,6 +246,7 @@ require (
github.com/prometheus/common v0.44.0 // indirect
github.com/prometheus/procfs v0.10.1 // indirect
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect
github.com/rm-hull/colorjson v0.0.0-20220923220430-b50ee91dc6f6 // indirect
github.com/rivo/uniseg v0.4.7 // indirect
github.com/rubenv/sql-migrate v1.5.2 // indirect
github.com/russross/blackfriday/v2 v2.1.0 // indirect
Expand Down
2 changes: 2 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -1038,6 +1038,8 @@ github.com/rivo/uniseg v0.1.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJ
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ=
github.com/rivo/uniseg v0.4.7/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.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8=
Expand Down
3 changes: 2 additions & 1 deletion internal/config/json/schemas/k9s.json
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,8 @@
"buffer": {"type": "integer"},
"sinceSeconds": {"type": "integer"},
"textWrap": {"type": "boolean"},
"showTime": {"type": "boolean"}
"showTime": {"type": "boolean"},
"showJSON": {"type": "boolean"}
}
},
"thresholds": {
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 {
SinceSeconds int64 `json:"sinceSeconds" yaml:"sinceSeconds"`
TextWrap bool `json:"textWrap" yaml:"textWrap"`
ShowTime bool `json:"showTime" yaml:"showTime"`
ShowJSON bool `json:"showJSON" yaml:"showJSON"`
}

// NewLogger returns a new instance.
Expand Down
1 change: 1 addition & 0 deletions internal/config/testdata/configs/default.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ k9s:
sinceSeconds: -1
textWrap: false
showTime: false
showJSON: false
thresholds:
cpu:
critical: 90
Expand Down
1 change: 1 addition & 0 deletions internal/config/testdata/configs/expected.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ k9s:
sinceSeconds: -1
textWrap: false
showTime: false
showJSON: false
thresholds:
cpu:
critical: 90
Expand Down
1 change: 1 addition & 0 deletions internal/config/testdata/configs/k9s.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ k9s:
sinceSeconds: -1
textWrap: false
showTime: false
showJSON: false
thresholds:
cpu:
critical: 90
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 @@ -5,6 +5,9 @@ package dao

import (
"bytes"
"encoding/json"

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

// LogChan represents a channel for logs.
Expand Down Expand Up @@ -67,7 +70,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 @@ -92,9 +95,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{}
rm-hull marked this conversation as resolved.
Show resolved Hide resolved
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
rm-hull marked this conversation as resolved.
Show resolved Hide resolved
}
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 @@ -88,7 +88,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 @@ -103,7 +103,7 @@ func BenchmarkLogItemRenderTS(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 @@ -116,6 +116,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 @@ -105,7 +105,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 @@ -122,28 +122,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 @@ -157,7 +157,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 @@ -171,26 +171,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 f, ok := internal.IsFuzzySelector(q); ok {
mm, ii := l.fuzzyFilter(index, f, showTime)
mm, ii := l.fuzzyFilter(index, f, 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 @@ -199,7 +199,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 internal.IsInverseSelector(q) {
invert = true
Expand All @@ -211,7 +211,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 @@ -74,7 +74,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 @@ -124,7 +124,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 @@ -26,6 +26,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 @@ -92,6 +92,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 @@ -149,7 +155,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 @@ -184,7 +190,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 @@ -198,7 +204,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 @@ -350,15 +356,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 @@ -367,7 +373,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 @@ -378,7 +384,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 @@ -157,7 +157,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 @@ -171,7 +171,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 @@ -184,7 +184,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 @@ -117,6 +117,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
Loading