|
|
|
|
@ -6,7 +6,7 @@ import "errors" |
|
|
|
|
// log event from keyvals, a variadic sequence of alternating keys and values.
|
|
|
|
|
// Implementations must be safe for concurrent use by multiple goroutines. In
|
|
|
|
|
// particular, any implementation of Logger that appends to keyvals or
|
|
|
|
|
// modifies any of its elements must make a copy first.
|
|
|
|
|
// modifies or retains any of its elements must make a copy first.
|
|
|
|
|
type Logger interface { |
|
|
|
|
Log(keyvals ...interface{}) error |
|
|
|
|
} |
|
|
|
|
@ -15,87 +15,22 @@ type Logger interface { |
|
|
|
|
// the missing value.
|
|
|
|
|
var ErrMissingValue = errors.New("(MISSING)") |
|
|
|
|
|
|
|
|
|
// NewContext returns a new Context that logs to logger.
|
|
|
|
|
func NewContext(logger Logger) *Context { |
|
|
|
|
if c, ok := logger.(*Context); ok { |
|
|
|
|
return c |
|
|
|
|
} |
|
|
|
|
return &Context{logger: logger} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// Context must always have the same number of stack frames between calls to
|
|
|
|
|
// its Log method and the eventual binding of Valuers to their value. This
|
|
|
|
|
// requirement comes from the functional requirement to allow a context to
|
|
|
|
|
// resolve application call site information for a log.Caller stored in the
|
|
|
|
|
// context. To do this we must be able to predict the number of logging
|
|
|
|
|
// functions on the stack when bindValues is called.
|
|
|
|
|
//
|
|
|
|
|
// Three implementation details provide the needed stack depth consistency.
|
|
|
|
|
// The first two of these details also result in better amortized performance,
|
|
|
|
|
// and thus make sense even without the requirements regarding stack depth.
|
|
|
|
|
// The third detail, however, is subtle and tied to the implementation of the
|
|
|
|
|
// Go compiler.
|
|
|
|
|
// With returns a new contextual logger with keyvals prepended to those passed
|
|
|
|
|
// to calls to Log. If logger is also a contextual logger created by With or
|
|
|
|
|
// WithPrefix, keyvals is appended to the existing context.
|
|
|
|
|
//
|
|
|
|
|
// 1. NewContext avoids introducing an additional layer when asked to
|
|
|
|
|
// wrap another Context.
|
|
|
|
|
// 2. With avoids introducing an additional layer by returning a newly
|
|
|
|
|
// constructed Context with a merged keyvals rather than simply
|
|
|
|
|
// wrapping the existing Context.
|
|
|
|
|
// 3. All of Context's methods take pointer receivers even though they
|
|
|
|
|
// do not mutate the Context.
|
|
|
|
|
//
|
|
|
|
|
// Before explaining the last detail, first some background. The Go compiler
|
|
|
|
|
// generates wrapper methods to implement the auto dereferencing behavior when
|
|
|
|
|
// calling a value method through a pointer variable. These wrapper methods
|
|
|
|
|
// are also used when calling a value method through an interface variable
|
|
|
|
|
// because interfaces store a pointer to the underlying concrete value.
|
|
|
|
|
// Calling a pointer receiver through an interface does not require generating
|
|
|
|
|
// an additional function.
|
|
|
|
|
//
|
|
|
|
|
// If Context had value methods then calling Context.Log through a variable
|
|
|
|
|
// with type Logger would have an extra stack frame compared to calling
|
|
|
|
|
// Context.Log through a variable with type Context. Using pointer receivers
|
|
|
|
|
// avoids this problem.
|
|
|
|
|
|
|
|
|
|
// A Context wraps a Logger and holds keyvals that it includes in all log
|
|
|
|
|
// events. When logging, a Context replaces all value elements (odd indexes)
|
|
|
|
|
// containing a Valuer with their generated value for each call to its Log
|
|
|
|
|
// method.
|
|
|
|
|
type Context struct { |
|
|
|
|
logger Logger |
|
|
|
|
keyvals []interface{} |
|
|
|
|
hasValuer bool |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// Log replaces all value elements (odd indexes) containing a Valuer in the
|
|
|
|
|
// stored context with their generated value, appends keyvals, and passes the
|
|
|
|
|
// result to the wrapped Logger.
|
|
|
|
|
func (l *Context) Log(keyvals ...interface{}) error { |
|
|
|
|
kvs := append(l.keyvals, keyvals...) |
|
|
|
|
if len(kvs)%2 != 0 { |
|
|
|
|
kvs = append(kvs, ErrMissingValue) |
|
|
|
|
} |
|
|
|
|
if l.hasValuer { |
|
|
|
|
// If no keyvals were appended above then we must copy l.keyvals so
|
|
|
|
|
// that future log events will reevaluate the stored Valuers.
|
|
|
|
|
if len(keyvals) == 0 { |
|
|
|
|
kvs = append([]interface{}{}, l.keyvals...) |
|
|
|
|
} |
|
|
|
|
bindValues(kvs[:len(l.keyvals)]) |
|
|
|
|
} |
|
|
|
|
return l.logger.Log(kvs...) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// With returns a new Context with keyvals appended to those of the receiver.
|
|
|
|
|
func (l *Context) With(keyvals ...interface{}) *Context { |
|
|
|
|
// The returned Logger replaces all value elements (odd indexes) containing a
|
|
|
|
|
// Valuer with their generated value for each call to its Log method.
|
|
|
|
|
func With(logger Logger, keyvals ...interface{}) Logger { |
|
|
|
|
if len(keyvals) == 0 { |
|
|
|
|
return l |
|
|
|
|
return logger |
|
|
|
|
} |
|
|
|
|
l := newContext(logger) |
|
|
|
|
kvs := append(l.keyvals, keyvals...) |
|
|
|
|
if len(kvs)%2 != 0 { |
|
|
|
|
kvs = append(kvs, ErrMissingValue) |
|
|
|
|
} |
|
|
|
|
return &Context{ |
|
|
|
|
return &context{ |
|
|
|
|
logger: l.logger, |
|
|
|
|
// Limiting the capacity of the stored keyvals ensures that a new
|
|
|
|
|
// backing array is created if the slice must grow in Log or With.
|
|
|
|
|
@ -106,12 +41,17 @@ func (l *Context) With(keyvals ...interface{}) *Context { |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// WithPrefix returns a new Context with keyvals prepended to those of the
|
|
|
|
|
// receiver.
|
|
|
|
|
func (l *Context) WithPrefix(keyvals ...interface{}) *Context { |
|
|
|
|
// WithPrefix returns a new contextual logger with keyvals prepended to those
|
|
|
|
|
// passed to calls to Log. If logger is also a contextual logger created by
|
|
|
|
|
// With or WithPrefix, keyvals is prepended to the existing context.
|
|
|
|
|
//
|
|
|
|
|
// The returned Logger replaces all value elements (odd indexes) containing a
|
|
|
|
|
// Valuer with their generated value for each call to its Log method.
|
|
|
|
|
func WithPrefix(logger Logger, keyvals ...interface{}) Logger { |
|
|
|
|
if len(keyvals) == 0 { |
|
|
|
|
return l |
|
|
|
|
return logger |
|
|
|
|
} |
|
|
|
|
l := newContext(logger) |
|
|
|
|
// Limiting the capacity of the stored keyvals ensures that a new
|
|
|
|
|
// backing array is created if the slice must grow in Log or With.
|
|
|
|
|
// Using the extra capacity without copying risks a data race that
|
|
|
|
|
@ -126,13 +66,64 @@ func (l *Context) WithPrefix(keyvals ...interface{}) *Context { |
|
|
|
|
kvs = append(kvs, ErrMissingValue) |
|
|
|
|
} |
|
|
|
|
kvs = append(kvs, l.keyvals...) |
|
|
|
|
return &Context{ |
|
|
|
|
return &context{ |
|
|
|
|
logger: l.logger, |
|
|
|
|
keyvals: kvs, |
|
|
|
|
hasValuer: l.hasValuer || containsValuer(keyvals), |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// context is the Logger implementation returned by With and WithPrefix. It
|
|
|
|
|
// wraps a Logger and holds keyvals that it includes in all log events. Its
|
|
|
|
|
// Log method calls bindValues to generate values for each Valuer in the
|
|
|
|
|
// context keyvals.
|
|
|
|
|
//
|
|
|
|
|
// A context must always have the same number of stack frames between calls to
|
|
|
|
|
// its Log method and the eventual binding of Valuers to their value. This
|
|
|
|
|
// requirement comes from the functional requirement to allow a context to
|
|
|
|
|
// resolve application call site information for a Caller stored in the
|
|
|
|
|
// context. To do this we must be able to predict the number of logging
|
|
|
|
|
// functions on the stack when bindValues is called.
|
|
|
|
|
//
|
|
|
|
|
// Two implementation details provide the needed stack depth consistency.
|
|
|
|
|
//
|
|
|
|
|
// 1. newContext avoids introducing an additional layer when asked to
|
|
|
|
|
// wrap another context.
|
|
|
|
|
// 2. With and WithPrefix avoid introducing an additional layer by
|
|
|
|
|
// returning a newly constructed context with a merged keyvals rather
|
|
|
|
|
// than simply wrapping the existing context.
|
|
|
|
|
type context struct { |
|
|
|
|
logger Logger |
|
|
|
|
keyvals []interface{} |
|
|
|
|
hasValuer bool |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
func newContext(logger Logger) *context { |
|
|
|
|
if c, ok := logger.(*context); ok { |
|
|
|
|
return c |
|
|
|
|
} |
|
|
|
|
return &context{logger: logger} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// Log replaces all value elements (odd indexes) containing a Valuer in the
|
|
|
|
|
// stored context with their generated value, appends keyvals, and passes the
|
|
|
|
|
// result to the wrapped Logger.
|
|
|
|
|
func (l *context) Log(keyvals ...interface{}) error { |
|
|
|
|
kvs := append(l.keyvals, keyvals...) |
|
|
|
|
if len(kvs)%2 != 0 { |
|
|
|
|
kvs = append(kvs, ErrMissingValue) |
|
|
|
|
} |
|
|
|
|
if l.hasValuer { |
|
|
|
|
// If no keyvals were appended above then we must copy l.keyvals so
|
|
|
|
|
// that future log events will reevaluate the stored Valuers.
|
|
|
|
|
if len(keyvals) == 0 { |
|
|
|
|
kvs = append([]interface{}{}, l.keyvals...) |
|
|
|
|
} |
|
|
|
|
bindValues(kvs[:len(l.keyvals)]) |
|
|
|
|
} |
|
|
|
|
return l.logger.Log(kvs...) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// LoggerFunc is an adapter to allow use of ordinary functions as Loggers. If
|
|
|
|
|
// f is a function with the appropriate signature, LoggerFunc(f) is a Logger
|
|
|
|
|
// object that calls f.
|
|
|
|
|
|