Core

/app/lib/telemetry/telemetry.go (3.0 KB)

  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
package telemetry

import (
"context"

"github.com/samber/lo"
"go.opentelemetry.io/otel"
"go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp"
"go.opentelemetry.io/otel/propagation"
"go.opentelemetry.io/otel/sdk/resource"
sdktrace "go.opentelemetry.io/otel/sdk/trace"
semconv "go.opentelemetry.io/otel/semconv/v1.4.0"
"go.opentelemetry.io/otel/trace"

"{{{ .Package }}}/app/util"
)

var (
Enabled = true
initialized = false
tracerProvider *sdktrace.TracerProvider
)

func InitializeIfNeeded(enabled bool, version string, logger util.Logger) bool {
if initialized {
return false
}
if enabled {
Initialize(version, logger)
}
return true
}

func Initialize(_ string, logger util.Logger) {
if initialized {
logger.Warn("double telemetry initialization")
return
}
otel.SetErrorHandler(&ErrHandler{logger: logger})
initialized = true

endpoint := "localhost:4318"
if env := util.GetEnv("telemetry_endpoint"); env != "" {
endpoint = env
}
logger.Debugf("initializing OpenTelemetry tracing using endpoint [%s]", endpoint)
tp, err := buildTP(endpoint)
if err != nil {
logger.Errorf("unable to create tracing provider: %+v", err)
return
}
tracerProvider = tp
p := propagation.NewCompositeTextMapPropagator(propagation.Baggage{}, propagation.TraceContext{})
otel.SetTextMapPropagator(p)
}

func buildTP(endpoint string) (*sdktrace.TracerProvider, error) {
exporter, err := otlptracehttp.New(context.Background(), otlptracehttp.WithEndpoint(endpoint), otlptracehttp.WithInsecure())
if err != nil {
return nil, err
}

batcher := sdktrace.NewBatchSpanProcessor(exporter)

tp := sdktrace.NewTracerProvider(
sdktrace.WithSampler(sdktrace.AlwaysSample()),
sdktrace.WithSpanProcessor(batcher),
sdktrace.WithResource(resource.NewWithAttributes(semconv.SchemaURL, semconv.ServiceNameKey.String(util.AppKey))),
)
otel.SetTracerProvider(tp)
return tp, nil
}

func Close(ctx context.Context) error {
return tracerProvider.Shutdown(ctx)
}

func StartSpan(ctx context.Context, spanName string, logger util.Logger, opts ...any) (context.Context, *Span, util.Logger) {
return spanCreate(ctx, spanName, logger, opts...)
}

func StartAsyncSpan(ctx context.Context, spanName string, logger util.Logger, opts ...any) (context.Context, *Span, util.Logger) {
parentSpan := trace.SpanFromContext(ctx)
asyncChildCtx := trace.ContextWithSpan(context.Background(), parentSpan)
return spanCreate(asyncChildCtx, spanName, logger, opts...)
}

func spanCreate(ctx context.Context, spanName string, logger util.Logger, opts ...any) (context.Context, *Span, util.Logger) {
tr := otel.GetTracerProvider().Tracer(util.AppKey)
ssos := []trace.SpanStartOption{trace.WithSpanKind(trace.SpanKindServer)}
lo.ForEach(opts, func(opt any, _ int) {
o, ok := opt.(trace.SpanStartOption)
if ok {
ssos = append(ssos, o)
}
})
ctx, ot := tr.Start(ctx, spanName, ssos...)
sp := &Span{OT: ot}
if logger != nil {
logger = LoggerFor(logger, sp)
}
return ctx, sp, logger
}