diff --git a/README.md b/README.md index 2c001de..8f7f0fb 100644 --- a/README.md +++ b/README.md @@ -30,6 +30,20 @@ requestLogger := logger.WithFields(nacelle.LogFields{ requestLogger.Info("Accepted request from %s", remoteAddr) ``` +### Caller Stack + +Sometimes it is useful to define helper functions that logs messages for you in a common way (applying common context, performing additional behavior on errors, etc). Unfortunately, this can interfere with the way the caller file and line number are discovered. The logger has a `WithIndirectCaller` method that will increase the depth used when scanning the stack for callers. This should be used at each log location that aggregates log calls (e.g. any place where knowing this source location would not be helpful). + +```go +func logForMe(message string) { + parentLogger.WithIndirectCaller(1).Log(message) +} + +logForMe("foobar") +``` + +Using a logger instance directly does not require this additional hint. + ### Adapters Nacelle ships with a handful of useful logging adapters. These are extensions of the logger interface that add additional behavior or additional structured data. A custom adapter can be created for behavior that is not provided here. @@ -59,7 +73,7 @@ logger := NewRollupAdapter( time.Second, // rollup window ) -for i:=0; i < 10000; i++ { +for i := 0; i < 10000; i++ { logger.Debug("Some problem here!") } ``` @@ -71,6 +85,7 @@ The following environment variables change the behavior of the loggers when usin | Environment Variable | Default | Description | | ---------------------------- | ------- | ----------- | | LOG_COLORIZE | true | Colorize log messages by level when true. Works with `console` encoding only. | +| LOG_JSON_FIELD_NAMES | | A JSON-encoded map to rename the fields for `message`, `timestamp`, and/or `level`. | | LOG_DISPLAY_FIELDS | true | Omit log fields from output when false. Works with `console` encoding only. | | LOG_DISPLAY_MULTILINE_FIELDS | false | Print fields on one line when true, one field per line when false. Works with `console` encoding only. | | LOG_ENCODING | console | `console` for human-readable output and `json` for JSON-formatted output. | diff --git a/base_logger.go b/base_logger.go index ba90e5b..446b9a2 100644 --- a/base_logger.go +++ b/base_logger.go @@ -67,7 +67,7 @@ func (s *baseShim) LogWithFields(level LogLevel, fields LogFields, format string } seq := atomic.AddUint64(&s.wrapper.sequence, 1) - fields = addCaller(fields).normalizeTimeValues() + fields = fields.normalizeTimeValues() fields["sequenceNumber"] = seq s.wrapper.logger.Log( diff --git a/base_logger_mock_test.go b/base_logger_mock_test.go index 25cae20..0c89c2e 100644 --- a/base_logger_mock_test.go +++ b/base_logger_mock_test.go @@ -1,6 +1,6 @@ // Code generated by github.com/efritz/go-mockgen 0.1.0; DO NOT EDIT. // This file was generated by robots at -// 2019-10-07T12:19:58-05:00 +// 2019-10-07T13:54:04-05:00 // using the command // $ go-mockgen -f github.com/go-nacelle/log -i baseLogger -o base_logger_mock_test.go diff --git a/caller.go b/caller.go index 84750e2..63b4177 100644 --- a/caller.go +++ b/caller.go @@ -6,20 +6,20 @@ import ( "strings" ) -func addCaller(fields LogFields) LogFields { +func addCaller(fields LogFields, depth int) LogFields { if fields == nil { fields = LogFields{} } if _, ok := fields["caller"]; !ok { - fields["caller"] = getCaller() + fields["caller"] = getCaller(depth) } return fields } -func getCaller() string { - for i := 3; ; i++ { +func getCaller(depth int) string { + for i := 3 + depth; ; i++ { _, file, line, _ := runtime.Caller(i) if file == "" { continue diff --git a/caller_test.go b/caller_test.go index e048180..8e4e161 100644 --- a/caller_test.go +++ b/caller_test.go @@ -126,6 +126,46 @@ func (s *CallerSuite) testRollup(init func(*Config) (Logger, error)) { Expect(data2["caller"]).To(Equal(fmt.Sprintf("log/caller_test.go:%d", start))) } +func (s *CallerSuite) testAdapter(init func(*Config) (Logger, error)) { + stderr := captureStderr(func() { + logger, err := init(&Config{LogLevel: "info", LogEncoding: "json"}) + Expect(err).To(BeNil()) + // Push caller stack out once for each indirection + indirectLogger := logger.WithIndirectCaller(3) + + log3 := func(message string) { indirectLogger.Info(message) } + log2 := func(message string) { log3(message) } + log1 := func(message string) { log2(message) } + + log1("X") + log1("Y") + log1("Z") + logger.Sync() + }) + + lines := strings.Split(strings.TrimSpace(stderr), "\n") + Expect(lines).To(HaveLen(3)) + + var ( + data1 = LogFields{} + data2 = LogFields{} + data3 = LogFields{} + ) + + Expect(json.Unmarshal([]byte(lines[0]), &data1)).To(BeNil()) + Expect(json.Unmarshal([]byte(lines[1]), &data2)).To(BeNil()) + Expect(json.Unmarshal([]byte(lines[2]), &data3)).To(BeNil()) + + // Note: this value refers to the line number containing `logger.Info("X")` in + // the function literal above. If code is added before that line, this value + // must be updated. + start := 140 + + Expect(data1["caller"]).To(Equal(fmt.Sprintf("log/caller_test.go:%d", start+0))) + Expect(data2["caller"]).To(Equal(fmt.Sprintf("log/caller_test.go:%d", start+1))) + Expect(data3["caller"]).To(Equal(fmt.Sprintf("log/caller_test.go:%d", start+2))) +} + func (s *CallerSuite) testFields(init func(*Config) (Logger, error)) { s.testBasic(func(config *Config) (Logger, error) { logger, err := init(config) @@ -172,6 +212,7 @@ func (s *CallerSuite) TestTrimPath(t sweet.T) { } func (s *CallerSuite) TestLogger(t sweet.T) { s.testBasic(InitLogger) } +func (s *CallerSuite) TestAdapter(t sweet.T) { s.testAdapter(InitLogger) } func (s *CallerSuite) TestLoggerWithFields(t sweet.T) { s.testFields(InitLogger) } func (s *CallerSuite) TestLoggerWithReplayAdapter(t sweet.T) { s.testReplayAdapter(InitLogger) } func (s *CallerSuite) TestLoggerWithRollupAdapter(t sweet.T) { s.testRollupAdapter(InitLogger) } diff --git a/config.go b/config.go index 2364eca..622456e 100644 --- a/config.go +++ b/config.go @@ -9,7 +9,7 @@ type Config struct { LogLevel string `env:"log_level" file:"log_level" default:"info"` LogEncoding string `env:"log_encoding" file:"log_encoding" default:"console"` LogColorize bool `env:"log_colorize" file:"log_colorize" default:"true"` - LogJSONFieldNames map[string]string `env:"log_json_field_names" file:"log_json_field_names"` // TODO - not in config + LogJSONFieldNames map[string]string `env:"log_json_field_names" file:"log_json_field_names"` LogInitialFields LogFields `env:"log_fields" file:"log_fields"` LogShortTime bool `env:"log_short_time" file:"log_short_time" default:"false"` LogDisplayFields bool `env:"log_display_fields" file:"log_display_fields" default:"true"` diff --git a/logger.go b/logger.go index 3184ce3..229ac35 100644 --- a/logger.go +++ b/logger.go @@ -2,6 +2,7 @@ package log type ( Logger interface { + WithIndirectCaller(frames int) Logger WithFields(LogFields) Logger LogWithFields(LogLevel, LogFields, string, ...interface{}) Sync() error diff --git a/main_test.go b/main_test.go index 92a9945..6e245e4 100644 --- a/main_test.go +++ b/main_test.go @@ -57,7 +57,7 @@ func (ts *testShim) LogWithFields(level LogLevel, fields LogFields, format strin ts.messages = append(ts.messages, &logMessage{ level: level, - fields: addCaller(fields), + fields: fields, format: format, args: args, }) diff --git a/mocks/logger.go b/mocks/logger.go index 7f9714a..8c30590 100644 --- a/mocks/logger.go +++ b/mocks/logger.go @@ -1,6 +1,6 @@ // Code generated by github.com/efritz/go-mockgen 0.1.0; DO NOT EDIT. // This file was generated by robots at -// 2019-06-19T18:58:08-05:00 +// 2019-10-07T13:54:02-05:00 // using the command // $ go-mockgen -f github.com/go-nacelle/log -i Logger -o logger.go @@ -11,7 +11,7 @@ import ( "sync" ) -// MockLogger is a mock implementation of the Logger interface (from the +// MockLogger is a mock impelementation of the Logger interface (from the // package github.com/go-nacelle/log) used for unit testing. type MockLogger struct { // DebugFunc is an instance of a mock function object controlling the @@ -53,6 +53,9 @@ type MockLogger struct { // WithFieldsFunc is an instance of a mock function object controlling // the behavior of the method WithFields. WithFieldsFunc *LoggerWithFieldsFunc + // WithIndirectCallerFunc is an instance of a mock function object + // controlling the behavior of the method WithIndirectCaller. + WithIndirectCallerFunc *LoggerWithIndirectCallerFunc } // NewMockLogger creates a new mock of the Logger interface. All methods @@ -124,6 +127,11 @@ func NewMockLogger() *MockLogger { return nil }, }, + WithIndirectCallerFunc: &LoggerWithIndirectCallerFunc{ + defaultHook: func(int) log.Logger { + return nil + }, + }, } } @@ -170,6 +178,9 @@ func NewMockLoggerFrom(i log.Logger) *MockLogger { WithFieldsFunc: &LoggerWithFieldsFunc{ defaultHook: i.WithFields, }, + WithIndirectCallerFunc: &LoggerWithIndirectCallerFunc{ + defaultHook: i.WithIndirectCaller, + }, } } @@ -197,7 +208,7 @@ func (f *LoggerDebugFunc) SetDefaultHook(hook func(string, ...interface{})) { } // PushHook adds a function to the end of hook queue. Each invocation of the -// Debug method of the parent MockLogger instance invokes the hook at the +// Debug method of the parent MockLogger instance inovkes the hook at the // front of the queue and discards it. After the queue is empty, the default // hook function is invoked for any future action. func (f *LoggerDebugFunc) PushHook(hook func(string, ...interface{})) { @@ -307,7 +318,7 @@ func (f *LoggerDebugWithFieldsFunc) SetDefaultHook(hook func(log.LogFields, stri } // PushHook adds a function to the end of hook queue. Each invocation of the -// DebugWithFields method of the parent MockLogger instance invokes the hook +// DebugWithFields method of the parent MockLogger instance inovkes the hook // at the front of the queue and discards it. After the queue is empty, the // default hook function is invoked for any future action. func (f *LoggerDebugWithFieldsFunc) PushHook(hook func(log.LogFields, string, ...interface{})) { @@ -419,7 +430,7 @@ func (f *LoggerErrorFunc) SetDefaultHook(hook func(string, ...interface{})) { } // PushHook adds a function to the end of hook queue. Each invocation of the -// Error method of the parent MockLogger instance invokes the hook at the +// Error method of the parent MockLogger instance inovkes the hook at the // front of the queue and discards it. After the queue is empty, the default // hook function is invoked for any future action. func (f *LoggerErrorFunc) PushHook(hook func(string, ...interface{})) { @@ -529,7 +540,7 @@ func (f *LoggerErrorWithFieldsFunc) SetDefaultHook(hook func(log.LogFields, stri } // PushHook adds a function to the end of hook queue. Each invocation of the -// ErrorWithFields method of the parent MockLogger instance invokes the hook +// ErrorWithFields method of the parent MockLogger instance inovkes the hook // at the front of the queue and discards it. After the queue is empty, the // default hook function is invoked for any future action. func (f *LoggerErrorWithFieldsFunc) PushHook(hook func(log.LogFields, string, ...interface{})) { @@ -641,7 +652,7 @@ func (f *LoggerFatalFunc) SetDefaultHook(hook func(string, ...interface{})) { } // PushHook adds a function to the end of hook queue. Each invocation of the -// Fatal method of the parent MockLogger instance invokes the hook at the +// Fatal method of the parent MockLogger instance inovkes the hook at the // front of the queue and discards it. After the queue is empty, the default // hook function is invoked for any future action. func (f *LoggerFatalFunc) PushHook(hook func(string, ...interface{})) { @@ -751,7 +762,7 @@ func (f *LoggerFatalWithFieldsFunc) SetDefaultHook(hook func(log.LogFields, stri } // PushHook adds a function to the end of hook queue. Each invocation of the -// FatalWithFields method of the parent MockLogger instance invokes the hook +// FatalWithFields method of the parent MockLogger instance inovkes the hook // at the front of the queue and discards it. After the queue is empty, the // default hook function is invoked for any future action. func (f *LoggerFatalWithFieldsFunc) PushHook(hook func(log.LogFields, string, ...interface{})) { @@ -863,7 +874,7 @@ func (f *LoggerInfoFunc) SetDefaultHook(hook func(string, ...interface{})) { } // PushHook adds a function to the end of hook queue. Each invocation of the -// Info method of the parent MockLogger instance invokes the hook at the +// Info method of the parent MockLogger instance inovkes the hook at the // front of the queue and discards it. After the queue is empty, the default // hook function is invoked for any future action. func (f *LoggerInfoFunc) PushHook(hook func(string, ...interface{})) { @@ -973,7 +984,7 @@ func (f *LoggerInfoWithFieldsFunc) SetDefaultHook(hook func(log.LogFields, strin } // PushHook adds a function to the end of hook queue. Each invocation of the -// InfoWithFields method of the parent MockLogger instance invokes the hook +// InfoWithFields method of the parent MockLogger instance inovkes the hook // at the front of the queue and discards it. After the queue is empty, the // default hook function is invoked for any future action. func (f *LoggerInfoWithFieldsFunc) PushHook(hook func(log.LogFields, string, ...interface{})) { @@ -1085,7 +1096,7 @@ func (f *LoggerLogWithFieldsFunc) SetDefaultHook(hook func(log.LogLevel, log.Log } // PushHook adds a function to the end of hook queue. Each invocation of the -// LogWithFields method of the parent MockLogger instance invokes the hook +// LogWithFields method of the parent MockLogger instance inovkes the hook // at the front of the queue and discards it. After the queue is empty, the // default hook function is invoked for any future action. func (f *LoggerLogWithFieldsFunc) PushHook(hook func(log.LogLevel, log.LogFields, string, ...interface{})) { @@ -1200,7 +1211,7 @@ func (f *LoggerSyncFunc) SetDefaultHook(hook func() error) { } // PushHook adds a function to the end of hook queue. Each invocation of the -// Sync method of the parent MockLogger instance invokes the hook at the +// Sync method of the parent MockLogger instance inovkes the hook at the // front of the queue and discards it. After the queue is empty, the default // hook function is invoked for any future action. func (f *LoggerSyncFunc) PushHook(hook func() error) { @@ -1299,7 +1310,7 @@ func (f *LoggerWarningFunc) SetDefaultHook(hook func(string, ...interface{})) { } // PushHook adds a function to the end of hook queue. Each invocation of the -// Warning method of the parent MockLogger instance invokes the hook at the +// Warning method of the parent MockLogger instance inovkes the hook at the // front of the queue and discards it. After the queue is empty, the default // hook function is invoked for any future action. func (f *LoggerWarningFunc) PushHook(hook func(string, ...interface{})) { @@ -1409,7 +1420,7 @@ func (f *LoggerWarningWithFieldsFunc) SetDefaultHook(hook func(log.LogFields, st } // PushHook adds a function to the end of hook queue. Each invocation of the -// WarningWithFields method of the parent MockLogger instance invokes the +// WarningWithFields method of the parent MockLogger instance inovkes the // hook at the front of the queue and discards it. After the queue is empty, // the default hook function is invoked for any future action. func (f *LoggerWarningWithFieldsFunc) PushHook(hook func(log.LogFields, string, ...interface{})) { @@ -1521,7 +1532,7 @@ func (f *LoggerWithFieldsFunc) SetDefaultHook(hook func(log.LogFields) log.Logge } // PushHook adds a function to the end of hook queue. Each invocation of the -// WithFields method of the parent MockLogger instance invokes the hook at +// WithFields method of the parent MockLogger instance inovkes the hook at // the front of the queue and discards it. After the queue is empty, the // default hook function is invoked for any future action. func (f *LoggerWithFieldsFunc) PushHook(hook func(log.LogFields) log.Logger) { @@ -1598,3 +1609,106 @@ func (c LoggerWithFieldsFuncCall) Args() []interface{} { func (c LoggerWithFieldsFuncCall) Results() []interface{} { return []interface{}{c.Result0} } + +// LoggerWithIndirectCallerFunc describes the behavior when the +// WithIndirectCaller method of the parent MockLogger instance is invoked. +type LoggerWithIndirectCallerFunc struct { + defaultHook func(int) log.Logger + hooks []func(int) log.Logger + history []LoggerWithIndirectCallerFuncCall + mutex sync.Mutex +} + +// WithIndirectCaller delegates to the next hook function in the queue and +// stores the parameter and result values of this invocation. +func (m *MockLogger) WithIndirectCaller(v0 int) log.Logger { + r0 := m.WithIndirectCallerFunc.nextHook()(v0) + m.WithIndirectCallerFunc.appendCall(LoggerWithIndirectCallerFuncCall{v0, r0}) + return r0 +} + +// SetDefaultHook sets function that is called when the WithIndirectCaller +// method of the parent MockLogger instance is invoked and the hook queue is +// empty. +func (f *LoggerWithIndirectCallerFunc) SetDefaultHook(hook func(int) log.Logger) { + f.defaultHook = hook +} + +// PushHook adds a function to the end of hook queue. Each invocation of the +// WithIndirectCaller method of the parent MockLogger instance inovkes the +// hook at the front of the queue and discards it. After the queue is empty, +// the default hook function is invoked for any future action. +func (f *LoggerWithIndirectCallerFunc) PushHook(hook func(int) log.Logger) { + f.mutex.Lock() + f.hooks = append(f.hooks, hook) + f.mutex.Unlock() +} + +// SetDefaultReturn calls SetDefaultDefaultHook with a function that returns +// the given values. +func (f *LoggerWithIndirectCallerFunc) SetDefaultReturn(r0 log.Logger) { + f.SetDefaultHook(func(int) log.Logger { + return r0 + }) +} + +// PushReturn calls PushDefaultHook with a function that returns the given +// values. +func (f *LoggerWithIndirectCallerFunc) PushReturn(r0 log.Logger) { + f.PushHook(func(int) log.Logger { + return r0 + }) +} + +func (f *LoggerWithIndirectCallerFunc) nextHook() func(int) log.Logger { + f.mutex.Lock() + defer f.mutex.Unlock() + + if len(f.hooks) == 0 { + return f.defaultHook + } + + hook := f.hooks[0] + f.hooks = f.hooks[1:] + return hook +} + +func (f *LoggerWithIndirectCallerFunc) appendCall(r0 LoggerWithIndirectCallerFuncCall) { + f.mutex.Lock() + f.history = append(f.history, r0) + f.mutex.Unlock() +} + +// History returns a sequence of LoggerWithIndirectCallerFuncCall objects +// describing the invocations of this function. +func (f *LoggerWithIndirectCallerFunc) History() []LoggerWithIndirectCallerFuncCall { + f.mutex.Lock() + history := make([]LoggerWithIndirectCallerFuncCall, len(f.history)) + copy(history, f.history) + f.mutex.Unlock() + + return history +} + +// LoggerWithIndirectCallerFuncCall is an object that describes an +// invocation of method WithIndirectCaller on an instance of MockLogger. +type LoggerWithIndirectCallerFuncCall struct { + // Arg0 is the value of the 1st argument passed to this method + // invocation. + Arg0 int + // Result0 is the value of the 1st result returned from this method + // invocation. + Result0 log.Logger +} + +// Args returns an interface slice containing the arguments of this +// invocation. +func (c LoggerWithIndirectCallerFuncCall) Args() []interface{} { + return []interface{}{c.Arg0} +} + +// Results returns an interface slice containing the results of this +// invocation. +func (c LoggerWithIndirectCallerFuncCall) Results() []interface{} { + return []interface{}{c.Result0} +} diff --git a/replay_adapter.go b/replay_adapter.go index 5bf9750..38eb4c3 100644 --- a/replay_adapter.go +++ b/replay_adapter.go @@ -77,8 +77,6 @@ func (s *replayShim) WithFields(fields LogFields) logShim { } func (s *replayShim) LogWithFields(level LogLevel, fields LogFields, format string, args ...interface{}) { - fields = addCaller(fields) - // Log immediately s.logger.LogWithFields(level, fields, format, args...) diff --git a/rollup_adapter.go b/rollup_adapter.go index 320b784..0b092bb 100644 --- a/rollup_adapter.go +++ b/rollup_adapter.go @@ -65,8 +65,6 @@ func (s *rollupShim) WithFields(fields LogFields) logShim { } func (s *rollupShim) LogWithFields(level LogLevel, fields LogFields, format string, args ...interface{}) { - fields = addCaller(fields) - if s.getWindow(format).record(s.logger, s.clock, s.windowDuration, level, fields, format, args...) { // Not rolling up, log immediately s.logger.LogWithFields(level, fields, format, args...) @@ -113,6 +111,10 @@ func (w *logWindow) record( format string, args ...interface{}, ) bool { + if fields == nil { + fields = LogFields{} + } + w.mutex.Lock() defer w.mutex.Unlock() diff --git a/shim.go b/shim.go index 4b4f2ab..48a0e89 100644 --- a/shim.go +++ b/shim.go @@ -8,7 +8,8 @@ type ( } shimAdapter struct { - shim logShim + shim logShim + depth int } replayShimAdapter struct { @@ -32,6 +33,14 @@ func adaptReplayShim(shim *replayShim) ReplayLogger { return &replayShimAdapter{adaptShim(shim), shim} } +func (sa *shimAdapter) WithIndirectCaller(frames int) Logger { + if frames <= 0 { + panic("WithIndirectCaller called with invalid frame count") + } + + return &shimAdapter{shim: sa.shim, depth: sa.depth + frames} +} + func (sa *shimAdapter) WithFields(fields LogFields) Logger { if len(fields) == 0 { return sa @@ -41,7 +50,7 @@ func (sa *shimAdapter) WithFields(fields LogFields) Logger { } func (sa *shimAdapter) LogWithFields(level LogLevel, fields LogFields, format string, args ...interface{}) { - sa.shim.LogWithFields(level, addCaller(fields), format, args...) + sa.shim.LogWithFields(level, addCaller(fields, sa.depth), format, args...) } func (sa *shimAdapter) Sync() error { @@ -49,43 +58,43 @@ func (sa *shimAdapter) Sync() error { } func (sa *shimAdapter) Debug(format string, args ...interface{}) { - sa.shim.LogWithFields(LevelDebug, addCaller(nil), format, args...) + sa.shim.LogWithFields(LevelDebug, addCaller(nil, sa.depth), format, args...) } func (sa *shimAdapter) Info(format string, args ...interface{}) { - sa.shim.LogWithFields(LevelInfo, addCaller(nil), format, args...) + sa.shim.LogWithFields(LevelInfo, addCaller(nil, sa.depth), format, args...) } func (sa *shimAdapter) Warning(format string, args ...interface{}) { - sa.shim.LogWithFields(LevelWarning, addCaller(nil), format, args...) + sa.shim.LogWithFields(LevelWarning, addCaller(nil, sa.depth), format, args...) } func (sa *shimAdapter) Error(format string, args ...interface{}) { - sa.shim.LogWithFields(LevelError, addCaller(nil), format, args...) + sa.shim.LogWithFields(LevelError, addCaller(nil, sa.depth), format, args...) } func (sa *shimAdapter) Fatal(format string, args ...interface{}) { - sa.shim.LogWithFields(LevelFatal, addCaller(nil), format, args...) + sa.shim.LogWithFields(LevelFatal, addCaller(nil, sa.depth), format, args...) } func (sa *shimAdapter) DebugWithFields(fields LogFields, format string, args ...interface{}) { - sa.shim.LogWithFields(LevelDebug, addCaller(fields), format, args...) + sa.shim.LogWithFields(LevelDebug, addCaller(fields, sa.depth), format, args...) } func (sa *shimAdapter) InfoWithFields(fields LogFields, format string, args ...interface{}) { - sa.shim.LogWithFields(LevelInfo, addCaller(fields), format, args...) + sa.shim.LogWithFields(LevelInfo, addCaller(fields, sa.depth), format, args...) } func (sa *shimAdapter) WarningWithFields(fields LogFields, format string, args ...interface{}) { - sa.shim.LogWithFields(LevelWarning, addCaller(fields), format, args...) + sa.shim.LogWithFields(LevelWarning, addCaller(fields, sa.depth), format, args...) } func (sa *shimAdapter) ErrorWithFields(fields LogFields, format string, args ...interface{}) { - sa.shim.LogWithFields(LevelError, addCaller(fields), format, args...) + sa.shim.LogWithFields(LevelError, addCaller(fields, sa.depth), format, args...) } func (sa *shimAdapter) FatalWithFields(fields LogFields, format string, args ...interface{}) { - sa.shim.LogWithFields(LevelFatal, addCaller(fields), format, args...) + sa.shim.LogWithFields(LevelFatal, addCaller(fields, sa.depth), format, args...) } func (a *replayShimAdapter) Replay(level LogLevel) {