forked from concourse/concourse
-
Notifications
You must be signed in to change notification settings - Fork 0
/
tracer.go
229 lines (198 loc) · 5.56 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
package tracing
import (
"context"
"go.opentelemetry.io/collector/translator/conventions"
"go.opentelemetry.io/otel/api/global"
"go.opentelemetry.io/otel/api/propagation"
"go.opentelemetry.io/otel/api/trace"
"go.opentelemetry.io/otel/codes"
"go.opentelemetry.io/otel/label"
export "go.opentelemetry.io/otel/sdk/export/trace"
"go.opentelemetry.io/otel/sdk/resource"
sdktrace "go.opentelemetry.io/otel/sdk/trace"
)
//go:generate go run github.com/maxbrunsfeld/counterfeiter/v6 go.opentelemetry.io/otel/api/trace.Tracer
//go:generate go run github.com/maxbrunsfeld/counterfeiter/v6 go.opentelemetry.io/otel/api/trace.Provider
//go:generate go run github.com/maxbrunsfeld/counterfeiter/v6 go.opentelemetry.io/otel/api/trace.Span
// Configured indicates whether tracing has been configured or not.
//
// This variable is needed in order to shortcircuit span generation when
// tracing hasn't been configured.
//
//
var Configured bool
type Config struct {
ServiceName string `long:"service-name" description:"service name to attach to traces as metadata" default:"concourse-web"`
Attributes map[string]string `long:"attribute" description:"attributes to attach to traces as metadata"`
Honeycomb Honeycomb
Jaeger Jaeger
Stackdriver Stackdriver
OTLP OTLP
}
func (c Config) resource() *resource.Resource {
attributes := []label.KeyValue{
label.String(conventions.AttributeTelemetrySDKName, "opentelemetry"),
label.String(conventions.AttributeTelemetrySDKLanguage, "go"),
label.String(conventions.AttributeServiceName, c.ServiceName),
}
for key, value := range c.Attributes {
attributes = append(attributes, label.String(key, value))
}
return resource.New(attributes...)
}
func (c Config) TraceProvider(exporter func() (export.SpanSyncer, error)) (trace.Provider, error) {
exp, err := exporter()
if err != nil {
return nil, err
}
provider, err := sdktrace.NewProvider(sdktrace.WithConfig(
sdktrace.Config{
DefaultSampler: sdktrace.AlwaysSample(),
}),
sdktrace.WithSyncer(exp),
sdktrace.WithResource(c.resource()),
)
if err != nil {
return nil, err
}
return provider, nil
}
func (c Config) Prepare() error {
var provider trace.Provider
var err error
switch {
case c.Honeycomb.IsConfigured():
provider, err = c.TraceProvider(c.Honeycomb.Exporter)
case c.Jaeger.IsConfigured():
provider, err = c.TraceProvider(c.Jaeger.Exporter)
case c.OTLP.IsConfigured():
provider, err = c.TraceProvider(c.OTLP.Exporter)
case c.Stackdriver.IsConfigured():
provider, err = c.TraceProvider(c.Stackdriver.Exporter)
}
if err != nil {
return err
}
if provider != nil {
ConfigureTraceProvider(provider)
}
return nil
}
// StartSpan creates a span, giving back a context that has itself added as the
// parent span.
//
// Calls to this function with a context that has been generated from a previous
// call to this method will make the resulting span a child of the span that
// preceded it.
//
// For instance:
//
// ```
// func fn () {
//
// rootCtx, rootSpan := StartSpan(context.Background(), "foo", nil)
// defer rootSpan.End()
//
// _, childSpan := StartSpan(rootCtx, "bar", nil)
// defer childSpan.End()
//
// }
// ```
//
// calling `fn()` will lead to the following trace:
//
// ```
// foo 0--------3
// bar 1----2
// ```
//
// where (0) is the start of the root span, which then gets a child `bar`
// initializing at (1), having its end called (2), and then the last span
// finalization happening for the root span (3) given how `defer` statements
// stack.
//
func StartSpan(
ctx context.Context,
component string,
attrs Attrs,
) (context.Context, trace.Span) {
return startSpan(ctx, component, attrs)
}
func FromContext(ctx context.Context) trace.Span {
return trace.SpanFromContext(ctx)
}
func Inject(ctx context.Context, supplier propagation.HTTPSupplier) {
trace.TraceContext{}.Inject(ctx, supplier)
}
type WithSpanContext interface {
SpanContext() propagation.HTTPSupplier
}
func StartSpanFollowing(
ctx context.Context,
following WithSpanContext,
component string,
attrs Attrs,
) (context.Context, trace.Span) {
if supplier := following.SpanContext(); supplier != nil {
ctx = trace.TraceContext{}.Extract(ctx, supplier)
}
return startSpan(ctx, component, attrs)
}
func StartSpanLinkedToFollowing(
linked context.Context,
following WithSpanContext,
component string,
attrs Attrs,
) (context.Context, trace.Span) {
ctx := context.Background()
if supplier := following.SpanContext(); supplier != nil {
ctx = trace.TraceContext{}.Extract(ctx, supplier)
}
linkedSpanContext := trace.SpanFromContext(linked).SpanContext()
return startSpan(
ctx,
component,
attrs,
trace.LinkedTo(linkedSpanContext),
)
}
func startSpan(
ctx context.Context,
component string,
attrs Attrs,
opts ...trace.StartOption,
) (context.Context, trace.Span) {
if !Configured {
return ctx, trace.NoopSpan{}
}
ctx, span := global.TraceProvider().Tracer("concourse").Start(
ctx,
component,
opts...,
)
if len(attrs) != 0 {
span.SetAttributes(keyValueSlice(attrs)...)
}
return ctx, span
}
func End(span trace.Span, err error) {
if !Configured {
return
}
if err != nil {
span.SetStatus(codes.Internal, "")
span.SetAttributes(
label.String("error-message", err.Error()),
)
}
span.End()
}
// ConfigureTraceProvider configures the sdk to use a given trace provider.
//
// By default, a noop tracer is registered, thus, it's safe to call StartSpan
// and other related methods even before `ConfigureTracer` it called.
//
func ConfigureTraceProvider(tp trace.Provider) {
global.SetTraceProvider(tp)
Configured = true
}