ctx: store logger in context when missing or different that stored one

Current implementation stores a copy of the logger as a pointer and
update its content, which is now unecessary since the introduction of
WithContext.

The new WithContext now takes the pointer and store it in the context if
none is stored already or if the last one stored is a different pointer.
This way it is still possible to update the context of a logger stored
in the context, but it is also possible to store a copy of the logger in
a sub-context.

Fix #80
This commit is contained in:
Olivier Poitrey 2018-07-02 15:58:04 -07:00
parent 1c6d99b455
commit eb64a6ddbc
2 changed files with 34 additions and 17 deletions

19
ctx.go
View File

@ -2,38 +2,39 @@ package zerolog
import ( import (
"context" "context"
"io/ioutil"
) )
var disabledLogger *Logger var disabledLogger *Logger
func init() { func init() {
l := New(ioutil.Discard).Level(Disabled) l := Nop()
disabledLogger = &l disabledLogger = &l
} }
type ctxKey struct{} type ctxKey struct{}
// WithContext returns a copy of ctx with l associated. If an instance of Logger // WithContext returns a copy of ctx with l associated. If an instance of Logger
// is already in the context, the pointer to this logger is updated with l. // is already in the context, the context is not updated.
// //
// For instance, to add a field to an existing logger in the context, use this // For instance, to add a field to an existing logger in the context, use this
// notation: // notation:
// //
// ctx := r.Context() // ctx := r.Context()
// l := zerolog.Ctx(ctx) // l := zerolog.Ctx(ctx)
// ctx = l.With().Str("foo", "bar").WithContext(ctx) // l.UpdateContext(func(c Context) Context {
func (l Logger) WithContext(ctx context.Context) context.Context { // return c.Str("bar", "baz")
// })
func (l *Logger) WithContext(ctx context.Context) context.Context {
if lp, ok := ctx.Value(ctxKey{}).(*Logger); ok { if lp, ok := ctx.Value(ctxKey{}).(*Logger); ok {
// Update existing pointer. if lp == l {
*lp = l // Do not store same logger.
return ctx return ctx
} }
if l.level == Disabled { } else if l.level == Disabled {
// Do not store disabled logger. // Do not store disabled logger.
return ctx return ctx
} }
return context.WithValue(ctx, ctxKey{}, &l) return context.WithValue(ctx, ctxKey{}, l)
} }
// Ctx returns the Logger associated with the ctx. If no logger // Ctx returns the Logger associated with the ctx. If no logger

View File

@ -30,18 +30,34 @@ func TestCtx(t *testing.T) {
} }
func TestCtxDisabled(t *testing.T) { func TestCtxDisabled(t *testing.T) {
ctx := disabledLogger.WithContext(context.Background()) dl := New(ioutil.Discard).Level(Disabled)
ctx := dl.WithContext(context.Background())
if ctx != context.Background() { if ctx != context.Background() {
t.Error("WithContext stored a disabled logger") t.Error("WithContext stored a disabled logger")
} }
ctx = New(ioutil.Discard).WithContext(ctx) l := New(ioutil.Discard).With().Str("foo", "bar").Logger()
if reflect.DeepEqual(Ctx(ctx), disabledLogger) { ctx = l.WithContext(ctx)
if Ctx(ctx) != &l {
t.Error("WithContext did not store logger") t.Error("WithContext did not store logger")
} }
ctx = disabledLogger.WithContext(ctx) l.UpdateContext(func(c Context) Context {
if !reflect.DeepEqual(Ctx(ctx), disabledLogger) { return c.Str("bar", "baz")
t.Error("WithContext did not update logger pointer with disabled logger") })
ctx = l.WithContext(ctx)
if Ctx(ctx) != &l {
t.Error("WithContext did not store updated logger")
}
l = l.Level(DebugLevel)
ctx = l.WithContext(ctx)
if Ctx(ctx) != &l {
t.Error("WithContext did not store copied logger")
}
ctx = dl.WithContext(ctx)
if Ctx(ctx) != &dl {
t.Error("WithContext did not overide logger with a disabled logger")
} }
} }