-
Notifications
You must be signed in to change notification settings - Fork 49
/
log.go
397 lines (340 loc) · 11.5 KB
/
log.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
// Package log is the logging library used by IPFS
// (https://github.com/ipfs/go-ipfs). It uses a modified version of
// https://godoc.org/github.com/whyrusleeping/go-logging .
package log
import (
"bytes"
"context"
"encoding/json"
"path"
"runtime"
"time"
writer "github.com/ipfs/go-log/writer"
opentrace "github.com/opentracing/opentracing-go"
otExt "github.com/opentracing/opentracing-go/ext"
)
var log = Logger("eventlog")
// StandardLogger provides API compatibility with standard printf loggers
// eg. go-logging
type StandardLogger interface {
Debug(args ...interface{})
Debugf(format string, args ...interface{})
Error(args ...interface{})
Errorf(format string, args ...interface{})
Fatal(args ...interface{})
Fatalf(format string, args ...interface{})
Info(args ...interface{})
Infof(format string, args ...interface{})
Panic(args ...interface{})
Panicf(format string, args ...interface{})
Warning(args ...interface{})
Warningf(format string, args ...interface{})
}
// EventLogger extends the StandardLogger interface to allow for log items
// containing structured metadata
type EventLogger interface {
StandardLogger
// Event merges structured data from the provided inputs into a single
// machine-readable log event.
//
// If the context contains metadata, a copy of this is used as the base
// metadata accumulator.
//
// If one or more loggable objects are provided, these are deep-merged into base blob.
//
// Next, the event name is added to the blob under the key "event". If
// the key "event" already exists, it will be over-written.
//
// Finally the timestamp and package name are added to the accumulator and
// the metadata is logged.
// DEPRECATED
Event(ctx context.Context, event string, m ...Loggable)
// DEPRECATED
EventBegin(ctx context.Context, event string, m ...Loggable) *EventInProgress
// Start starts an opentracing span with `name`, using
// any Span found within `ctx` as a ChildOfRef. If no such parent could be
// found, Start creates a root (parentless) Span.
//
// The return value is a context.Context object built around the
// returned Span.
//
// Example usage:
//
// SomeFunction(ctx context.Context, ...) {
// ctx := log.Start(ctx, "SomeFunction")
// defer log.Finish(ctx)
// ...
// }
Start(ctx context.Context, name string) context.Context
// StartFromParentState starts an opentracing span with `name`, using
// any Span found within `ctx` as a ChildOfRef. If no such parent could be
// found, StartSpanFromParentState creates a root (parentless) Span.
//
// StartFromParentState will attempt to deserialize a SpanContext from `parent`,
// using any Span found within to continue the trace
//
// The return value is a context.Context object built around the
// returned Span.
//
// An error is returned when `parent` cannot be deserialized to a SpanContext
//
// Example usage:
//
// SomeFunction(ctx context.Context, bParent []byte) {
// ctx := log.StartFromParentState(ctx, "SomeFunction", bParent)
// defer log.Finish(ctx)
// ...
// }
StartFromParentState(ctx context.Context, name string, parent []byte) (context.Context, error)
// Finish completes the span associated with `ctx`.
//
// Finish() must be the last call made to any span instance, and to do
// otherwise leads to undefined behavior.
// Finish will do its best to notify (log) when used in correctly
// .e.g called twice, or called on a spanless `ctx`
Finish(ctx context.Context)
// FinishWithErr completes the span associated with `ctx` and also calls
// SetErr if `err` is non-nil
//
// FinishWithErr() must be the last call made to any span instance, and to do
// otherwise leads to undefined behavior.
// FinishWithErr will do its best to notify (log) when used in correctly
// .e.g called twice, or called on a spanless `ctx`
FinishWithErr(ctx context.Context, err error)
// SetErr tags the span associated with `ctx` to reflect an error occured, and
// logs the value `err` under key `error`.
SetErr(ctx context.Context, err error)
// LogKV records key:value logging data about an event stored in `ctx`
// Eexample:
// log.LogKV(
// "error", "resolve failure",
// "type", "cache timeout",
// "waited.millis", 1500)
LogKV(ctx context.Context, alternatingKeyValues ...interface{})
// SetTag tags key `k` and value `v` on the span associated with `ctx`
SetTag(ctx context.Context, key string, value interface{})
// SetTags tags keys from the `tags` maps on the span associated with `ctx`
// Example:
// log.SetTags(ctx, map[string]{
// "type": bizStruct,
// "request": req,
// })
SetTags(ctx context.Context, tags map[string]interface{})
// SerializeContext takes the SpanContext instance stored in `ctx` and Seralizes
// it to bytes. An error is returned if the `ctx` cannot be serialized to
// a bytes array
SerializeContext(ctx context.Context) ([]byte, error)
}
// Logger retrieves an event logger by name
func Logger(system string) EventLogger {
// TODO if we would like to adjust log levels at run-time. Store this event
// logger in a map (just like the util.Logger impl)
if len(system) == 0 {
setuplog := getLogger("setup-logger")
setuplog.Warning("Missing name parameter")
system = "undefined"
}
logger := getLogger(system)
return &eventLogger{system: system, StandardLogger: logger}
}
// eventLogger implements the EventLogger and wraps a go-logging Logger
type eventLogger struct {
StandardLogger
system string
// TODO add log-level
}
func (el *eventLogger) Start(ctx context.Context, operationName string) context.Context {
span, ctx := opentrace.StartSpanFromContext(ctx, operationName)
span.SetTag("system", el.system)
return ctx
}
func (el *eventLogger) StartFromParentState(ctx context.Context, operationName string, parent []byte) (context.Context, error) {
sc, err := deserializeContext(parent)
if err != nil {
return nil, err
}
//TODO RPCServerOption is probably not the best tag, as this is likely from a peer
span, ctx := opentrace.StartSpanFromContext(ctx, operationName, otExt.RPCServerOption(sc))
span.SetTag("system", el.system)
return ctx, nil
}
func (el *eventLogger) SerializeContext(ctx context.Context) ([]byte, error) {
gTracer := opentrace.GlobalTracer()
b := make([]byte, 0)
carrier := bytes.NewBuffer(b)
span := opentrace.SpanFromContext(ctx)
if err := gTracer.Inject(span.Context(), opentrace.Binary, carrier); err != nil {
return nil, err
}
return carrier.Bytes(), nil
}
func (el *eventLogger) LogKV(ctx context.Context, alternatingKeyValues ...interface{}) {
span := opentrace.SpanFromContext(ctx)
if span == nil {
_, file, line, _ := runtime.Caller(1)
log.Errorf("LogKV with no Span in context called on %s:%d", path.Base(file), line)
return
}
span.LogKV(alternatingKeyValues...)
}
func (el *eventLogger) SetTag(ctx context.Context, k string, v interface{}) {
span := opentrace.SpanFromContext(ctx)
if span == nil {
_, file, line, _ := runtime.Caller(1)
log.Errorf("SetTag with no Span in context called on %s:%d", path.Base(file), line)
return
}
span.SetTag(k, v)
}
func (el *eventLogger) SetTags(ctx context.Context, tags map[string]interface{}) {
span := opentrace.SpanFromContext(ctx)
if span == nil {
_, file, line, _ := runtime.Caller(1)
log.Errorf("SetTags with no Span in context called on %s:%d", path.Base(file), line)
return
}
for k, v := range tags {
span.SetTag(k, v)
}
}
func (el *eventLogger) SetErr(ctx context.Context, err error) {
span := opentrace.SpanFromContext(ctx)
if span == nil {
_, file, line, _ := runtime.Caller(1)
log.Errorf("SetErr with no Span in context called on %s:%d", path.Base(file), line)
return
}
if err == nil {
return
}
otExt.Error.Set(span, true)
span.LogKV("error", err.Error())
}
func (el *eventLogger) Finish(ctx context.Context) {
span := opentrace.SpanFromContext(ctx)
if span == nil {
_, file, line, _ := runtime.Caller(1)
log.Errorf("Finish with no Span in context called on %s:%d", path.Base(file), line)
return
}
span.Finish()
}
func (el *eventLogger) FinishWithErr(ctx context.Context, err error) {
el.SetErr(ctx, err)
el.Finish(ctx)
}
func deserializeContext(bCtx []byte) (opentrace.SpanContext, error) {
gTracer := opentrace.GlobalTracer()
carrier := bytes.NewReader(bCtx)
spanContext, err := gTracer.Extract(opentrace.Binary, carrier)
if err != nil {
log.Warning("Failed to deserialize context %s", err)
return nil, err
}
return spanContext, nil
}
// DEPRECATED use `Start`
func (el *eventLogger) EventBegin(ctx context.Context, event string, metadata ...Loggable) *EventInProgress {
ctx = el.Start(ctx, event)
for _, m := range metadata {
for l, v := range m.Loggable() {
el.LogKV(ctx, l, v)
}
}
eip := &EventInProgress{}
eip.doneFunc = func(additional []Loggable) {
// anything added during the operation
// e.g. deprecated methods event.Append(...) or event.SetError(...)
for _, m := range eip.loggables {
for l, v := range m.Loggable() {
el.LogKV(ctx, l, v)
}
}
el.Finish(ctx)
}
return eip
}
type activeEventKeyType struct{}
var activeEventKey = activeEventKeyType{}
// DEPRECATED use `Start`
func (el *eventLogger) Event(ctx context.Context, event string, metadata ...Loggable) {
// short circuit if theres nothing to write to
if !writer.WriterGroup.Active() {
return
}
// Collect loggables for later logging
var loggables []Loggable
// get any existing metadata from the context
existing, err := MetadataFromContext(ctx)
if err != nil {
existing = Metadata{}
}
loggables = append(loggables, existing)
for _, datum := range metadata {
loggables = append(loggables, datum)
}
e := entry{
loggables: loggables,
system: el.system,
event: event,
}
accum := Metadata{}
for _, loggable := range e.loggables {
accum = DeepMerge(accum, loggable.Loggable())
}
// apply final attributes to reserved keys
// TODO accum["level"] = level
accum["event"] = e.event
accum["system"] = e.system
accum["time"] = FormatRFC3339(time.Now())
out, err := json.Marshal(accum)
if err != nil {
el.Errorf("ERROR FORMATTING EVENT ENTRY: %s", err)
return
}
writer.WriterGroup.Write(append(out, '\n'))
}
// DEPRECATED
// EventInProgress represent and event which is happening
type EventInProgress struct {
loggables []Loggable
doneFunc func([]Loggable)
}
// DEPRECATED use `LogKV` or `SetTag`
// Append adds loggables to be included in the call to Done
func (eip *EventInProgress) Append(l Loggable) {
eip.loggables = append(eip.loggables, l)
}
// DEPRECATED use `SetError(ctx, error)`
// SetError includes the provided error
func (eip *EventInProgress) SetError(err error) {
eip.loggables = append(eip.loggables, LoggableMap{
"error": err.Error(),
})
}
// DEPRECATED use `Finish`
// Done creates a new Event entry that includes the duration and appended
// loggables.
func (eip *EventInProgress) Done() {
eip.doneFunc(eip.loggables) // create final event with extra data
}
// DEPRECATED use `FinishWithErr`
// DoneWithErr creates a new Event entry that includes the duration and appended
// loggables. DoneWithErr accepts an error, if err is non-nil, it is set on
// the EventInProgress. Otherwise the logic is the same as the `Done()` method
func (eip *EventInProgress) DoneWithErr(err error) {
if err != nil {
eip.SetError(err)
}
eip.doneFunc(eip.loggables)
}
// DEPRECATED use `Finish`
// Close is an alias for done
func (eip *EventInProgress) Close() error {
eip.Done()
return nil
}
// FormatRFC3339 returns the given time in UTC with RFC3999Nano format.
func FormatRFC3339(t time.Time) string {
return t.UTC().Format(time.RFC3339Nano)
}