22 | 22 |
var ErrMissingValue = errors.New("(MISSING)")
|
23 | 23 |
|
24 | 24 |
// NewContext returns a new Context that logs to logger.
|
25 | |
func NewContext(logger Logger) Context {
|
26 | |
if c, ok := logger.(Context); ok {
|
|
25 |
func NewContext(logger Logger) *Context {
|
|
26 |
if c, ok := logger.(*Context); ok {
|
27 | 27 |
return c
|
28 | 28 |
}
|
29 | |
return Context{logger: logger}
|
|
29 |
return &Context{logger: logger}
|
30 | 30 |
}
|
|
31 |
|
|
32 |
// Context must always have the same number of stack frames between calls to
|
|
33 |
// its Log method and the eventual binding of Valuers to their value. This
|
|
34 |
// requirement comes from the functional requirement to allow a context to
|
|
35 |
// resolve application call site information for a log.Caller stored in the
|
|
36 |
// context. To do this we must be able to predict the number of logging
|
|
37 |
// functions on the stack when bindValues is called.
|
|
38 |
//
|
|
39 |
// Three implementation details provide the needed stack depth consistency.
|
|
40 |
// The first two of these details also result in better amortized performance,
|
|
41 |
// and thus make sense even without the requirements regarding stack depth.
|
|
42 |
// The third detail, however, is subtle and tied to the implementation of the
|
|
43 |
// Go compiler.
|
|
44 |
//
|
|
45 |
// 1. NewContext avoids introducing an additional layer when asked to
|
|
46 |
// wrap another Context.
|
|
47 |
// 2. With avoids introducing an additional layer by returning a newly
|
|
48 |
// constructed Context with a merged keyvals rather than simply
|
|
49 |
// wrapping the existing Context.
|
|
50 |
// 3. All of Context's methods take pointer receivers even though they
|
|
51 |
// do not mutate the Context.
|
|
52 |
//
|
|
53 |
// Before explaining the last detail, first some background. The Go compiler
|
|
54 |
// generates wrapper methods to implement the auto dereferencing behavior when
|
|
55 |
// calling a value method through a pointer variable. These wrapper methods
|
|
56 |
// are also used when calling a value method through an interface variable
|
|
57 |
// because interfaces store a pointer to the underlying concrete value.
|
|
58 |
// Calling a pointer receiver through an interface does not require generating
|
|
59 |
// an additional function.
|
|
60 |
//
|
|
61 |
// If Context had value methods then calling Context.Log through a variable
|
|
62 |
// with type Logger would have an extra stack frame compared to calling
|
|
63 |
// Context.Log through a variable with type Context. Using pointer receivers
|
|
64 |
// avoids this problem.
|
31 | 65 |
|
32 | 66 |
// A Context wraps a Logger and holds keyvals that it includes in all log
|
33 | 67 |
// events. When logging, a Context replaces all value elements (odd indexes)
|
|
42 | 76 |
// Log replaces all value elements (odd indexes) containing a Valuer in the
|
43 | 77 |
// stored context with their generated value, appends keyvals, and passes the
|
44 | 78 |
// result to the wrapped Logger.
|
45 | |
func (l Context) Log(keyvals ...interface{}) error {
|
|
79 |
func (l *Context) Log(keyvals ...interface{}) error {
|
46 | 80 |
kvs := append(l.keyvals, keyvals...)
|
47 | 81 |
if len(kvs)%2 != 0 {
|
48 | 82 |
kvs = append(kvs, ErrMissingValue)
|
|
59 | 93 |
}
|
60 | 94 |
|
61 | 95 |
// With returns a new Context with keyvals appended to those of the receiver.
|
62 | |
func (l Context) With(keyvals ...interface{}) Context {
|
|
96 |
func (l *Context) With(keyvals ...interface{}) *Context {
|
63 | 97 |
if len(keyvals) == 0 {
|
64 | 98 |
return l
|
65 | 99 |
}
|
|
67 | 101 |
if len(kvs)%2 != 0 {
|
68 | 102 |
kvs = append(kvs, ErrMissingValue)
|
69 | 103 |
}
|
70 | |
return Context{
|
|
104 |
return &Context{
|
71 | 105 |
logger: l.logger,
|
72 | 106 |
// Limiting the capacity of the stored keyvals ensures that a new
|
73 | 107 |
// backing array is created if the slice must grow in Log or With.
|
|
80 | 114 |
|
81 | 115 |
// WithPrefix returns a new Context with keyvals prepended to those of the
|
82 | 116 |
// receiver.
|
83 | |
func (l Context) WithPrefix(keyvals ...interface{}) Context {
|
|
117 |
func (l *Context) WithPrefix(keyvals ...interface{}) *Context {
|
84 | 118 |
if len(keyvals) == 0 {
|
85 | 119 |
return l
|
86 | 120 |
}
|
|
98 | 132 |
kvs = append(kvs, ErrMissingValue)
|
99 | 133 |
}
|
100 | 134 |
kvs = append(kvs, l.keyvals...)
|
101 | |
return Context{
|
|
135 |
return &Context{
|
102 | 136 |
logger: l.logger,
|
103 | 137 |
keyvals: kvs,
|
104 | 138 |
hasValuer: l.hasValuer || containsValuer(keyvals),
|