blob: f222ded797c17a32fd4f6f01e398cde09c796a37 [file] [log] [blame]
khenaidooc6c7bda2020-06-17 17:20:18 -04001package 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
khenaidood948f772021-08-11 17:49:24 -0400125// Error adds an error with the key "error.object" to a Span.LogFields() record
khenaidooc6c7bda2020-06-17 17:20:18 -0400126func Error(err error) Field {
127 return Field{
khenaidood948f772021-08-11 17:49:24 -0400128 key: "error.object",
khenaidooc6c7bda2020-06-17 17:20:18 -0400129 fieldType: errorType,
130 interfaceVal: err,
131 }
132}
133
134// Object adds an object-valued key:value pair to a Span.LogFields() record
khenaidood948f772021-08-11 17:49:24 -0400135// 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.
khenaidooc6c7bda2020-06-17 17:20:18 -0400138func Object(key string, obj interface{}) Field {
139 return Field{
140 key: key,
141 fieldType: objectType,
142 interfaceVal: obj,
143 }
144}
145
khenaidood948f772021-08-11 17:49:24 -0400146// 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
khenaidooc6c7bda2020-06-17 17:20:18 -0400156// 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}