1package log 2 3import "errors" 4 5// Logger is the fundamental interface for all log operations. Log creates a 6// log event from keyvals, a variadic sequence of alternating keys and values. 7// Implementations must be safe for concurrent use by multiple goroutines. In 8// particular, any implementation of Logger that appends to keyvals or 9// modifies or retains any of its elements must make a copy first. 10type Logger interface { 11 Log(keyvals ...interface{}) error 12} 13 14// ErrMissingValue is appended to keyvals slices with odd length to substitute 15// the missing value. 16var ErrMissingValue = errors.New("(MISSING)") 17 18// With returns a new contextual logger with keyvals prepended to those passed 19// to calls to Log. If logger is also a contextual logger created by With, 20// WithPrefix, or WithSuffix, keyvals is appended to the existing context. 21// 22// The returned Logger replaces all value elements (odd indexes) containing a 23// Valuer with their generated value for each call to its Log method. 24func With(logger Logger, keyvals ...interface{}) Logger { 25 if len(keyvals) == 0 { 26 return logger 27 } 28 l := newContext(logger) 29 kvs := append(l.keyvals, keyvals...) 30 if len(kvs)%2 != 0 { 31 kvs = append(kvs, ErrMissingValue) 32 } 33 return &context{ 34 logger: l.logger, 35 // Limiting the capacity of the stored keyvals ensures that a new 36 // backing array is created if the slice must grow in Log or With. 37 // Using the extra capacity without copying risks a data race that 38 // would violate the Logger interface contract. 39 keyvals: kvs[:len(kvs):len(kvs)], 40 hasValuer: l.hasValuer || containsValuer(keyvals), 41 sKeyvals: l.sKeyvals, 42 sHasValuer: l.sHasValuer, 43 } 44} 45 46// WithPrefix returns a new contextual logger with keyvals prepended to those 47// passed to calls to Log. If logger is also a contextual logger created by 48// With, WithPrefix, or WithSuffix, keyvals is prepended to the existing context. 49// 50// The returned Logger replaces all value elements (odd indexes) containing a 51// Valuer with their generated value for each call to its Log method. 52func WithPrefix(logger Logger, keyvals ...interface{}) Logger { 53 if len(keyvals) == 0 { 54 return logger 55 } 56 l := newContext(logger) 57 // Limiting the capacity of the stored keyvals ensures that a new 58 // backing array is created if the slice must grow in Log or With. 59 // Using the extra capacity without copying risks a data race that 60 // would violate the Logger interface contract. 61 n := len(l.keyvals) + len(keyvals) 62 if len(keyvals)%2 != 0 { 63 n++ 64 } 65 kvs := make([]interface{}, 0, n) 66 kvs = append(kvs, keyvals...) 67 if len(kvs)%2 != 0 { 68 kvs = append(kvs, ErrMissingValue) 69 } 70 kvs = append(kvs, l.keyvals...) 71 return &context{ 72 logger: l.logger, 73 keyvals: kvs, 74 hasValuer: l.hasValuer || containsValuer(keyvals), 75 sKeyvals: l.sKeyvals, 76 sHasValuer: l.sHasValuer, 77 } 78} 79 80// WithSuffix returns a new contextual logger with keyvals appended to those 81// passed to calls to Log. If logger is also a contextual logger created by 82// With, WithPrefix, or WithSuffix, keyvals is appended to the existing context. 83// 84// The returned Logger replaces all value elements (odd indexes) containing a 85// Valuer with their generated value for each call to its Log method. 86func WithSuffix(logger Logger, keyvals ...interface{}) Logger { 87 if len(keyvals) == 0 { 88 return logger 89 } 90 l := newContext(logger) 91 // Limiting the capacity of the stored keyvals ensures that a new 92 // backing array is created if the slice must grow in Log or With. 93 // Using the extra capacity without copying risks a data race that 94 // would violate the Logger interface contract. 95 n := len(l.sKeyvals) + len(keyvals) 96 if len(keyvals)%2 != 0 { 97 n++ 98 } 99 kvs := make([]interface{}, 0, n) 100 kvs = append(kvs, keyvals...) 101 if len(kvs)%2 != 0 { 102 kvs = append(kvs, ErrMissingValue) 103 } 104 kvs = append(l.sKeyvals, kvs...) 105 return &context{ 106 logger: l.logger, 107 keyvals: l.keyvals, 108 hasValuer: l.hasValuer, 109 sKeyvals: kvs, 110 sHasValuer: l.sHasValuer || containsValuer(keyvals), 111 } 112} 113 114// context is the Logger implementation returned by With, WithPrefix, and 115// WithSuffix. It wraps a Logger and holds keyvals that it includes in all 116// log events. Its Log method calls bindValues to generate values for each 117// Valuer in the context keyvals. 118// 119// A context must always have the same number of stack frames between calls to 120// its Log method and the eventual binding of Valuers to their value. This 121// requirement comes from the functional requirement to allow a context to 122// resolve application call site information for a Caller stored in the 123// context. To do this we must be able to predict the number of logging 124// functions on the stack when bindValues is called. 125// 126// Two implementation details provide the needed stack depth consistency. 127// 128// 1. newContext avoids introducing an additional layer when asked to 129// wrap another context. 130// 2. With, WithPrefix, and WithSuffix avoid introducing an additional 131// layer by returning a newly constructed context with a merged keyvals 132// rather than simply wrapping the existing context. 133type context struct { 134 logger Logger 135 keyvals []interface{} 136 sKeyvals []interface{} // suffixes 137 hasValuer bool 138 sHasValuer bool 139} 140 141func newContext(logger Logger) *context { 142 if c, ok := logger.(*context); ok { 143 return c 144 } 145 return &context{logger: logger} 146} 147 148// Log replaces all value elements (odd indexes) containing a Valuer in the 149// stored context with their generated value, appends keyvals, and passes the 150// result to the wrapped Logger. 151func (l *context) Log(keyvals ...interface{}) error { 152 kvs := append(l.keyvals, keyvals...) 153 if len(kvs)%2 != 0 { 154 kvs = append(kvs, ErrMissingValue) 155 } 156 if l.hasValuer { 157 // If no keyvals were appended above then we must copy l.keyvals so 158 // that future log events will reevaluate the stored Valuers. 159 if len(keyvals) == 0 { 160 kvs = append([]interface{}{}, l.keyvals...) 161 } 162 bindValues(kvs[:(len(l.keyvals))]) 163 } 164 kvs = append(kvs, l.sKeyvals...) 165 if l.sHasValuer { 166 bindValues(kvs[len(kvs)-len(l.sKeyvals):]) 167 } 168 return l.logger.Log(kvs...) 169} 170 171// LoggerFunc is an adapter to allow use of ordinary functions as Loggers. If 172// f is a function with the appropriate signature, LoggerFunc(f) is a Logger 173// object that calls f. 174type LoggerFunc func(...interface{}) error 175 176// Log implements Logger by calling f(keyvals...). 177func (f LoggerFunc) Log(keyvals ...interface{}) error { 178 return f(keyvals...) 179} 180