From 3efb3f2596cdf62866576b206755bf01268f4c09 Mon Sep 17 00:00:00 2001 From: derailed Date: Sat, 14 Mar 2020 10:30:21 -0600 Subject: [PATCH] provides logger config options. Fix #623 --- go.sum | 5 ++++ internal/config/config.go | 3 +++ internal/config/config_test.go | 17 +++++++----- internal/config/k9s.go | 33 ++++++++++-------------- internal/config/k9s_test.go | 8 +++--- internal/config/logger.go | 44 ++++++++++++++++++++++++++++++++ internal/config/logger_test.go | 24 +++++++++++++++++ internal/config/testdata/k9s.yml | 5 ++-- internal/model/log.go | 25 ++++++++++-------- internal/ui/config.go | 2 +- internal/view/log.go | 21 +++++++-------- 11 files changed, 133 insertions(+), 54 deletions(-) create mode 100644 internal/config/logger.go create mode 100644 internal/config/logger_test.go diff --git a/go.sum b/go.sum index 9697d1f9..c887f6a9 100644 --- a/go.sum +++ b/go.sum @@ -4,6 +4,7 @@ cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMT cloud.google.com/go v0.38.0 h1:ROfEUZz+Gh5pa62DJWXSaonyu3StP6EA6lPEXPI6mCo= cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU= github.com/Azure/azure-sdk-for-go v32.5.0+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc= +github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78 h1:w+iIsaOQNcT7OZ575w+acHgRric5iCyQh+xv+KJ4HB8= github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78/go.mod h1:LmzpDX56iTiv29bbRTIsUNlaFfuhWRQBWjQdVyAevI8= github.com/Azure/go-autorest/autorest v0.9.0 h1:MRvx8gncNaXJqOoLmhNjUAKh33JJF8LyxPhomEtOsjs= github.com/Azure/go-autorest/autorest v0.9.0/go.mod h1:xyHB1BMZT0cuDHU7I0+g046+BFDTQ8rEZB0s4Yfa6bI= @@ -37,6 +38,7 @@ github.com/Masterminds/sprig/v3 v3.0.2 h1:wz22D0CiSctrliXiI9ZO3HoNApweeRGftyDN+B github.com/Masterminds/sprig/v3 v3.0.2/go.mod h1:oesJ8kPONMONaZgtiHNzUShJbksypC5kWczhZAf6+aU= github.com/Masterminds/vcs v1.13.0/go.mod h1:N09YCmOQr6RLxC6UNHzuVwAdodYbbnycGHSmwVJjcKA= github.com/Microsoft/go-winio v0.4.11/go.mod h1:VhR8bwka0BXejwEJY73c50VrPtXAaKcyvVC4A4RozmA= +github.com/Microsoft/go-winio v0.4.12 h1:xAfWHN1IrQ0NJ9TBC0KBZoqLjzDTr1ML+4MywiUOryc= github.com/Microsoft/go-winio v0.4.12/go.mod h1:VhR8bwka0BXejwEJY73c50VrPtXAaKcyvVC4A4RozmA= github.com/Microsoft/hcsshim v0.0.0-20190417211021-672e52e9209d/go.mod h1:Op3hHsoHPAvb6lceZHDtd9OkTew38wNoXnJs8iY7rUg= github.com/Microsoft/hcsshim v0.8.6/go.mod h1:Op3hHsoHPAvb6lceZHDtd9OkTew38wNoXnJs8iY7rUg= @@ -97,6 +99,7 @@ github.com/containerd/containerd v1.0.2/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMX github.com/containerd/containerd v1.3.0-beta.2.0.20190823190603-4a2f61c4f2b4/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA= github.com/containerd/containerd v1.3.0 h1:xjvXQWABwS2uiv3TWgQt5Uth60Gu86LTGZXMJkjc7rY= github.com/containerd/containerd v1.3.0/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA= +github.com/containerd/continuity v0.0.0-20181203112020-004b46473808 h1:4BX8f882bXEDKfWIf0wa8HRvpnBoPszJJXL+TVbBw4M= github.com/containerd/continuity v0.0.0-20181203112020-004b46473808/go.mod h1:GL3xCUCBDV3CZiTSEKksMWbLE66hEyuu9qyDOOqM47Y= github.com/containerd/typeurl v0.0.0-20190228175220-2a93cfde8c20/go.mod h1:Cm3kwCdlkCfMSHURc+r6fwoGH6/F1hH3S4sg0rLFWPc= github.com/containernetworking/cni v0.7.1/go.mod h1:LGwApLUm2FpoOfxTDEeq8T9ipbpZ61X79hmU3w8FmsY= @@ -320,6 +323,7 @@ github.com/imdario/mergo v0.3.5/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJ github.com/imdario/mergo v0.3.7/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= github.com/imdario/mergo v0.3.8 h1:CGgOkSJeqMRmt0D9XLWExdT4m4F1vd3FV3VPt+0VxkQ= github.com/imdario/mergo v0.3.8/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= +github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM= github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= github.com/jimstudt/http-authentication v0.0.0-20140401203705-3eca13d6893a/go.mod h1:wK6yTYYcgjHE1Z1QtXACPDjcFJyBskHEdagmnq3vsP8= github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k= @@ -337,6 +341,7 @@ github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvW github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= github.com/klauspost/cpuid v1.2.0/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek= +github.com/konsorten/go-windows-terminal-sequences v1.0.1 h1:mweAR1A6xJ3oS2pRaGiHgQ4OO8tzTaLawm8vnODuwDk= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= diff --git a/internal/config/config.go b/internal/config/config.go index e06fef6c..620c1b0d 100644 --- a/internal/config/config.go +++ b/internal/config/config.go @@ -199,6 +199,9 @@ func (c *Config) Load(path string) error { if cfg.K9s != nil { c.K9s = cfg.K9s } + if c.K9s.Logger == nil { + c.K9s.Logger = NewLogger() + } return nil } diff --git a/internal/config/config_test.go b/internal/config/config_test.go index a7b2cd09..1b19daa2 100644 --- a/internal/config/config_test.go +++ b/internal/config/config_test.go @@ -96,7 +96,8 @@ func TestConfigLoad(t *testing.T) { assert.Nil(t, cfg.Load("testdata/k9s.yml")) assert.Equal(t, 2, cfg.K9s.RefreshRate) - assert.Equal(t, 200, cfg.K9s.LogBufferSize) + assert.Equal(t, 2000, cfg.K9s.Logger.BufferSize) + assert.Equal(t, 200, cfg.K9s.Logger.TailCount) assert.Equal(t, "minikube", cfg.K9s.CurrentContext) assert.Equal(t, "minikube", cfg.K9s.CurrentCluster) assert.NotNil(t, cfg.K9s.Clusters) @@ -206,8 +207,8 @@ func TestConfigSaveFile(t *testing.T) { assert.Nil(t, cfg.Load("testdata/k9s.yml")) cfg.K9s.RefreshRate = 100 cfg.K9s.ReadOnly = true - cfg.K9s.LogBufferSize = 500 - cfg.K9s.LogRequestSize = 100 + cfg.K9s.Logger.TailCount = 500 + cfg.K9s.Logger.BufferSize = 800 cfg.K9s.CurrentContext = "blee" cfg.K9s.CurrentCluster = "blee" cfg.Validate() @@ -262,8 +263,9 @@ var expectedConfig = `k9s: refreshRate: 100 headless: false readOnly: true - logBufferSize: 500 - logRequestSize: 100 + logger: + tail: 500 + buffer: 800 currentContext: blee currentCluster: blee fullScreenLogs: false @@ -310,8 +312,9 @@ var resetConfig = `k9s: refreshRate: 2 headless: false readOnly: false - logBufferSize: 200 - logRequestSize: 200 + logger: + tail: 200 + buffer: 2000 currentContext: blee currentCluster: blee fullScreenLogs: false diff --git a/internal/config/k9s.go b/internal/config/k9s.go index 0e6cfdc4..43f4a12a 100644 --- a/internal/config/k9s.go +++ b/internal/config/k9s.go @@ -3,10 +3,8 @@ package config import "github.com/derailed/k9s/internal/client" const ( - defaultRefreshRate = 2 - defaultLogRequestSize = 200 - defaultLogBufferSize = 1000 - defaultReadOnly = false + defaultRefreshRate = 2 + defaultReadOnly = false ) // K9s tracks K9s configuration options. @@ -14,8 +12,7 @@ type K9s struct { RefreshRate int `yaml:"refreshRate"` Headless bool `yaml:"headless"` ReadOnly bool `yaml:"readOnly"` - LogBufferSize int `yaml:"logBufferSize"` - LogRequestSize int `yaml:"logRequestSize"` + Logger *Logger `yaml:"logger"` CurrentContext string `yaml:"currentContext"` CurrentCluster string `yaml:"currentCluster"` FullScreenLogs bool `yaml:"fullScreenLogs"` @@ -30,12 +27,11 @@ type K9s struct { // NewK9s create a new K9s configuration. func NewK9s() *K9s { return &K9s{ - RefreshRate: defaultRefreshRate, - ReadOnly: defaultReadOnly, - LogBufferSize: defaultLogBufferSize, - LogRequestSize: defaultLogRequestSize, - Clusters: make(map[string]*Cluster), - Thresholds: NewThreshold(), + RefreshRate: defaultRefreshRate, + ReadOnly: defaultReadOnly, + Logger: NewLogger(), + Clusters: make(map[string]*Cluster), + Thresholds: NewThreshold(), } } @@ -106,14 +102,6 @@ func (k *K9s) validateDefaults() { if k.RefreshRate <= 0 { k.RefreshRate = defaultRefreshRate } - - if k.LogBufferSize <= 0 { - k.LogBufferSize = defaultLogBufferSize - } - - if k.LogRequestSize <= 0 { - k.LogRequestSize = defaultLogRequestSize - } } func (k *K9s) checkClusters(ks KubeSettings) { @@ -140,6 +128,11 @@ func (k *K9s) Validate(c client.Connection, ks KubeSettings) { } k.checkClusters(ks) + if k.Logger == nil { + k.Logger = NewLogger() + } else { + k.Logger.Validate(c, ks) + } if k.Thresholds == nil { k.Thresholds = NewThreshold() } diff --git a/internal/config/k9s_test.go b/internal/config/k9s_test.go index 4b061de6..e8c5d019 100644 --- a/internal/config/k9s_test.go +++ b/internal/config/k9s_test.go @@ -22,8 +22,8 @@ func TestK9sValidate(t *testing.T) { c.Validate(mc, mk) assert.Equal(t, 2, c.RefreshRate) - assert.Equal(t, 1000, c.LogBufferSize) - assert.Equal(t, 200, c.LogRequestSize) + assert.Equal(t, 50, c.Logger.TailCount) + assert.Equal(t, 1_000, c.Logger.BufferSize) assert.Equal(t, "ctx1", c.CurrentContext) assert.Equal(t, "c1", c.CurrentCluster) assert.Equal(t, 1, len(c.Clusters)) @@ -45,8 +45,8 @@ func TestK9sValidateBlank(t *testing.T) { c.Validate(mc, mk) assert.Equal(t, 2, c.RefreshRate) - assert.Equal(t, 1000, c.LogBufferSize) - assert.Equal(t, 200, c.LogRequestSize) + assert.Equal(t, 50, c.Logger.TailCount) + assert.Equal(t, 1_000, c.Logger.BufferSize) assert.Equal(t, "ctx1", c.CurrentContext) assert.Equal(t, "c1", c.CurrentCluster) assert.Equal(t, 1, len(c.Clusters)) diff --git a/internal/config/logger.go b/internal/config/logger.go new file mode 100644 index 00000000..1f5b1f72 --- /dev/null +++ b/internal/config/logger.go @@ -0,0 +1,44 @@ +package config + +import ( + "github.com/derailed/k9s/internal/client" +) + +const ( + // DefaultLoggerTailCount tracks log tail size. + DefaultLoggerTailCount = 50 + // DefaultLoggerBufferSize tracks the buffer size. + DefaultLoggerBufferSize = 1_000 + // MaxLogThreshold sets the max value for log size. + MaxLogThreshold = 5_000 +) + +// Logger tracks logger options +type Logger struct { + TailCount int `yaml:"tail"` + BufferSize int `yaml:"buffer"` +} + +// NewLogger returns a new instance. +func NewLogger() *Logger { + return &Logger{ + TailCount: DefaultLoggerTailCount, + BufferSize: DefaultLoggerBufferSize, + } +} + +// Validate checks thresholds and make sure we're cool. If not use defaults. +func (l *Logger) Validate(_ client.Connection, _ KubeSettings) { + if l.TailCount <= 0 { + l.TailCount = DefaultLoggerTailCount + } + if l.TailCount > MaxLogThreshold { + l.TailCount = MaxLogThreshold + } + if l.BufferSize <= 0 { + l.BufferSize = DefaultLoggerBufferSize + } + if l.BufferSize > MaxLogThreshold { + l.BufferSize = MaxLogThreshold + } +} diff --git a/internal/config/logger_test.go b/internal/config/logger_test.go new file mode 100644 index 00000000..0b077328 --- /dev/null +++ b/internal/config/logger_test.go @@ -0,0 +1,24 @@ +package config_test + +import ( + "testing" + + "github.com/derailed/k9s/internal/config" + "github.com/stretchr/testify/assert" +) + +func TestNewLogger(t *testing.T) { + l := config.NewLogger() + l.Validate(nil, nil) + + assert.Equal(t, 50, l.TailCount) + assert.Equal(t, 1_000, l.BufferSize) +} + +func TestLoggerValidate(t *testing.T) { + var l config.Logger + l.Validate(nil, nil) + + assert.Equal(t, 50, l.TailCount) + assert.Equal(t, 1_000, l.BufferSize) +} diff --git a/internal/config/testdata/k9s.yml b/internal/config/testdata/k9s.yml index 91f4ea26..7bf42504 100644 --- a/internal/config/testdata/k9s.yml +++ b/internal/config/testdata/k9s.yml @@ -1,7 +1,8 @@ k9s: refreshRate: 2 - logBufferSize: 200 - logRequestSize: 200 + logger: + tail: 200 + buffer: 2000 currentContext: minikube currentCluster: minikube clusters: diff --git a/internal/model/log.go b/internal/model/log.go index 5bba7ad6..2a7e54fd 100644 --- a/internal/model/log.go +++ b/internal/model/log.go @@ -10,13 +10,12 @@ import ( "github.com/derailed/k9s/internal" "github.com/derailed/k9s/internal/client" + "github.com/derailed/k9s/internal/config" "github.com/derailed/k9s/internal/dao" "github.com/rs/zerolog/log" "github.com/sahilm/fuzzy" ) -const logMaxBufferSize = 100 - // LogsListener represents a log model listener. type LogsListener interface { // LogChanged notifies the model changed. @@ -39,21 +38,27 @@ type Log struct { cancelFn context.CancelFunc mx sync.RWMutex filter string + bufferSize int lastSent int showTimestamp bool - timeOut time.Duration + flushTimeout time.Duration } // NewLog returns a new model. -func NewLog(gvr client.GVR, opts dao.LogOptions, timeOut time.Duration) *Log { +func NewLog(gvr client.GVR, opts dao.LogOptions, flushTimeout time.Duration) *Log { return &Log{ - gvr: gvr, - logOptions: opts, - lines: nil, - timeOut: timeOut, + gvr: gvr, + logOptions: opts, + lines: nil, + flushTimeout: flushTimeout, } } +// Configure sets logger configuration. +func (l *Log) Configure(opts *config.Logger) { + l.bufferSize, l.logOptions.Lines = opts.BufferSize, int64(opts.TailCount) +} + // GetPath returns resource path. func (l *Log) GetPath() string { return l.logOptions.Path } @@ -217,13 +222,13 @@ func (l *Log) updateLogs(ctx context.Context, c <-chan []byte) { var overflow bool l.mx.RLock() { - overflow = len(l.lines)-l.lastSent > logMaxBufferSize + overflow = len(l.lines)-l.lastSent > l.bufferSize } l.mx.RUnlock() if overflow { l.Notify(true) } - case <-time.After(l.timeOut): + case <-time.After(l.flushTimeout): l.Notify(true) case <-ctx.Done(): return diff --git a/internal/ui/config.go b/internal/ui/config.go index 17640570..2e426c43 100644 --- a/internal/ui/config.go +++ b/internal/ui/config.go @@ -74,7 +74,7 @@ func (c *Configurator) RefreshCustomViews() { } if err := c.CustomView.Load(config.K9sViewConfigFile); err != nil { - log.Debug().Msgf("No view custom configuration file found -- %s", config.K9sViewConfigFile) + log.Error().Err(err).Msgf("Custom view load failed %s", config.K9sViewConfigFile) return } } diff --git a/internal/view/log.go b/internal/view/log.go index 1f001748..2029345a 100644 --- a/internal/view/log.go +++ b/internal/view/log.go @@ -20,14 +20,11 @@ import ( ) const ( - logTitle = "logs" - logMessage = "[:orange:b]Waiting for logs...[::]" - logCoFmt = " Logs([fg:bg:]%s:[hilite:bg:b]%s[-:bg:-]) " - logFmt = " Logs([fg:bg:]%s) " - - // BOZO!! Canned! Need config tail line counts! - tailLineCount = 50 - defaultTimeout = 200 * time.Millisecond + logTitle = "logs" + logMessage = "[:orange:b]Waiting for logs...[::]" + logCoFmt = " Logs([fg:bg:]%s:[hilite:bg:b]%s[-:bg:-]) " + logFmt = " Logs([fg:bg:]%s) " + flushTimeout = 200 * time.Millisecond ) // Log represents a generic log viewer. @@ -40,6 +37,7 @@ type Log struct { ansiWriter io.Writer cmdBuff *ui.CmdBuff model *model.Log + counts int } var _ model.Component = (*Log)(nil) @@ -49,7 +47,7 @@ func NewLog(gvr client.GVR, path, co string, prev bool) *Log { l := Log{ Flex: tview.NewFlex(), cmdBuff: ui.NewCmdBuff('/', ui.FilterBuff), - model: model.NewLog(gvr, buildLogOpts(path, co, prev, true, tailLineCount), defaultTimeout), + model: model.NewLog(gvr, buildLogOpts(path, co, prev, true, config.DefaultLoggerTailCount), flushTimeout), } return &l @@ -60,6 +58,8 @@ func (l *Log) Init(ctx context.Context) (err error) { if l.app, err = extractApp(ctx); err != nil { return err } + l.model.Configure(l.app.Config.K9s.Logger) + l.SetBorder(true) l.SetBorderPadding(0, 0, 1, 1) l.SetDirection(tview.FlexRow) @@ -74,7 +74,7 @@ func (l *Log) Init(ctx context.Context) (err error) { } l.logs.SetText(logMessage) l.logs.SetWrap(false) - l.logs.SetMaxBuffer(l.app.Config.K9s.LogBufferSize) + l.logs.SetMaxBuffer(l.app.Config.K9s.Logger.BufferSize) l.ansiWriter = tview.ANSIWriter(l.logs, l.app.Styles.Views().Log.FgColor.String(), l.app.Styles.Views().Log.BgColor.String()) l.AddItem(l.logs, 0, 1, true) @@ -97,6 +97,7 @@ func (l *Log) Init(ctx context.Context) (err error) { // LogCleared clears the logs. func (l *Log) LogCleared() { + l.counts = 0 l.app.QueueUpdateDraw(func() { l.logs.Clear() l.logs.ScrollTo(0, 0)