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)
24
25// Field instances are constructed via LogBool, LogString, and so on.
26// Tracing implementations may then handle them via the Field.Marshal
27// method.
28//
29// "heavily influenced by" (i.e., partially stolen from)
30// https://github.com/uber-go/zap
31type Field struct {
32	key          string
33	fieldType    fieldType
34	numericVal   int64
35	stringVal    string
36	interfaceVal interface{}
37}
38
39// String adds a string-valued key:value pair to a Span.LogFields() record
40func String(key, val string) Field {
41	return Field{
42		key:       key,
43		fieldType: stringType,
44		stringVal: val,
45	}
46}
47
48// Bool adds a bool-valued key:value pair to a Span.LogFields() record
49func Bool(key string, val bool) Field {
50	var numericVal int64
51	if val {
52		numericVal = 1
53	}
54	return Field{
55		key:        key,
56		fieldType:  boolType,
57		numericVal: numericVal,
58	}
59}
60
61// Int adds an int-valued key:value pair to a Span.LogFields() record
62func Int(key string, val int) Field {
63	return Field{
64		key:        key,
65		fieldType:  intType,
66		numericVal: int64(val),
67	}
68}
69
70// Int32 adds an int32-valued key:value pair to a Span.LogFields() record
71func Int32(key string, val int32) Field {
72	return Field{
73		key:        key,
74		fieldType:  int32Type,
75		numericVal: int64(val),
76	}
77}
78
79// Int64 adds an int64-valued key:value pair to a Span.LogFields() record
80func Int64(key string, val int64) Field {
81	return Field{
82		key:        key,
83		fieldType:  int64Type,
84		numericVal: val,
85	}
86}
87
88// Uint32 adds a uint32-valued key:value pair to a Span.LogFields() record
89func Uint32(key string, val uint32) Field {
90	return Field{
91		key:        key,
92		fieldType:  uint32Type,
93		numericVal: int64(val),
94	}
95}
96
97// Uint64 adds a uint64-valued key:value pair to a Span.LogFields() record
98func Uint64(key string, val uint64) Field {
99	return Field{
100		key:        key,
101		fieldType:  uint64Type,
102		numericVal: int64(val),
103	}
104}
105
106// Float32 adds a float32-valued key:value pair to a Span.LogFields() record
107func Float32(key string, val float32) Field {
108	return Field{
109		key:        key,
110		fieldType:  float32Type,
111		numericVal: int64(math.Float32bits(val)),
112	}
113}
114
115// Float64 adds a float64-valued key:value pair to a Span.LogFields() record
116func Float64(key string, val float64) Field {
117	return Field{
118		key:        key,
119		fieldType:  float64Type,
120		numericVal: int64(math.Float64bits(val)),
121	}
122}
123
124// Error adds an error with the key "error" to a Span.LogFields() record
125func Error(err error) Field {
126	return Field{
127		key:          "error",
128		fieldType:    errorType,
129		interfaceVal: err,
130	}
131}
132
133// Object adds an object-valued key:value pair to a Span.LogFields() record
134func Object(key string, obj interface{}) Field {
135	return Field{
136		key:          key,
137		fieldType:    objectType,
138		interfaceVal: obj,
139	}
140}
141
142// LazyLogger allows for user-defined, late-bound logging of arbitrary data
143type LazyLogger func(fv Encoder)
144
145// Lazy adds a LazyLogger to a Span.LogFields() record; the tracing
146// implementation will call the LazyLogger function at an indefinite time in
147// the future (after Lazy() returns).
148func Lazy(ll LazyLogger) Field {
149	return Field{
150		fieldType:    lazyLoggerType,
151		interfaceVal: ll,
152	}
153}
154
155// Encoder allows access to the contents of a Field (via a call to
156// Field.Marshal).
157//
158// Tracer implementations typically provide an implementation of Encoder;
159// OpenTracing callers typically do not need to concern themselves with it.
160type Encoder interface {
161	EmitString(key, value string)
162	EmitBool(key string, value bool)
163	EmitInt(key string, value int)
164	EmitInt32(key string, value int32)
165	EmitInt64(key string, value int64)
166	EmitUint32(key string, value uint32)
167	EmitUint64(key string, value uint64)
168	EmitFloat32(key string, value float32)
169	EmitFloat64(key string, value float64)
170	EmitObject(key string, value interface{})
171	EmitLazyLogger(value LazyLogger)
172}
173
174// Marshal passes a Field instance through to the appropriate
175// field-type-specific method of an Encoder.
176func (lf Field) Marshal(visitor Encoder) {
177	switch lf.fieldType {
178	case stringType:
179		visitor.EmitString(lf.key, lf.stringVal)
180	case boolType:
181		visitor.EmitBool(lf.key, lf.numericVal != 0)
182	case intType:
183		visitor.EmitInt(lf.key, int(lf.numericVal))
184	case int32Type:
185		visitor.EmitInt32(lf.key, int32(lf.numericVal))
186	case int64Type:
187		visitor.EmitInt64(lf.key, int64(lf.numericVal))
188	case uint32Type:
189		visitor.EmitUint32(lf.key, uint32(lf.numericVal))
190	case uint64Type:
191		visitor.EmitUint64(lf.key, uint64(lf.numericVal))
192	case float32Type:
193		visitor.EmitFloat32(lf.key, math.Float32frombits(uint32(lf.numericVal)))
194	case float64Type:
195		visitor.EmitFloat64(lf.key, math.Float64frombits(uint64(lf.numericVal)))
196	case errorType:
197		if err, ok := lf.interfaceVal.(error); ok {
198			visitor.EmitString(lf.key, err.Error())
199		} else {
200			visitor.EmitString(lf.key, "<nil>")
201		}
202	case objectType:
203		visitor.EmitObject(lf.key, lf.interfaceVal)
204	case lazyLoggerType:
205		visitor.EmitLazyLogger(lf.interfaceVal.(LazyLogger))
206	}
207}
208
209// Key returns the field's key.
210func (lf Field) Key() string {
211	return lf.key
212}
213
214// Value returns the field's value as interface{}.
215func (lf Field) Value() interface{} {
216	switch lf.fieldType {
217	case stringType:
218		return lf.stringVal
219	case boolType:
220		return lf.numericVal != 0
221	case intType:
222		return int(lf.numericVal)
223	case int32Type:
224		return int32(lf.numericVal)
225	case int64Type:
226		return int64(lf.numericVal)
227	case uint32Type:
228		return uint32(lf.numericVal)
229	case uint64Type:
230		return uint64(lf.numericVal)
231	case float32Type:
232		return math.Float32frombits(uint32(lf.numericVal))
233	case float64Type:
234		return math.Float64frombits(uint64(lf.numericVal))
235	case errorType, objectType, lazyLoggerType:
236		return lf.interfaceVal
237	default:
238		return nil
239	}
240}
241
242// String returns a string representation of the key and value.
243func (lf Field) String() string {
244	return fmt.Sprint(lf.key, ":", lf.Value())
245}
246