package telemetry import ( "context" "errors" "time" "gitea.nik4nao.com/nik/home-services/discord-bot/internal/logger" "go.opentelemetry.io/otel" "go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc" "go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc" "go.opentelemetry.io/otel/metric/noop" "go.opentelemetry.io/otel/propagation" sdkmetric "go.opentelemetry.io/otel/sdk/metric" "go.opentelemetry.io/otel/sdk/resource" sdktrace "go.opentelemetry.io/otel/sdk/trace" semconv "go.opentelemetry.io/otel/semconv/v1.26.0" tracenoop "go.opentelemetry.io/otel/trace/noop" "gitea.nik4nao.com/nik/home-services/discord-bot/internal/config" ) func Setup(ctx context.Context, serviceName, version string, cfg *config.Config) (shutdown func(context.Context) error, err error) { if cfg.OTELEndpoint == "" { otel.SetTracerProvider(tracenoop.NewTracerProvider()) otel.SetMeterProvider(noop.NewMeterProvider()) logger.FromContext(ctx).Debug("otel disabled — OTEL_ENDPOINT not set") return func(context.Context) error { return nil }, nil } res := resource.NewWithAttributes( semconv.SchemaURL, semconv.ServiceNameKey.String(serviceName), semconv.ServiceVersionKey.String(version), ) traceExp, err := otlptracegrpc.New(ctx, otlptracegrpc.WithEndpoint(cfg.OTELEndpoint), otlptracegrpc.WithInsecure(), ) if err != nil { return nil, err } tp := sdktrace.NewTracerProvider( sdktrace.WithBatcher(traceExp), sdktrace.WithResource(res), sdktrace.WithSampler(sdktrace.AlwaysSample()), ) otel.SetTracerProvider(tp) otel.SetTextMapPropagator(propagation.NewCompositeTextMapPropagator( propagation.TraceContext{}, propagation.Baggage{}, )) metricExp, err := otlpmetricgrpc.New(ctx, otlpmetricgrpc.WithEndpoint(cfg.OTELEndpoint), otlpmetricgrpc.WithInsecure(), ) if err != nil { _ = tp.Shutdown(ctx) return nil, err } mp := sdkmetric.NewMeterProvider( sdkmetric.WithReader(sdkmetric.NewPeriodicReader(metricExp, sdkmetric.WithInterval(30*time.Second))), sdkmetric.WithResource(res), ) otel.SetMeterProvider(mp) return func(ctx context.Context) error { shutdownCtx, cancel := context.WithTimeout(ctx, 5*time.Second) defer cancel() var shutdownErr error if err := tp.Shutdown(shutdownCtx); err != nil { shutdownErr = errors.Join(shutdownErr, err) } if err := mp.Shutdown(shutdownCtx); err != nil { shutdownErr = errors.Join(shutdownErr, err) } return shutdownErr }, nil }