blob: 50feea341a732865402478bdd1f741bf3df8a4b6 [file] [log] [blame]
mpagenkoaf801632020-07-03 10:00:42 +00001package 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}