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

Add JSON parsing to Log View #2973

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
6 changes: 6 additions & 0 deletions cmd/testdata/k9s.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,12 @@ k9s:
logger:
tail: 200
buffer: 2000
decodeJson: false
json:
debug: false
globalExpressions: ""
defaultTemplate: ""
templates: []
currentContext: minikube
currentCluster: minikube
clusters:
Expand Down
2 changes: 2 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -175,6 +175,8 @@ require (
github.com/iancoleman/strcase v0.3.0 // indirect
github.com/imdario/mergo v0.3.16 // indirect
github.com/inconshreveable/mousetrap v1.1.0 // indirect
github.com/itchyny/gojq v0.12.16
github.com/itchyny/timefmt-go v0.1.6 // indirect
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 // indirect
github.com/jinzhu/copier v0.4.0 // indirect
github.com/jinzhu/inflection v1.0.0 // indirect
Expand Down
4 changes: 4 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -758,6 +758,10 @@ github.com/imdario/mergo v0.3.16/go.mod h1:WBLT9ZmE3lPoWsEzCh9LPo3TiwVN+ZKEjmz+h
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
github.com/itchyny/gojq v0.12.16 h1:yLfgLxhIr/6sJNVmYfQjTIv0jGctu6/DgDoivmxTr7g=
github.com/itchyny/gojq v0.12.16/go.mod h1:6abHbdC2uB9ogMS38XsErnfqJ94UlngIJGlRAIj4jTM=
github.com/itchyny/timefmt-go v0.1.6 h1:ia3s54iciXDdzWzwaVKXZPbiXzxxnv1SPGFfM/myJ5Q=
github.com/itchyny/timefmt-go v0.1.6/go.mod h1:RRDZYC5s9ErkjQvTvvU7keJjxUYzIISJGxm9/mAERQg=
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 h1:BQSFePA1RWJOlocH6Fxy8MmwDt+yVQYULKfN0RoTN8A=
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99/go.mod h1:1lJo3i6rXxKeerYnT8Nvf0QmHCRC1n8sfWVwXF2Frvo=
github.com/jinzhu/copier v0.4.0 h1:w3ciUoD19shMCRargcpm0cm91ytaBhDvuRpz1ODO/U8=
Expand Down
25 changes: 24 additions & 1 deletion internal/config/json/schemas/k9s.json
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,30 @@
"buffer": {"type": "integer"},
"sinceSeconds": {"type": "integer"},
"textWrap": {"type": "boolean"},
"showTime": {"type": "boolean"}
"showTime": {"type": "boolean"},
"decodeJson": {"type": "boolean"},
"json": {
"type": "object",
"additionalProperties": false,
"properties": {
"debug": {"type": "boolean"},
"globalExpressions": {"type": "string"},
"defaultTemplate": {"type": "string"},
"templates": {
"type": "array",
"items": {
"type": "object",
"additionalProperties": false,
"properties": {
"name": {"type": "string"},
"loglevel": {"type": "string"},
"datetime": {"type": "string"},
"message": {"type": "string"}
}
}
}
}
}
}
},
"thresholds": {
Expand Down
37 changes: 32 additions & 5 deletions internal/config/logger.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,13 +14,39 @@ const (
DefaultSinceSeconds = -1 // tail logs by default
)

type JsonTemplate struct {
Name string `json:"name" yaml:"name"`
LogLevelExpression string `json:"loglevel" yaml:"loglevel"`
DateTimeExpression string `json:"datetime" yaml:"datetime"`
MessageExpression string `json:"message" yaml:"message"`
}

type JsonConfig struct {
Debug bool `json:"debug" yaml:"debug"`
GlobalExpressions string `json:"globalExpressions" yaml:"globalExpressions"`
DefaultTemplate string `json:"defaultTemplate" yaml:"defaultTemplate"`
Templates []JsonTemplate `json:"templates" yaml:"templates"`
}

// NewJsonConfig returns a new instance.
func NewJsonConfig() JsonConfig {
return JsonConfig{
Debug: false,
GlobalExpressions: "",
DefaultTemplate: "",
Templates: []JsonTemplate{},
}
}

// Logger tracks logger options.
type Logger struct {
TailCount int64 `json:"tail" yaml:"tail"`
BufferSize int `json:"buffer" yaml:"buffer"`
SinceSeconds int64 `json:"sinceSeconds" yaml:"sinceSeconds"`
TextWrap bool `json:"textWrap" yaml:"textWrap"`
ShowTime bool `json:"showTime" yaml:"showTime"`
TailCount int64 `json:"tail" yaml:"tail"`
BufferSize int `json:"buffer" yaml:"buffer"`
SinceSeconds int64 `json:"sinceSeconds" yaml:"sinceSeconds"`
TextWrap bool `json:"textWrap" yaml:"textWrap"`
ShowTime bool `json:"showTime" yaml:"showTime"`
DecodeJson bool `json:"decodeJson" yaml:"decodeJson"`
JsonConfig JsonConfig `json:"json" yaml:"json"`
}

// NewLogger returns a new instance.
Expand All @@ -29,6 +55,7 @@ func NewLogger() Logger {
TailCount: DefaultLoggerTailCount,
BufferSize: MaxLogThreshold,
SinceSeconds: DefaultSinceSeconds,
JsonConfig: NewJsonConfig(),
}
}

