Skip to content

Commit e80e530

Browse files
committed
Move all the OTEL meters to crate-level statics
1 parent 3bfab8a commit e80e530

File tree

8 files changed

+77
-57
lines changed

8 files changed

+77
-57
lines changed

crates/cli/src/app_state.rs

Lines changed: 5 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,8 @@ use opentelemetry::{metrics::Histogram, KeyValue};
2929
use rand::SeedableRng;
3030
use sqlx::PgPool;
3131

32+
use crate::telemetry::METER;
33+
3234
#[derive(Clone)]
3335
pub struct AppState {
3436
pub pool: PgPool,
@@ -53,15 +55,8 @@ pub struct AppState {
5355
impl AppState {
5456
/// Init the metrics for the app state.
5557
pub fn init_metrics(&mut self) {
56-
// XXX: do we want to put that somewhere else?
57-
let scope = opentelemetry::InstrumentationScope::builder(env!("CARGO_PKG_NAME"))
58-
.with_version(env!("CARGO_PKG_VERSION"))
59-
.with_schema_url(opentelemetry_semantic_conventions::SCHEMA_URL)
60-
.build();
61-
let meter = opentelemetry::global::meter_with_scope(scope);
62-
6358
let pool = self.pool.clone();
64-
meter
59+
METER
6560
.i64_observable_up_down_counter("db.connections.usage")
6661
.with_description("The number of connections that are currently in `state` described by the state attribute.")
6762
.with_unit("{connection}")
@@ -74,7 +69,7 @@ impl AppState {
7469
.build();
7570

7671
let pool = self.pool.clone();
77-
meter
72+
METER
7873
.i64_observable_up_down_counter("db.connections.max")
7974
.with_description("The maximum number of open connections allowed.")
8075
.with_unit("{connection}")
@@ -85,7 +80,7 @@ impl AppState {
8580
.build();
8681

8782
// Track the connection acquisition time
88-
let histogram = meter
83+
let histogram = METER
8984
.u64_histogram("db.client.connections.create_time")
9085
.with_description("The time it took to create a new connection.")
9186
.with_unit("ms")

crates/cli/src/main.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -119,11 +119,11 @@ async fn try_main() -> anyhow::Result<ExitCode> {
119119
});
120120

121121
// Setup OpenTelemetry tracing and metrics
122-
let tracer = telemetry::setup(&telemetry_config).context("failed to setup OpenTelemetry")?;
122+
self::telemetry::setup(&telemetry_config).context("failed to setup OpenTelemetry")?;
123123

124-
let telemetry_layer = tracer.map(|tracer| {
124+
let telemetry_layer = self::telemetry::TRACER.get().map(|tracer| {
125125
tracing_opentelemetry::layer()
126-
.with_tracer(tracer)
126+
.with_tracer(tracer.clone())
127127
.with_tracked_inactivity(false)
128128
.with_filter(LevelFilter::INFO)
129129
});

crates/cli/src/telemetry.rs

Lines changed: 42 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,10 @@
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

912
use anyhow::Context as _;
1013
use bytes::Bytes;
@@ -15,45 +18,53 @@ use mas_config::{
1518
TracingExporterKind,
1619
};
1720
use opentelemetry::{
18-
global,
21+
metrics::Meter,
1922
propagation::{TextMapCompositePropagator, TextMapPropagator},
2023
trace::TracerProvider as _,
2124
InstrumentationScope, KeyValue,
2225
};
2326
use opentelemetry_otlp::{WithExportConfig, WithHttpConfig};
2427
use opentelemetry_prometheus::PrometheusExporter;
2528
use opentelemetry_sdk::{
26-
self,
2729
metrics::{ManualReader, PeriodicReader, SdkMeterProvider},
2830
propagation::{BaggagePropagator, TraceContextPropagator},
2931
trace::{Sampler, Tracer, TracerProvider},
3032
Resource,
3133
};
3234
use opentelemetry_semantic_conventions as semcov;
3335
use prometheus::Registry;
34-
use tokio::sync::OnceCell;
3536
use 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

5566
pub 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

124133
fn otlp_metric_reader(endpoint: Option<&url::Url>) -> anyhow::Result<PeriodicReader> {
@@ -175,7 +184,7 @@ fn prometheus_service_fn<T>(_req: T) -> PromServiceFuture {
175184
}
176185

177186
pub 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

185194
fn 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
}

crates/handlers/src/activity_tracker/worker.rs

Lines changed: 6 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,10 @@ use sqlx::PgPool;
1616
use tokio_util::sync::CancellationToken;
1717
use ulid::Ulid;
1818

19-
use crate::activity_tracker::{Message, SessionKind};
19+
use crate::{
20+
activity_tracker::{Message, SessionKind},
21+
METER,
22+
};
2023

2124
/// The maximum number of pending activity records before we flush them to the
2225
/// database automatically.
@@ -48,13 +51,7 @@ pub struct Worker {
4851

4952
impl Worker {
5053
pub(crate) fn new(pool: PgPool) -> Self {
51-
let scope = opentelemetry::InstrumentationScope::builder(env!("CARGO_PKG_NAME"))
52-
.with_version(env!("CARGO_PKG_VERSION"))
53-
.with_schema_url(opentelemetry_semantic_conventions::SCHEMA_URL)
54-
.build();
55-
let meter = opentelemetry::global::meter_with_scope(scope);
56-
57-
let message_counter = meter
54+
let message_counter = METER
5855
.u64_counter("mas.activity_tracker.messages")
5956
.with_description("The number of messages received by the activity tracker")
6057
.with_unit("{messages}")
@@ -77,7 +74,7 @@ impl Worker {
7774
message_counter.add(0, &[KeyValue::new(TYPE, "flush")]);
7875
message_counter.add(0, &[KeyValue::new(TYPE, "shutdown")]);
7976

80-
let flush_time_histogram = meter
77+
let flush_time_histogram = METER
8178
.u64_histogram("mas.activity_tracker.flush_time")
8279
.with_description("The time it took to flush the activity tracker")
8380
.with_unit("ms")

crates/handlers/src/lib.rs

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
clippy::let_with_type_underscore,
1616
)]
1717

18-
use std::{convert::Infallible, time::Duration};
18+
use std::{convert::Infallible, sync::LazyLock, time::Duration};
1919

2020
use axum::{
2121
extract::{FromRef, FromRequestParts, OriginalUri, RawQuery, State},
@@ -41,6 +41,7 @@ use mas_policy::Policy;
4141
use mas_router::{Route, UrlBuilder};
4242
use mas_storage::{BoxClock, BoxRepository, BoxRng};
4343
use mas_templates::{ErrorContext, NotFoundContext, TemplateContext, Templates};
44+
use opentelemetry::metrics::Meter;
4445
use passwords::PasswordManager;
4546
use sqlx::PgPool;
4647
use tower::util::AndThenLayer;
@@ -62,6 +63,15 @@ mod rate_limit;
6263
#[cfg(test)]
6364
mod test_utils;
6465

66+
static METER: LazyLock<Meter> = LazyLock::new(|| {
67+
let scope = opentelemetry::InstrumentationScope::builder(env!("CARGO_PKG_NAME"))
68+
.with_version(env!("CARGO_PKG_VERSION"))
69+
.with_schema_url(opentelemetry_semantic_conventions::SCHEMA_URL)
70+
.build();
71+
72+
opentelemetry::global::meter_with_scope(scope)
73+
});
74+
6575
/// Implement `From<E>` for `RouteError`, for "internal server error" kind of
6676
/// errors.
6777
#[macro_export]

crates/tower/src/lib.rs

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,18 +6,22 @@
66

77
#![allow(clippy::module_name_repetitions)]
88

9+
use std::sync::LazyLock;
10+
11+
use opentelemetry::metrics::Meter;
12+
913
mod metrics;
1014
mod trace_context;
1115
mod tracing;
1216
mod utils;
1317

1418
pub use self::{metrics::*, trace_context::*, tracing::*, utils::*};
1519

16-
fn meter() -> opentelemetry::metrics::Meter {
20+
static METER: LazyLock<Meter> = LazyLock::new(|| {
1721
let scope = opentelemetry::InstrumentationScope::builder(env!("CARGO_PKG_NAME"))
1822
.with_version(env!("CARGO_PKG_VERSION"))
1923
.with_schema_url(opentelemetry_semantic_conventions::SCHEMA_URL)
2024
.build();
2125

2226
opentelemetry::global::meter_with_scope(scope)
23-
}
27+
});

crates/tower/src/metrics/duration.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ use opentelemetry::{metrics::Histogram, KeyValue};
1010
use pin_project_lite::pin_project;
1111
use tower::{Layer, Service};
1212

13-
use crate::{utils::FnWrapper, MetricsAttributes};
13+
use crate::{utils::FnWrapper, MetricsAttributes, METER};
1414

1515
/// A [`Layer`] that records the duration of requests in milliseconds.
1616
#[derive(Clone, Debug)]
@@ -25,7 +25,7 @@ impl DurationRecorderLayer {
2525
/// Create a new [`DurationRecorderLayer`].
2626
#[must_use]
2727
pub fn new(name: &'static str) -> Self {
28-
let histogram = crate::meter().u64_histogram(name).build();
28+
let histogram = METER.u64_histogram(name).build();
2929
Self {
3030
histogram,
3131
on_request: (),

crates/tower/src/metrics/in_flight.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ use opentelemetry::{metrics::UpDownCounter, KeyValue};
1010
use pin_project_lite::pin_project;
1111
use tower::{Layer, Service};
1212

13-
use crate::MetricsAttributes;
13+
use crate::{MetricsAttributes, METER};
1414

1515
/// A [`Layer`] that records the number of in-flight requests.
1616
///
@@ -27,7 +27,7 @@ impl InFlightCounterLayer {
2727
/// Create a new [`InFlightCounterLayer`].
2828
#[must_use]
2929
pub fn new(name: &'static str) -> Self {
30-
let counter = crate::meter()
30+
let counter = METER
3131
.i64_up_down_counter(name)
3232
.with_unit("{request}")
3333
.with_description("The number of in-flight requests")

0 commit comments

Comments
 (0)