package tracing import ( "context" "crypto/tls" "crypto/x509" "os" "go.ntppool.org/common/logger" "go.ntppool.org/common/version" "go.opentelemetry.io/otel" "go.opentelemetry.io/otel/attribute" "go.opentelemetry.io/otel/exporters/otlp/otlptrace" "go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp" "go.opentelemetry.io/otel/propagation" "go.opentelemetry.io/otel/sdk/resource" otelsdktrace "go.opentelemetry.io/otel/sdk/trace" semconv "go.opentelemetry.io/otel/semconv/v1.21.0" "go.opentelemetry.io/otel/trace" ) // https://github.com/open-telemetry/opentelemetry-go/blob/main/exporters/otlp/otlptrace/otlptracehttp/example_test.go type TpShutdownFunc func(ctx context.Context) error func Tracer() trace.Tracer { traceProvider := otel.GetTracerProvider() return traceProvider.Tracer("ntppool-tracer") } func Start(ctx context.Context, spanName string, opts ...trace.SpanStartOption) (context.Context, trace.Span) { return Tracer().Start(ctx, spanName, opts...) } type GetClientCertificate func(*tls.CertificateRequestInfo) (*tls.Certificate, error) type TracerConfig struct { ServiceName string Environment string Endpoint string CertificateProvider GetClientCertificate RootCAs *x509.CertPool } var emptyTpShutdownFunc = func(_ context.Context) error { return nil } func InitTracer(ctx context.Context, cfg *TracerConfig) (TpShutdownFunc, error) { log := logger.Setup() // exporter, err := srv.newStdoutExporter(os.Stdout) var err error var exporter otelsdktrace.SpanExporter if otlpEndPoint := os.Getenv("OTEL_EXPORTER_OTLP_ENDPOINT"); len(otlpEndPoint) > 0 || len(cfg.Endpoint) > 0 { exporter, err = newOLTPExporter(ctx, cfg) } if err != nil { return emptyTpShutdownFunc, err } if exporter == nil { log.Warn("tracing not configured") return emptyTpShutdownFunc, nil } tp := otelsdktrace.NewTracerProvider( otelsdktrace.WithSampler(otelsdktrace.AlwaysSample()), otelsdktrace.WithBatcher(exporter), otelsdktrace.WithResource(newResource(cfg)), ) otel.SetTracerProvider(tp) otel.SetTextMapPropagator( propagation.NewCompositeTextMapPropagator( propagation.TraceContext{}, // W3C Trace Context format; https://www.w3.org/TR/trace-context/ propagation.Baggage{}, ), ) return tp.Shutdown, nil } func newOLTPExporter(ctx context.Context, cfg *TracerConfig) (otelsdktrace.SpanExporter, error) { opts := []otlptracehttp.Option{otlptracehttp.WithCompression(otlptracehttp.GzipCompression)} if cfg.CertificateProvider != nil { opts = append(opts, otlptracehttp.WithTLSClientConfig(&tls.Config{ GetClientCertificate: cfg.CertificateProvider, RootCAs: cfg.RootCAs, })) } if len(cfg.Endpoint) > 0 { opts = append(opts, otlptracehttp.WithEndpoint(cfg.Endpoint)) } client := otlptracehttp.NewClient(opts...) exporter, err := otlptrace.New(ctx, client) if err != nil { logger.Setup().Error("creating OTLP trace exporter", "err", err) } return exporter, err } // func (srv *Server) newStdoutExporter(w io.Writer) (sdktrace.SpanExporter, error) { // return stdouttrace.New( // stdouttrace.WithWriter(w), // // Use human-readable output. // stdouttrace.WithPrettyPrint(), // // Do not print timestamps for the demo. // stdouttrace.WithoutTimestamps(), // ) // } // newResource returns a resource describing this application. func newResource(cfg *TracerConfig) *resource.Resource { r, err := resource.Merge( resource.Default(), resource.NewWithAttributes( semconv.SchemaURL, semconv.ServiceNameKey.String(cfg.ServiceName), semconv.ServiceVersionKey.String(version.Version()), attribute.String("environment", cfg.Environment), ), ) if err != nil { panic(err) } return r }