diff --git a/agent/agent.go b/agent/agent.go index 8011657e6..2beb87bf2 100644 --- a/agent/agent.go +++ b/agent/agent.go @@ -5,6 +5,7 @@ package agent import ( "context" "io" + "log/slog" "os" "os/signal" "sync" @@ -65,10 +66,11 @@ type Agent struct { // New creates a new Agent. func New(cfg Config) *Agent { - logger.Prefix = cfg.Name - return &Agent{ - Logger: logger.New("main", "main"), + Logger: logger.New().With( + slog.String("component", "agent"), + slog.String("job", "main"), + ), Name: cfg.Name, ConfDir: cfg.ConfDir, ModulesConfDir: cfg.ModulesConfDir, @@ -186,7 +188,7 @@ func (a *Agent) run(ctx context.Context) { jobsManager.Modules = enabledModules // TODO: rm 'if' after https://github.com/netdata/netdata/issues/16079 - if logger.IsDebug() { + if logger.Level.Enabled(slog.LevelDebug) { dyncfgDiscovery, _ := dyncfg.NewDiscovery(dyncfg.Config{ Plugin: a.Name, API: netdataapi.New(a.Out), diff --git a/agent/discovery/dummy/discovery.go b/agent/discovery/dummy/discovery.go index 4e6625dff..176d7f7e3 100644 --- a/agent/discovery/dummy/discovery.go +++ b/agent/discovery/dummy/discovery.go @@ -5,6 +5,7 @@ package dummy import ( "context" "fmt" + "log/slog" "github.com/netdata/go.d.plugin/agent/confgroup" "github.com/netdata/go.d.plugin/logger" @@ -15,9 +16,12 @@ func NewDiscovery(cfg Config) (*Discovery, error) { return nil, fmt.Errorf("config validation: %v", err) } d := &Discovery{ - Logger: logger.New("discovery", "dummy"), - reg: cfg.Registry, - names: cfg.Names, + Logger: logger.New().With( + slog.String("component", "discovery"), + slog.String("job", "dummy"), + ), + reg: cfg.Registry, + names: cfg.Names, } return d, nil } diff --git a/agent/discovery/dyncfg/dyncfg.go b/agent/discovery/dyncfg/dyncfg.go index d493d5512..79b6a5542 100644 --- a/agent/discovery/dyncfg/dyncfg.go +++ b/agent/discovery/dyncfg/dyncfg.go @@ -6,6 +6,7 @@ import ( "bytes" "context" "fmt" + "log/slog" "strings" "sync" @@ -25,7 +26,10 @@ func NewDiscovery(cfg Config) (*Discovery, error) { } mgr := &Discovery{ - Logger: logger.New("dyncfg", "manager"), + Logger: logger.New().With( + slog.String("component", "discovery"), + slog.String("job", "dyncfg"), + ), Plugin: cfg.Plugin, API: cfg.API, Modules: cfg.Modules, diff --git a/agent/discovery/file/discovery.go b/agent/discovery/file/discovery.go index 70ee681a8..29a2867ff 100644 --- a/agent/discovery/file/discovery.go +++ b/agent/discovery/file/discovery.go @@ -6,6 +6,7 @@ import ( "context" "errors" "fmt" + "log/slog" "sync" "github.com/netdata/go.d.plugin/agent/confgroup" @@ -18,7 +19,10 @@ func NewDiscovery(cfg Config) (*Discovery, error) { } d := Discovery{ - Logger: logger.New("discovery", "file manager"), + Logger: logger.New().With( + slog.String("component", "discovery"), + slog.String("job", "file manager"), + ), } if err := d.registerDiscoverers(cfg); err != nil { diff --git a/agent/discovery/file/read.go b/agent/discovery/file/read.go index ef4e7aa80..8d1485f57 100644 --- a/agent/discovery/file/read.go +++ b/agent/discovery/file/read.go @@ -4,6 +4,7 @@ package file import ( "context" + "log/slog" "os" "path/filepath" @@ -21,7 +22,10 @@ type ( func NewReader(reg confgroup.Registry, paths []string) *Reader { return &Reader{ - Logger: logger.New("discovery", "file reader"), + Logger: logger.New().With( + slog.String("component", "discovery"), + slog.String("job", "file reader"), + ), reg: reg, paths: paths, diff --git a/agent/discovery/file/watch.go b/agent/discovery/file/watch.go index 77217058e..a6fd3c374 100644 --- a/agent/discovery/file/watch.go +++ b/agent/discovery/file/watch.go @@ -4,6 +4,7 @@ package file import ( "context" + "log/slog" "os" "path/filepath" "strings" @@ -35,7 +36,10 @@ func (c cache) put(path string, modTime time.Time) { c[path] = modTime } func NewWatcher(reg confgroup.Registry, paths []string) *Watcher { d := &Watcher{ - Logger: logger.New("discovery", "file watcher"), + Logger: logger.New().With( + slog.String("component", "discovery"), + slog.String("job", "file watcher"), + ), paths: paths, reg: reg, watcher: nil, diff --git a/agent/discovery/manager.go b/agent/discovery/manager.go index 5e53d2161..d6e1783c1 100644 --- a/agent/discovery/manager.go +++ b/agent/discovery/manager.go @@ -6,6 +6,7 @@ import ( "context" "errors" "fmt" + "log/slog" "sync" "time" @@ -21,7 +22,10 @@ func NewManager(cfg Config) (*Manager, error) { } mgr := &Manager{ - Logger: logger.New("discovery", "manager"), + Logger: logger.New().With( + slog.String("component", "discovery"), + slog.String("job", "manager"), + ), send: make(chan struct{}, 1), sendEvery: time.Second * 2, // timeout to aggregate changes discoverers: make([]discoverer, 0), diff --git a/agent/discovery/sd/hostsocket/net.go b/agent/discovery/sd/hostsocket/net.go index 34b4b0ed2..761a06527 100644 --- a/agent/discovery/sd/hostsocket/net.go +++ b/agent/discovery/sd/hostsocket/net.go @@ -8,6 +8,7 @@ import ( "context" "errors" "fmt" + "log/slog" "os" "os/exec" "path/filepath" @@ -60,7 +61,10 @@ func NewNetSocketDiscoverer(cfg NetworkSocketConfig) (*NetDiscoverer, error) { } d := &NetDiscoverer{ - Logger: logger.New("hostsocket", "net"), + Logger: logger.New().With( + slog.String("component", "discovery"), + slog.String("job", "sd hostsocket"), + ), interval: time.Second * 60, ll: &localListenersExec{ binPath: filepath.Join(dir, "local-listeners"), diff --git a/agent/discovery/sd/kubernetes/kubernetes.go b/agent/discovery/sd/kubernetes/kubernetes.go index e59a6c1cc..88845ceec 100644 --- a/agent/discovery/sd/kubernetes/kubernetes.go +++ b/agent/discovery/sd/kubernetes/kubernetes.go @@ -5,6 +5,7 @@ package kubernetes import ( "context" "fmt" + "log/slog" "os" "strings" "sync" @@ -44,7 +45,10 @@ func NewKubeDiscoverer(cfg Config) (*KubeDiscoverer, error) { } d := &KubeDiscoverer{ - Logger: logger.New("k8s td manager", ""), + Logger: logger.New().With( + slog.String("component", "discovery"), + slog.String("job", "sd k8s manager"), + ), namespaces: ns, podConf: cfg.Pod, svcConf: cfg.Service, diff --git a/agent/discovery/sd/kubernetes/pod.go b/agent/discovery/sd/kubernetes/pod.go index c808712d0..11f83c303 100644 --- a/agent/discovery/sd/kubernetes/pod.go +++ b/agent/discovery/sd/kubernetes/pod.go @@ -5,6 +5,7 @@ package kubernetes import ( "context" "fmt" + "log/slog" "net" "strconv" "strings" @@ -67,7 +68,10 @@ func newPodDiscoverer(pod, cmap, secret cache.SharedInformer) *podDiscoverer { }) return &podDiscoverer{ - Logger: logger.New("k8s pod td", ""), + Logger: logger.New().With( + slog.String("component", "discovery"), + slog.String("job", "sd k8s pod"), + ), podInformer: pod, cmapInformer: cmap, secretInformer: secret, diff --git a/agent/discovery/sd/kubernetes/service.go b/agent/discovery/sd/kubernetes/service.go index e6fa50e8a..1712b0361 100644 --- a/agent/discovery/sd/kubernetes/service.go +++ b/agent/discovery/sd/kubernetes/service.go @@ -5,6 +5,7 @@ package kubernetes import ( "context" "fmt" + "log/slog" "net" "strconv" "strings" @@ -69,7 +70,10 @@ func newServiceDiscoverer(inf cache.SharedInformer) *serviceDiscoverer { }) return &serviceDiscoverer{ - Logger: logger.New("k8s service td", ""), + Logger: logger.New().With( + slog.String("component", "discovery"), + slog.String("job", "sd k8s service"), + ), informer: inf, queue: queue, } diff --git a/agent/discovery/sd/pipeline/pipeline.go b/agent/discovery/sd/pipeline/pipeline.go index af2fc2c9d..3ae0aa47c 100644 --- a/agent/discovery/sd/pipeline/pipeline.go +++ b/agent/discovery/sd/pipeline/pipeline.go @@ -4,10 +4,11 @@ package pipeline import ( "context" - "github.com/netdata/go.d.plugin/agent/discovery/sd/hostsocket" + "log/slog" "time" "github.com/netdata/go.d.plugin/agent/confgroup" + "github.com/netdata/go.d.plugin/agent/discovery/sd/hostsocket" "github.com/netdata/go.d.plugin/agent/discovery/sd/kubernetes" "github.com/netdata/go.d.plugin/agent/discovery/sd/model" "github.com/netdata/go.d.plugin/logger" @@ -19,7 +20,10 @@ func New(cfg Config) (*Pipeline, error) { } p := &Pipeline{ - Logger: logger.New("sd pipeline", cfg.Name), + Logger: logger.New().With( + slog.String("component", "discovery"), + slog.String("job", "sd pipeline"), + ), accum: newAccumulator(), discoverers: make([]model.Discoverer, 0), items: make(map[string]map[uint64][]confgroup.Config), diff --git a/agent/discovery/sd/pipeline/sim_test.go b/agent/discovery/sd/pipeline/sim_test.go index 286087b8b..eec7f417e 100644 --- a/agent/discovery/sd/pipeline/sim_test.go +++ b/agent/discovery/sd/pipeline/sim_test.go @@ -45,7 +45,7 @@ func (sim discoverySim) run(t *testing.T) { accum.sendEvery = time.Second * 2 pl := &Pipeline{ - Logger: logger.New("sd pipeline", "test"), + Logger: logger.New(), accum: accum, discoverers: sim.discoverers, clr: mockClr, diff --git a/agent/discovery/sd/sim_test.go b/agent/discovery/sd/sim_test.go index e9e489b47..9ddb15e50 100644 --- a/agent/discovery/sd/sim_test.go +++ b/agent/discovery/sd/sim_test.go @@ -26,7 +26,7 @@ type discoverySim struct { func (sim *discoverySim) run(t *testing.T) { fact := &mockFactory{} mgr := &ServiceDiscovery{ - Logger: logger.New("test", "test"), + Logger: logger.New(), sdFactory: fact, confProv: &mockConfigProvider{ configs: sim.configs, diff --git a/agent/filestatus/manager.go b/agent/filestatus/manager.go index 7ff06bc05..64669126e 100644 --- a/agent/filestatus/manager.go +++ b/agent/filestatus/manager.go @@ -4,6 +4,7 @@ package filestatus import ( "context" + "log/slog" "os" "time" @@ -13,7 +14,10 @@ import ( func NewManager(path string) *Manager { return &Manager{ - Logger: logger.New("status save", "manager"), + Logger: logger.New().With( + slog.String("component", "filestatus"), + slog.String("job", "manager"), + ), path: path, store: &Store{}, flushEvery: time.Second * 5, diff --git a/agent/functions/manager.go b/agent/functions/manager.go index ed208ce7b..2240f868a 100644 --- a/agent/functions/manager.go +++ b/agent/functions/manager.go @@ -6,6 +6,7 @@ import ( "bufio" "context" "io" + "log/slog" "os" "strings" "sync" @@ -20,7 +21,10 @@ var isTerminal = isatty.IsTerminal(os.Stdout.Fd()) || isatty.IsTerminal(os.Stdin func NewManager() *Manager { return &Manager{ - Logger: logger.New("functions", "manager"), + Logger: logger.New().With( + slog.String("component", "functions"), + slog.String("job", "manager"), + ), Input: os.Stdin, mux: &sync.Mutex{}, FunctionRegistry: make(map[string]func(Function)), diff --git a/agent/jobmgr/manager.go b/agent/jobmgr/manager.go index 8b6156983..88d9406c7 100644 --- a/agent/jobmgr/manager.go +++ b/agent/jobmgr/manager.go @@ -6,6 +6,7 @@ import ( "context" "fmt" "io" + "log/slog" "os" "strings" "sync" @@ -46,7 +47,10 @@ const ( func NewManager() *Manager { np := noop{} mgr := &Manager{ - Logger: logger.New("job", "manager"), + Logger: logger.New().With( + slog.String("component", "job"), + slog.String("job", "manager"), + ), Out: io.Discard, FileLock: np, StatusSaver: np, diff --git a/agent/module/job.go b/agent/module/job.go index 8af098ce1..6aa2617f2 100644 --- a/agent/module/job.go +++ b/agent/module/job.go @@ -6,6 +6,7 @@ import ( "bytes" "fmt" "io" + "log/slog" "os" "regexp" "runtime/debug" @@ -185,8 +186,9 @@ func (j *Job) AutoDetection() (ok bool) { ok = false j.panicked = true j.disableAutoDetection() + j.Errorf("PANIC %v", r) - if logger.IsDebug() { + if logger.Level.Enabled(slog.LevelDebug) { j.Errorf("STACK: %s", debug.Stack()) } } @@ -255,9 +257,6 @@ func (j *Job) disableAutoDetection() { } func (j *Job) Cleanup() { - if j.Logger != nil { - logger.GlobalMsgCountWatcher.Unregister(j.Logger) - } j.buf.Reset() if !shouldObsoleteCharts() { return @@ -294,7 +293,10 @@ func (j *Job) init() bool { return true } - log := logger.NewLimited(j.ModuleName(), j.Name()) + log := logger.New().With( + slog.String("module", j.ModuleName()), + slog.String("job", j.Name()), + ) j.Logger = log j.module.GetBase().Logger = log @@ -349,7 +351,7 @@ func (j *Job) collect() (result map[string]int64) { if r := recover(); r != nil { j.panicked = true j.Errorf("PANIC: %v", r) - if logger.IsDebug() { + if logger.Level.Enabled(slog.LevelDebug) { j.Errorf("STACK: %s", debug.Stack()) } } diff --git a/agent/vnodes/vnodes.go b/agent/vnodes/vnodes.go index e5f66c1ba..6e86a47e5 100644 --- a/agent/vnodes/vnodes.go +++ b/agent/vnodes/vnodes.go @@ -5,6 +5,7 @@ package vnodes import ( "io" "io/fs" + "log/slog" "os" "path/filepath" "sync" @@ -18,7 +19,10 @@ var Disabled = false // TODO: remove after Netdata v1.39.0. Fix for "from source func New(confDir string) *Vnodes { vn := &Vnodes{ - Logger: logger.New("vnode", "registry"), + Logger: logger.New().With( + slog.String("component", "vnodes"), + slog.String("job", "registry"), + ), confDir: confDir, vnodes: make(map[string]*VirtualNode), diff --git a/cmd/godplugin/main.go b/cmd/godplugin/main.go index 5ff8db08a..281a12c01 100644 --- a/cmd/godplugin/main.go +++ b/cmd/godplugin/main.go @@ -4,6 +4,7 @@ package main import ( "fmt" + "log/slog" "os" "os/user" "path/filepath" @@ -21,14 +22,14 @@ import ( ) var ( - cd, _ = os.Getwd() - name = "go.d" - userDir = os.Getenv("NETDATA_USER_CONFIG_DIR") - stockDir = os.Getenv("NETDATA_STOCK_CONFIG_DIR") - varLibDir = os.Getenv("NETDATA_LIB_DIR") - lockDir = os.Getenv("NETDATA_LOCK_DIR") - watchPath = os.Getenv("NETDATA_PLUGINS_GOD_WATCH_PATH") - envLogSeverityLevel = os.Getenv("NETDATA_LOG_SEVERITY_LEVEL") + cd, _ = os.Getwd() + name = "go.d" + userDir = os.Getenv("NETDATA_USER_CONFIG_DIR") + stockDir = os.Getenv("NETDATA_STOCK_CONFIG_DIR") + varLibDir = os.Getenv("NETDATA_LIB_DIR") + lockDir = os.Getenv("NETDATA_LOCK_DIR") + watchPath = os.Getenv("NETDATA_PLUGINS_GOD_WATCH_PATH") + envLogLevelLevel = os.Getenv("NETDATA_LOG_SEVERITY_LEVEL") version = "unknown" ) @@ -97,12 +98,12 @@ func main() { return } - if envLogSeverityLevel != "" { - logger.SetSeverityByName(envLogSeverityLevel) + if envLogLevelLevel != "" { + logger.Level.SetByName(envLogLevelLevel) } if opts.Debug { - logger.SetSeverity(logger.DEBUG) + logger.Level.Set(slog.LevelDebug) } a := agent.New(agent.Config{ diff --git a/examples/simple/main.go b/examples/simple/main.go index 8f8f7a3e5..9982b91fc 100644 --- a/examples/simple/main.go +++ b/examples/simple/main.go @@ -4,6 +4,7 @@ package main import ( "fmt" + "log/slog" "math/rand" "os" "path" @@ -90,7 +91,7 @@ func main() { opt := parseCLI() if opt.Debug { - logger.SetSeverity(logger.DEBUG) + logger.Level.Set(slog.LevelDebug) } if opt.Version { fmt.Println(version) diff --git a/logger/countwatcher.go b/logger/countwatcher.go deleted file mode 100644 index 93e5b252c..000000000 --- a/logger/countwatcher.go +++ /dev/null @@ -1,79 +0,0 @@ -// SPDX-License-Identifier: GPL-3.0-or-later - -package logger - -import ( - "sync" - "sync/atomic" - "time" -) - -var ( - resetEvery = time.Second -) - -// GlobalMsgCountWatcher is an initiated instance of MsgCountWatcher. -// It resets message counter for every registered logger every 1 seconds. -var GlobalMsgCountWatcher = newMsgCountWatcher(resetEvery) - -func newMsgCountWatcher(resetEvery time.Duration) *MsgCountWatcher { - t := &MsgCountWatcher{ - ticker: time.NewTicker(resetEvery), - shutdown: make(chan struct{}), - items: make(map[int64]*Logger), - } - go t.start() - - return t -} - -// MsgCountWatcher MsgCountWatcher -type MsgCountWatcher struct { - shutdown chan struct{} - ticker *time.Ticker - - mux sync.Mutex - items map[int64]*Logger -} - -// Register adds logger to the collection. -func (m *MsgCountWatcher) Register(logger *Logger) { - m.mux.Lock() - defer m.mux.Unlock() - - m.items[logger.id] = logger -} - -// Unregister removes logger from the collection. -func (m *MsgCountWatcher) Unregister(logger *Logger) { - m.mux.Lock() - defer m.mux.Unlock() - - delete(m.items, logger.id) -} - -func (m *MsgCountWatcher) start() { -LOOP: - for { - select { - case <-m.shutdown: - break LOOP - case <-m.ticker.C: - m.resetCount() - } - } -} - -func (m *MsgCountWatcher) stop() { - m.shutdown <- struct{}{} - m.ticker.Stop() -} - -func (m *MsgCountWatcher) resetCount() { - m.mux.Lock() - defer m.mux.Unlock() - - for _, v := range m.items { - atomic.StoreInt64(&v.msgCount, 0) - } -} diff --git a/logger/countwatcher_test.go b/logger/countwatcher_test.go deleted file mode 100644 index 77bc92785..000000000 --- a/logger/countwatcher_test.go +++ /dev/null @@ -1,59 +0,0 @@ -// SPDX-License-Identifier: GPL-3.0-or-later - -package logger - -import ( - "io" - "sync/atomic" - "testing" - "time" - - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" -) - -func TestMsgCountWatcher_Register(t *testing.T) { - cw := newMsgCountWatcher(time.Second) - defer cw.stop() - - require.Len(t, cw.items, 0) - - logger := New("", "") - cw.Register(logger) - - require.Len(t, cw.items, 1) - require.Equal(t, logger, cw.items[logger.id]) - -} - -func TestMsgCountWatcher_Unregister(t *testing.T) { - cw := newMsgCountWatcher(time.Second) - defer cw.stop() - - require.Len(t, cw.items, 0) - - logger := New("", "") - cw.items[logger.id] = logger - cw.Unregister(logger) - - require.Len(t, cw.items, 0) -} - -func TestMsgCountWatcher(t *testing.T) { - reset := time.Millisecond * 500 - cw := newMsgCountWatcher(reset) - defer cw.stop() - - logger := New("", "") - logger.limited = true - logger.formatter.SetOutput(io.Discard) - cw.Register(logger) - - for i := 0; i < 3; i++ { - for m := 0; m < 100; m++ { - logger.Info() - } - time.Sleep(reset * 2) - assert.Equal(t, int64(0), atomic.LoadInt64(&logger.msgCount)) - } -} diff --git a/logger/default.go b/logger/default.go new file mode 100644 index 000000000..c8bfb4d42 --- /dev/null +++ b/logger/default.go @@ -0,0 +1,30 @@ +// SPDX-License-Identifier: GPL-3.0-or-later + +package logger + +import ( + "log/slog" + "os" + + "github.com/mattn/go-isatty" +) + +func newDefaultLogger() *Logger { + if isatty.IsTerminal(os.Stderr.Fd()) { + // skip 2 slog pkg calls, 3 this pkg calls + return &Logger{sl: slog.New(withCallDepth(5, newTerminalHandler()))} + } + return &Logger{sl: slog.New(newTextHandler()).With(pluginAttr)} +} + +var defaultLogger = newDefaultLogger() + +func Error(a ...any) { defaultLogger.Error(a...) } +func Warning(a ...any) { defaultLogger.Warning(a...) } +func Info(a ...any) { defaultLogger.Info(a...) } +func Debug(a ...any) { defaultLogger.Debug(a...) } +func Errorf(format string, a ...any) { defaultLogger.Errorf(format, a...) } +func Warningf(format string, a ...any) { defaultLogger.Warningf(format, a...) } +func Infof(format string, a ...any) { defaultLogger.Infof(format, a...) } +func Debugf(format string, a ...any) { defaultLogger.Debugf(format, a...) } +func With(args ...any) *Logger { return defaultLogger.With(args...) } diff --git a/logger/formatter.go b/logger/formatter.go deleted file mode 100644 index 26b377b4b..000000000 --- a/logger/formatter.go +++ /dev/null @@ -1,200 +0,0 @@ -// SPDX-License-Identifier: GPL-3.0-or-later - -package logger - -import ( - "io" - "log" - "runtime" - "sync" - "time" -) - -type ( - formatter struct { - colored bool - prefix string - out io.Writer // destination for output - flag int // properties - - mu sync.Mutex // ensures atomic writes; protects the following fields - buf []byte // for accumulating text to write - } -) - -func newFormatter(out io.Writer, isCLI bool, prefix string) *formatter { - if isCLI { - return &formatter{ - out: out, - colored: true, - flag: log.Lshortfile, - buf: make([]byte, 0, 120), - } - } - return &formatter{ - out: out, - colored: false, - prefix: prefix + " ", - flag: log.Ldate | log.Ltime, - buf: make([]byte, 0, 120), - } -} - -func (l *formatter) SetOutput(out io.Writer) { - l.out = out -} - -func (l *formatter) Output(severity Severity, module, job string, callDepth int, s string) { - now := time.Now() // get this early. - var file string - var line int - if l.flag&(log.Lshortfile|log.Llongfile) != 0 { - var ok bool - _, file, line, ok = runtime.Caller(callDepth) - if !ok { - file = "???" - line = 0 - } - } - - l.mu.Lock() - defer l.mu.Unlock() - - l.formatTimestamp(now) - l.buf = append(l.buf, l.prefix...) - l.formatSeverity(severity) - l.formatModuleJob(module, job) - l.formatFile(file, line) - l.buf = append(l.buf, s...) - if s == "" || s[len(s)-1] != '\n' { - l.buf = append(l.buf, '\n') - } - _, _ = l.out.Write(l.buf) - l.buf = l.buf[:0] -} - -// formatModuleJob write module name and job name to buf -// format: $module[$job] -func (l *formatter) formatModuleJob(module string, job string) { - l.buf = append(l.buf, module...) - l.buf = append(l.buf, '[') - l.buf = append(l.buf, job...) - l.buf = append(l.buf, "] "...) -} - -// formatTimestamp writes timestamp to buf -// format: YYYY-MM-DD hh:mm:ss: -func (l *formatter) formatTimestamp(t time.Time) { - if l.flag&(log.Ldate|log.Ltime|log.Lmicroseconds) != 0 { - if l.flag&log.LUTC != 0 { - t = t.UTC() - } - if l.flag&log.Ldate != 0 { - year, month, day := t.Date() - itoa(&l.buf, year, 4) - l.buf = append(l.buf, '-') - itoa(&l.buf, int(month), 2) - l.buf = append(l.buf, '-') - itoa(&l.buf, day, 2) - l.buf = append(l.buf, ' ') - } - if l.flag&(log.Ltime|log.Lmicroseconds) != 0 { - hour, min, sec := t.Clock() - itoa(&l.buf, hour, 2) - l.buf = append(l.buf, ':') - itoa(&l.buf, min, 2) - l.buf = append(l.buf, ':') - itoa(&l.buf, sec, 2) - if l.flag&log.Lmicroseconds != 0 { - l.buf = append(l.buf, '.') - itoa(&l.buf, t.Nanosecond()/1e3, 6) - } - l.buf = append(l.buf, ' ') - } - l.buf[len(l.buf)-1] = ':' - l.buf = append(l.buf, ' ') - } -} - -// formatSeverity write severity to buf -// format (CLI): [ $severity ] -// format (file): $severity: -func (l *formatter) formatSeverity(severity Severity) { - if l.colored { - switch severity { - case DEBUG: - l.buf = append(l.buf, "\x1b[0;36m[ "...) // Cyan text - case INFO: - l.buf = append(l.buf, "\x1b[0;32m[ "...) // Green text - case WARNING: - l.buf = append(l.buf, "\x1b[0;33m[ "...) // Yellow text - case ERROR: - l.buf = append(l.buf, "\x1b[0;31m[ "...) // Red text - case CRITICAL: - l.buf = append(l.buf, "\x1b[0;37;41m[ "...) // White text with Red background - } - putString(&l.buf, severity.ShortString(), 5) - l.buf = append(l.buf, " ]\x1b[0m "...) // clear color scheme - } else { - l.buf = append(l.buf, severity.String()...) - l.buf = append(l.buf, ": "...) - } -} - -// formatFile writes file info to buf -// format: $file:$line -func (l *formatter) formatFile(file string, line int) { - if l.flag&(log.Lshortfile|log.Llongfile) == 0 { - return - } - if l.flag&log.Lshortfile != 0 { - short := file - for i := len(file) - 1; i > 0; i-- { - if file[i] == '/' { - short = file[i+1:] - break - } - } - file = short - } - - if l.colored { - l.buf = append(l.buf, "\x1b[0;90m"...) - } - l.buf = append(l.buf, file...) - l.buf = append(l.buf, ':') - itoa(&l.buf, line, -1) - if l.colored { - l.buf = append(l.buf, "\x1b[0m "...) - } else { - l.buf = append(l.buf, ' ') - } -} - -// itoa Cheap integer to fixed-width decimal ASCII. Give a negative width to avoid zero-padding. -func itoa(buf *[]byte, i int, wid int) { - // Assemble decimal in reverse order. - var b [20]byte - bp := len(b) - 1 - for i >= 10 || wid > 1 { - wid-- - q := i / 10 - b[bp] = byte('0' + i - q*10) - bp-- - i = q - } - // i < 10 - b[bp] = byte('0' + i) - *buf = append(*buf, b[bp:]...) -} - -// putString Cheap sprintf("%*s", s, wid) -func putString(buf *[]byte, s string, wid int) { - *buf = append(*buf, s...) - space := wid - len(s) - if space > 0 { - for i := 0; i < space; i++ { - *buf = append(*buf, ' ') - } - } -} diff --git a/logger/formatter_test.go b/logger/formatter_test.go deleted file mode 100644 index a76f3a517..000000000 --- a/logger/formatter_test.go +++ /dev/null @@ -1,34 +0,0 @@ -// SPDX-License-Identifier: GPL-3.0-or-later - -package logger - -import ( - "bytes" - "testing" - - "github.com/stretchr/testify/assert" -) - -func TestFormatter_Output_cli(t *testing.T) { - out := &bytes.Buffer{} - fmtter := newFormatter(out, true, "test") - - fmtter.Output(INFO, "mod1", "job1", 1, "hello") - assert.NotRegexp(t, `\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}: `, out.String()) - assert.Contains(t, out.String(), "INFO") - assert.Contains(t, out.String(), "mod1[job1]") - assert.Contains(t, out.String(), "formatter_test.go:") - assert.Contains(t, out.String(), "hello") -} - -func TestFormatter_Output_file(t *testing.T) { - out := &bytes.Buffer{} - fmtter := newFormatter(out, false, "test") - - fmtter.Output(INFO, "mod1", "job1", 1, "hello") - assert.Regexp(t, `\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}: `, out.String()) - assert.Contains(t, out.String(), "INFO") - assert.Contains(t, out.String(), "mod1[job1]") - assert.NotContains(t, out.String(), "formatter_test.go:") - assert.Contains(t, out.String(), "hello") -} diff --git a/logger/handler.go b/logger/handler.go new file mode 100644 index 000000000..fb2992c2f --- /dev/null +++ b/logger/handler.go @@ -0,0 +1,63 @@ +package logger + +import ( + "context" + "log/slog" + "os" + "runtime" + "strings" + + "github.com/lmittmann/tint" +) + +func newTextHandler() slog.Handler { + return slog.NewTextHandler(os.Stderr, &slog.HandlerOptions{ + Level: Level.lvl, + ReplaceAttr: func(groups []string, a slog.Attr) slog.Attr { + if a.Key == slog.LevelKey { + if v, ok := a.Value.Any().(slog.Level); ok { + a.Value = slog.StringValue(strings.ToLower(v.String())) + } + } + return a + }, + }) +} + +func newTerminalHandler() slog.Handler { + return tint.NewHandler(os.Stderr, &tint.Options{ + AddSource: true, + Level: Level.lvl, + }) +} + +func withCallDepth(depth int, sh slog.Handler) slog.Handler { + return &callDepthHandler{depth: depth, sh: sh} +} + +type callDepthHandler struct { + depth int + sh slog.Handler +} + +func (h *callDepthHandler) Enabled(ctx context.Context, level slog.Level) bool { + return h.sh.Enabled(ctx, level) +} + +func (h *callDepthHandler) WithAttrs(attrs []slog.Attr) slog.Handler { + return &callDepthHandler{depth: h.depth, sh: h.sh.WithAttrs(attrs)} +} + +func (h *callDepthHandler) WithGroup(name string) slog.Handler { + return &callDepthHandler{depth: h.depth, sh: h.sh.WithGroup(name)} +} + +func (h *callDepthHandler) Handle(ctx context.Context, r slog.Record) error { + // https://pkg.go.dev/log/slog#example-package-Wrapping + var pcs [1]uintptr + // skip Callers and this function + runtime.Callers(h.depth+2, pcs[:]) + r.PC = pcs[0] + + return h.sh.Handle(ctx, r) +} diff --git a/slogger/level.go b/logger/level.go similarity index 52% rename from slogger/level.go rename to logger/level.go index 5774816c6..5889a275f 100644 --- a/slogger/level.go +++ b/logger/level.go @@ -1,15 +1,27 @@ // SPDX-License-Identifier: GPL-3.0-or-later -package slogger +package logger import ( "log/slog" "strings" ) -var Level = &slog.LevelVar{} +var Level = &level{lvl: &slog.LevelVar{}} -func SetLevelByName(level string) { +type level struct { + lvl *slog.LevelVar +} + +func (l *level) Enabled(level slog.Level) bool { + return level >= l.lvl.Level() +} + +func (l *level) Set(level slog.Level) { + l.lvl.Set(level) +} + +func (l *level) SetByName(level string) { switch strings.ToLower(level) { case "err", "error": Level.Set(slog.LevelError) diff --git a/logger/logger.go b/logger/logger.go index 79760c929..66d985968 100644 --- a/logger/logger.go +++ b/logger/logger.go @@ -3,191 +3,56 @@ package logger import ( + "context" "fmt" + "log/slog" "os" - "sync/atomic" - "github.com/mattn/go-isatty" -) - -const ( - msgPerSecondLimit = 60 -) + "github.com/netdata/go.d.plugin/agent/executable" -var ( - base = New("base", "base") - initialID = int64(1) - isCLI = func() bool { - switch os.Getenv("NETDATA_FORCE_COLOR") { - case "1", "true": - return true - case "0", "false": - return true - default: - return isatty.IsTerminal(os.Stderr.Fd()) - } - }() - Prefix = "goplugin" + "github.com/mattn/go-isatty" ) -// Logger represents a logger object -type Logger struct { - formatter *formatter - - id int64 - modName string - jobName string +var pluginAttr = slog.String("plugin", executable.Name) - limited bool - msgCount int64 -} - -// New creates a new logger. -func New(modName, jobName string) *Logger { - return &Logger{ - formatter: newFormatter(os.Stderr, isCLI, Prefix), - modName: modName, - jobName: jobName, - id: uniqueID(), +func New() *Logger { + if isatty.IsTerminal(os.Stderr.Fd()) { + // skip 2 slog pkg calls, 2 this pkg calls + return &Logger{sl: slog.New(withCallDepth(4, newTerminalHandler()))} } + return &Logger{sl: slog.New(newTextHandler()).With(pluginAttr)} } -// NewLimited creates a new limited logger -func NewLimited(modName, jobName string) *Logger { - logger := New(modName, jobName) - logger.limited = true - GlobalMsgCountWatcher.Register(logger) - - return logger -} - -// Panic logs a message with the Critical severity then panic -func (l *Logger) Panic(a ...interface{}) { - s := fmt.Sprint(a...) - l.output(CRITICAL, 1, s) - panic(s) -} - -// Critical logs a message with the Critical severity -func (l *Logger) Critical(a ...interface{}) { - l.output(CRITICAL, 1, fmt.Sprint(a...)) -} - -// Error logs a message with the Error severity -func (l *Logger) Error(a ...interface{}) { - l.output(ERROR, 1, fmt.Sprint(a...)) -} - -// Warning logs a message with the Warning severity -func (l *Logger) Warning(a ...interface{}) { - l.output(WARNING, 1, fmt.Sprint(a...)) -} - -// Info logs a message with the Info severity -func (l *Logger) Info(a ...interface{}) { - l.output(INFO, 1, fmt.Sprint(a...)) -} - -// Print logs a message with the Info severity (same as Info) -func (l *Logger) Print(a ...interface{}) { - l.output(INFO, 1, fmt.Sprint(a...)) -} - -// Debug logs a message with the Debug severity -func (l *Logger) Debug(a ...interface{}) { - l.output(DEBUG, 1, fmt.Sprint(a...)) -} - -// Panicln logs a message with the Critical severity then panic -func (l *Logger) Panicln(a ...interface{}) { - s := fmt.Sprintln(a...) - l.output(CRITICAL, 1, s) - panic(s) -} - -// Criticalln logs a message with the Critical severity -func (l *Logger) Criticalln(a ...interface{}) { - l.output(CRITICAL, 1, fmt.Sprintln(a...)) -} - -// Errorln logs a message with the Error severity -func (l *Logger) Errorln(a ...interface{}) { - l.output(ERROR, 1, fmt.Sprintln(a...)) -} - -// Warningln logs a message with the Warning severity -func (l *Logger) Warningln(a ...interface{}) { - l.output(WARNING, 1, fmt.Sprintln(a...)) -} - -// Infoln logs a message with the Info severity -func (l *Logger) Infoln(a ...interface{}) { - l.output(INFO, 1, fmt.Sprintln(a...)) -} - -// Println logs a message with the Info severity (same as Infoln) -func (l *Logger) Println(a ...interface{}) { - l.output(INFO, 1, fmt.Sprintln(a...)) -} - -// Debugln logs a message with the Debug severity -func (l *Logger) Debugln(a ...interface{}) { - l.output(DEBUG, 1, fmt.Sprintln(a...)) -} - -// Panicf logs a message with the Critical severity using the same syntax and options as fmt.Printf then panic -func (l *Logger) Panicf(format string, a ...interface{}) { - s := fmt.Sprintf(format, a...) - l.output(CRITICAL, 1, s) - panic(s) -} - -// Criticalf logs a message with the Critical severity using the same syntax and options as fmt.Printf -func (l *Logger) Criticalf(format string, a ...interface{}) { - l.output(CRITICAL, 1, fmt.Sprintf(format, a...)) -} - -// Errorf logs a message with the Error severity using the same syntax and options as fmt.Printf -func (l *Logger) Errorf(format string, a ...interface{}) { - l.output(ERROR, 1, fmt.Sprintf(format, a...)) -} - -// Warningf logs a message with the Warning severity using the same syntax and options as fmt.Printf -func (l *Logger) Warningf(format string, a ...interface{}) { - l.output(WARNING, 1, fmt.Sprintf(format, a...)) -} - -// Infof logs a message with the Info severity using the same syntax and options as fmt.Printf -func (l *Logger) Infof(format string, a ...interface{}) { - l.output(INFO, 1, fmt.Sprintf(format, a...)) -} - -// Printf logs a message with the Info severity using the same syntax and options as fmt.Printf -func (l *Logger) Printf(format string, a ...interface{}) { - l.output(INFO, 1, fmt.Sprintf(format, a...)) +type Logger struct { + sl *slog.Logger } -// Debugf logs a message with the Debug severity using the same syntax and options as fmt.Printf -func (l *Logger) Debugf(format string, a ...interface{}) { - l.output(DEBUG, 1, fmt.Sprintf(format, a...)) -} +func (l *Logger) Error(a ...any) { l.log(slog.LevelError, fmt.Sprint(a...)) } +func (l *Logger) Warning(a ...any) { l.log(slog.LevelWarn, fmt.Sprint(a...)) } +func (l *Logger) Info(a ...any) { l.log(slog.LevelInfo, fmt.Sprint(a...)) } +func (l *Logger) Debug(a ...any) { l.log(slog.LevelDebug, fmt.Sprint(a...)) } +func (l *Logger) Errorf(format string, a ...any) { l.log(slog.LevelError, fmt.Sprintf(format, a...)) } +func (l *Logger) Warningf(format string, a ...any) { l.log(slog.LevelWarn, fmt.Sprintf(format, a...)) } +func (l *Logger) Infof(format string, a ...any) { l.log(slog.LevelInfo, fmt.Sprintf(format, a...)) } +func (l *Logger) Debugf(format string, a ...any) { l.log(slog.LevelDebug, fmt.Sprintf(format, a...)) } -func (l *Logger) output(severity Severity, callDepth int, msg string) { - if severity > globalSeverity { - return - } - - if l == nil || l.formatter == nil { - base.formatter.Output(severity, base.modName, base.jobName, callDepth+2, msg) - return +func (l *Logger) With(args ...any) *Logger { + if l.isNil() { + return &Logger{sl: New().sl.With(args...)} } + return &Logger{sl: l.sl.With(args...)} +} - if l.limited && globalSeverity < DEBUG && atomic.AddInt64(&l.msgCount, 1) > msgPerSecondLimit { - return +func (l *Logger) log(level slog.Level, msg string) { + if l.isNil() { + nilLogger.sl.Log(context.Background(), level, msg) + } else { + l.sl.Log(context.Background(), level, msg) } - l.formatter.Output(severity, l.modName, l.jobName, callDepth+2, msg) } -func uniqueID() int64 { - return atomic.AddInt64(&initialID, 1) +func (l *Logger) isNil() bool { + return l == nil || l.sl == nil } + +var nilLogger = New() diff --git a/logger/logger_test.go b/logger/logger_test.go index da24e28a0..df7049d0a 100644 --- a/logger/logger_test.go +++ b/logger/logger_test.go @@ -1,214 +1,21 @@ -// SPDX-License-Identifier: GPL-3.0-or-later - package logger import ( - "bytes" - "io" - "log" "testing" - "time" "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" ) -func TestSetSeverity(t *testing.T) { - require.Equal(t, globalSeverity, INFO) - SetSeverity(DEBUG) - - assert.Equal(t, globalSeverity, DEBUG) -} - func TestNew(t *testing.T) { - assert.IsType( - t, - (*Logger)(nil), - New("", ""), - ) -} - -func TestNewLimited(t *testing.T) { - logger := NewLimited("", "") - assert.True(t, logger.limited) - - _, ok := GlobalMsgCountWatcher.items[logger.id] - require.True(t, ok) - GlobalMsgCountWatcher.Unregister(logger) -} - -func TestLogger_Critical(t *testing.T) { - buf := bytes.Buffer{} - logger := New("", "") - logger.formatter.SetOutput(&buf) - logger.formatter.flag = log.Lshortfile - logger.Critical() - assert.Contains(t, buf.String(), CRITICAL.ShortString()) - assert.Contains(t, buf.String(), " logger_test.go") -} - -func TestLogger_Criticalf(t *testing.T) { - buf := bytes.Buffer{} - logger := New("", "") - logger.formatter.SetOutput(&buf) - logger.formatter.flag = log.Lshortfile - logger.Criticalf("") - assert.Contains(t, buf.String(), CRITICAL.ShortString()) - assert.Contains(t, buf.String(), " logger_test.go") -} - -func TestLogger_Error(t *testing.T) { - buf := bytes.Buffer{} - logger := New("", "") - logger.formatter.SetOutput(&buf) - - logger.Error() - assert.Contains(t, buf.String(), ERROR.ShortString()) -} - -func TestLogger_Errorf(t *testing.T) { - buf := bytes.Buffer{} - logger := New("", "") - logger.formatter.SetOutput(&buf) - - logger.Errorf("") - assert.Contains(t, buf.String(), ERROR.ShortString()) -} - -func TestLogger_Warning(t *testing.T) { - buf := bytes.Buffer{} - logger := New("", "") - logger.formatter.SetOutput(&buf) - - logger.Warning() - assert.Contains(t, buf.String(), WARNING.ShortString()) -} - -func TestLogger_Warningf(t *testing.T) { - buf := bytes.Buffer{} - logger := New("", "") - logger.formatter.SetOutput(&buf) - - logger.Warningf("") - assert.Contains(t, buf.String(), WARNING.ShortString()) -} - -func TestLogger_Info(t *testing.T) { - buf := bytes.Buffer{} - logger := New("", "") - logger.formatter.SetOutput(&buf) - - logger.Info() - assert.Contains(t, buf.String(), INFO.ShortString()) -} - -func TestLogger_Infof(t *testing.T) { - buf := bytes.Buffer{} - logger := New("", "") - logger.formatter.SetOutput(&buf) - - logger.Infof("") - assert.Contains(t, buf.String(), INFO.ShortString()) -} - -func TestLogger_Debug(t *testing.T) { - buf := bytes.Buffer{} - logger := New("", "") - logger.formatter.SetOutput(&buf) - - logger.Debug() - assert.Contains(t, buf.String(), DEBUG.ShortString()) -} - -func TestLogger_Debugf(t *testing.T) { - buf := bytes.Buffer{} - logger := New("", "") - logger.formatter.SetOutput(&buf) - - logger.Debugf("") - assert.Contains(t, buf.String(), DEBUG.ShortString()) -} - -func TestLogger_NotInitialized(t *testing.T) { - var logger Logger - f := func() { - logger.Info() - } - assert.NotPanics(t, f) -} - -func TestLogger_NotInitializedPtr(t *testing.T) { - var logger *Logger - f := func() { - logger.Info() - } - assert.NotPanics(t, f) -} - -func TestLogger_Unlimited(t *testing.T) { - logger := New("", "") - - wr := countWriter(0) - logger.formatter.SetOutput(&wr) - - num := 1000 - - for i := 0; i < num; i++ { - logger.Info() - } - - require.Equal(t, num, int(wr)) -} - -func TestLogger_Limited(t *testing.T) { - SetSeverity(INFO) - - logger := New("", "") - logger.limited = true - - wr := countWriter(0) - logger.formatter.SetOutput(&wr) - - num := 1000 - - for i := 0; i < num; i++ { - logger.Info() - } - - require.Equal(t, msgPerSecondLimit, int(wr)) -} - -func TestLogger_Info_race(t *testing.T) { - logger := New("", "") - logger.formatter.SetOutput(io.Discard) - for i := 0; i < 10; i++ { - go func() { - for j := 0; j < 10; j++ { - logger.Info("hello ", "world") - } - }() - } - time.Sleep(time.Second) -} - -type countWriter int - -func (c *countWriter) Write(b []byte) (n int, err error) { - *c++ - return len(b), nil -} - -func BenchmarkLogger_Infof(b *testing.B) { - l := New("test", "test") - l.formatter.SetOutput(io.Discard) - for i := 0; i < b.N; i++ { - l.Infof("hello %s", "world") + tests := map[string]*Logger{ + "default logger": New(), + "nil logger": nil, } -} -func BenchmarkLog_Printf(b *testing.B) { - logger := log.New(io.Discard, "", log.Lshortfile) - for i := 0; i < b.N; i++ { - logger.Printf("hello %s", "world") + for name, logger := range tests { + t.Run(name, func(t *testing.T) { + f := func() { logger.Infof("test %s", "test") } + assert.NotPanics(t, f) + }) } } diff --git a/logger/severity.go b/logger/severity.go deleted file mode 100644 index 186515891..000000000 --- a/logger/severity.go +++ /dev/null @@ -1,81 +0,0 @@ -// SPDX-License-Identifier: GPL-3.0-or-later - -package logger - -import "strings" - -var globalSeverity = INFO - -// Severity is a logging severity level -type Severity int - -const ( - // CRITICAL severity level - CRITICAL Severity = iota - // ERROR severity level - ERROR - // WARNING severity level - WARNING - // INFO severity level - INFO - // DEBUG severity level - DEBUG -) - -// String returns human-readable string -func (s Severity) String() string { - switch s { - case CRITICAL: - return "CRITICAL" - case ERROR: - return "ERROR" - case WARNING: - return "WARNING" - case INFO: - return "INFO" - case DEBUG: - return "DEBUG" - } - return "UNKNOWN" -} - -// ShortString returns human-readable short string -func (s Severity) ShortString() string { - switch s { - case CRITICAL: - return "CRIT" - case ERROR: - return "ERROR" - case WARNING: - return "WARN" - case INFO: - return "INFO" - case DEBUG: - return "DEBUG" - } - return "UNKNOWN" -} - -// SetSeverity sets global severity level -func SetSeverity(severity Severity) { - globalSeverity = severity -} - -func SetSeverityByName(severity string) { - switch strings.ToUpper(severity) { - case "CRIT", "CRITICAL": - globalSeverity = CRITICAL - case "ERR", "ERROR": - globalSeverity = ERROR - case "WARN", "WARNING": - globalSeverity = WARNING - case "INFO": - globalSeverity = INFO - case "DEBUG": - globalSeverity = DEBUG - } -} - -func IsDebug() bool { - return globalSeverity == DEBUG -} diff --git a/logger/static.go b/logger/static.go deleted file mode 100644 index 6e09bed7f..000000000 --- a/logger/static.go +++ /dev/null @@ -1,101 +0,0 @@ -// SPDX-License-Identifier: GPL-3.0-or-later - -package logger - -import "fmt" - -// Panic logs a message with the Critical severity then panic -func Panic(a ...interface{}) { - s := fmt.Sprint(a...) - base.output(CRITICAL, 1, s) - panic(s) -} - -// Critical logs a message with the Critical severity -func Critical(a ...interface{}) { - base.output(CRITICAL, 1, fmt.Sprint(a...)) -} - -// Error logs a message with the Error severity -func Error(a ...interface{}) { - base.output(ERROR, 1, fmt.Sprint(a...)) -} - -// Warning logs a message with the Warning severity -func Warning(a ...interface{}) { - base.output(WARNING, 1, fmt.Sprint(a...)) -} - -// Info logs a message with the Info severity -func Info(a ...interface{}) { - base.output(INFO, 1, fmt.Sprint(a...)) -} - -// Debug logs a message with the Debug severity -func Debug(a ...interface{}) { - base.output(DEBUG, 1, fmt.Sprint(a...)) -} - -// Panicln logs a message with the Critical severity then panic -func Panicln(a ...interface{}) { - s := fmt.Sprintln(a...) - base.output(CRITICAL, 1, s) - panic(s) -} - -// Criticalln logs a message with the Critical severity -func Criticalln(a ...interface{}) { - base.output(CRITICAL, 1, fmt.Sprintln(a...)) -} - -// Errorln logs a message with the Error severity -func Errorln(a ...interface{}) { - base.output(ERROR, 1, fmt.Sprintln(a...)) -} - -// Warningln logs a message with the Warning severity -func Warningln(a ...interface{}) { - base.output(WARNING, 1, fmt.Sprintln(a...)) -} - -// Infoln logs a message with the Info severity -func Infoln(a ...interface{}) { - base.output(INFO, 1, fmt.Sprintln(a...)) -} - -// Debugln logs a message with the Debug severity -func Debugln(a ...interface{}) { - base.output(DEBUG, 1, fmt.Sprintln(a...)) -} - -// Panicf logs a message with the Critical severity using the same syntax and options as fmt.Printf then panic -func Panicf(format string, a ...interface{}) { - s := fmt.Sprintf(format, a...) - base.output(CRITICAL, 1, s) - panic(s) -} - -// Criticalf logs a message with the Critical severity using the same syntax and options as fmt.Printf -func Criticalf(format string, a ...interface{}) { - base.output(CRITICAL, 1, fmt.Sprintf(format, a...)) -} - -// Errorf logs a message with the Error severity using the same syntax and options as fmt.Printf -func Errorf(format string, a ...interface{}) { - base.output(ERROR, 1, fmt.Sprintf(format, a...)) -} - -// Warningf logs a message with the Warning severity using the same syntax and options as fmt.Printf -func Warningf(format string, a ...interface{}) { - base.output(WARNING, 1, fmt.Sprintf(format, a...)) -} - -// Infof logs a message with the Info severity using the same syntax and options as fmt.Printf -func Infof(format string, a ...interface{}) { - base.output(INFO, 1, fmt.Sprintf(format, a...)) -} - -// Debugf logs a message with the Debug severity using the same syntax and options as fmt.Printf -func Debugf(format string, a ...interface{}) { - base.output(DEBUG, 1, fmt.Sprintf(format, a...)) -} diff --git a/pkg/stm/stm.go b/pkg/stm/stm.go index 58373dfa2..7d07ba9a4 100644 --- a/pkg/stm/stm.go +++ b/pkg/stm/stm.go @@ -4,11 +4,10 @@ package stm import ( "fmt" + "log" "reflect" "strconv" "strings" - - "github.com/netdata/go.d.plugin/logger" ) const ( @@ -34,7 +33,7 @@ func ToMap(s ...interface{}) map[string]int64 { func toMap(value reflect.Value, rv map[string]int64, key string, mul, div int) { if !value.IsValid() { - logger.Panicf("value is not valid key=%s", key) + log.Panicf("value is not valid key=%s", key) } if value.CanInterface() { val, ok := value.Interface().(Value) @@ -61,7 +60,7 @@ func toMap(value reflect.Value, rv map[string]int64, key string, mul, div int) { case reflect.Interface: convertInterface(value, rv, key, mul, div) default: - logger.Panic("unsupported data type: ", value.Kind()) + log.Panicf("unsupported data type: %v", value.Kind()) } } @@ -91,7 +90,7 @@ func convertStruct(value reflect.Value, rv map[string]int64, key string) { func convertMap(value reflect.Value, rv map[string]int64, key string, mul, div int) { if value.IsNil() { - logger.Panicf("value is nil key=%s", key) + log.Panicf("value is nil key=%s", key) } for _, k := range value.MapKeys() { toMap(value.MapIndex(k), rv, joinPrefix(key, k.String()), mul, div) @@ -106,7 +105,7 @@ func convertArraySlice(value reflect.Value, rv map[string]int64, key string, mul func convertBool(value reflect.Value, rv map[string]int64, key string) { if _, ok := rv[key]; ok { - logger.Panic("duplicate key: ", key) + log.Panic("duplicate key: ", key) } if value.Bool() { rv[key] = 1 @@ -117,7 +116,7 @@ func convertBool(value reflect.Value, rv map[string]int64, key string) { func convertInteger(value reflect.Value, rv map[string]int64, key string, mul, div int) { if _, ok := rv[key]; ok { - logger.Panic("duplicate key: ", key) + log.Panic("duplicate key: ", key) } intVal := value.Int() rv[key] = intVal * int64(mul) / int64(div) @@ -125,7 +124,7 @@ func convertInteger(value reflect.Value, rv map[string]int64, key string, mul, d func convertFloat(value reflect.Value, rv map[string]int64, key string, mul, div int) { if _, ok := rv[key]; ok { - logger.Panic("duplicate key: ", key) + log.Panic("duplicate key: ", key) } floatVal := value.Float() rv[key] = int64(floatVal * float64(mul) / float64(div)) @@ -155,19 +154,19 @@ func parseTag(tag string) (prefix string, mul int, div int) { case 3: div, err = strconv.Atoi(tokens[2]) if err != nil { - logger.Panic(err) + log.Panic(err) } fallthrough case 2: mul, err = strconv.Atoi(tokens[1]) if err != nil { - logger.Panic(err) + log.Panic(err) } fallthrough case 1: prefix = tokens[0] default: - logger.Panic(fmt.Errorf("invalid tag format: %s", tag)) + log.Panic(fmt.Errorf("invalid tag format: %s", tag)) } return } diff --git a/slogger/default.go b/slogger/default.go deleted file mode 100644 index afb5c4f37..000000000 --- a/slogger/default.go +++ /dev/null @@ -1,46 +0,0 @@ -// SPDX-License-Identifier: GPL-3.0-or-later - -package slogger - -import ( - "log/slog" - "os" - "strings" - - "github.com/netdata/go.d.plugin/agent/executable" - - "github.com/lmittmann/tint" -) - -var base = newBaseLogger() - -var pluginAttr = slog.String("plugin", executable.Name) - -func newBaseLogger() *Logger { - return New() -} - -func newTerminalLogger() *Logger { - return &Logger{ - sl: slog.New(tint.NewHandler(os.Stderr, &tint.Options{ - AddSource: Level.Level() == slog.LevelDebug, - Level: Level, - })), - } -} - -func newLogger() *Logger { - return &Logger{ - sl: slog.New(slog.NewTextHandler(os.Stderr, &slog.HandlerOptions{ - Level: Level, - ReplaceAttr: func(groups []string, a slog.Attr) slog.Attr { - if a.Key == slog.LevelKey { - if v, ok := a.Value.Any().(slog.Level); ok { - a.Value = slog.StringValue(strings.ToLower(v.String())) - } - } - return a - }, - })), - } -} diff --git a/slogger/slogger.go b/slogger/slogger.go deleted file mode 100644 index 3b616123e..000000000 --- a/slogger/slogger.go +++ /dev/null @@ -1,71 +0,0 @@ -// SPDX-License-Identifier: GPL-3.0-or-later - -package slogger - -import ( - "context" - "fmt" - "log/slog" - "os" - - "github.com/mattn/go-isatty" -) - -func New() *Logger { - var l *Logger - - if isatty.IsTerminal(os.Stderr.Fd()) { - l = newTerminalLogger() - } else { - l = newLogger() - } - - l = l.With(pluginAttr) - - return l -} - -type Logger struct { - sl *slog.Logger -} - -func (l *Logger) Errorf(format string, a ...any) { - l.logf(slog.LevelError, format, a...) -} - -func (l *Logger) Warningf(format string, a ...any) { - l.logf(slog.LevelWarn, format, a...) -} - -func (l *Logger) Infof(format string, a ...any) { - l.logf(slog.LevelInfo, format, a...) -} - -func (l *Logger) Debugf(format string, a ...any) { - l.logf(slog.LevelDebug, format, a...) -} - -func (l *Logger) logf(level slog.Level, format string, a ...any) { - if level < Level.Level() { - return - } - - msg := fmt.Sprintf(format, a...) - - if l.isNil() { - base.sl.Log(context.Background(), level, msg) - } else { - l.sl.Log(context.Background(), level, msg) - } -} - -func (l *Logger) With(args ...any) *Logger { - if l.isNil() { - return &Logger{sl: newBaseLogger().sl.With(args...)} - } - return &Logger{sl: l.sl.With(args...)} -} - -func (l *Logger) isNil() bool { - return l == nil || l.sl == nil -} diff --git a/slogger/slogger_test.go b/slogger/slogger_test.go deleted file mode 100644 index 4f37b7e6f..000000000 --- a/slogger/slogger_test.go +++ /dev/null @@ -1,21 +0,0 @@ -package slogger - -import ( - "testing" - - "github.com/stretchr/testify/assert" -) - -func TestNew(t *testing.T) { - tests := map[string]*Logger{ - "default logger": New(), - "nil logger": nil, - } - - for name, logger := range tests { - t.Run(name, func(t *testing.T) { - f := func() { logger.Infof("test %s", "test") } - assert.NotPanics(t, f) - }) - } -}