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 } var ( tracerContextKey = contextKey{"tracer"} ) 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, } } type ConfigureSpanStartFunc = func(context.Context) (context.Context, []trace.SpanStartOption, []sentry.SpanOption) 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) { t.propagator.Inject(ctx, propagation.HeaderCarrier(headers)) 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 { headers.Set("sentry-trace", sentrySpan.ToSentryTrace()) headers.Set("sentry-baggage", sentrySpan.ToBaggage()) } } func (t *Telemetry) ContinueFromRequest(r *http.Request) ConfigureSpanStartFunc { return func(ctx context.Context) (context.Context, []trace.SpanStartOption, []sentry.SpanOption) { ctx = t.propagator.Extract(ctx, propagation.HeaderCarrier(r.Header)) return ctx, []trace.SpanStartOption{}, []sentry.SpanOption{sentry.ContinueFromRequest(r), sentry.WithTransactionSource(sentry.SourceURL)} } } 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{} } }