Skip to content

Commit 7635b24

Browse files
authored
Allow logging of caller info (#4731)
This commit extends our logging wrapper code to allow the addition of automatically generated caller information to log entries. It also adds a way to selectively suppress this caller information for individual sinks. This commit does not change any of trufflehog's behavior - it just enables future functionality in case we want it to use it later. This commit also provides an exportable type alias for sink configuration option functions so that callers have more flexibility in how they prepare groups of them. This commit also adds some new tests for the existing redactionCore and fixes an existing bug in it. (The bug hasn't yet caused problems, but it created the potential for future problems.)
1 parent b78fbfd commit 7635b24

File tree

5 files changed

+397
-142
lines changed

5 files changed

+397
-142
lines changed

pkg/log/log.go

Lines changed: 51 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -22,10 +22,19 @@ type logConfig struct {
2222
err error
2323
}
2424

25+
type SinkOption func(*sinkConfig)
26+
2527
// New creates a new log object with the provided configurations. If no sinks
2628
// are provided, a no-op sink will be used. Returns the logger and a cleanup
2729
// function that should be executed before the program exits.
2830
func New(service string, configs ...logConfig) (logr.Logger, func() error) {
31+
return NewWithCaller(service, false, configs...)
32+
}
33+
34+
// NewWithCaller creates a new logger named after the specified service with the provided sink configurations. If
35+
// addCaller is true, call site information will be attached to each emitted log message. (This behavior can be disabled
36+
// on a per-sink basis using WithSuppressCaller.)
37+
func NewWithCaller(service string, addCaller bool, configs ...logConfig) (logr.Logger, func() error) {
2938
var cores []zapcore.Core
3039
var cleanupFuncs []func() error
3140

@@ -40,7 +49,7 @@ func New(service string, configs ...logConfig) (logr.Logger, func() error) {
4049
}
4150
}
4251
// create logger
43-
zapLogger := zap.New(zapcore.NewTee(cores...))
52+
zapLogger := zap.New(zapcore.NewTee(cores...), zap.WithCaller(addCaller))
4453
cleanupFuncs = append(cleanupFuncs, zapLogger.Sync)
4554
logger := zapr.NewLogger(zapLogger).WithName(service)
4655

@@ -85,14 +94,15 @@ func WithSentry(opts sentry.ClientOptions, tags map[string]string) logConfig {
8594
}
8695

8796
type sinkConfig struct {
88-
encoder zapcore.Encoder
89-
sink zapcore.WriteSyncer
90-
level levelSetter
91-
redactor *dynamicRedactor
97+
encoder zapcore.Encoder
98+
sink zapcore.WriteSyncer
99+
level levelSetter
100+
redactor *dynamicRedactor
101+
suppressCaller bool
92102
}
93103

