package fx import ( "context" "log/slog" "time" "go.uber.org/fx" "go.opentelemetry.io/contrib/bridges/otelslog" "go.opentelemetry.io/otel" "go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp" "go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp" "go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp" "go.opentelemetry.io/otel/log/global" "go.opentelemetry.io/otel/propagation" olog "go.opentelemetry.io/otel/sdk/log" "go.opentelemetry.io/otel/sdk/metric" "go.opentelemetry.io/otel/sdk/trace" ) func NewOtelTracerProvider(lc fx.Lifecycle) *trace.TracerProvider { tp, err := newTracerProvider() if err != nil { panic(err) } lc.Append(fx.Hook{ OnStop: func(ctx context.Context) error { return tp.Shutdown(ctx) }, }) return tp } func NewOtelMeterProvider(lc fx.Lifecycle) *metric.MeterProvider { mp, err := newMeterProvider() if err != nil { panic(err) } lc.Append(fx.Hook{ OnStop: func(ctx context.Context) error { return mp.Shutdown(ctx) }, }) return mp } func NewOtelLoggerProvider(lc fx.Lifecycle) *olog.LoggerProvider { lp, err := newLoggerProvider() if err != nil { panic(err) } lc.Append(fx.Hook{ OnStop: func(ctx context.Context) error { return lp.Shutdown(ctx) }, }) return lp } func NewLogger(loggerProvider *olog.LoggerProvider) *slog.Logger { logger := otelslog.NewLogger("Application", otelslog.WithLoggerProvider(loggerProvider)) return logger } // setupOTelSDK bootstraps the OpenTelemetry pipeline. // If it does not return an error, make sure to call shutdown for proper cleanup. func SetupOTelSDK(tracerProvider *trace.TracerProvider, meterProvider *metric.MeterProvider, loggerProvider *olog.LoggerProvider) { // Set up propagator. prop := newPropagator() otel.SetTextMapPropagator(prop) // Set up trace provider. otel.SetTracerProvider(tracerProvider) otel.SetMeterProvider(meterProvider) // Set up logger provider. global.SetLoggerProvider(loggerProvider) } func newPropagator() propagation.TextMapPropagator { return propagation.NewCompositeTextMapPropagator( propagation.TraceContext{}, propagation.Baggage{}, ) } func newTracerProvider() (*trace.TracerProvider, error) { traceExporter, err := otlptracehttp.New(context.Background()) if err != nil { return nil, err } tracerProvider := trace.NewTracerProvider( trace.WithBatcher(traceExporter, // Default is 5s. Set to 1s for demonstrative purposes. trace.WithBatchTimeout(time.Second)), ) return tracerProvider, nil } func newMeterProvider() (*metric.MeterProvider, error) { metricExporter, err := otlpmetrichttp.New(context.Background()) 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(3*time.Second))), ) return meterProvider, nil } func newLoggerProvider() (*olog.LoggerProvider, error) { logExporter, err := otlploghttp.New(context.Background()) if err != nil { return nil, err } loggerProvider := olog.NewLoggerProvider( olog.WithProcessor(olog.NewBatchProcessor(logExporter)), ) logger := otelslog.NewLogger("sloger", otelslog.WithLoggerProvider(loggerProvider)) logger.Info("Hello") return loggerProvider, nil }