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
|
|
|
|
}
|
|
|
|
|
|
|
|
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) {
|
|
|
|
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{}
|
|
|
|
}
|
|
|
|
}
|
2024-07-28 16:53:47 +02:00
|
|
|
|
|
|
|
func RunTraced[T any](t *Telemetry, op string, fn func(context.Context, ...any) T) func(context.Context, string, ...any) T {
|
|
|
|
return func(ctx context.Context, name string, args ...any) T {
|
|
|
|
span := t.StartSpan(ctx, op, name)
|
|
|
|
defer span.End()
|
|
|
|
ctx = span.Context()
|
|
|
|
|
|
|
|
return fn(ctx, args...)
|
|
|
|
}
|
|
|
|
}
|