From 651d361cfeb9d0f5b9f1d14b162c66c82509ef6d Mon Sep 17 00:00:00 2001 From: Mike Camp Date: Mon, 4 Mar 2019 19:41:18 -0500 Subject: [PATCH] Add CallerWithSkipFrameCount to the Context (#98) (#135) Add the ability to skip a specified number of stack frames on a per context basis. Before this toe CallerSkipFrameCount could only be set globally. --- context.go | 38 ++++++++++++++++++++++++++++++++------ log_test.go | 14 ++++++++++++++ 2 files changed, 46 insertions(+), 6 deletions(-) diff --git a/context.go b/context.go index 9eff31a..1815a08 100644 --- a/context.go +++ b/context.go @@ -2,6 +2,7 @@ package zerolog import ( "io/ioutil" + "math" "net" "time" ) @@ -347,14 +348,31 @@ func (c Context) Interface(key string, i interface{}) Context { return c } -type callerHook struct{} - -func (ch callerHook) Run(e *Event, level Level, msg string) { - // Extra frames to skip (added by hook infra). - e.caller(CallerSkipFrameCount + contextCallerSkipFrameCount) +type callerHook struct { + callerSkipFrameCount int } -var ch = callerHook{} +func newCallerHook(skipFrameCount int) callerHook { + return callerHook{callerSkipFrameCount: skipFrameCount} +} + +func (ch callerHook) Run(e *Event, level Level, msg string) { + switch ch.callerSkipFrameCount { + case useGlobalSkipFrameCount: + // Extra frames to skip (added by hook infra). + e.caller(CallerSkipFrameCount + contextCallerSkipFrameCount) + default: + // Extra frames to skip (added by hook infra). + e.caller(ch.callerSkipFrameCount + contextCallerSkipFrameCount) + } +} + +// useGlobalSkipFrameCount acts as a flag to informat callerHook.Run +// to use the global CallerSkipFrameCount. +const useGlobalSkipFrameCount = math.MinInt32 + +// ch is the default caller hook using the global CallerSkipFrameCount. +var ch = newCallerHook(useGlobalSkipFrameCount) // Caller adds the file:line of the caller with the zerolog.CallerFieldName key. func (c Context) Caller() Context { @@ -362,6 +380,14 @@ func (c Context) Caller() Context { return c } +// CallerWithSkipFrameCount adds the file:line of the caller with the zerolog.CallerFieldName key. +// The specified skipFrameCount int will override the global CallerSkipFrameCount for this context's respective logger. +// If set to -1 the global CallerSkipFrameCount will be used. +func (c Context) CallerWithSkipFrameCount(skipFrameCount int) Context { + c.l = c.l.Hook(newCallerHook(skipFrameCount)) + return c +} + type stackTraceHook struct{} func (sh stackTraceHook) Run(e *Event, level Level, msg string) { diff --git a/log_test.go b/log_test.go index b796631..c7891e4 100644 --- a/log_test.go +++ b/log_test.go @@ -107,6 +107,20 @@ func TestWith(t *testing.T) { if got, want := decodeIfBinaryToString(out.Bytes()), `{"string":"foo","bytes":"bar","hex":"12ef","json":{"some":"json"},"error":"some error","bool":true,"int":1,"int8":2,"int16":3,"int32":4,"int64":5,"uint":6,"uint8":7,"uint16":8,"uint32":9,"uint64":10,"float32":11.101,"float64":12.30303,"time":"0001-01-01T00:00:00Z","caller":"`+caller+`"}`+"\n"; got != want { t.Errorf("invalid log output:\ngot: %v\nwant: %v", got, want) } + + // Validate CallerWithSkipFrameCount. + out.Reset() + _, file, line, _ = runtime.Caller(0) + caller = fmt.Sprintf("%s:%d", file, line+5) + log = ctx.CallerWithSkipFrameCount(3).Logger() + func() { + log.Log().Msg("") + }() + // The above line is a little contrived, but the line above should be the line due + // to the extra frame skip. + if got, want := decodeIfBinaryToString(out.Bytes()), `{"string":"foo","bytes":"bar","hex":"12ef","json":{"some":"json"},"error":"some error","bool":true,"int":1,"int8":2,"int16":3,"int32":4,"int64":5,"uint":6,"uint8":7,"uint16":8,"uint32":9,"uint64":10,"float32":11.101,"float64":12.30303,"time":"0001-01-01T00:00:00Z","caller":"`+caller+`"}`+"\n"; got != want { + t.Errorf("invalid log output:\ngot: %v\nwant: %v", got, want) + } } func TestFieldsMap(t *testing.T) {