package obs import ( "context" "fmt" "net/url" "os" "time" "go.opentelemetry.io/otel" "go.opentelemetry.io/otel/attribute" "go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp" "go.opentelemetry.io/otel/propagation" "go.opentelemetry.io/otel/sdk/resource" sdktrace "go.opentelemetry.io/otel/sdk/trace" ) // TracingOptions says where traces go. type TracingOptions struct { Endpoint string // http(s)://host:4318; empty reads OTEL_EXPORTER_OTLP_ENDPOINT SampleRatio float64 // of traces the caller did not already decide; zero takes 1 Service string Version string } // SetupTracing installs the global tracer provider and the W3C propagator, // and returns what flushes it on the way out. With no endpoint it installs // nothing, and every span is the no-op one: tracing that is off costs a // function call per span. func SetupTracing(ctx context.Context, o TracingOptions) (func(context.Context) error, error) { noop := func(context.Context) error { return nil } endpoint := o.Endpoint if endpoint == "" { endpoint = os.Getenv("OTEL_EXPORTER_OTLP_ENDPOINT") } if endpoint == "" { return noop, nil } u, err := url.Parse(endpoint) if err != nil || (u.Scheme != "http" && u.Scheme != "https") || u.Host == "" { return noop, fmt.Errorf("tracing endpoint %q is not an http(s) address", endpoint) } opts := []otlptracehttp.Option{otlptracehttp.WithEndpoint(u.Host)} if u.Scheme == "http" { opts = append(opts, otlptracehttp.WithInsecure()) } if u.Path != "" && u.Path != "/" { opts = append(opts, otlptracehttp.WithURLPath(u.Path)) } exp, err := otlptracehttp.New(ctx, opts...) if err != nil { return noop, err } ratio := o.SampleRatio if ratio <= 0 || ratio > 1 { ratio = 1 } res := resource.NewSchemaless( attribute.String("service.name", o.Service), attribute.String("service.version", o.Version), ) tp := sdktrace.NewTracerProvider( sdktrace.WithBatcher(exp, sdktrace.WithBatchTimeout(5*time.Second)), sdktrace.WithResource(res), // A caller that sent a sampled traceparent has already decided; the // ratio is for requests that arrive without one. sdktrace.WithSampler(sdktrace.ParentBased(sdktrace.TraceIDRatioBased(ratio))), ) otel.SetTracerProvider(tp) otel.SetTextMapPropagator(propagation.TraceContext{}) return tp.Shutdown, nil }