diff --git a/context.go b/context.go index 8753929..4137287 100644 --- a/context.go +++ b/context.go @@ -302,3 +302,17 @@ func (c Context) Interface(key string, i interface{}) Context { c.l.context = json.AppendInterface(json.AppendKey(c.l.context, key), i) return c } + +type callerHook struct{} + +func (ch callerHook) Run(e *Event, level Level, msg string) { + e.caller(4) +} + +var ch = callerHook{} + +// Caller adds the file:line of the caller with the zerolog.CallerFieldName key. +func (c Context) Caller() Context { + c.l = c.l.Hook(ch) + return c +} diff --git a/event.go b/event.go index deae8db..66e0489 100644 --- a/event.go +++ b/event.go @@ -4,6 +4,8 @@ import ( "fmt" "io/ioutil" "os" + "runtime" + "strconv" "sync" "time" @@ -561,3 +563,20 @@ func (e *Event) Interface(key string, i interface{}) *Event { e.buf = json.AppendInterface(json.AppendKey(e.buf, key), i) return e } + +// Caller adds the file:line of the caller with the zerolog.CallerFieldName key. +func (e *Event) Caller() *Event { + return e.caller(2) +} + +func (e *Event) caller(skip int) *Event { + if e == nil { + return e + } + _, file, line, ok := runtime.Caller(skip) + if !ok { + return e + } + e.buf = json.AppendString(json.AppendKey(e.buf, CallerFieldName), file+":"+strconv.Itoa(line)) + return e +} diff --git a/globals.go b/globals.go index beb2055..b65d0cb 100644 --- a/globals.go +++ b/globals.go @@ -16,6 +16,9 @@ var ( // ErrorFieldName is the field name used for error fields. ErrorFieldName = "error" + // CallerFieldName is the field name used for caller field. + CallerFieldName = "caller" + // TimeFieldFormat defines the time format of the Time field type. // If set to an empty string, the time is formatted as an UNIX timestamp // as integer. diff --git a/log_test.go b/log_test.go index b5ee231..a0aeb03 100644 --- a/log_test.go +++ b/log_test.go @@ -3,7 +3,9 @@ package zerolog import ( "bytes" "errors" + "fmt" "reflect" + "runtime" "testing" "time" ) @@ -74,7 +76,7 @@ func TestInfo(t *testing.T) { func TestWith(t *testing.T) { out := &bytes.Buffer{} - log := New(out).With(). + ctx := New(out).With(). Str("foo", "bar"). AnErr("some_err", nil). Err(errors.New("some error")). @@ -91,10 +93,12 @@ func TestWith(t *testing.T) { Uint64("uint64", 10). Float32("float32", 11). Float64("float64", 12). - Time("time", time.Time{}). - Logger() + Time("time", time.Time{}) + _, file, line, _ := runtime.Caller(0) + caller := fmt.Sprintf("%s:%d", file, line+3) + log := ctx.Caller().Logger() log.Log().Msg("") - if got, want := out.String(), `{"foo":"bar","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,"float64":12,"time":"0001-01-01T00:00:00Z"}`+"\n"; got != want { + if got, want := out.String(), `{"foo":"bar","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,"float64":12,"time":"0001-01-01T00:00:00Z","caller":"`+caller+`"}`+"\n"; got != want { t.Errorf("invalid log output:\ngot: %v\nwant: %v", got, want) } } @@ -132,7 +136,10 @@ func TestFields(t *testing.T) { out := &bytes.Buffer{} log := New(out) now := time.Now() + _, file, line, _ := runtime.Caller(0) + caller := fmt.Sprintf("%s:%d", file, line+3) log.Log(). + Caller(). Str("string", "foo"). Bytes("bytes", []byte("bar")). AnErr("some_err", nil). @@ -154,7 +161,7 @@ func TestFields(t *testing.T) { Time("time", time.Time{}). TimeDiff("diff", now, now.Add(-10*time.Second)). Msg("") - if got, want := out.String(), `{"string":"foo","bytes":"bar","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,"float64":12,"dur":1000,"time":"0001-01-01T00:00:00Z","diff":10000}`+"\n"; got != want { + if got, want := out.String(), `{"caller":"`+caller+`","string":"foo","bytes":"bar","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,"float64":12,"dur":1000,"time":"0001-01-01T00:00:00Z","diff":10000}`+"\n"; got != want { t.Errorf("invalid log output:\ngot: %v\nwant: %v", got, want) } }