/
tracer.go
141 lines (120 loc) 路 4.5 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
package tracer
import (
"context"
"fmt"
"sync"
"time"
"go.opentelemetry.io/otel"
"go.opentelemetry.io/otel/attribute"
"go.opentelemetry.io/otel/exporters/otlp/otlptrace"
"go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc"
"go.opentelemetry.io/otel/propagation"
"go.opentelemetry.io/otel/sdk/resource"
"go.opentelemetry.io/otel/sdk/trace"
semconv "go.opentelemetry.io/otel/semconv/v1.18.0"
oteltrace "go.opentelemetry.io/otel/trace"
"go.opentelemetry.io/otel/trace/noop"
)
var myAppName string
var appNameOnce = &sync.Once{}
// InitTracer returns the configured OTEL tracer.
//
//nolint:gocritic
func InitTracer(appName, endpoint string, probability float64) (oteltrace.Tracer, func(context.Context)) {
appNameOnce.Do(func() {
myAppName = appName
})
switch true {
case probability < 0:
probability = 0
case probability > 1:
probability = 1
}
if endpoint == "" ||
probability == 0 {
return noopTracer(appName)
}
// Bootstrap tracer
traceProvider, err := startTracing(
appName,
endpoint,
probability,
)
if err != nil {
return nil, nil
}
tracerShutdown := func(ctx context.Context) {
err := traceProvider.Shutdown(ctx)
if err != nil {
//nolint:forbidigo
fmt.Printf("failed to shutdown tracer: %v\n", err)
}
}
tracer := traceProvider.Tracer(appName)
return tracer, tracerShutdown
}
// NewSpan is a wrapper for tracer.NewSpan(ctx, spanName, opts).
// If the context.Context provided in `ctx` contains a Span then the newly-created
// Span will be a child of that span, otherwise it will be a root span.
// Don't forget to defer span.End and use the newly provided context.
func NewSpan(ctx context.Context, spanName string, opts ...oteltrace.SpanStartOption) (context.Context, oteltrace.Span) {
return otel.Tracer(myAppName).Start(ctx, spanName, opts...)
}
// NewSpanFromSpanContext is like NewSpan but uses a custom span context to work
//
//nolint:lll // The name of the parameters are long.
func NewSpanFromSpanContext(ctx context.Context, spCtx oteltrace.SpanContext, spanName string, opts ...oteltrace.SpanStartOption) (context.Context, oteltrace.Span) {
return otel.Tracer(myAppName).Start(oteltrace.ContextWithSpanContext(ctx, spCtx), spanName, opts...)
}
// NewSpanFromContext is like NewSpanFromSpanContext but uses a span from a context to work
func NewSpanFromContext(ctx, sctx context.Context, spanName string, opts ...oteltrace.SpanStartOption) (context.Context, oteltrace.Span) {
spCtx := oteltrace.SpanContextFromContext(sctx)
return otel.Tracer(myAppName).Start(oteltrace.ContextWithSpanContext(ctx, spCtx), spanName, opts...)
}
//nolint:gocritic
func noopTracer(appName string) (oteltrace.Tracer, func(context.Context)) {
traceProvider := noop.NewTracerProvider()
otel.SetTracerProvider(traceProvider)
return traceProvider.Tracer(appName), func(context.Context) {}
}
// startTracing configure OpenTelemetry.
func startTracing(serviceName, reporterURI string, probability float64) (*trace.TracerProvider, error) {
ctx := context.Background()
exporter, err := otlptrace.New(
ctx,
otlptracegrpc.NewClient(
otlptracegrpc.WithInsecure(), // This should be configurable
otlptracegrpc.WithEndpoint(reporterURI),
),
)
if err != nil {
return nil, fmt.Errorf("creating new exporter: %w", err)
}
resources, err := resource.New(
ctx,
// resource.WithFromEnv(), // pull attributes from OTEL_RESOURCE_ATTRIBUTES and OTEL_SERVICE_NAME environment variables
// resource.WithProcess(), // This option configures a set of Detectors that discover process information
// resource.WithOS(), // This option configures a set of Detectors that discover OS information
// resource.WithContainer(), // This option configures a set of Detectors that discover container information
// resource.WithHost(), // This option configures a set of Detectors that discover host information
resource.WithAttributes(
semconv.ServiceNameKey.String(serviceName),
attribute.String("library.language", "go"),
),
)
if err != nil {
return nil, fmt.Errorf("creating new exporter: %w", err)
}
traceProvider := trace.NewTracerProvider(
trace.WithSampler(trace.TraceIDRatioBased(probability)),
trace.WithBatcher(exporter,
trace.WithMaxExportBatchSize(trace.DefaultMaxExportBatchSize),
trace.WithBatchTimeout(trace.DefaultScheduleDelay*time.Millisecond),
),
trace.WithResource(resources),
)
// We must set this provider as the global provider.
otel.SetTracerProvider(traceProvider)
otel.SetTextMapPropagator(propagation.TraceContext{})
return traceProvider, nil
}