package main import ( "context" "errors" "fmt" "net/http" "time" "git.loyso.art/frx/kurious/internal/common/config" "git.loyso.art/frx/kurious/pkg/xdefault" "github.com/gorilla/mux" "go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp" "go.opentelemetry.io/otel" "go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc" "go.opentelemetry.io/otel/exporters/stdout/stdoutmetric" "go.opentelemetry.io/otel/exporters/stdout/stdouttrace" "go.opentelemetry.io/otel/propagation" "go.opentelemetry.io/otel/sdk/metric" "go.opentelemetry.io/otel/sdk/resource" "go.opentelemetry.io/otel/sdk/trace" ) var webtracer = otel.Tracer("kuriweb") type shutdownFunc func(context.Context) error func setupOtelSDK(ctx context.Context, cfg config.Trace) (shutdown shutdownFunc, err error) { var shutdownFuncs []shutdownFunc shutdown = func(ctx context.Context) error { var err error for _, f := range shutdownFuncs { err = errors.Join(err, f(ctx)) } shutdownFuncs = nil return err } handleError := func(inErr error) error { err = errors.Join(inErr, shutdown(ctx)) return err } prop := newPropagator() otel.SetTextMapPropagator(prop) tracerProvider, err := newTraceProvider(ctx, cfg.Endpoint, cfg.LicenseKey) if err != nil { return nil, handleError(err) } shutdownFuncs = append(shutdownFuncs, tracerProvider.Shutdown) otel.SetTracerProvider(tracerProvider) meterProvider, err := newMeterProvider() if err != nil { return nil, handleError(err) } shutdownFuncs = append(shutdownFuncs, meterProvider.Shutdown) otel.SetMeterProvider(meterProvider) return shutdown, nil } func newPropagator() propagation.TextMapPropagator { return propagation.NewCompositeTextMapPropagator( propagation.TraceContext{}, propagation.Baggage{}, ) } const defaultNewRelicEndpoint = "otlp.eu01.nr-data.net:443" func newTraceProvider(ctx context.Context, endpoint, licensekey string) (traceProvider *trace.TracerProvider, err error) { opts := make([]trace.TracerProviderOption, 0, 2) opts = append( opts, trace.WithSampler(trace.AlwaysSample()), trace.WithResource(resource.Default()), ) if licensekey != "" { endpoint = xdefault.WithFallback(endpoint, defaultNewRelicEndpoint) client, err := otlptracegrpc.New( ctx, otlptracegrpc.WithEndpoint(endpoint), otlptracegrpc.WithHeaders(map[string]string{ "api-key": licensekey, }), ) if err != nil { return nil, fmt.Errorf("making grpc client: %w", err) } opts = append(opts, trace.WithBatcher(client, trace.WithBatchTimeout(time.Second*10))) } else { traceExporter, err := stdouttrace.New( stdouttrace.WithPrettyPrint()) if err != nil { return nil, err } opts = append( opts, trace.WithBatcher(traceExporter, trace.WithBatchTimeout(time.Second*5)), ) } traceProvider = trace.NewTracerProvider(opts...) return traceProvider, nil } func newMeterProvider() (*metric.MeterProvider, error) { metricExporter, err := stdoutmetric.New() if err != nil { return nil, err } meterProvider := metric.NewMeterProvider( metric.WithReader(metric.NewPeriodicReader(metricExporter, // Default is 1m. Set to 3s for demonstrative purposes. metric.WithInterval(60*time.Second))), ) return meterProvider, nil } func muxHandleFunc(router *mux.Router, path string, hf http.HandlerFunc) *mux.Route { h := otelhttp.WithRouteTag(path, hf) return router.Handle(path, h) }