From dbadb8d1e14e594b1988e6ccafb5576ec6b815ae Mon Sep 17 00:00:00 2001 From: Tiago Queiroz Date: Tue, 23 May 2023 16:45:59 +0200 Subject: [PATCH] Implement BlockSelectors for level debug This commit implements a block list of selectors for debug logging on `logp`. --- logp/config.go | 7 ++++--- logp/core.go | 35 +++++++++++++++++++++-------------- logp/options.go | 8 ++++++++ logp/selective.go | 22 +++++++++++++++++++--- logp/selective_test.go | 30 ++++++++++++++++++++++++++++++ 5 files changed, 82 insertions(+), 20 deletions(-) diff --git a/logp/config.go b/logp/config.go index 1e82b6e..d0d3d82 100644 --- a/logp/config.go +++ b/logp/config.go @@ -24,9 +24,10 @@ import ( // Config contains the configuration options for the logger. To create a Config // from a common.Config use logp/config.Build. type Config struct { - Beat string `config:",ignore"` // Name of the Beat (for default file name). - Level Level `config:"level"` // Logging level (error, warning, info, debug). - Selectors []string `config:"selectors"` // Selectors for debug level logging. + Beat string `config:",ignore"` // Name of the Beat (for default file name). + Level Level `config:"level"` // Logging level (error, warning, info, debug). + Selectors []string `config:"selectors"` // Selectors for debug level logging. + BlockSelectors []string `config:"block_selectors"` // Block list of selectors for debug logging. toObserver bool toIODiscard bool diff --git a/logp/core.go b/logp/core.go index 7eca6a5..e974518 100644 --- a/logp/core.go +++ b/logp/core.go @@ -56,12 +56,13 @@ func init() { } type coreLogger struct { - selectors map[string]struct{} // Set of enabled debug selectors. - rootLogger *zap.Logger // Root logger without any options configured. - globalLogger *zap.Logger // Logger used by legacy global functions (e.g. logp.Info). - logger *Logger // Logger that is the basis for all logp.Loggers. - level zap.AtomicLevel // The minimum level being printed - observedLogs *observer.ObservedLogs // Contains events generated while in observation mode (a testing mode). + selectors map[string]struct{} // Set of enabled debug selectors. + blockSelectors map[string]struct{} // Set of blocked debug selectors. + rootLogger *zap.Logger // Root logger without any options configured. + globalLogger *zap.Logger // Logger used by legacy global functions (e.g. logp.Info). + logger *Logger // Logger that is the basis for all logp.Loggers. + level zap.AtomicLevel // The minimum level being printed + observedLogs *observer.ObservedLogs // Contains events generated while in observation mode (a testing mode). } // Configure configures the logp package. @@ -97,7 +98,12 @@ func ConfigureWithOutputs(cfg Config, outputs ...zapcore.Core) error { // Enabled selectors when debug is enabled. selectors := make(map[string]struct{}, len(cfg.Selectors)) - if cfg.Level.Enabled(DebugLevel) && len(cfg.Selectors) > 0 { + blockSelectors := make(map[string]struct{}, len(cfg.BlockSelectors)) + if cfg.Level.Enabled(DebugLevel) && (len(cfg.Selectors) > 0 || len(cfg.BlockSelectors) > 0) { + for _, sel := range cfg.BlockSelectors { + blockSelectors[strings.TrimSpace(sel)] = struct{}{} + } + for _, sel := range cfg.Selectors { selectors[strings.TrimSpace(sel)] = struct{}{} } @@ -115,18 +121,19 @@ func ConfigureWithOutputs(cfg Config, outputs ...zapcore.Core) error { golog.SetOutput(_defaultGoLog) } - sink = selectiveWrapper(sink, selectors) + sink = selectiveWrapper(sink, selectors, blockSelectors) } sink = newMultiCore(append(outputs, sink)...) root := zap.New(sink, makeOptions(cfg)...) storeLogger(&coreLogger{ - selectors: selectors, - rootLogger: root, - globalLogger: root.WithOptions(zap.AddCallerSkip(1)), - logger: newLogger(root, ""), - level: level, - observedLogs: observedLogs, + selectors: selectors, + blockSelectors: blockSelectors, + rootLogger: root, + globalLogger: root.WithOptions(zap.AddCallerSkip(1)), + logger: newLogger(root, ""), + level: level, + observedLogs: observedLogs, }) return nil } diff --git a/logp/options.go b/logp/options.go index 4b7efa3..7aff217 100644 --- a/logp/options.go +++ b/logp/options.go @@ -35,6 +35,14 @@ func WithSelectors(selectors ...string) Option { } } +// WithBlockSelectors specifies what debug selectors are disabled. If no selectors +// are specified, then nothing is blocked +func WithBlockSelectors(blockList ...string) Option { + return func(cfg *Config) { + cfg.BlockSelectors = append(cfg.BlockSelectors, blockList...) + } +} + // ToObserverOutput specifies that the output should be collected in memory so // that they can be read by an observer by calling ObserverLogs(). func ToObserverOutput() Option { diff --git a/logp/selective.go b/logp/selective.go index cf73a3f..739d427 100644 --- a/logp/selective.go +++ b/logp/selective.go @@ -24,6 +24,7 @@ import ( type selectiveCore struct { allSelectors bool selectors map[string]struct{} + blockList map[string]struct{} core zapcore.Core } @@ -33,12 +34,24 @@ func HasSelector(selector string) bool { return found } -func selectiveWrapper(core zapcore.Core, selectors map[string]struct{}) zapcore.Core { +// IsBlocked returns true if the given selector was explicitly set. +func IsBlocked(selector string) bool { + l := loadLogger() + _, found := l.blockSelectors[selector] + return found +} + +func selectiveWrapper(core zapcore.Core, selectors, blockList map[string]struct{}) zapcore.Core { if len(selectors) == 0 { return core } _, allSelectors := selectors["*"] - return &selectiveCore{selectors: selectors, core: core, allSelectors: allSelectors} + return &selectiveCore{ + selectors: selectors, + core: core, + allSelectors: allSelectors, + blockList: blockList, + } } // Enabled returns whether a given logging level is enabled when logging a @@ -49,7 +62,7 @@ func (c *selectiveCore) Enabled(level zapcore.Level) bool { // With adds structured context to the Core. func (c *selectiveCore) With(fields []zapcore.Field) zapcore.Core { - return selectiveWrapper(c.core.With(fields), c.selectors) + return selectiveWrapper(c.core.With(fields), c.selectors, c.blockList) } // Check determines whether the supplied Entry should be logged (using the @@ -61,6 +74,9 @@ func (c *selectiveCore) With(fields []zapcore.Field) zapcore.Core { func (c *selectiveCore) Check(ent zapcore.Entry, ce *zapcore.CheckedEntry) *zapcore.CheckedEntry { if c.Enabled(ent.Level) { if ent.Level == zapcore.DebugLevel { + if _, blocked := c.blockList[ent.LoggerName]; blocked { + return ce + } if c.allSelectors { return ce.AddCore(ent, c) } else if _, enabled := c.selectors[ent.LoggerName]; enabled { diff --git a/logp/selective_test.go b/logp/selective_test.go index e03d5bd..8c2a7c5 100644 --- a/logp/selective_test.go +++ b/logp/selective_test.go @@ -54,3 +54,33 @@ func TestLoggerSelectors(t *testing.T) { logs = ObserverLogs().TakeAll() assert.Len(t, logs, 1) } + +func TestLoggerBlockList(t *testing.T) { + if err := DevelopmentSetup(WithBlockSelectors("tooVerbose", " sensitiveInformation "), ToObserverOutput()); err != nil { + t.Fatal(err) + } + + require.True(t, IsBlocked("tooVerbose")) + require.True(t, IsBlocked("sensitiveInformation")) + + doesNotLog := NewLogger("tooVerbose") + doesNotLog2 := NewLogger("sensitiveInformation") + willLog := NewLogger("notVerbose") + + doesNotLog.Debug("is not logged") + logs := ObserverLogs().TakeAll() + assert.Len(t, logs, 0) + + doesNotLog2.Debug("is not logged") + logs = ObserverLogs().TakeAll() + assert.Len(t, logs, 0) + + // Selectors only apply to debug level logs. + willLog.Debug("is logged") + logs = ObserverLogs().TakeAll() + assert.Len(t, logs, 1) + + willLog.Info("is also logged") + logs = ObserverLogs().TakeAll() + assert.Len(t, logs, 1) +}