94104
// WithJSONSink adds a JSON encoded output to the logger.
95-
func WithJSONSink(sink io.Writer, opts ...func(*sinkConfig)) logConfig {
105+
func WithJSONSink(sink io.Writer, opts ...SinkOption) logConfig {
96106
return newCoreConfig(
97107
zapcore.NewJSONEncoder(defaultEncoderConfig()),
98108
zapcore.Lock(zapcore.AddSync(sink)),
@@ -102,7 +112,7 @@ func WithJSONSink(sink io.Writer, opts ...func(*sinkConfig)) logConfig {
102112
}
103113

104114
// WithConsoleSink adds a console-style output to the logger.
105-
func WithConsoleSink(sink io.Writer, opts ...func(*sinkConfig)) logConfig {
115+
func WithConsoleSink(sink io.Writer, opts ...SinkOption) logConfig {
106116
return newCoreConfig(
107117
zapcore.NewConsoleEncoder(defaultEncoderConfig()),
108118
zapcore.Lock(zapcore.AddSync(sink)),
@@ -162,10 +172,21 @@ func AddSink(l logr.Logger, sink logConfig, keysAndValues ...any) (logr.Logger,
162172
if err != nil {
163173
return l, nil, errors.New("unsupported logr implementation")
164174
}
165-
zapLogger = zapLogger.WithOptions(zap.WrapCore(func(core zapcore.Core) zapcore.Core {
166-
return zapcore.NewTee(core, newSinkCore)
167-
}))
168-
return zapr.NewLogger(zapLogger), firstErrorFunc(zapLogger.Sync, sink.cleanup), nil
175+
176+
newLoggerOptions := []zap.Option{
177+
// Tee the new core together with the original core
178+
zap.WrapCore(func(core zapcore.Core) zapcore.Core { return zapcore.NewTee(core, newSinkCore) }),
179+
180+
// CMR: zapr.NewLogger, for whatever reason, assumes that the passed-in logger doesn't have its caller frame
181+
// adjustment already set up, so it adds a frame skip of 2. However, that assumption doesn't hold here because
182+
// we're adding a core to an existing logger rather than creating a new one. I can't figure out a way to disable
183+
// this automatic frame adjustment, so we compensate for it with the hamfisted kludge of a compensating offset.
184+
zap.AddCallerSkip(-2),
185+
}
186+
187+
zapLogger = zapLogger.WithOptions(newLoggerOptions...)
188+
newLogger := zapr.NewLogger(zapLogger)
189+
return newLogger, firstErrorFunc(zapLogger.Sync, sink.cleanup), nil
169190
}
170191

171192
// getZapLogger is a helper function that gets the underlying zap logger from a
@@ -179,7 +200,7 @@ func getZapLogger(l logr.Logger) (*zap.Logger, error) {
179200

180201
// WithLevel sets the sink's level to a static level. This option prevents
181202
// changing the log level for this sink later on.
182-
func WithLevel(level int8) func(*sinkConfig) {
203+
func WithLevel(level int8) SinkOption {
183204
return WithLeveler(
184205
// Zap's levels get more verbose as the number gets smaller, as explained
185206
// by zapr here: https://github.com/go-logr/zapr#increasing-verbosity
@@ -189,19 +210,27 @@ func WithLevel(level int8) func(*sinkConfig) {
189210
}
190211

191212
// WithLeveler sets the sink's level enabler to leveler.
192-
func WithLeveler(leveler levelSetter) func(*sinkConfig) {
213+
func WithLeveler(leveler levelSetter) SinkOption {
193214
return func(conf *sinkConfig) {
194215
conf.level = leveler
195216
}
196217
}
197218

198219
// WithGlobalRedaction adds values to be redacted from logs.
199-
func WithGlobalRedaction() func(*sinkConfig) {
220+
func WithGlobalRedaction() SinkOption {
200221
return func(conf *sinkConfig) {
201222
conf.redactor = globalRedactor
202223
}
203224
}
204225

226+
// WithSuppressCaller prevents the sink being configured from logging any caller information, irrespective of any other
227+
// logger settings.
228+
func WithSuppressCaller() SinkOption {
229+
return func(conf *sinkConfig) {
230+
conf.suppressCaller = true
231+
}
232+
}
233+
205234
// ToLogger converts the logr.Logger into a legacy *log.Logger.
206235
func ToLogger(l logr.Logger) *log.Logger {
207236
return slog.NewLogLogger(logr.ToSlogHandler(l), slog.LevelInfo)
@@ -235,7 +264,7 @@ func newCoreConfig(
235264
defaultEncoder zapcore.Encoder,
236265
defaultSink zapcore.WriteSyncer,
237266
defaultLevel levelSetter,
238-
opts ...func(*sinkConfig),
267+
opts ...SinkOption,
239268
) logConfig {
240269
conf := sinkConfig{
241270
encoder: defaultEncoder,
@@ -251,9 +280,13 @@ func newCoreConfig(
251280
conf.level,
252281
)
253282

254-
if conf.redactor == nil {
255-
return logConfig{core: core}
283+
if conf.redactor != nil {
284+
core = NewRedactionCore(core, conf.redactor)
256285
}
257286

258-
return logConfig{core: NewRedactionCore(core, conf.redactor)}
287+
if conf.suppressCaller {
288+
core = &suppressCallerCore{Core: core}
289+
}
290+
291+
return logConfig{core: core}
259292
}

0 commit comments

Comments
 (0)