From 06e67bc573ea729dac2aa4b5953f3b611603a9e7 Mon Sep 17 00:00:00 2001 From: Marcus Efraimsson Date: Thu, 4 Jul 2024 18:11:59 +0200 Subject: [PATCH] API Server: Fix stack overflow panic when tracing is disabled (#90075) --- pkg/cmd/grafana/apiserver/cmd.go | 17 ++++++-- pkg/infra/tracing/tracing.go | 66 ++++++++++++++++++-------------- 2 files changed, 52 insertions(+), 31 deletions(-) diff --git a/pkg/cmd/grafana/apiserver/cmd.go b/pkg/cmd/grafana/apiserver/cmd.go index 9f09a382b4b..c5800e55f55 100644 --- a/pkg/cmd/grafana/apiserver/cmd.go +++ b/pkg/cmd/grafana/apiserver/cmd.go @@ -3,6 +3,7 @@ package apiserver import ( "context" "os" + "sync" "github.com/spf13/cobra" "go.opentelemetry.io/otel" @@ -116,16 +117,17 @@ type lateInitializedTracingProvider struct { } func (tp lateInitializedTracingProvider) Tracer(name string, options ...trace.TracerOption) trace.Tracer { - return tp.tracer + return tp.tracer.getTracer() } type lateInitializedTracingService struct { tracing.Tracer + mutex sync.RWMutex } func newLateInitializedTracingService() *lateInitializedTracingService { ts := &lateInitializedTracingService{ - Tracer: tracing.InitializeTracerForTest(), + Tracer: tracing.NewNoopTracerService(), } tp := &lateInitializedTracingProvider{ @@ -137,8 +139,17 @@ func newLateInitializedTracingService() *lateInitializedTracingService { return ts } -func (s *lateInitializedTracingService) InitTracer(tracer tracing.Tracer) { +func (s *lateInitializedTracingService) getTracer() tracing.Tracer { + s.mutex.RLock() + t := s.Tracer + s.mutex.RUnlock() + return t +} + +func (s *lateInitializedTracingService) InitTracer(tracer *tracing.TracingService) { + s.mutex.Lock() s.Tracer = tracer + s.mutex.Unlock() } var _ tracing.Tracer = &lateInitializedTracingService{} diff --git a/pkg/infra/tracing/tracing.go b/pkg/infra/tracing/tracing.go index 29dbee5e9e6..624f892214c 100644 --- a/pkg/infra/tracing/tracing.go +++ b/pkg/infra/tracing/tracing.go @@ -99,38 +99,20 @@ func ProvideService(tracingCfg *TracingConfig) (*TracingService, error) { return ots, nil } +func NewNoopTracerService() *TracingService { + tp := &noopTracerProvider{TracerProvider: noop.NewTracerProvider()} + otel.SetTracerProvider(tp) + + cfg := NewEmptyTracingConfig() + ots := &TracingService{cfg: cfg, tracerProvider: tp} + _ = ots.initOpentelemetryTracer() + return ots +} + func (ots *TracingService) GetTracerProvider() tracerProvider { return ots.tracerProvider } -func TraceIDFromContext(ctx context.Context, requireSampled bool) string { - spanCtx := trace.SpanContextFromContext(ctx) - if !spanCtx.HasTraceID() || !spanCtx.IsValid() || (requireSampled && !spanCtx.IsSampled()) { - return "" - } - - return spanCtx.TraceID().String() -} - -// Error sets the status to error and record the error as an exception in the provided span. -func Error(span trace.Span, err error) error { - attr := []attribute.KeyValue{} - grafanaErr := errutil.Error{} - if errors.As(err, &grafanaErr) { - attr = append(attr, attribute.String("message_id", grafanaErr.MessageID)) - } - - span.SetStatus(codes.Error, err.Error()) - span.RecordError(err, trace.WithAttributes(attr...)) - return err -} - -// Errorf wraps fmt.Errorf and also sets the status to error and record the error as an exception in the provided span. -func Errorf(span trace.Span, format string, args ...any) error { - err := fmt.Errorf(format, args...) - return Error(span, err) -} - type noopTracerProvider struct { trace.TracerProvider } @@ -389,3 +371,31 @@ func (rl *rateLimiter) ShouldSample(p tracesdk.SamplingParameters) tracesdk.Samp } func (rl *rateLimiter) Description() string { return rl.description } + +func TraceIDFromContext(ctx context.Context, requireSampled bool) string { + spanCtx := trace.SpanContextFromContext(ctx) + if !spanCtx.HasTraceID() || !spanCtx.IsValid() || (requireSampled && !spanCtx.IsSampled()) { + return "" + } + + return spanCtx.TraceID().String() +} + +// Error sets the status to error and record the error as an exception in the provided span. +func Error(span trace.Span, err error) error { + attr := []attribute.KeyValue{} + grafanaErr := errutil.Error{} + if errors.As(err, &grafanaErr) { + attr = append(attr, attribute.String("message_id", grafanaErr.MessageID)) + } + + span.SetStatus(codes.Error, err.Error()) + span.RecordError(err, trace.WithAttributes(attr...)) + return err +} + +// Errorf wraps fmt.Errorf and also sets the status to error and record the error as an exception in the provided span. +func Errorf(span trace.Span, format string, args ...any) error { + err := fmt.Errorf(format, args...) + return Error(span, err) +}