2024-07-23 17:46:15 +02:00
|
|
|
package unitel
|
|
|
|
|
|
|
|
import (
|
|
|
|
"context"
|
|
|
|
"net/http"
|
|
|
|
|
|
|
|
"github.com/getsentry/sentry-go"
|
|
|
|
"github.com/rs/zerolog/log"
|
|
|
|
"go.opentelemetry.io/otel/propagation"
|
|
|
|
"go.opentelemetry.io/otel/trace"
|
|
|
|
)
|
|
|
|
|
|
|
|
type contextKey struct {
|
|
|
|
Key string
|
|
|
|
}
|
|
|
|
|
2024-08-11 02:54:27 +02:00
|
|
|
const (
|
|
|
|
SentryTraceHeader = "sentry-trace"
|
|
|
|
SentryBaggageHeader = "sentry-baggage"
|
|
|
|
)
|
|
|
|
|
2024-07-23 17:46:15 +02:00
|
|
|
var (
|
|
|
|
tracerContextKey = contextKey{"tracer"}
|
|
|
|
)
|
|
|
|
|
2024-07-28 16:53:47 +02:00
|
|
|
type ConfigureSpanStartFunc = func(context.Context) (context.Context, []trace.SpanStartOption, []sentry.SpanOption)
|
|
|
|
|
2024-07-23 17:46:15 +02:00
|
|
|
func (t *Telemetry) StartSpan(ctx context.Context, operation, name string, cfgs ...ConfigureSpanStartFunc) *Span {
|
|
|
|
otelStartOpts := make([]trace.SpanStartOption, 0)
|
|
|
|
sentryStartOpts := []sentry.SpanOption{sentry.WithTransactionName(name), sentry.WithDescription(name)}
|
|
|
|
|
|
|
|
for _, opt := range cfgs {
|
|
|
|
var otel []trace.SpanStartOption
|
|
|
|
var sentry []sentry.SpanOption
|
|
|
|
|
|
|
|
ctx, otel, sentry = opt(ctx)
|
|
|
|
|
|
|
|
otelStartOpts = append(otelStartOpts, otel...)
|
|
|
|
sentryStartOpts = append(sentryStartOpts, sentry...)
|
|
|
|
}
|
|
|
|
|
|
|
|
var otelTracer trace.Tracer
|
|
|
|
if tracer, ok := ctx.Value(tracerContextKey).(trace.Tracer); ok && tracer != nil {
|
|
|
|
otelTracer = tracer
|
|
|
|
} else {
|
|
|
|
otelTracer = t.Tracer
|
|
|
|
}
|
|
|
|
|
|
|
|
ctx, otelSpan := otelTracer.Start(ctx, name, otelStartOpts...)
|
|
|
|
sentrySpan := sentry.StartSpan(ctx, operation, sentryStartOpts...)
|
|
|
|
|
|
|
|
return &Span{
|
|
|
|
otel: otelSpan,
|
|
|
|
sentry: sentrySpan,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func WithOtelOptions(opts ...trace.SpanStartOption) ConfigureSpanStartFunc {
|
|
|
|
return func(ctx context.Context) (context.Context, []trace.SpanStartOption, []sentry.SpanOption) {
|
|
|
|
return ctx, opts, []sentry.SpanOption{}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (t *Telemetry) InjectIntoHeaders(ctx context.Context, headers http.Header) {
|
2024-08-23 00:34:37 +02:00
|
|
|
t.Propagator.Inject(ctx, propagation.HeaderCarrier(headers))
|
2024-07-23 17:46:15 +02:00
|
|
|
|
|
|
|
if sentrySpan := sentry.SpanFromContext(ctx); sentrySpan == nil {
|
|
|
|
// this should never happen...
|
|
|
|
log.Trace().Msgf("failed to inject Sentry span ID, Sentry span could not be extracted from the context.Context")
|
|
|
|
} else {
|
2024-08-11 02:54:27 +02:00
|
|
|
headers.Set(SentryTraceHeader, sentrySpan.ToSentryTrace())
|
|
|
|
headers.Set(SentryBaggageHeader, sentrySpan.ToBaggage())
|
2024-07-23 17:46:15 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-08-11 02:54:27 +02:00
|
|
|
func (t *Telemetry) InjectIntoMap(ctx context.Context, m map[string]string) {
|
2024-08-23 00:34:37 +02:00
|
|
|
t.Propagator.Inject(ctx, propagation.MapCarrier(m))
|
2024-08-11 02:54:27 +02:00
|
|
|
|
|
|
|
if sentrySpan := sentry.SpanFromContext(ctx); sentrySpan == nil {
|
|
|
|
// this should never happen...
|
|
|
|
log.Trace().Msgf("failed to inject Sentry span ID, Sentry span could not be extracted from the context.Context")
|
|
|
|
} else {
|
|
|
|
m[SentryTraceHeader] = sentrySpan.ToSentryTrace()
|
|
|
|
m[SentryBaggageHeader] = sentrySpan.ToBaggage()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (t *Telemetry) ContinueFromHeaders(h http.Header) ConfigureSpanStartFunc {
|
2024-07-23 17:46:15 +02:00
|
|
|
return func(ctx context.Context) (context.Context, []trace.SpanStartOption, []sentry.SpanOption) {
|
2024-08-23 00:34:37 +02:00
|
|
|
ctx = t.Propagator.Extract(ctx, propagation.HeaderCarrier(h))
|
2024-08-11 02:54:27 +02:00
|
|
|
|
|
|
|
return ctx,
|
|
|
|
[]trace.SpanStartOption{},
|
|
|
|
[]sentry.SpanOption{
|
|
|
|
sentry.ContinueFromHeaders(h.Get(sentry.SentryTraceHeader), h.Get(sentry.SentryBaggageHeader)),
|
|
|
|
sentry.WithTransactionSource(sentry.SourceURL),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (t *Telemetry) ContinueFromMap(m map[string]string) ConfigureSpanStartFunc {
|
|
|
|
return func(ctx context.Context) (context.Context, []trace.SpanStartOption, []sentry.SpanOption) {
|
2024-08-23 00:34:37 +02:00
|
|
|
ctx = t.Propagator.Extract(ctx, propagation.MapCarrier(m))
|
2024-08-11 02:54:27 +02:00
|
|
|
|
|
|
|
sentryTrace := m[sentry.SentryTraceHeader]
|
|
|
|
sentryBaggage := m[sentry.SentryBaggageHeader]
|
2024-07-23 17:46:15 +02:00
|
|
|
|
2024-08-11 02:54:27 +02:00
|
|
|
return ctx,
|
|
|
|
[]trace.SpanStartOption{},
|
|
|
|
[]sentry.SpanOption{
|
|
|
|
sentry.ContinueFromHeaders(sentryTrace, sentryBaggage),
|
|
|
|
sentry.WithTransactionSource(sentry.SourceURL),
|
|
|
|
}
|
2024-07-23 17:46:15 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func WithOtelTracer(tracer trace.Tracer) ConfigureSpanStartFunc {
|
|
|
|
return func(ctx context.Context) (context.Context, []trace.SpanStartOption, []sentry.SpanOption) {
|
|
|
|
return context.WithValue(ctx, tracerContextKey, tracer), []trace.SpanStartOption{}, []sentry.SpanOption{}
|
|
|
|
}
|
|
|
|
}
|
2024-07-28 16:53:47 +02:00
|
|
|
|
2024-07-28 17:03:48 +02:00
|
|
|
type TracedFunc[T any] func(context.Context, string, ...any) TracedFuncResult[T]
|
|
|
|
type TracedFuncResult[T any] struct {
|
|
|
|
Result T
|
|
|
|
Err error
|
|
|
|
}
|
2024-07-28 16:58:29 +02:00
|
|
|
|
2024-07-28 17:03:48 +02:00
|
|
|
func RunTraced[T any](t *Telemetry, op string, fn func(context.Context, ...any) TracedFuncResult[T]) TracedFunc[T] {
|
|
|
|
return func(ctx context.Context, name string, args ...any) TracedFuncResult[T] {
|
2024-07-28 16:53:47 +02:00
|
|
|
span := t.StartSpan(ctx, op, name)
|
|
|
|
defer span.End()
|
|
|
|
ctx = span.Context()
|
|
|
|
|
|
|
|
return fn(ctx, args...)
|
|
|
|
}
|
|
|
|
}
|