44// SPDX-License-Identifier: AGPL-3.0-only
55// Please see LICENSE in the repository root for full details.
66
7- use std:: time:: Duration ;
7+ use std:: {
8+ sync:: { LazyLock , OnceLock } ,
9+ time:: Duration ,
10+ } ;
811
912use anyhow:: Context as _;
1013use bytes:: Bytes ;
@@ -15,45 +18,53 @@ use mas_config::{
1518 TracingExporterKind ,
1619} ;
1720use opentelemetry:: {
18- global ,
21+ metrics :: Meter ,
1922 propagation:: { TextMapCompositePropagator , TextMapPropagator } ,
2023 trace:: TracerProvider as _,
2124 InstrumentationScope , KeyValue ,
2225} ;
2326use opentelemetry_otlp:: { WithExportConfig , WithHttpConfig } ;
2427use opentelemetry_prometheus:: PrometheusExporter ;
2528use opentelemetry_sdk:: {
26- self ,
2729 metrics:: { ManualReader , PeriodicReader , SdkMeterProvider } ,
2830 propagation:: { BaggagePropagator , TraceContextPropagator } ,
2931 trace:: { Sampler , Tracer , TracerProvider } ,
3032 Resource ,
3133} ;
3234use opentelemetry_semantic_conventions as semcov;
3335use prometheus:: Registry ;
34- use tokio:: sync:: OnceCell ;
3536use url:: Url ;
3637
37- static METER_PROVIDER : OnceCell < SdkMeterProvider > = OnceCell :: const_new ( ) ;
38- static PROMETHEUS_REGISTRY : OnceCell < Registry > = OnceCell :: const_new ( ) ;
38+ static SCOPE : LazyLock < InstrumentationScope > = LazyLock :: new ( || {
39+ InstrumentationScope :: builder ( env ! ( "CARGO_PKG_NAME" ) )
40+ . with_version ( env ! ( "CARGO_PKG_VERSION" ) )
41+ . with_schema_url ( semcov:: SCHEMA_URL )
42+ . build ( )
43+ } ) ;
3944
40- pub fn setup ( config : & TelemetryConfig ) -> anyhow:: Result < Option < Tracer > > {
45+ pub static METER : LazyLock < Meter > =
46+ LazyLock :: new ( || opentelemetry:: global:: meter_with_scope ( SCOPE . clone ( ) ) ) ;
47+
48+ pub static TRACER : OnceLock < Tracer > = OnceLock :: new ( ) ;
49+ static METER_PROVIDER : OnceLock < SdkMeterProvider > = OnceLock :: new ( ) ;
50+ static PROMETHEUS_REGISTRY : OnceLock < Registry > = OnceLock :: new ( ) ;
51+
52+ pub fn setup ( config : & TelemetryConfig ) -> anyhow:: Result < ( ) > {
4153 let propagator = propagator ( & config. tracing . propagators ) ;
4254
4355 // The CORS filter needs to know what headers it should whitelist for
4456 // CORS-protected requests.
4557 mas_http:: set_propagator ( & propagator) ;
46- global:: set_text_map_propagator ( propagator) ;
47-
48- let tracer = tracer ( & config. tracing ) . context ( "Failed to configure traces exporter" ) ?;
58+ opentelemetry:: global:: set_text_map_propagator ( propagator) ;
4959
60+ init_tracer ( & config. tracing ) . context ( "Failed to configure traces exporter" ) ?;
5061 init_meter ( & config. metrics ) . context ( "Failed to configure metrics exporter" ) ?;
5162
52- Ok ( tracer )
63+ Ok ( ( ) )
5364}
5465
5566pub fn shutdown ( ) {
56- global:: shutdown_tracer_provider ( ) ;
67+ opentelemetry :: global:: shutdown_tracer_provider ( ) ;
5768
5869 if let Some ( meter_provider) = METER_PROVIDER . get ( ) {
5970 meter_provider. shutdown ( ) . unwrap ( ) ;
@@ -93,32 +104,30 @@ fn otlp_tracer_provider(endpoint: Option<&Url>) -> anyhow::Result<TracerProvider
93104 . build ( )
94105 . context ( "Failed to configure OTLP trace exporter" ) ?;
95106
96- let tracer = opentelemetry_sdk:: trace:: TracerProvider :: builder ( )
107+ let tracer_provider = opentelemetry_sdk:: trace:: TracerProvider :: builder ( )
97108 . with_batch_exporter ( exporter, opentelemetry_sdk:: runtime:: Tokio )
98109 . with_resource ( resource ( ) )
99110 . with_sampler ( Sampler :: AlwaysOn )
100111 . build ( ) ;
101112
102- Ok ( tracer )
113+ Ok ( tracer_provider )
103114}
104115
105- fn tracer ( config : & TracingConfig ) -> anyhow:: Result < Option < Tracer > > {
116+ fn init_tracer ( config : & TracingConfig ) -> anyhow:: Result < ( ) > {
106117 let tracer_provider = match config. exporter {
107- TracingExporterKind :: None => return Ok ( None ) ,
118+ TracingExporterKind :: None => return Ok ( ( ) ) ,
108119 TracingExporterKind :: Stdout => stdout_tracer_provider ( ) ,
109120 TracingExporterKind :: Otlp => otlp_tracer_provider ( config. endpoint . as_ref ( ) ) ?,
110121 } ;
111122
112- let scope = InstrumentationScope :: builder ( env ! ( "CARGO_PKG_NAME" ) )
113- . with_version ( env ! ( "CARGO_PKG_VERSION" ) )
114- . with_schema_url ( semcov :: SCHEMA_URL )
115- . build ( ) ;
123+ let tracer = tracer_provider . tracer_with_scope ( SCOPE . clone ( ) ) ;
124+ TRACER
125+ . set ( tracer )
126+ . map_err ( |_| anyhow :: anyhow! ( "TRACER was set twice" ) ) ? ;
116127
117- let tracer = tracer_provider . tracer_with_scope ( scope ) ;
128+ opentelemetry :: global :: set_tracer_provider ( tracer_provider ) ;
118129
119- global:: set_tracer_provider ( tracer_provider) ;
120-
121- Ok ( Some ( tracer) )
130+ Ok ( ( ) )
122131}
123132
124133fn otlp_metric_reader ( endpoint : Option < & url:: Url > ) -> anyhow:: Result < PeriodicReader > {
@@ -175,7 +184,7 @@ fn prometheus_service_fn<T>(_req: T) -> PromServiceFuture {
175184}
176185
177186pub fn prometheus_service < T > ( ) -> tower:: util:: ServiceFn < fn ( T ) -> PromServiceFuture > {
178- if ! PROMETHEUS_REGISTRY . initialized ( ) {
187+ if PROMETHEUS_REGISTRY . get ( ) . is_none ( ) {
179188 tracing:: warn!( "A Prometheus resource was mounted on a listener, but the Prometheus exporter was not setup in the config" ) ;
180189 }
181190
@@ -184,7 +193,10 @@ pub fn prometheus_service<T>() -> tower::util::ServiceFn<fn(T) -> PromServiceFut
184193
185194fn prometheus_metric_reader ( ) -> anyhow:: Result < PrometheusExporter > {
186195 let registry = Registry :: new ( ) ;
187- PROMETHEUS_REGISTRY . set ( registry. clone ( ) ) ?;
196+
197+ PROMETHEUS_REGISTRY
198+ . set ( registry. clone ( ) )
199+ . map_err ( |_| anyhow:: anyhow!( "PROMETHEUS_REGISTRY was set twice" ) ) ?;
188200
189201 let exporter = opentelemetry_prometheus:: exporter ( )
190202 . with_registry ( registry)
@@ -209,8 +221,10 @@ fn init_meter(config: &MetricsConfig) -> anyhow::Result<()> {
209221
210222 let meter_provider = meter_provider_builder. with_resource ( resource ( ) ) . build ( ) ;
211223
212- METER_PROVIDER . set ( meter_provider. clone ( ) ) ?;
213- global:: set_meter_provider ( meter_provider. clone ( ) ) ;
224+ METER_PROVIDER
225+ . set ( meter_provider. clone ( ) )
226+ . map_err ( |_| anyhow:: anyhow!( "METER_PROVIDER was set twice" ) ) ?;
227+ opentelemetry:: global:: set_meter_provider ( meter_provider. clone ( ) ) ;
214228
215229 Ok ( ( ) )
216230}
0 commit comments