package tracing import ( "context" "fmt" rtrace "github.com/opencloud-eu/reva/v2/pkg/trace" "go.opentelemetry.io/otel/attribute" "go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc" "go.opentelemetry.io/otel/exporters/stdout/stdouttrace" "go.opentelemetry.io/otel/propagation" "go.opentelemetry.io/otel/sdk/resource" sdktrace "go.opentelemetry.io/otel/sdk/trace" semconv "go.opentelemetry.io/otel/semconv/v1.37.0" "go.opentelemetry.io/otel/trace" ) // Propagator ensures the importer module uses the same trace propagation strategy. var Propagator = propagation.NewCompositeTextMapPropagator( propagation.Baggage{}, propagation.TraceContext{}, ) // Deprecated: GetServiceTraceProvider returns a configured open-telemetry trace provider. Use GetTraceProvider. func GetServiceTraceProvider(exporter, serviceName string) (trace.TracerProvider, error) { return GetTraceProvider(context.Background(), exporter, serviceName) } // GetPropagator gets a configured propagator. func GetPropagator() propagation.TextMapPropagator { return propagation.NewCompositeTextMapPropagator( propagation.Baggage{}, propagation.TraceContext{}, ) } // GetTraceProvider returns a configured open-telemetry trace provider. func GetTraceProvider(ctx context.Context, exporter, serviceName string) (*sdktrace.TracerProvider, error) { // Create resource - shared across all exporters resources, err := createResource(ctx, serviceName) if err != nil { return nil, fmt.Errorf("failed to create resource: %w", err) } var tp *sdktrace.TracerProvider switch exporter { case "", "none": // No-op exporter - never sample tp = sdktrace.NewTracerProvider( sdktrace.WithSampler(sdktrace.NeverSample()), sdktrace.WithResource(resources), ) case "console": // Console exporter - prints to stdout (useful for debugging) consoleExporter, err := stdouttrace.New( stdouttrace.WithPrettyPrint(), ) if err != nil { return nil, fmt.Errorf("failed to create console exporter: %w", err) } // Use SimpleSpanProcessor for console to get immediate output tp = sdktrace.NewTracerProvider( sdktrace.WithSpanProcessor(sdktrace.NewSimpleSpanProcessor(consoleExporter)), sdktrace.WithResource(resources), ) case "otlp": // OTLP exporter - connects to collector // This automatically reads: // - OTEL_EXPORTER_OTLP_ENDPOINT // - OTEL_EXPORTER_OTLP_TRACES_ENDPOINT (takes precedence) // - OTEL_EXPORTER_OTLP_HEADERS // - OTEL_EXPORTER_OTLP_INSECURE // - OTEL_EXPORTER_OTLP_CERTIFICATE (for custom CA) otlpExporter, err := otlptracegrpc.New(ctx) if err != nil { return nil, fmt.Errorf("failed to create OTLP exporter: %w", err) } // Create tracer provider // This automatically reads: // - OTEL_TRACES_SAMPLER // - OTEL_TRACES_SAMPLER_ARG tp = sdktrace.NewTracerProvider( sdktrace.WithBatcher(otlpExporter), sdktrace.WithResource(resources), ) default: return nil, fmt.Errorf("unsupported trace exporter: %q (supported: none, console, otlp)", exporter) } // Set as global default rtrace.SetDefaultTracerProvider(tp) return tp, nil } // createResource creates a resource with service information func createResource(ctx context.Context, serviceName string) (*resource.Resource, error) { return resource.New(ctx, // Reads OTEL_RESOURCE_ATTRIBUTES and OTEL_SERVICE_NAME resource.WithFromEnv(), // Host Information resource.WithHost(), // Process Information // Resource WithProcessOwner is deliberately omitted because // inside containers where process might run as an arbitrary // uid without a username associated this would fail. resource.WithProcessPID(), resource.WithProcessCommandArgs(), resource.WithProcessExecutableName(), resource.WithProcessExecutablePath(), resource.WithProcessRuntimeDescription(), resource.WithProcessRuntimeName(), resource.WithProcessRuntimeVersion(), // Service attributes resource.WithAttributes( semconv.ServiceName(serviceName), attribute.String("library.language", "go"), ), ) }