Expand Down
6 changes: 6 additions & 0 deletions internal/config/testdata/configs/default.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,12 @@ k9s:
sinceSeconds: -1
textWrap: false
showTime: false
decodeJson: false
json:
debug: false
globalExpressions: ""
defaultTemplate: ""
templates: []
thresholds:
cpu:
critical: 90
Expand Down
6 changes: 6 additions & 0 deletions internal/config/testdata/configs/expected.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,12 @@ k9s:
sinceSeconds: -1
textWrap: false
showTime: false
decodeJson: false
json:
debug: false
globalExpressions: ""
defaultTemplate: ""
templates: []
thresholds:
cpu:
critical: 90
Expand Down
6 changes: 6 additions & 0 deletions internal/config/testdata/configs/k9s.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,12 @@ k9s:
sinceSeconds: -1
textWrap: false
showTime: false
decodeJson: false
json:
debug: false
globalExpressions: ""
defaultTemplate: ""
templates: []
thresholds:
cpu:
critical: 90
Expand Down
11 changes: 9 additions & 2 deletions internal/dao/log_item.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,16 @@ package dao

import (
"bytes"
"regexp"
)

// LogChan represents a channel for logs.
type LogChan chan *LogItem

var ItemEOF = new(LogItem)

var dateTimePrefixRegEx = regexp.MustCompile(`^\d{4}-\d{2}-\d{2}T?.*`)

// LogItem represents a container log line.
type LogItem struct {
Pod, Container string
Expand Down Expand Up @@ -68,13 +71,17 @@ func (l *LogItem) Size() int {

// Render returns a log line as string.
func (l *LogItem) Render(paint string, showTime bool, bb *bytes.Buffer) {
index := bytes.Index(l.Bytes, []byte{' '})
var index = -1
if dateTimePrefixRegEx.MatchString(string(l.Bytes)) {
index = bytes.Index(l.Bytes, []byte{' '})
}
if showTime && index > 0 {
bb.WriteString("[gray::b]")
bb.Write(l.Bytes[:index])
bb.WriteString(" ")
if l := 30 - len(l.Bytes[:index]); l > 0 {
bb.Write(bytes.Repeat([]byte{' '}, l))
// turned off to avoid empty column spaces
// bb.Write(bytes.Repeat([]byte{' '}, l))
}
bb.WriteString("[-::-]")
}
Expand Down
40 changes: 40 additions & 0 deletions internal/dao/log_options.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,14 @@
package dao

import (
"bytes"
"errors"
"fmt"
"io"
"time"

"github.com/derailed/k9s/internal/client"
"github.com/rs/zerolog/log"
v1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)
Expand All @@ -27,6 +31,8 @@ type LogOptions struct {
MultiPods bool
ShowTimestamp bool
AllContainers bool
DecodeJson bool
Json JsonOptions
}

// Info returns the option pod and container info.
Expand All @@ -52,6 +58,8 @@ func (o *LogOptions) Clone() *LogOptions {
SinceTime: o.SinceTime,
SinceSeconds: o.SinceSeconds,
AllContainers: o.AllContainers,
DecodeJson: o.DecodeJson,
Json: o.Json,
}
}

Expand Down Expand Up @@ -111,6 +119,38 @@ func (o *LogOptions) ToPodLogOptions() *v1.PodLogOptions {
return &opts
}

// HandleJson if JSON decoding is turned on, processes JSON templates with JQ
func (o *LogOptions) HandleJson(bb []byte) []byte {
if !o.DecodeJson {
return bb
}

var b bytes.Buffer
orgLine := string(bb)
result, _ := o.Json.GetCompiledJsonQuery().Run(orgLine).Next()
err := PrintJsonResult(result, &b)
if err != nil {
log.Trace().AnErr("Error", err).Msg("Error printing JQ result")
fmt.Fprintf(&b, "%s: %s\n", "JQ", err)
fmt.Fprintf(&b, "Original line: %s", orgLine)
}
if b.Bytes()[b.Len()-1] != '\n' {
b.WriteByte('\n')
}
return b.Bytes()
}

func PrintJsonResult(value any, outStream io.Writer) error {
if err, ok := value.(error); ok {
return err
}
if s, ok := value.(string); ok {
_, err := outStream.Write([]byte(s))
return err
}
return errors.New("JQ result not a string")
}

// ToLogItem add a log header to display po/co information along with the log message.
func (o *LogOptions) ToLogItem(bytes []byte) *LogItem {
item := NewLogItem(bytes)
Expand Down
Loading