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" to a Span.LogFields() record 126func Error(err error) Field { 127 return Field{ 128 key: "error", 129 fieldType: errorType, 130 interfaceVal: err, 131 } 132} 133 134// Object adds an object-valued key:value pair to a Span.LogFields() record 135func Object(key string, obj interface{}) Field { 136 return Field{ 137 key: key, 138 fieldType: objectType, 139 interfaceVal: obj, 140 } 141} 142 143// LazyLogger allows for user-defined, late-bound logging of arbitrary data 144type LazyLogger func(fv Encoder) 145 146// Lazy adds a LazyLogger to a Span.LogFields() record; the tracing 147// implementation will call the LazyLogger function at an indefinite time in 148// the future (after Lazy() returns). 149func Lazy(ll LazyLogger) Field { 150 return Field{ 151 fieldType: lazyLoggerType, 152 interfaceVal: ll, 153 } 154} 155 156// Noop creates a no-op log field that should be ignored by the tracer. 157// It can be used to capture optional fields, for example those that should 158// only be logged in non-production environment: 159// 160// func customerField(order *Order) log.Field { 161// if os.Getenv("ENVIRONMENT") == "dev" { 162// return log.String("customer", order.Customer.ID) 163// } 164// return log.Noop() 165// } 166// 167// span.LogFields(log.String("event", "purchase"), customerField(order)) 168// 169func Noop() Field { 170 return Field{ 171 fieldType: noopType, 172 } 173} 174 175// Encoder allows access to the contents of a Field (via a call to 176// Field.Marshal). 177// 178// Tracer implementations typically provide an implementation of Encoder; 179// OpenTracing callers typically do not need to concern themselves with it. 180type Encoder interface { 181 EmitString(key, value string) 182 EmitBool(key string, value bool) 183 EmitInt(key string, value int) 184 EmitInt32(key string, value int32) 185 EmitInt64(key string, value int64) 186 EmitUint32(key string, value uint32) 187 EmitUint64(key string, value uint64) 188 EmitFloat32(key string, value float32) 189 EmitFloat64(key string, value float64) 190 EmitObject(key string, value interface{}) 191 EmitLazyLogger(value LazyLogger) 192} 193 194// Marshal passes a Field instance through to the appropriate 195// field-type-specific method of an Encoder. 196func (lf Field) Marshal(visitor Encoder) { 197 switch lf.fieldType { 198 case stringType: 199 visitor.EmitString(lf.key, lf.stringVal) 200 case boolType: 201 visitor.EmitBool(lf.key, lf.numericVal != 0) 202 case intType: 203 visitor.EmitInt(lf.key, int(lf.numericVal)) 204 case int32Type: 205 visitor.EmitInt32(lf.key, int32(lf.numericVal)) 206 case int64Type: 207 visitor.EmitInt64(lf.key, int64(lf.numericVal)) 208 case uint32Type: 209 visitor.EmitUint32(lf.key, uint32(lf.numericVal)) 210 case uint64Type: 211 visitor.EmitUint64(lf.key, uint64(lf.numericVal)) 212 case float32Type: 213 visitor.EmitFloat32(lf.key, math.Float32frombits(uint32(lf.numericVal))) 214 case float64Type: 215 visitor.EmitFloat64(lf.key, math.Float64frombits(uint64(lf.numericVal))) 216 case errorType: 217 if err, ok := lf.interfaceVal.(error); ok { 218 visitor.EmitString(lf.key, err.Error()) 219 } else { 220 visitor.EmitString(lf.key, "<nil>") 221 } 222 case objectType: 223 visitor.EmitObject(lf.key, lf.interfaceVal) 224 case lazyLoggerType: 225 visitor.EmitLazyLogger(lf.interfaceVal.(LazyLogger)) 226 case noopType: 227 // intentionally left blank 228 } 229} 230 231// Key returns the field's key. 232func (lf Field) Key() string { 233 return lf.key 234} 235 236// Value returns the field's value as interface{}. 237func (lf Field) Value() interface{} { 238 switch lf.fieldType { 239 case stringType: 240 return lf.stringVal 241 case boolType: 242 return lf.numericVal != 0 243 case intType: 244 return int(lf.numericVal) 245 case int32Type: 246 return int32(lf.numericVal) 247 case int64Type: 248 return int64(lf.numericVal) 249 case uint32Type: 250 return uint32(lf.numericVal) 251 case uint64Type: 252 return uint64(lf.numericVal) 253 case float32Type: 254 return math.Float32frombits(uint32(lf.numericVal)) 255 case float64Type: 256 return math.Float64frombits(uint64(lf.numericVal)) 257 case errorType, objectType, lazyLoggerType: 258 return lf.interfaceVal 259 case noopType: 260 return nil 261 default: 262 return nil 263 } 264} 265 266// String returns a string representation of the key and value. 267func (lf Field) String() string { 268 return fmt.Sprint(lf.key, ":", lf.Value()) 269} 270