1package log 2 3import ( 4 "fmt" 5 "math" 6) 7 8type fieldType int 9 10const ( 11 stringType fieldType = iota 12 boolType 13 intType 14 int32Type 15 uint32Type 16 int64Type 17 uint64Type 18 float32Type 19 float64Type 20 errorType 21 objectType 22 lazyLoggerType 23 noopType 24) 25 26// Field instances are constructed via LogBool, LogString, and so on. 27// Tracing implementations may then handle them via the Field.Marshal 28// method. 29// 30// "heavily influenced by" (i.e., partially stolen from) 31// https://github.com/uber-go/zap 32type Field struct { 33 key string 34 fieldType fieldType 35 numericVal int64 36 stringVal string 37 interfaceVal interface{} 38} 39 40// String adds a string-valued key:value pair to a Span.LogFields() record 41func String(key, val string) Field { 42 return Field{ 43 key: key, 44 fieldType: stringType, 45 stringVal: val, 46 } 47} 48 49// Bool adds a bool-valued key:value pair to a Span.LogFields() record 50func Bool(key string, val bool) Field { 51 var numericVal int64 52 if val { 53 numericVal = 1 54 } 55 return Field{ 56 key: key, 57 fieldType: boolType, 58 numericVal: numericVal, 59 } 60} 61 62// Int adds an int-valued key:value pair to a Span.LogFields() record 63func Int(key string, val int) Field { 64 return Field{ 65 key: key, 66 fieldType: intType, 67 numericVal: int64(val), 68 } 69} 70 71// Int32 adds an int32-valued key:value pair to a Span.LogFields() record 72func Int32(key string, val int32) Field { 73 return Field{ 74 key: key, 75 fieldType: int32Type, 76 numericVal: int64(val), 77 } 78} 79 80// Int64 adds an int64-valued key:value pair to a Span.LogFields() record 81func Int64(key string, val int64) Field { 82 return Field{ 83 key: key, 84 fieldType: int64Type, 85 numericVal: val, 86 } 87} 88 89// Uint32 adds a uint32-valued key:value pair to a Span.LogFields() record 90func Uint32(key string, val uint32) Field { 91 return Field{ 92 key: key, 93 fieldType: uint32Type, 94 numericVal: int64(val), 95 } 96} 97 98// Uint64 adds a uint64-valued key:value pair to a Span.LogFields() record 99func Uint64(key string, val uint64) Field { 100 return Field{ 101 key: key, 102 fieldType: uint64Type, 103 numericVal: int64(val), 104 } 105} 106 107// Float32 adds a float32-valued key:value pair to a Span.LogFields() record 108func Float32(key string, val float32) Field { 109 return Field{ 110 key: key, 111 fieldType: float32Type, 112 numericVal: int64(math.Float32bits(val)), 113 } 114} 115 116// Float64 adds a float64-valued key:value pair to a Span.LogFields() record 117func Float64(key string, val float64) Field { 118 return Field{ 119 key: key, 120 fieldType: float64Type, 121 numericVal: int64(math.Float64bits(val)), 122 } 123} 124 125// Error adds an error with the key "error.object" to a Span.LogFields() record 126func Error(err error) Field { 127 return Field{ 128 key: "error.object", 129 fieldType: errorType, 130 interfaceVal: err, 131 } 132} 133 134// Object adds an object-valued key:value pair to a Span.LogFields() record 135// Please pass in an immutable object, otherwise there may be concurrency issues. 136// Such as passing in the map, log.Object may result in "fatal error: concurrent map iteration and map write". 137// Because span is sent asynchronously, it is possible that this map will also be modified. 138func Object(key string, obj interface{}) Field { 139 return Field{ 140 key: key, 141 fieldType: objectType, 142 interfaceVal: obj, 143 } 144} 145 146// Event creates a string-valued Field for span logs with key="event" and value=val. 147func Event(val string) Field { 148 return String("event", val) 149} 150 151// Message creates a string-valued Field for span logs with key="message" and value=val. 152func Message(val string) Field { 153 return String("message", val) 154} 155 156// LazyLogger allows for user-defined, late-bound logging of arbitrary data 157type LazyLogger func(fv Encoder) 158 159// Lazy adds a LazyLogger to a Span.LogFields() record; the tracing 160// implementation will call the LazyLogger function at an indefinite time in 161// the future (after Lazy() returns). 162func Lazy(ll LazyLogger) Field { 163 return Field{ 164 fieldType: lazyLoggerType, 165 interfaceVal: ll, 166 } 167} 168 169// Noop creates a no-op log field that should be ignored by the tracer. 170// It can be used to capture optional fields, for example those that should 171// only be logged in non-production environment: 172// 173// func customerField(order *Order) log.Field { 174// if os.Getenv("ENVIRONMENT") == "dev" { 175// return log.String("customer", order.Customer.ID) 176// } 177// return log.Noop() 178// } 179// 180// span.LogFields(log.String("event", "purchase"), customerField(order)) 181// 182func Noop() Field { 183 return Field{ 184 fieldType: noopType, 185 } 186} 187 188// Encoder allows access to the contents of a Field (via a call to 189// Field.Marshal). 190// 191// Tracer implementations typically provide an implementation of Encoder; 192// OpenTracing callers typically do not need to concern themselves with it. 193type Encoder interface { 194 EmitString(key, value string) 195 EmitBool(key string, value bool) 196 EmitInt(key string, value int) 197 EmitInt32(key string, value int32) 198 EmitInt64(key string, value int64) 199 EmitUint32(key string, value uint32) 200 EmitUint64(key string, value uint64) 201 EmitFloat32(key string, value float32) 202 EmitFloat64(key string, value float64) 203 EmitObject(key string, value interface{}) 204 EmitLazyLogger(value LazyLogger) 205} 206 207// Marshal passes a Field instance through to the appropriate 208// field-type-specific method of an Encoder. 209func (lf Field) Marshal(visitor Encoder) { 210 switch lf.fieldType { 211 case stringType: 212 visitor.EmitString(lf.key, lf.stringVal) 213 case boolType: 214 visitor.EmitBool(lf.key, lf.numericVal != 0) 215 case intType: 216 visitor.EmitInt(lf.key, int(lf.numericVal)) 217 case int32Type: 218 visitor.EmitInt32(lf.key, int32(lf.numericVal)) 219 case int64Type: 220 visitor.EmitInt64(lf.key, int64(lf.numericVal)) 221 case uint32Type: 222 visitor.EmitUint32(lf.key, uint32(lf.numericVal)) 223 case uint64Type: 224 visitor.EmitUint64(lf.key, uint64(lf.numericVal)) 225 case float32Type: 226 visitor.EmitFloat32(lf.key, math.Float32frombits(uint32(lf.numericVal))) 227 case float64Type: 228 visitor.EmitFloat64(lf.key, math.Float64frombits(uint64(lf.numericVal))) 229 case errorType: 230 if err, ok := lf.interfaceVal.(error); ok { 231 visitor.EmitString(lf.key, err.Error()) 232 } else { 233 visitor.EmitString(lf.key, "<nil>") 234 } 235 case objectType: 236 visitor.EmitObject(lf.key, lf.interfaceVal) 237 case lazyLoggerType: 238 visitor.EmitLazyLogger(lf.interfaceVal.(LazyLogger)) 239 case noopType: 240 // intentionally left blank 241 } 242} 243 244// Key returns the field's key. 245func (lf Field) Key() string { 246 return lf.key 247} 248 249// Value returns the field's value as interface{}. 250func (lf Field) Value() interface{} { 251 switch lf.fieldType { 252 case stringType: 253 return lf.stringVal 254 case boolType: 255 return lf.numericVal != 0 256 case intType: 257 return int(lf.numericVal) 258 case int32Type: 259 return int32(lf.numericVal) 260 case int64Type: 261 return int64(lf.numericVal) 262 case uint32Type: 263 return uint32(lf.numericVal) 264 case uint64Type: 265 return uint64(lf.numericVal) 266 case float32Type: 267 return math.Float32frombits(uint32(lf.numericVal)) 268 case float64Type: 269 return math.Float64frombits(uint64(lf.numericVal)) 270 case errorType, objectType, lazyLoggerType: 271 return lf.interfaceVal 272 case noopType: 273 return nil 274 default: 275 return nil 276 } 277} 278 279// String returns a string representation of the key and value. 280func (lf Field) String() string { 281 return fmt.Sprint(lf.key, ":", lf.Value()) 282} 283