From 1b21f598cf2bec23798b2af691495acb255dae08 Mon Sep 17 00:00:00 2001 From: Ryan Fitzpatrick Date: Wed, 17 Aug 2022 12:49:32 -0400 Subject: [PATCH] smartagent: refactor logrus shim and support monitorID field (#1885) --- go.mod | 12 +- go.sum | 20 +- internal/receiver/smartagentreceiver/log.go | 252 ++++------- .../receiver/smartagentreceiver/log_test.go | 412 +++++++++--------- .../receiver/smartagentreceiver/receiver.go | 28 +- 5 files changed, 337 insertions(+), 387 deletions(-) diff --git a/go.mod b/go.mod index e8fe9b1262d..c1e768c5fe4 100644 --- a/go.mod +++ b/go.mod @@ -72,7 +72,7 @@ require ( github.com/openzipkin/zipkin-go v0.4.0 github.com/signalfx/defaults v1.2.2-0.20180531161417-70562fe60657 github.com/signalfx/golib/v3 v3.3.45 - github.com/signalfx/signalfx-agent v1.0.1-0.20220726135153-ffcbf412e1fa + github.com/signalfx/signalfx-agent v1.0.1-0.20220810191306-a41fb5c94d53 github.com/signalfx/splunk-otel-collector/tests v0.0.0-00010101000000-000000000000 github.com/sirupsen/logrus v1.9.0 github.com/spf13/cast v1.5.0 @@ -199,7 +199,7 @@ require ( github.com/golang/mock v1.6.0 // indirect github.com/golang/protobuf v1.5.2 // indirect github.com/golang/snappy v0.0.4 // indirect - github.com/google/cadvisor v0.44.1 // indirect + github.com/google/cadvisor v0.45.0 // indirect github.com/google/flatbuffers v2.0.0+incompatible // indirect github.com/google/gnostic v0.5.7-v3refs // indirect github.com/google/go-cmp v0.5.8 // indirect @@ -326,7 +326,7 @@ require ( github.com/open-telemetry/opentelemetry-collector-contrib/pkg/translator/signalfx v0.58.0 // indirect github.com/opencontainers/go-digest v1.0.0 // indirect github.com/opencontainers/image-spec v1.0.3-0.20211202183452-c5a74bcca799 // indirect - github.com/opencontainers/runc v1.1.2 // indirect + github.com/opencontainers/runc v1.1.3 // indirect github.com/openlyinc/pointy v1.1.2 // indirect github.com/openshift/api v3.9.0+incompatible // indirect github.com/openshift/client-go v0.0.0-20210521082421-73d9475a9142 // indirect @@ -361,10 +361,10 @@ require ( github.com/signalfx/golib v2.5.1+incompatible // indirect github.com/signalfx/ingest-protocols v0.1.13 // indirect github.com/signalfx/sapm-proto v0.11.0 // indirect - github.com/signalfx/signalfx-agent/pkg/apm v0.0.0-20201202163743-65b4fa925fc8 // indirect + github.com/signalfx/signalfx-agent/pkg/apm v0.0.0-20220810191306-a41fb5c94d53 // indirect github.com/signalfx/signalfx-go v1.23.0 // indirect github.com/sijms/go-ora/v2 v2.4.26 // indirect - github.com/snowflakedb/gosnowflake v1.6.11 // indirect + github.com/snowflakedb/gosnowflake v1.6.12 // indirect github.com/soniah/gosnmp v0.0.0-20190220004421-68e8beac0db9 // indirect github.com/spaolacci/murmur3 v1.1.0 // indirect github.com/spf13/afero v1.8.2 // indirect @@ -451,7 +451,7 @@ replace ( github.com/influxdata/telegraf => github.com/signalfx/telegraf v0.10.2-0.20210820123244-82265917ca87 github.com/prometheus/prometheus => github.com/prometheus/prometheus v0.35.1-0.20220503184552-2381d7be5731 - github.com/signalfx/signalfx-agent/pkg/apm => github.com/signalfx/signalfx-agent/pkg/apm v0.0.0-20220726135153-ffcbf412e1fa + github.com/signalfx/signalfx-agent/pkg/apm => github.com/signalfx/signalfx-agent/pkg/apm v0.0.0-20220810191306-a41fb5c94d53 github.com/soheilhy/cmux => github.com/soheilhy/cmux v0.1.5-0.20210205191134-5ec6847320e5 // required for smartagentreceiver to drop google.golang.org/grpc/examples/helloworld/helloworld test dep ) diff --git a/go.sum b/go.sum index 70872bd0583..b9d29ba8f0d 100644 --- a/go.sum +++ b/go.sum @@ -481,12 +481,10 @@ github.com/dnaeon/go-vcr v1.2.0/go.mod h1:R4UdLID7HZT3taECzJs4YgbbH6PIGXB6W/sc5O github.com/dnephin/pflag v1.0.7/go.mod h1:uxE91IoWURlOiTUIA8Mq5ZZkAv3dPUfZNaT80Zm7OQE= github.com/docker/cli v0.0.0-20191017083524-a8ff7f821017/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8= github.com/docker/distribution v2.7.1+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w= -github.com/docker/distribution v2.8.0+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w= github.com/docker/distribution v2.8.1+incompatible h1:Q50tZOPR6T/hjNsyc9g8/syEs6bk8XXApsHjKukMl68= github.com/docker/distribution v2.8.1+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w= github.com/docker/docker v1.4.2-0.20190924003213-a8608b5b67c7/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= github.com/docker/docker v20.10.11+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= -github.com/docker/docker v20.10.12+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= github.com/docker/docker v20.10.14+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= github.com/docker/docker v20.10.17+incompatible h1:JYCuMrWaVNophQTOrMMoSwudOVEfcegoZZrleKc1xwE= github.com/docker/docker v20.10.17+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= @@ -899,8 +897,8 @@ github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Z github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/btree v1.0.1 h1:gK4Kx5IaGY9CD5sPJ36FHiBJ6ZXl0kilRiiCj+jdYp4= github.com/google/btree v1.0.1/go.mod h1:xXMiIv4Fb/0kKde4SpL7qlzvu5cMJDRkFDxJfI9uaxA= -github.com/google/cadvisor v0.44.1 h1:hsAxDZOY+5xSCXH12d/G9cxYTfP+32cMT3J7aatrgDY= -github.com/google/cadvisor v0.44.1/go.mod h1:GQ9KQfz0iNHQk3D6ftzJWK4TXabfIgM10Oy3FkR+Gzg= +github.com/google/cadvisor v0.45.0 h1:bXQze1sd8srZiQwiQ19Qaq/AoMIZS8YceBXrIaEvkX0= +github.com/google/cadvisor v0.45.0/go.mod h1:vsMT3Uv2XjQ8M7WUtKARV74mU/HN64C4XtM1bJhUKcU= github.com/google/flatbuffers v1.12.0/go.mod h1:1AeVuKshWv4vARoZatz6mlQ0JxURH0Kv5+zNeJKJCa8= github.com/google/flatbuffers v2.0.0+incompatible h1:dicJ2oXwypfwUGnB2/TYWYEKiuk9eYQlQO/AnOHl5mI= github.com/google/flatbuffers v2.0.0+incompatible/go.mod h1:1AeVuKshWv4vARoZatz6mlQ0JxURH0Kv5+zNeJKJCa8= @@ -1562,7 +1560,7 @@ github.com/onsi/gomega v1.10.4/go.mod h1:g/HbgYopi++010VEqkFgJHKC09uJiW9UkXvMUuK github.com/onsi/gomega v1.13.0/go.mod h1:lRk9szgn8TxENtWd0Tp4c3wjlRfMTMH27I+3Je41yGY= github.com/onsi/gomega v1.15.0/go.mod h1:cIuvLEne0aoVhAgh/O6ac0Op8WWw9H6eYCriF+tEHG0= github.com/onsi/gomega v1.16.0/go.mod h1:HnhC7FXeEQY45zxNK3PPoIUhzk/80Xly9PcubAlGdZY= -github.com/onsi/gomega v1.19.0 h1:4ieX6qQjPP/BfC3mpsAtIGGlxTWPeA3Inl/7DtXw1tw= +github.com/onsi/gomega v1.20.0 h1:8W0cWlwFkflGPLltQvLRB7ZVD5HuP6ng320w2IS245Q= github.com/op/go-logging v0.0.0-20160315200505-970db520ece7/go.mod h1:HzydrMdWErDVzsI23lYNej1Htcns9BCg93Dk0bBINWk= github.com/open-telemetry/opentelemetry-collector-contrib/exporter/fileexporter v0.58.0 h1:5xWQzReUN5+VIOYlMYJviLBFgSpLO2oMf2Wi/agRTws= github.com/open-telemetry/opentelemetry-collector-contrib/exporter/fileexporter v0.58.0/go.mod h1:ii4xZNQY+JfNnypUudyOreppe52bgrAId/ogGCU+zPk= @@ -1984,10 +1982,10 @@ github.com/signalfx/sapm-proto v0.4.0/go.mod h1:x3gtwJ1GRejtkghB4nYpwixh2zqJrLbP github.com/signalfx/sapm-proto v0.7.2/go.mod h1:HLufOh6Gd2altGxbeve+s6hh0EWCWoOM7MmuYuvs5PI= github.com/signalfx/sapm-proto v0.11.0 h1:TIbJNKJ/J00yyASLlbF1DlVZv+X1bY4/BiaYSU7wfxc= github.com/signalfx/sapm-proto v0.11.0/go.mod h1:bYZgdl2ZoMriE4arrRThWH8M6DkVbN7+xsxg4v2HP8Q= -github.com/signalfx/signalfx-agent v1.0.1-0.20220726135153-ffcbf412e1fa h1:6WODJoVNoX1O8o576bbkm5RkLpFGZ6zMZNsqc/c30jQ= -github.com/signalfx/signalfx-agent v1.0.1-0.20220726135153-ffcbf412e1fa/go.mod h1:UCKTG/dGaroeHSL2au72xzM4++/S6uqI8AVZSbp2fEM= -github.com/signalfx/signalfx-agent/pkg/apm v0.0.0-20220726135153-ffcbf412e1fa h1:SETSBNCY4dBr1qQHz+EbI5XpTih6HA2nO7AFAjMUEwc= -github.com/signalfx/signalfx-agent/pkg/apm v0.0.0-20220726135153-ffcbf412e1fa/go.mod h1:II1zi/nz5Af7ep689U012sr0HD9vRShFKaAjw39D+7g= +github.com/signalfx/signalfx-agent v1.0.1-0.20220810191306-a41fb5c94d53 h1:A+pCo4BzlS0xBq2tc7nGGiAqyjq84h6896thPaL2Uqs= +github.com/signalfx/signalfx-agent v1.0.1-0.20220810191306-a41fb5c94d53/go.mod h1:NEJ85TpSB1UyFNbWqfBq7zh1HnbNTK/i9cgXussYMXE= +github.com/signalfx/signalfx-agent/pkg/apm v0.0.0-20220810191306-a41fb5c94d53 h1:mCPfYCY4wOBzbFAWIK0WmjUO8eKJZHzIP0EquzbZJ0o= +github.com/signalfx/signalfx-agent/pkg/apm v0.0.0-20220810191306-a41fb5c94d53/go.mod h1:II1zi/nz5Af7ep689U012sr0HD9vRShFKaAjw39D+7g= github.com/signalfx/signalfx-go v1.23.0 h1:gWLOw2TuYckzzqQ6/aMWrKypNLIzx5s2XwkINw5m+Ck= github.com/signalfx/signalfx-go v1.23.0/go.mod h1:YhPTMdQJDfphcRBdk+9acbbAw1gYY7z5BIHUWzLmGlA= github.com/signalfx/telegraf v0.10.2-0.20210820123244-82265917ca87 h1:ayeUHxiUjcxzuEzjWVkXJxf42UYNw8UKmYmIQu9mAqo= @@ -2009,8 +2007,8 @@ github.com/smartystreets/assertions v1.2.0 h1:42S6lae5dvLc7BrLu/0ugRtcFVjoJNMC/N github.com/smartystreets/goconvey v1.6.4-0.20190306220146-200a235640ff/go.mod h1:KSQcGKpxUMHk3nbYzs/tIBAM2iDooCn0BmttHOJEbLs= github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= github.com/smartystreets/goconvey v1.7.2 h1:9RBaZCeXEQ3UselpuwUQHltGVXvdwm6cv1hgR6gDIPg= -github.com/snowflakedb/gosnowflake v1.6.11 h1:iA8BO+CstFArdfMw3OoXZnn/F8oFluX64KOGg1wVQtU= -github.com/snowflakedb/gosnowflake v1.6.11/go.mod h1:BoZ0gnLERaUEiziH4Dumim10LN8cvoaCKovsAfhxzrE= +github.com/snowflakedb/gosnowflake v1.6.12 h1:6K0IB+f7vTf/IWYLsKRHkRWqXXQxHsaZshOICOncUhc= +github.com/snowflakedb/gosnowflake v1.6.12/go.mod h1:BoZ0gnLERaUEiziH4Dumim10LN8cvoaCKovsAfhxzrE= github.com/softlayer/softlayer-go v0.0.0-20180806151055-260589d94c7d h1:bVQRCxQvfjNUeRqaY/uT0tFuvuFY0ulgnczuR684Xic= github.com/soheilhy/cmux v0.1.5-0.20210205191134-5ec6847320e5/go.mod h1:T7TcVDs9LWfQgPlPsdngu6I6QIoyIFZDDC6sNE1GqG0= github.com/sonatard/noctx v0.0.1/go.mod h1:9D2D/EoULe8Yy2joDHJj7bv3sZoq9AaSb8B4lqBjiZI= diff --git a/internal/receiver/smartagentreceiver/log.go b/internal/receiver/smartagentreceiver/log.go index d20c3d3e411..86a00aadc5f 100644 --- a/internal/receiver/smartagentreceiver/log.go +++ b/internal/receiver/smartagentreceiver/log.go @@ -16,8 +16,7 @@ package smartagentreceiver import ( "fmt" - "io/ioutil" - "log" + "io" "strings" "sync" @@ -28,25 +27,27 @@ import ( var ( logrusToZapLevel = map[logrus.Level]zapcore.Level{ - logrus.FatalLevel: zapcore.FatalLevel, - logrus.PanicLevel: zapcore.PanicLevel, + logrus.DebugLevel: zapcore.DebugLevel, logrus.ErrorLevel: zapcore.ErrorLevel, - logrus.WarnLevel: zapcore.WarnLevel, + logrus.FatalLevel: zapcore.FatalLevel, logrus.InfoLevel: zapcore.InfoLevel, - logrus.DebugLevel: zapcore.DebugLevel, + logrus.PanicLevel: zapcore.PanicLevel, // No zap level equivalent to trace. Mapping trace to debug. logrus.TraceLevel: zapcore.DebugLevel, + logrus.WarnLevel: zapcore.WarnLevel, } zapToLogrusLevel = map[zapcore.Level]logrus.Level{ + zapcore.DebugLevel: logrus.DebugLevel, + zapcore.ErrorLevel: logrus.ErrorLevel, zapcore.FatalLevel: logrus.FatalLevel, + zapcore.InfoLevel: logrus.InfoLevel, zapcore.PanicLevel: logrus.PanicLevel, - zapcore.ErrorLevel: logrus.ErrorLevel, zapcore.WarnLevel: logrus.WarnLevel, - zapcore.InfoLevel: logrus.InfoLevel, - zapcore.DebugLevel: logrus.DebugLevel, } + // zapLevels must be in lowest to highest sensitivity order + // given current getLevelFromCore() implementation zapLevels = []zapcore.Level{ zapcore.DebugLevel, zapcore.InfoLevel, @@ -60,198 +61,137 @@ var ( var _ logrus.Hook = (*logrusToZap)(nil) -// logrusToZap stores a mapping of logrus to zap loggers in loggerMap and hooks to the logrus loggers. +// logrusToZap provides a logrus.Hook ~singleton that redirects logrus.Logger.Log() calls +// to the desired registered zap.Logger routed by agent-set "monitorType" and "monitorID" field values. type logrusToZap struct { - loggerMap map[logrusKey][]*zap.Logger + // ~sync.Map(map[monitorLogrus]*zap.Logger) + loggerMap *sync.Map noopLogger *logrus.Logger defaultLogger *zap.Logger - mu sync.Mutex -} - -type noopFormatter struct{} - -type logrusKey struct { - *logrus.Logger - monitorType string -} - -func (l *noopFormatter) Format(*logrus.Entry) ([]byte, error) { - return nil, nil } -func (l *logrusKey) reportCaller() { - if !l.ReportCaller { - l.SetReportCaller(true) - } -} - -func (l *logrusKey) addHookUnique(newHook logrus.Hook) { - for _, hooks := range l.Hooks { - for _, hook := range hooks { - if hook == newHook { - return - } - } - } - l.AddHook(newHook) -} - -func (l *logrusKey) removeHook(remove logrus.Hook, levels ...logrus.Level) { - if levels == nil { - levels = logrus.AllLevels - } - - keep := make(logrus.LevelHooks) - for _, level := range levels { - keep[level] = make([]logrus.Hook, 0) - for _, hook := range l.Hooks[level] { - if hook != remove { - keep[level] = append(keep[level], hook) - } - } - } - - l.ReplaceHooks(keep) -} - -func loggerProvider(core zapcore.Core) func() *zap.Logger { - return func() *zap.Logger { - logger, err := newDefaultLoggerCfg(core).Build() - if err != nil { - log.Fatalf("Cannot initialize the default zap logger: %v", err) - } - return logger - } -} - -func newLogrusToZap(loggerProvider func() *zap.Logger) *logrusToZap { - logger := loggerProvider() - defer logger.Sync() - +func newLogrusToZap(defaultLogger *zap.Logger) *logrusToZap { return &logrusToZap{ - loggerMap: make(map[logrusKey][]*zap.Logger), - mu: sync.Mutex{}, - defaultLogger: logger, + loggerMap: &sync.Map{}, + defaultLogger: defaultLogger, noopLogger: &logrus.Logger{ - Out: ioutil.Discard, + Out: io.Discard, Formatter: new(noopFormatter), Hooks: make(logrus.LevelHooks), }, } } -func newDefaultLoggerCfg(core zapcore.Core) *zap.Config { - defaultLoggerCfg := zap.NewProductionConfig() - defaultLoggerCfg.Encoding = "console" - defaultLoggerCfg.EncoderConfig.EncodeTime = zapcore.ISO8601TimeEncoder - defaultLoggerCfg.EncoderConfig.EncodeLevel = zapcore.CapitalLevelEncoder - defaultLoggerCfg.InitialFields = map[string]any{ - "component_kind": "receiver", - "component_type": "smartagent", - } - defaultLoggerCfg.Level.SetLevel(getLevelFromCore(core)) - return &defaultLoggerCfg -} - -func getLevelFromCore(core zapcore.Core) zapcore.Level { - for i := range zapLevels { - if core.Enabled(zapLevels[i]) { - return zapLevels[i] - } - } - return zapcore.InfoLevel -} - -func (l *logrusToZap) redirect(src logrusKey, dst *zap.Logger) { - l.mu.Lock() - defer l.mu.Unlock() - - if _, ok := l.loggerMap[src]; !ok { - l.loggerMap[src] = make([]*zap.Logger, 0) - } - +// redirect prepares the src monitorLogrus to reflect the dst zap.Logger's settings +// and registers it for rerouting in the logrus.Hook's Fire() +func (l *logrusToZap) redirect(src monitorLogrus, dst *zap.Logger) { if desiredLogrusLevel, ok := zapToLogrusLevel[getLevelFromCore(dst.Core())]; ok { src.Logger.SetLevel(desiredLogrusLevel) } - src.reportCaller() - src.addHookUnique(l) + src.initialize() + src.addLogrusToZapHook(l) - l.loggerMap[src] = append(l.loggerMap[src], dst) + // we only register for the first monitorLogrus instance + // since there should only be one logger per component + _, _ = l.loggerMap.LoadOrStore(src, dst) } -func (l *logrusToZap) unRedirect(src logrusKey, dst *zap.Logger) { - l.mu.Lock() - defer l.mu.Unlock() - - vLen := len(l.loggerMap[src]) - if vLen == 0 || vLen == 1 { - src.removeHook(l) - delete(l.loggerMap, src) - return - } - - keep := make([]*zap.Logger, 0) - for _, logger := range l.loggerMap[src] { - if logger != dst { - keep = append(keep, logger) +func (l *logrusToZap) getZapLogger(src monitorLogrus) *zap.Logger { + logger := l.defaultLogger + if l.loggerMap != nil { + if z, ok := l.loggerMap.Load(src); ok { + logger = z.(*zap.Logger) } } - l.loggerMap[src] = keep + return logger } -func (l *logrusToZap) loggerMapValue0(src logrusKey) (*zap.Logger, bool) { - l.mu.Lock() - defer l.mu.Unlock() - - if l.loggerMap == nil { - return nil, false - } - - loggers, inMap := l.loggerMap[src] - - if len(loggers) > 0 { - return loggers[0], inMap - } - - return nil, inMap -} - -// Levels is a logrus.Hook implementation that returns all logrus logging levels. +// Levels is a logrus.Hook method that returns all logrus logging levels so +// that its Fire() is executed for all logrus logging activity. func (l *logrusToZap) Levels() []logrus.Level { return logrus.AllLevels } -// Fire is a logrus.Hook implementation that is called when logging on the logging levels returned by Levels. +// Fire is a logrus.Hook method that is called when logging on the logging levels returned by Levels. // A zap log entry is created from the supplied logrus entry and written out. -func (l *logrusToZap) Fire(e *logrus.Entry) error { +func (l *logrusToZap) Fire(entry *logrus.Entry) error { var monitorType string + var monitorID string fields := make([]zapcore.Field, 0) - // Creating zap entry fields from logrus entry fields. - for k, v := range e.Data { + for k, v := range entry.Data { if k == "monitorType" { monitorType = strings.TrimSpace(fmt.Sprintf("%v", v)) + } else if k == "monitorID" { + monitorID = strings.TrimSpace(fmt.Sprintf("%v", v)) } fields = append(fields, zap.Any(k, v)) } - logger, _ := l.loggerMapValue0(logrusKey{e.Logger, monitorType}) - if logger == nil { - logger = l.defaultLogger - } + zapLogger := l.getZapLogger(monitorLogrus{ + Logger: entry.Logger, + monitorType: monitorType, + monitorID: monitorID, + }) - if ce := logger.Check(logrusToZapLevel[e.Level], e.Message); ce != nil { - ce.Time = e.Time + if ce := zapLogger.Check(logrusToZapLevel[entry.Level], entry.Message); ce != nil { + ce.Time = entry.Time + // clear stack so that it's not for parent Check() ce.Stack = "" - if e.Caller != nil { - ce.Caller = zapcore.NewEntryCaller(e.Caller.PC, e.Caller.File, e.Caller.Line, true) + if entry.Caller != nil { + ce.Caller = zapcore.NewEntryCaller(entry.Caller.PC, entry.Caller.File, entry.Caller.Line, true) } ce.Write(fields...) } - e.Logger = l.noopLogger + // we must set the Entry's logger to noop to prevent writing to the + // StandardLogger during further processing. This will only be for the lifetime + // of the hook evaluation chain + entry.Logger = l.noopLogger return nil } + +type monitorLogrus struct { + // in practice this will always be the logrus.StandardLogger + // but embed it to support potential others + *logrus.Logger + // the monitor-logged "monitorType" field + monitorType string + // the monitor-logged "monitorID" field + monitorID string +} + +func (ml *monitorLogrus) initialize() { + if !ml.ReportCaller { + ml.SetReportCaller(true) + } +} + +func (ml *monitorLogrus) addLogrusToZapHook(l *logrusToZap) { + for _, hooks := range ml.Hooks { + for _, existing := range hooks { + if existing == l { + return + } + } + } + ml.AddHook(l) +} + +func getLevelFromCore(core zapcore.Core) zapcore.Level { + for _, level := range zapLevels { + if core.Enabled(level) { + return level + } + } + return zapcore.InfoLevel +} + +type noopFormatter struct{} + +func (n *noopFormatter) Format(*logrus.Entry) ([]byte, error) { + return nil, nil +} diff --git a/internal/receiver/smartagentreceiver/log_test.go b/internal/receiver/smartagentreceiver/log_test.go index 68ed0db1a1e..deb7a2d84fe 100644 --- a/internal/receiver/smartagentreceiver/log_test.go +++ b/internal/receiver/smartagentreceiver/log_test.go @@ -26,224 +26,50 @@ import ( "go.uber.org/zap/zaptest/observer" ) -func logAt(entry *logrus.Entry, level logrus.Level, msg string) { - entry.Logger.Level = level - switch level { - case logrus.FatalLevel: - entry.Fatal(msg) - case logrus.PanicLevel: - entry.Panic(msg) - case logrus.ErrorLevel: - entry.Error(msg) - case logrus.WarnLevel: - entry.Warn(msg) - case logrus.InfoLevel: - entry.Info(msg) - case logrus.DebugLevel: - entry.Debug(msg) - case logrus.TraceLevel: - entry.Trace(msg) - } -} - -func TestRedirectMonitorLogs(t *testing.T) { - for logrusLevel, zapLevel := range logrusToZapLevel { - //TODO: handle fatal and panic levels - if logrusLevel == logrus.FatalLevel || logrusLevel == logrus.PanicLevel { - continue - } - - // Simulating the creation of logrus entry/loggers in monitors (monitor1, monitor2). - monitor1LogrusLogger := logrus.WithFields(logrus.Fields{"monitorType": "monitor1"}) - monitor2LogrusLogger := logrus.WithFields(logrus.Fields{"monitorType": "monitor2"}) - - // Case where logs do not have "monitorType" field - monitor3LogrusLogger := logrus.WithFields(logrus.Fields{}) - - // Checking that the logrus standard logger is the logger in the monitors. - require.Same(t, logrus.StandardLogger(), monitor1LogrusLogger.Logger, "Expected the standard logrus logger") - require.Same(t, logrus.StandardLogger(), monitor2LogrusLogger.Logger, "Expected the standard logrus logger") - require.Same(t, logrus.StandardLogger(), monitor3LogrusLogger.Logger, "Expected the standard logrus logger") - - // Simulating the creation of logrus keys for the monitors where: - // 1. the monitor types (i.e. monitor1, monitor2) are known. - // 2. the logger is assumed to be the standard logrus logger. - monitor1LogrusKey := logrusKey{Logger: logrus.StandardLogger(), monitorType: "monitor1"} - monitor2LogrusKey := logrusKey{Logger: logrus.StandardLogger(), monitorType: "monitor2"} - monitor3LogrusKey := logrusKey{Logger: logrus.StandardLogger(), monitorType: "monitor3"} - - monitor1ZapLogger, monitor1ZapLogs := newObservedLogs(zapLevel) - monitor2ZapLogger, monitor2ZapLogs := newObservedLogs(zapLevel) - monitor3ZapLogger, monitor3ZapLogs := newObservedLogs(zapLevel) - defaultZapLogger, defaultLogs := newObservedLogs(zapLevel) - - // Logrus to zap redirections. - logToZap := newLogrusToZap(func() *zap.Logger { return defaultZapLogger }) - logToZap.redirect(monitor1LogrusKey, monitor1ZapLogger) - logToZap.redirect(monitor2LogrusKey, monitor2ZapLogger) - logToZap.redirect(monitor3LogrusKey, monitor3ZapLogger) - - expectedLogrusLevel := logrusLevel - if logrusLevel == logrus.TraceLevel { - expectedLogrusLevel = logrus.DebugLevel - } - require.Equal(t, expectedLogrusLevel, monitor1LogrusKey.Level) - require.Equal(t, expectedLogrusLevel, monitor2LogrusKey.Level) - require.Equal(t, expectedLogrusLevel, monitor3LogrusKey.Level) - - logMsg1 := "a log msg1" - logMsg2 := "a log msg2" - logMsg3 := "a log msg3" - - // Logrus logging. - logAt(monitor1LogrusLogger, logrusLevel, logMsg1) - logAt(monitor2LogrusLogger, logrusLevel, logMsg2) - logAt(monitor3LogrusLogger, logrusLevel, logMsg3) - - // Checking zap logs for redirected logrus logs. - require.Equal(t, 1, monitor1ZapLogs.Len(), fmt.Sprintf("Expected 1 log message, got %d", monitor1ZapLogs.Len())) - require.Equal(t, logMsg1, monitor1ZapLogs.All()[0].Message, fmt.Sprintf("Expected message '%s', got '%s'", logMsg1, monitor1ZapLogs.All()[0].Message)) - - require.Equal(t, 1, monitor2ZapLogs.Len(), fmt.Sprintf("Expected 1 log message, got %d", monitor2ZapLogs.Len())) - require.Equal(t, logMsg2, monitor2ZapLogs.All()[0].Message, fmt.Sprintf("Expected message '%s', got '%s'", logMsg2, monitor2ZapLogs.All()[0].Message)) - - require.Equal(t, 0, monitor3ZapLogs.Len(), fmt.Sprintf("Expected 0 log message, got %d", monitor3ZapLogs.Len())) - require.Equal(t, logMsg3, defaultLogs.All()[0].Message, fmt.Sprintf("Expected message '%s', got '%s'", logMsg3, defaultLogs.All()[0].Message)) - - require.Equal(t, 1, defaultLogs.Len(), fmt.Sprintf("Expected 1 log message, got %d", defaultLogs.Len())) - - logToZap.unRedirect(monitor1LogrusKey, monitor1ZapLogger) - logToZap.unRedirect(monitor2LogrusKey, monitor2ZapLogger) - logToZap.unRedirect(monitor3LogrusKey, monitor3ZapLogger) - } -} - -func TestRedirectMonitorLogsWithNilLoggerMap(t *testing.T) { - for logrusLevel, zapLevel := range logrusToZapLevel { - //TODO: handle fatal and panic levels - if logrusLevel == logrus.FatalLevel || logrusLevel == logrus.PanicLevel { - continue - } - - // Simulating the creation of logrus entry/logger in monitor1. - monitor1LogrusLogger := logrus.WithFields(logrus.Fields{"monitorType": "monitor1"}) - - // Simulating the creation of logrus key for monitor1 in the smart agent receiver where: - // 1. the monitor types (i.e. monitor1) are known. - // 2. the logger is assumed to be the standard logrus logger. - monitor1LogrusKey := logrusKey{Logger: logrus.StandardLogger(), monitorType: "monitor1"} - - monitor1ZapLogger, monitor1ZapLogs := newObservedLogs(zapLevel) - defaultZapLogger, defaultZapLogs := newObservedLogs(zapLevel) - - // Logrus to zap redirection. - logToZap := newLogrusToZap(func() *zap.Logger { return defaultZapLogger }) - logToZap.redirect(monitor1LogrusKey, monitor1ZapLogger) - - require.Len(t, logToZap.loggerMap, 1) - require.Len(t, logToZap.loggerMap[monitor1LogrusKey], 1) - require.Equal(t, monitor1ZapLogger, logToZap.loggerMap[monitor1LogrusKey][0]) - - // Setting loggerMap to nil - logToZap.loggerMap = nil - - logMsg := "a log msg" - - // Logrus logging. - logAt(monitor1LogrusLogger, logrusLevel, logMsg) - - // Logrus logs should not be redirected to monitor1ZapLogger. - assert.Equal(t, 0, monitor1ZapLogs.Len(), fmt.Sprintf("Expected 0 log message, got %d", monitor1ZapLogs.Len())) - - // Logrus logs should be redirected to defaultZapLogger when loggerMap is nil. - assert.Equal(t, 1, defaultZapLogs.Len(), fmt.Sprintf("Expected 1 log message, got %d", monitor1ZapLogs.Len())) - require.Equal(t, logMsg, defaultZapLogs.All()[0].Message, fmt.Sprintf("Expected message '%s', got '%s'", logMsg, defaultZapLogs.All()[0].Message)) - - logToZap.unRedirect(monitor1LogrusKey, monitor1ZapLogger) - } -} - -func TestRedirectSameMonitorManyInstancesLogs(t *testing.T) { - for logrusLevel, zapLevel := range logrusToZapLevel { - //TODO: handle fatal and panic levels - if logrusLevel == logrus.FatalLevel || logrusLevel == logrus.PanicLevel { - continue - } - - // Simulating the creation of logrus entry/loggers in instances of a monitor (monitor1). - instance1LogrusLogger := logrus.WithFields(logrus.Fields{"monitorType": "monitor1"}) - instance2LogrusLogger := logrus.WithFields(logrus.Fields{"monitorType": "monitor1"}) - - // Simulating the creation of logrus keys for the instances of monitor1 in the smart agent receiver where: - // 1. the monitor type (i.e. monitor1) is known. - // 2. the logger is assumed to be the standard logrus logger. - instance1LogrusKey := logrusKey{Logger: logrus.StandardLogger(), monitorType: "monitor1"} - instance2LogrusKey := logrusKey{Logger: logrus.StandardLogger(), monitorType: "monitor1"} - - // Checking that the logrus standard logger is the logger for both monitor1 instances. - require.Same(t, logrus.StandardLogger(), instance1LogrusLogger.Logger, "Expected the standard logrus logger") - require.Same(t, logrus.StandardLogger(), instance2LogrusLogger.Logger, "Expected the standard logrus logger") - // Checking that the logrus keys are equal. - require.Equal(t, instance1LogrusKey, instance2LogrusKey, "Expected the standard logrus logger") - - instance1ZapLogger, instance1ZapLogs := newObservedLogs(zapLevel) - instance2ZapLogger, instance2ZapLogs := newObservedLogs(zapLevel) - - // Simulating logrus to zap redirections in in the smart agent receiver. - logToZap := newLogrusToZap(zap.NewNop) - logToZap.redirect(instance1LogrusKey, instance1ZapLogger) - logToZap.redirect(instance2LogrusKey, instance2ZapLogger) - - logMsg1 := "a log msg1" - logMsg2 := "a log msg2" - - // Simulating logging messages in the instances of monitor1. - logAt(instance1LogrusLogger, logrusLevel, logMsg1) - logAt(instance2LogrusLogger, logrusLevel, logMsg2) - - // Asserting that messages logged by all instances get logged by the zap logger of the first instance. - require.Equal(t, 2, instance1ZapLogs.Len(), fmt.Sprintf("Expected 2 log message, got %d", instance1ZapLogs.Len())) - // Asserting that no messages get logged by the zap logger of the other instances. - require.Equal(t, 0, instance2ZapLogs.Len(), fmt.Sprintf("Expected 0 log message, got %d", instance2ZapLogs.Len())) - - // Asserting messages logged by the zap logger of the first instance. - require.Equal(t, logMsg1, instance1ZapLogs.All()[0].Message, fmt.Sprintf("Expected message '%s', got '%s'", logMsg1, instance1ZapLogs.All()[0].Message)) - require.Equal(t, logMsg2, instance1ZapLogs.All()[1].Message, fmt.Sprintf("Expected message '%s', got '%s'", logMsg2, instance1ZapLogs.All()[1].Message)) - - logToZap.unRedirect(instance1LogrusKey, instance1ZapLogger) - logToZap.unRedirect(instance2LogrusKey, instance2ZapLogger) - } +// This test confirms that the major assumption of the logrusToZap +// mechanism is accurate for the current logrus dep version. +// If it fails a workaround will need to be determined before adopting the +// breaking version. +func TestAllStdBasedLogrusLoggersWrapStdInstance(t *testing.T) { + std := logrus.StandardLogger() + one := logrus.WithField("one", "one") + two := one.WithField("one", "one") + three := two.WithField("one", "one") + require.Same(t, std, one.Logger) + require.Same(t, std, two.Logger) + require.Same(t, std, three.Logger) } func TestLevelsMapShouldIncludeAllLogrusLevels(t *testing.T) { for _, level := range logrus.AllLevels { _, ok := logrusToZapLevel[level] - require.True(t, ok, fmt.Sprintf("Expected log level %s not found", level.String())) + require.True(t, ok, fmt.Sprintf("Expected log level %q not found", level.String())) } } func TestLevelsShouldReturnAllLogrusLevels(t *testing.T) { - hook := newLogrusToZap(zap.NewNop) + hook := newLogrusToZap(zap.NewNop()) levels := hook.Levels() for i := range logrus.AllLevels { require.Equal(t, logrus.AllLevels[i], levels[i], fmt.Sprintf("Expected log level %s not found", logrus.AllLevels[i].String())) } } -func TestRedirectShouldSetLogrusKeyLoggerReportCallerTrue(t *testing.T) { - src := logrusKey{Logger: logrus.New(), monitorType: "monitor1"} +func TestRedirectShouldSetMonitorLogrusLoggerReportCallerTrue(t *testing.T) { + defer unredirect() + src := monitorLogrus{Logger: logrus.New(), monitorType: "monitor1"} dst := zap.NewNop() - logToZap := newLogrusToZap(zap.NewNop) + logToZap := newLogrusToZap(zap.NewNop()) logToZap.redirect(src, dst) require.True(t, src.ReportCaller, "Expected the logrus key logger to report caller") - logToZap.unRedirect(src, dst) } -func TestRedirectShouldUniquelyAddHooksToLogrusKeyLogger(t *testing.T) { - src := logrusKey{Logger: logrus.New(), monitorType: "monitor1"} +func TestRedirectShouldUniquelyAddHooksToMonitorLogrusLogger(t *testing.T) { + unredirect() + src := monitorLogrus{Logger: logrus.New(), monitorType: "monitor1"} require.Equal(t, 0, len(src.Hooks), fmt.Sprintf("Expected 0 hooks, got %d", len(src.Hooks))) - logToZap := newLogrusToZap(zap.NewNop) + logToZap := newLogrusToZap(zap.NewNop()) // These multiple redirect calls should add hook once to log levels. logToZap.redirect(src, zap.NewNop()) logToZap.redirect(src, zap.NewNop()) @@ -380,7 +206,7 @@ func TestLogrusToZapLevel(t *testing.T) { logger, err := test.zapConfig.Build() require.NoError(t, err) - logToZap := newLogrusToZap(loggerProvider(logger.Core())) + logToZap := newLogrusToZap(logger) defaultLoggerCore := logToZap.defaultLogger.Core() for _, level := range test.levelsEnabled { @@ -394,7 +220,195 @@ func TestLogrusToZapLevel(t *testing.T) { } } -func newObservedLogs(level zapcore.Level) (*zap.Logger, *observer.ObservedLogs) { +func TestRedirectMonitorLogs(t *testing.T) { + defer setup()() + for _, lLevel := range logrus.AllLevels { + logrusLevel := lLevel + zapLevel := logrusToZapLevel[logrusLevel] + t.Run(logrusLevel.String(), func(t *testing.T) { + defer unredirect() + defaultZap, defaultLogs := newObservedZap(zapLevel) + logToZap := newLogrusToZap(defaultZap) + + expectedLogrusLevel := logrusLevel + if logrusLevel == logrus.TraceLevel { + expectedLogrusLevel = logrus.DebugLevel + } + + logrus1 := logrus.WithField("monitorType", "monitor1").WithField("monitorID", "id1") + monitorLogrus1 := monitorLogrus{Logger: logrus.StandardLogger(), monitorType: "monitor1", monitorID: "id1"} + zap1, zap1Logs := newObservedZap(zapLevel) + logToZap.redirect(monitorLogrus1, zap1) + require.Equal(t, expectedLogrusLevel, monitorLogrus1.Level) + require.Same(t, zap1, logToZap.getZapLogger(monitorLogrus1)) + + logrus2 := logrus.WithField("monitorType", "monitor2").WithField("monitorID", "id2") + monitorLogrus2 := monitorLogrus{Logger: logrus.StandardLogger(), monitorType: "monitor2", monitorID: "id2"} + zap2, zap2Logs := newObservedZap(zapLevel) + logToZap.redirect(monitorLogrus2, zap2) + require.Equal(t, expectedLogrusLevel, monitorLogrus2.Level) + require.Same(t, zap2, logToZap.getZapLogger(monitorLogrus2)) + + logrus3 := logrus.WithField("irrelevant", "irrelevant") + monitorLogrus3 := monitorLogrus{Logger: logrus.StandardLogger(), monitorType: "without-match"} + zap3, zap3Logs := newObservedZap(zapLevel) + logToZap.redirect(monitorLogrus3, zap3) + require.Equal(t, expectedLogrusLevel, monitorLogrus3.Level) + require.Same(t, zap3, logToZap.getZapLogger(monitorLogrus3)) + + asserter := getAsserter(logrusLevel) + msg1 := fmt.Sprintf("%v - a log msg", zapLevel.String()) + asserter(t, func() { logAt(logrus1, logrusLevel, msg1) }) + msg2 := fmt.Sprintf("%v - another log msg", zapLevel.String()) + asserter(t, func() { logAt(logrus2, logrusLevel, msg2) }) + msg3 := fmt.Sprintf("%v - yet another log msg", zapLevel.String()) + asserter(t, func() { logAt(logrus3, logrusLevel, msg3) }) + + zap1.Sync() + require.Equal(t, 1, zap1Logs.Len()) + require.Equal(t, msg1, zap1Logs.All()[0].Message) + + zap2.Sync() + require.Equal(t, 1, zap2Logs.Len()) + require.Equal(t, msg2, zap2Logs.All()[0].Message) + + zap3.Sync() + defaultZap.Sync() + require.Equal(t, 0, zap3Logs.Len()) + require.Equal(t, 1, defaultLogs.Len()) + require.Equal(t, msg3, defaultLogs.All()[0].Message) + }) + } +} + +func TestRedirectMonitorLogsWithMissingMapEntryUsesDefaultLogger(t *testing.T) { + defer setup()() + for _, lLevel := range logrus.AllLevels { + logrusLevel := lLevel + zapLevel := logrusToZapLevel[logrusLevel] + t.Run(logrusLevel.String(), func(t *testing.T) { + defer unredirect() + defaultZap, defaultZapLogs := newObservedZap(zapLevel) + logToZap := newLogrusToZap(defaultZap) + + logrus1 := logrus.WithField("monitorType", "monitor1").WithField("monitorID", "id1") + monitorLogrus1 := monitorLogrus{Logger: logrus.StandardLogger(), monitorType: "monitor1", monitorID: "id1"} + zap1, zap1Logs := newObservedZap(zapLevel) + logToZap.redirect(monitorLogrus1, zap1) + require.Same(t, zap1, logToZap.getZapLogger(monitorLogrus1)) + + // remove zap1 from map + z, ok := logToZap.loggerMap.LoadAndDelete(monitorLogrus1) + require.True(t, ok) + require.Equal(t, zap1, z.(*zap.Logger)) + require.Same(t, defaultZap, logToZap.getZapLogger(monitorLogrus1)) + + asserter := getAsserter(logrusLevel) + msg1 := fmt.Sprintf("%v - a log msg", zapLevel.String()) + asserter(t, func() { logAt(logrus1, logrusLevel, msg1) }) + + zap1.Sync() + defaultZap.Sync() + assert.Equal(t, 0, zap1Logs.Len()) + require.Equal(t, 1, defaultZapLogs.Len()) + require.Equal(t, msg1, defaultZapLogs.All()[0].Message) + }) + } +} + +func TestRedirectSameMonitorManyInstancesLogs(t *testing.T) { + defer setup()() + for _, lLevel := range logrus.AllLevels { + logrusLevel := lLevel + zapLevel := logrusToZapLevel[logrusLevel] + t.Run(logrusLevel.String(), func(t *testing.T) { + defer unredirect() + logToZap := newLogrusToZap(zap.NewNop()) + + logrus1 := logrus.WithField("monitorType", "monitor1").WithField("monitorID", "id1") + logrus2 := logrus.WithField("monitorType", "monitor1").WithField("monitorID", "id2") + monitorLogrus1 := monitorLogrus{Logger: logrus.StandardLogger(), monitorType: "monitor1", monitorID: "id1"} + monitorLogrus2 := monitorLogrus{Logger: logrus.StandardLogger(), monitorType: "monitor1", monitorID: "id2"} + + zap1, zap1Logs := newObservedZap(zapLevel) + logToZap.redirect(monitorLogrus1, zap1) + + zap2, zap2Logs := newObservedZap(zapLevel) + logToZap.redirect(monitorLogrus2, zap2) + + asserter := getAsserter(logrusLevel) + msg1 := fmt.Sprintf("%v - a log msg", zapLevel.String()) + msg2 := fmt.Sprintf("%v - another log msg", zapLevel.String()) + asserter(t, func() { logAt(logrus1, logrusLevel, msg1) }) + asserter(t, func() { logAt(logrus2, logrusLevel, msg2) }) + + zap1.Sync() + zap2.Sync() + require.Equal(t, 1, zap1Logs.Len()) + require.Equal(t, msg1, zap1Logs.All()[0].Message) + require.Equal(t, 1, zap2Logs.Len()) + require.Equal(t, msg2, zap2Logs.All()[0].Message) + }) + } +} + +func setup() func() { + stdLogger := logrus.StandardLogger() + // don't let Fatal() actually exit the process + orig := stdLogger.ExitFunc + stdLogger.ExitFunc = func(code int) {} + return func() { + stdLogger.ExitFunc = orig + } +} + +func getAsserter(level logrus.Level) func(t require.TestingT, f assert.PanicTestFunc, msgAndArgs ...any) { + asserter := require.NotPanics + if level == logrus.FatalLevel || level == logrus.PanicLevel { + asserter = require.Panics + } + return asserter +} + +func newObservedZap(level zapcore.Level) (*zap.Logger, *observer.ObservedLogs) { core, logs := observer.New(level) - return zap.New(core), logs + return zap.New(core).WithOptions(zap.WithFatalHook(zapcore.WriteThenPanic)), logs +} + +func logAt(entry *logrus.Entry, level logrus.Level, msg string) { + origLevel := entry.Logger.Level + defer func() { + entry.Logger.Level = origLevel + }() + entry.Logger.Level = level + switch level { + case logrus.FatalLevel: + entry.Fatal(msg) + case logrus.PanicLevel: + entry.Panic(msg) + case logrus.ErrorLevel: + entry.Error(msg) + case logrus.WarnLevel: + entry.Warn(msg) + case logrus.InfoLevel: + entry.Info(msg) + case logrus.DebugLevel: + entry.Debug(msg) + case logrus.TraceLevel: + entry.Trace(msg) + } +} + +func unredirect() { + logger := logrus.StandardLogger() + keep := make(logrus.LevelHooks) + for _, level := range logrus.AllLevels { + keep[level] = make([]logrus.Hook, 0) + for _, hook := range logger.Hooks[level] { + if _, ok := hook.(*logrusToZap); !ok { + keep[level] = append(keep[level], hook) + } + } + } + logger.ReplaceHooks(keep) } diff --git a/internal/receiver/smartagentreceiver/receiver.go b/internal/receiver/smartagentreceiver/receiver.go index cce749cfb5d..3eb145bd766 100644 --- a/internal/receiver/smartagentreceiver/receiver.go +++ b/internal/receiver/smartagentreceiver/receiver.go @@ -57,12 +57,12 @@ type Receiver struct { var _ component.MetricsReceiver = (*Receiver)(nil) var ( - rusToZap *logrusToZap - configureCollectdOnce sync.Once - configureEnvironmentOnce sync.Once saConfig *saconfig.Config - configureRusToZapOnce sync.Once nonWordCharacters = regexp.MustCompile(`[^\w]+`) + logrusShim *logrusToZap + configureCollectdOnce sync.Once + configureEnvironmentOnce sync.Once + configureLogrusOnce sync.Once ) func NewReceiver(params component.ReceiverCreateSettings, config Config) *Receiver { @@ -104,17 +104,20 @@ func (r *Receiver) Start(_ context.Context, host component.Host) error { configCore := r.config.monitorConfig.MonitorConfigCore() monitorType := configCore.Type - monitorName := nonWordCharacters.ReplaceAllString(r.config.ID().String(), "") - configCore.MonitorID = types.MonitorID(monitorName) + monitorID := nonWordCharacters.ReplaceAllString(r.config.ID().String(), "") + configCore.MonitorID = types.MonitorID(monitorID) - configureRusToZapOnce.Do(func() { - rusToZap = newLogrusToZap(loggerProvider(r.logger.Core())) + configureLogrusOnce.Do(func() { + // we need a default logger that doesn't tie to a particular receiver instance + // but still uses the underlying service TelemetrySettings.Logger: + logrusShim = newLogrusToZap(r.logger.With(zap.String("name", "default"))) }) - // source logger set to the standard logrus logger because it is assumed that is what the monitor is using. - rusToZap.redirect(logrusKey{ + // source logger set to the logrus StandardLogger because it is assumed that the monitor's is derived from it + logrusShim.redirect(monitorLogrus{ Logger: logrus.StandardLogger(), monitorType: r.config.monitorConfig.MonitorConfigCore().Type, + monitorID: monitorID, }, r.logger) if !r.config.acceptsEndpoints { @@ -131,11 +134,6 @@ func (r *Receiver) Start(_ context.Context, host component.Host) error { } func (r *Receiver) Shutdown(context.Context) error { - defer rusToZap.unRedirect(logrusKey{ - Logger: logrus.StandardLogger(), - monitorType: r.config.monitorConfig.MonitorConfigCore().Type, - }, r.logger) - if r.monitor == nil { return fmt.Errorf("smartagentreceiver's Shutdown() called before Start() or with invalid monitor state") } else if shutdownable, ok := (r.monitor).(monitors.Shutdownable); !ok {