File: tracer.go

package info (click to toggle)
golang-github-openzipkin-zipkin-go 0.1.5%2Bgit20190103.2fd7f4a-1
  • links: PTS, VCS
  • area: main
  • in suites: buster
  • size: 532 kB
  • sloc: makefile: 22
file content (173 lines) | stat: -rw-r--r-- 4,513 bytes parent folder | download | duplicates (3)
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
package zipkin

import (
	"context"
	"sync/atomic"
	"time"

	"github.com/openzipkin/zipkin-go/idgenerator"
	"github.com/openzipkin/zipkin-go/model"
	"github.com/openzipkin/zipkin-go/propagation"
	"github.com/openzipkin/zipkin-go/reporter"
)

// Tracer is our Zipkin tracer implementation. It should be initialized using
// the NewTracer method.
type Tracer struct {
	defaultTags          map[string]string
	extractFailurePolicy ExtractFailurePolicy
	sampler              Sampler
	generate             idgenerator.IDGenerator
	reporter             reporter.Reporter
	localEndpoint        *model.Endpoint
	noop                 int32 // used as atomic bool (1 = true, 0 = false)
	sharedSpans          bool
	unsampledNoop        bool
}

// NewTracer returns a new Zipkin Tracer.
func NewTracer(rep reporter.Reporter, opts ...TracerOption) (*Tracer, error) {
	// set default tracer options
	t := &Tracer{
		defaultTags:          make(map[string]string),
		extractFailurePolicy: ExtractFailurePolicyRestart,
		sampler:              AlwaysSample,
		generate:             idgenerator.NewRandom64(),
		reporter:             rep,
		localEndpoint:        nil,
		noop:                 0,
		sharedSpans:          true,
		unsampledNoop:        false,
	}

	// if no reporter was provided we default to noop implementation.
	if t.reporter == nil {
		t.reporter = reporter.NewNoopReporter()
		t.noop = 1
	}

	// process functional options
	for _, opt := range opts {
		if err := opt(t); err != nil {
			return nil, err
		}
	}

	return t, nil
}

// StartSpanFromContext creates and starts a span using the span found in
// context as parent. If no parent span is found a root span is created.
func (t *Tracer) StartSpanFromContext(ctx context.Context, name string, options ...SpanOption) (Span, context.Context) {
	if parentSpan := SpanFromContext(ctx); parentSpan != nil {
		options = append(options, Parent(parentSpan.Context()))
	}
	span := t.StartSpan(name, options...)
	return span, NewContext(ctx, span)
}

// StartSpan creates and starts a span.
func (t *Tracer) StartSpan(name string, options ...SpanOption) Span {
	if atomic.LoadInt32(&t.noop) == 1 {
		return &noopSpan{}
	}
	s := &spanImpl{
		SpanModel: model.SpanModel{
			Kind:          model.Undetermined,
			Name:          name,
			LocalEndpoint: t.localEndpoint,
			Annotations:   make([]model.Annotation, 0),
			Tags:          make(map[string]string),
		},
		flushOnFinish: true,
		tracer:        t,
	}

	// add default tracer tags to span
	for k, v := range t.defaultTags {
		s.Tag(k, v)
	}

	// handle provided functional options
	for _, option := range options {
		option(t, s)
	}

	if s.TraceID.Empty() {
		// create root span
		s.SpanContext.TraceID = t.generate.TraceID()
		s.SpanContext.ID = t.generate.SpanID(s.SpanContext.TraceID)
	} else {
		// valid parent context found
		if t.sharedSpans && s.Kind == model.Server {
			// join span
			s.Shared = true
		} else {
			// regular child span
			parentID := s.SpanContext.ID
			s.SpanContext.ParentID = &parentID
			s.SpanContext.ID = t.generate.SpanID(model.TraceID{})
		}
	}

	if !s.SpanContext.Debug && s.Sampled == nil {
		// deferred sampled context found, invoke sampler
		sampled := t.sampler(s.SpanContext.TraceID.Low)
		s.SpanContext.Sampled = &sampled
		if sampled {
			s.mustCollect = 1
		}
	} else {
		if s.SpanContext.Debug || *s.Sampled {
			s.mustCollect = 1
		}
	}

	if t.unsampledNoop && s.mustCollect == 0 {
		// trace not being sampled and noop requested
		return &noopSpan{
			SpanContext: s.SpanContext,
		}
	}

	// add start time
	if s.Timestamp.IsZero() {
		s.Timestamp = time.Now()
	}

	return s
}

// Extract extracts a SpanContext using the provided Extractor function.
func (t *Tracer) Extract(extractor propagation.Extractor) (sc model.SpanContext) {
	if atomic.LoadInt32(&t.noop) == 1 {
		return
	}
	psc, err := extractor()
	if psc != nil {
		sc = *psc
	}
	sc.Err = err
	return
}

// SetNoop allows for killswitch behavior. If set to true the tracer will return
// noopSpans and all data is dropped. This allows operators to stop tracing in
// risk scenarios. Set back to false to resume tracing.
func (t *Tracer) SetNoop(noop bool) {
	if noop {
		atomic.CompareAndSwapInt32(&t.noop, 0, 1)
	} else {
		atomic.CompareAndSwapInt32(&t.noop, 1, 0)
	}
}

// LocalEndpoint returns a copy of the currently set local endpoint of the
// tracer instance.
func (t *Tracer) LocalEndpoint() *model.Endpoint {
	if t.localEndpoint == nil {
		return nil
	}
	ep := *t.localEndpoint
	return &ep
}