1 package log 2 3 import "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. 10 type 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. 16 var 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. 24 func 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. 52 func 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. 86 func 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. 133 type context struct { 134 logger Logger 135 keyvals []interface{} 136 sKeyvals []interface{} // suffixes 137 hasValuer bool 138 sHasValuer bool 139 } 140 141 func 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. 151 func (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. 174 type LoggerFunc func(...interface{}) error 175 176 // Log implements Logger by calling f(keyvals...). 177 func (f LoggerFunc) Log(keyvals ...interface{}) error { 178 return f(keyvals...) 179 } 180