Skip to content

Commit

Permalink
Add New Function Entry
Browse files Browse the repository at this point in the history
  • Loading branch information
wijayaerick committed Jun 25, 2020
1 parent 39aa3a1 commit 942a29c
Show file tree
Hide file tree
Showing 10 changed files with 178 additions and 50 deletions.
2 changes: 2 additions & 0 deletions config.go
Expand Up @@ -101,6 +101,7 @@ func NewProductionEncoderConfig() zapcore.EncoderConfig {
LevelKey: "level",
NameKey: "logger",
CallerKey: "caller",
FunctionKey: zapcore.OmitKey,
MessageKey: "msg",
StacktraceKey: "stacktrace",
LineEnding: zapcore.DefaultLineEnding,
Expand Down Expand Up @@ -140,6 +141,7 @@ func NewDevelopmentEncoderConfig() zapcore.EncoderConfig {
LevelKey: "L",
NameKey: "N",
CallerKey: "C",
FunctionKey: zapcore.OmitKey,
MessageKey: "M",
StacktraceKey: "S",
LineEnding: zapcore.DefaultLineEnding,
Expand Down
21 changes: 16 additions & 5 deletions logger.go
Expand Up @@ -45,8 +45,9 @@ type Logger struct {
name string
errorOutput zapcore.WriteSyncer

addCaller bool
addStack zapcore.LevelEnabler
addCaller bool
addFunction bool
addStack zapcore.LevelEnabler

callerSkip int
}
Expand Down Expand Up @@ -296,12 +297,22 @@ func (log *Logger) check(lvl zapcore.Level, msg string) *zapcore.CheckedEntry {

// Thread the error output through to the CheckedEntry.
ce.ErrorOutput = log.errorOutput
if log.addCaller {
ce.Entry.Caller = zapcore.NewEntryCaller(runtime.Caller(log.callerSkip + callerSkipOffset))
if !ce.Entry.Caller.Defined {
if log.addCaller || log.addFunction {
pc := make([]uintptr, 1)
numFrames := runtime.Callers(log.callerSkip+callerSkipOffset+1, pc)
frame, _ := runtime.CallersFrames(pc[:numFrames]).Next()
defined := frame.PC != 0
if !defined {
fmt.Fprintf(log.errorOutput, "%v Logger.check error: failed to get caller\n", time.Now().UTC())
log.errorOutput.Sync()
}

if log.addCaller {
ce.Entry.Caller = zapcore.NewEntryCaller(frame.PC, frame.File, frame.Line, defined)
}
if log.addFunction {
ce.Entry.Function = frame.Function
}
}
if log.addStack.Enabled(ce.Entry.Level) {
ce.Entry.Stack = Stack("").String
Expand Down
51 changes: 51 additions & 0 deletions logger_test.go
Expand Up @@ -385,6 +385,57 @@ func TestLoggerAddCallerFail(t *testing.T) {
})
}

func TestLoggerAddFunction(t *testing.T) {
tests := []struct {
options []Option
pat string
}{
{opts(), `^$`},
{opts(WithFunction(false)), `^$`},
{opts(AddFunction()), `zap.TestLoggerAddFunction.func1$`},
{opts(AddFunction(), WithFunction(false)), `^$`},
{opts(WithFunction(true)), `zap.TestLoggerAddFunction.func1$`},
{opts(WithFunction(true), WithFunction(false)), `^$`},
{opts(AddFunction(), AddCallerSkip(1), AddCallerSkip(-1)), `zap.TestLoggerAddFunction.func1$`},
{opts(AddFunction(), AddCallerSkip(1)), `zap.withLogger$`},
{opts(AddFunction(), AddCallerSkip(1), AddCallerSkip(3)), `runtime.goexit$`},
}
for _, tt := range tests {
withLogger(t, DebugLevel, tt.options, func(logger *Logger, logs *observer.ObservedLogs) {
// Make sure that sugaring and desugaring resets caller skip properly.
logger = logger.Sugar().Desugar()
logger.Info("")
output := logs.AllUntimed()
assert.Equal(t, 1, len(output), "Unexpected number of logs written out.")
assert.Regexp(
t,
tt.pat,
output[0].Entry.Function,
"Expected to find function name in output.",
)
})
}
}

func TestLoggerAddFunctionFail(t *testing.T) {
errBuf := &ztest.Buffer{}
withLogger(t, DebugLevel, opts(AddFunction(), ErrorOutput(errBuf)), func(log *Logger, logs *observer.ObservedLogs) {
log.callerSkip = 1e3
log.Info("Failure.")
assert.Regexp(
t,
`Logger.check error: failed to get caller`,
errBuf.String(),
"Didn't find expected failure message.",
)
assert.Equal(
t,
logs.AllUntimed()[0].Entry.Message,
"Failure.",
"Expected original message to survive failures in runtime.Caller.")
})
}

func TestLoggerReplaceCore(t *testing.T) {
replace := WrapCore(func(zapcore.Core) zapcore.Core {
return zapcore.NewNopCore()
Expand Down
24 changes: 19 additions & 5 deletions options.go
Expand Up @@ -87,7 +87,7 @@ func Development() Option {
}

// AddCaller configures the Logger to annotate each message with the filename
// and line number of zap's caller. See also WithCaller.
// and line number of zap's caller. See also WithCaller.
func AddCaller() Option {
return WithCaller(true)
}
Expand All @@ -101,10 +101,24 @@ func WithCaller(enabled bool) Option {
})
}

// AddCallerSkip increases the number of callers skipped by caller annotation
// (as enabled by the AddCaller option). When building wrappers around the
// Logger and SugaredLogger, supplying this Option prevents zap from always
// reporting the wrapper code as the caller.
// AddFunction configures the Logger to annotate each message with the function
// name of zap's caller. See also WithFunction.
func AddFunction() Option {
return WithFunction(true)
}

// WithFunction configures the Logger to annotate each message with the function
// name of zap's caller, or not, depending on the value of enabled.
func WithFunction(enabled bool) Option {
return optionFunc(func(log *Logger) {
log.addFunction = enabled
})
}

// AddCallerSkip increases the number of callers skipped by caller and function
// annotations (as enabled by the AddCaller and AddFunction options). When
// building wrappers around the Logger and SugaredLogger, supplying this Option
// prevents zap from always reporting the wrapper code as the caller.
func AddCallerSkip(skip int) Option {
return optionFunc(func(log *Logger) {
log.callerSkip += skip
Expand Down
3 changes: 3 additions & 0 deletions zapcore/console_encoder.go
Expand Up @@ -92,6 +92,9 @@ func (c consoleEncoder) EncodeEntry(ent Entry, fields []Field) (*buffer.Buffer,
if ent.Caller.Defined && c.CallerKey != "" && c.EncodeCaller != nil {
c.EncodeCaller(ent.Caller, arr)
}
if ent.Function != "" && c.FunctionKey != "" {
arr.AppendString(ent.Function)
}
for i := range arr.elems {
if i > 0 {
line.AppendByte('\t')
Expand Down
1 change: 1 addition & 0 deletions zapcore/encoder.go
Expand Up @@ -279,6 +279,7 @@ type EncoderConfig struct {
TimeKey string `json:"timeKey" yaml:"timeKey"`
NameKey string `json:"nameKey" yaml:"nameKey"`
CallerKey string `json:"callerKey" yaml:"callerKey"`
FunctionKey string `json:"functionKey" yaml:"functionKey"`
StacktraceKey string `json:"stacktraceKey" yaml:"stacktraceKey"`
LineEnding string `json:"lineEnding" yaml:"lineEnding"`
// Configure the primitive representations of common complex types. For
Expand Down

0 comments on commit 942a29c

Please sign in to comment.