/
jaeger.go
130 lines (105 loc) · 3.68 KB
/
jaeger.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
package jaeger_client
import (
"context"
"errors"
"fmt"
"go.opentelemetry.io/otel"
"go.opentelemetry.io/otel/attribute"
"go.opentelemetry.io/otel/exporters/otlp/otlptrace"
"go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp"
"go.opentelemetry.io/otel/propagation"
"go.opentelemetry.io/otel/sdk/resource"
tracesdk "go.opentelemetry.io/otel/sdk/trace"
semconv "go.opentelemetry.io/otel/semconv/v1.7.0"
"go.opentelemetry.io/otel/trace"
"net/http"
"os"
"time"
)
type contextKey int
const spanNameContextKey contextKey = iota
// ErrInvalidHost is used when the Jaeger Agent host is either not given or invalid.
var ErrInvalidHost = errors.New("jaeger: invalid agent host")
var name string
var cleanShutdownFunc = func() error { return nil }
// InitializeJaeger initializes a tracer provider that sends traces to Jaeger, and then sets it as
// the global tracer provider.
// additionalAttributes is an optional parameter that specifies additional attributes that should be added to every trace.
func InitializeJaeger(serviceName string, additionalAttributes ...attribute.KeyValue) error {
// if there is no URL set for the trace collector, do not configure the tracer.
// This is fine, it just means that any traces we generate will be discarded.
// Prevents any weirdness involving traces when running the service locally.
jaegerHost := os.Getenv("OTEL_EXPORTER_OTLP_TRACES_ENDPOINT")
if jaegerHost == "" {
return ErrInvalidHost
}
if serviceName == "" {
return errors.New("jaeger: invalid service name")
}
// store this at the package level for later use
name = serviceName
attributes := []attribute.KeyValue{semconv.ServiceNameKey.String(name)}
attributes = append(attributes, additionalAttributes...)
client := otlptracehttp.NewClient()
exporter, err := otlptrace.New(context.Background(), client)
if err != nil {
return fmt.Errorf("creating OTLP trace exporter: %w", err)
}
tracerProvider := tracesdk.NewTracerProvider(
tracesdk.WithBatcher(exporter),
tracesdk.WithResource(resource.NewWithAttributes(
semconv.SchemaURL,
attributes...,
)),
)
otel.SetTracerProvider(tracerProvider)
// set up inter-service trace propagation
otel.SetTextMapPropagator(
propagation.NewCompositeTextMapPropagator(
propagation.TraceContext{},
),
)
// set up a function that will ensure all traces get flushed from memory before the process is killed.
// This has a timeout of 5 seconds, so it will not hang indefinitely if there is some problem with flushing.
cleanShutdownFunc = func() error {
ctx, cancel := context.WithTimeout(context.Background(), time.Second*5)
defer cancel()
if err := tracerProvider.Shutdown(ctx); err != nil {
return fmt.Errorf("cleanly shut down tracer provider: %w", err)
}
return nil
}
return nil
}
func Shutdown() error {
return cleanShutdownFunc()
}
func Trace() trace.Tracer {
tp := otel.GetTracerProvider()
return tp.Tracer(name)
}
func SpanFromContext(ctx context.Context, name string) (context.Context, trace.Span) {
tr := Trace()
return tr.Start(ctx, name)
}
func SpanNameFromContext(ctx context.Context) string {
spanName, ok := ctx.Value(spanNameContextKey).(string)
if !ok {
return ""
}
return spanName
}
func InjectSpanName(ctx context.Context, name string) context.Context {
return context.WithValue(ctx, spanNameContextKey, name)
}
func UninjectSpanName(ctx context.Context) context.Context {
// injecting a blank span name is effectively the same as "uninjecting" it
return InjectSpanName(ctx, "")
}
func SpanNameFormatter(operation string, req *http.Request) string {
spanName := SpanNameFromContext(req.Context())
if spanName == "" {
spanName = fmt.Sprintf("%s %s", req.Method, req.URL.Path)
}
return spanName
}