forked from tracer/tracer
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathtracer.go
412 lines (369 loc) · 10.2 KB
/
tracer.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
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
// Package tracer implements a Dapper-style tracing system. It is
// compatible with the OpenTracing specification.
//
// Sampling
//
// To keep the overhead incurred by tracing low, only a subset of
// requests should be traced. This is achieved by a sampler. Each
// tracer has a sampler that may sample requests based on chance, a
// rate, or possibly other mechanisms. The default sampler samples all
// requests, which is useful for testing, and viable for low-traffic
// systems.
//
// Only root spans make sampling decisions. Child spans will inherit
// the sampling decisions of the root spans.
//
// Errors and logging
//
// The instrumentation is defensive and will never purposefully panic.
// At the same time, most functions do not return errors, because
// they'll be called by automatic instrumentation, hidden from the
// user. Instead, errors will be logged.
package tracer
import (
"crypto/rand"
"encoding/binary"
"encoding/hex"
"log"
"reflect"
"sync"
"time"
"github.com/opentracing/opentracing-go"
"github.com/opentracing/opentracing-go/ext"
)
// The various flags of a Span.
const (
// The Span has been sampled.
FlagSampled = 1 << iota
)
// A Logger logs messages.
type Logger interface {
// Printf logs a single message, given a format and values. The
// format is documented in the fmt package.
Printf(format string, values ...interface{})
}
type defaultLogger struct{}
func (defaultLogger) Printf(format string, values ...interface{}) {
log.Printf(format, values...)
}
// valueType returns the broad categorization of a value's type and
// whether it is permitted as a payload.
func valueType(v interface{}) (string, bool) {
if v == nil {
return "", true
}
rv := reflect.ValueOf(v)
if !rv.IsValid() {
return "", false
}
switch rv.Type().Kind() {
case reflect.Bool:
return "boolean", true
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32,
reflect.Int64, reflect.Uint, reflect.Uint8, reflect.Uint16,
reflect.Uint32, reflect.Uint64, reflect.Float32, reflect.Float64:
return "number", true
case reflect.String:
return "string", true
}
return "", false
}
// A RawTrace contains all the data associated with a trace.
type RawTrace struct {
TraceID uint64 `json:"trace_id"`
Spans []RawSpan `json:"spans"`
Relations []RawRelation `json:"relations"`
}
// A RawRelation represents the relation between two spans.
type RawRelation struct {
ParentID uint64 `json:"parent_id"`
ChildID uint64 `json:"child_id"`
Kind string `json:"kind"`
}
// Span is an implementation of the OpenTracing Span interface.
type Span struct {
mu sync.RWMutex
tracer *Tracer
raw RawSpan
}
// A RawSpan contains all the data associated with a span.
type RawSpan struct {
SpanContext
ServiceName string `json:"service_name"`
OperationName string `json:"operation_name"`
StartTime time.Time `json:"start_time"`
FinishTime time.Time `json:"finish_time"`
Tags map[string]interface{} `json:"tags"`
Logs []opentracing.LogData `json:"logs"`
}
// RawSpan returns a deep copy of the span's underlying data.
func (sp *Span) RawSpan() RawSpan {
sp.mu.Lock()
defer sp.mu.Unlock()
raw := sp.raw
tags := raw.Tags
raw.Tags = map[string]interface{}{}
for k, v := range tags {
raw.Tags[k] = v
}
raw.Logs = append([]opentracing.LogData(nil), raw.Logs...)
baggage := raw.Baggage
raw.Baggage = map[string]string{}
for k, v := range baggage {
raw.Baggage[k] = v
}
return raw
}
// Sampled reports whether this span was sampled.
func (sp *Span) Sampled() bool {
sp.mu.RLock()
defer sp.mu.RUnlock()
return sp.sampled()
}
func (sp *Span) sampled() bool {
return (sp.raw.Flags & FlagSampled) > 0
}
// SetOperationName implements the opentracing.Span interface.
func (sp *Span) SetOperationName(name string) opentracing.Span {
sp.mu.Lock()
defer sp.mu.Unlock()
sp.raw.OperationName = name
return sp
}
// SetTag implements the opentracing.Span interface.
func (sp *Span) SetTag(key string, value interface{}) opentracing.Span {
sp.mu.Lock()
defer sp.mu.Unlock()
if !sp.sampled() {
return sp
}
if _, ok := valueType(value); !ok {
sp.tracer.Logger.Printf("unsupported tag value type for tag %q: %T", key, value)
return sp
}
if sp.raw.Tags == nil {
sp.raw.Tags = map[string]interface{}{}
}
sp.raw.Tags[key] = value
return sp
}
// SetBaggageItem implements the opentracing.Tracer interface.
func (sp *Span) SetBaggageItem(key, value string) opentracing.Span {
sp.raw.SpanContext.Baggage[key] = value
return sp
}
// BaggageItem implements the opentracing.Tracer interface.
func (sp *Span) BaggageItem(key string) string {
return sp.raw.SpanContext.Baggage[key]
}
// Finish implements the opentracing.Span interface.
func (sp *Span) Finish() {
if !sp.Sampled() {
return
}
sp.FinishWithOptions(opentracing.FinishOptions{})
}
// FinishWithOptions implements the opentracing.Span interface.
func (sp *Span) FinishWithOptions(opts opentracing.FinishOptions) {
sp.mu.Lock()
defer sp.mu.Unlock()
if !sp.sampled() {
return
}
if opts.FinishTime.IsZero() {
opts.FinishTime = time.Now()
}
sp.raw.FinishTime = opts.FinishTime
for _, log := range opts.BulkLogData {
sp.log(log)
}
if err := sp.tracer.storer.Store(sp.raw); err != nil {
sp.tracer.Logger.Printf("error while storing tracing span: %s", err)
}
}
// LogEvent implements the opentracing.Span interface.
func (sp *Span) LogEvent(event string) {
if !sp.Sampled() {
return
}
sp.Log(opentracing.LogData{
Event: event,
})
}
// LogEventWithPayload implements the opentracing.Span interface.
func (sp *Span) LogEventWithPayload(event string, payload interface{}) {
if !sp.Sampled() {
return
}
sp.Log(opentracing.LogData{
Event: event,
Payload: payload,
})
}
// Log implements the opentracing.Span interface.
func (sp *Span) Log(data opentracing.LogData) {
sp.mu.Lock()
defer sp.mu.Unlock()
sp.log(data)
}
func (sp *Span) log(data opentracing.LogData) {
if !sp.sampled() {
return
}
if _, ok := valueType(data.Payload); !ok {
sp.tracer.Logger.Printf("unsupported log payload type for event %q: %T", data.Event, data.Payload)
return
}
if data.Timestamp.IsZero() {
data.Timestamp = time.Now()
}
sp.raw.Logs = append(sp.raw.Logs, data)
}
// Context implements the opentracing.Span interface.
func (sp *Span) Context() opentracing.SpanContext {
sp.mu.RLock()
defer sp.mu.RUnlock()
return sp.raw.SpanContext
}
// Tracer implements the opentracing.Span interface.
func (sp *Span) Tracer() opentracing.Tracer {
sp.mu.RLock()
defer sp.mu.RUnlock()
return sp.tracer
}
// Tracer is an implementation of the OpenTracing Tracer interface.
type Tracer struct {
ServiceName string
Logger Logger
Sampler Sampler
storer Storer
idGenerator IDGenerator
}
// NewTracer returns a new tracer.
func NewTracer(serviceName string, storer Storer, idGenerator IDGenerator) *Tracer {
return &Tracer{
ServiceName: serviceName,
Logger: defaultLogger{},
Sampler: NewConstSampler(true),
storer: storer,
idGenerator: idGenerator,
}
}
// StartSpan implements the opentracing.Tracer interface.
func (tr *Tracer) StartSpan(operationName string, opts ...opentracing.StartSpanOption) opentracing.Span {
var sopts opentracing.StartSpanOptions
for _, opt := range opts {
opt.Apply(&sopts)
}
if sopts.StartTime.IsZero() {
sopts.StartTime = time.Now()
}
id := tr.idGenerator.GenerateID()
sp := &Span{
tracer: tr,
raw: RawSpan{
SpanContext: SpanContext{
SpanID: id,
TraceID: id,
},
ServiceName: tr.ServiceName,
OperationName: operationName,
StartTime: sopts.StartTime,
},
}
if len(sopts.References) > 0 {
// TODO(dh): support multiple parents, support ChildOf and
// FollowsFrom as separate kinds of relations.
parent, ok := sopts.References[0].ReferencedContext.(SpanContext)
if !ok {
panic("parent span must be of type *Span")
}
sp.raw.ParentID = parent.SpanID
sp.raw.TraceID = parent.TraceID
sp.raw.Flags = parent.Flags
} else {
if n, _ := sopts.Tags[string(ext.SamplingPriority)].(uint16); n > 0 {
sp.raw.Flags |= FlagSampled
} else if tr.Sampler.Sample(id) {
sp.raw.Flags |= FlagSampled
}
}
sp.raw.Tags = sopts.Tags
return sp
}
func (tr *Tracer) Flush() error {
f, ok := tr.storer.(Flusher)
if !ok {
return nil
}
return f.Flush()
}
func idToHex(id uint64) string {
b := make([]byte, 8)
binary.BigEndian.PutUint64(b, id)
return hex.EncodeToString(b)
}
func idFromHex(s string) uint64 {
b, _ := hex.DecodeString(s)
return binary.BigEndian.Uint64(b)
}
// Inject implements the opentracing.Tracer interface.
func (tr *Tracer) Inject(sm opentracing.SpanContext, format interface{}, carrier interface{}) error {
context, ok := sm.(SpanContext)
if !ok {
return opentracing.ErrInvalidSpanContext
}
injecter, ok := injecters[format]
if !ok {
return opentracing.ErrUnsupportedFormat
}
return injecter(context, carrier)
}
// Extract implements the opentracing.Tracer interface.
func (tr *Tracer) Extract(format interface{}, carrier interface{}) (opentracing.SpanContext, error) {
extracter, ok := extracters[format]
if !ok {
return nil, opentracing.ErrUnsupportedFormat
}
context, err := extracter(carrier)
if err != nil {
return nil, err
}
return context, nil
}
// IDGenerator generates IDs for traces and spans. The ID with value 0
// is reserved to mean "no parent span" and should not be generated.
type IDGenerator interface {
GenerateID() uint64
}
// A Storer stores a finished span. "Storing" a span may either mean
// saving it in a storage engine, or sending it to a remote
// collector.
//
// If a span with the same ID and the same trace ID already exists,
// the existing and new spans should be merged into one span.
//
// Because spans are only stored once they're done, children will be
// stored before their parents.
type Storer interface {
Store(sp RawSpan) error
}
// Flusher is an optional interface that when implemented allows a
// Storer to flush buffered spawns.
type Flusher interface {
Flush() error
}
var _ IDGenerator = RandomID{}
// RandomID generates random IDs by using crypto/rand.
type RandomID struct{}
// GenerateID generates an ID.
func (RandomID) GenerateID() uint64 {
b := make([]byte, 8)
for {
_, _ = rand.Read(b)
x := binary.BigEndian.Uint64(b)
if x != 0 {
return x
}
}
}