Move all the OTEL meters to crate-level statics

This commit is contained in:
Quentin Gliech
2024-12-12 15:09:50 +01:00
parent 781d5f104e
commit 773c4d656d
8 changed files with 77 additions and 57 deletions
+5 -10
View File
@@ -29,6 +29,8 @@ use opentelemetry::{metrics::Histogram, KeyValue};
use rand::SeedableRng;
use sqlx::PgPool;
use crate::telemetry::METER;
#[derive(Clone)]
pub struct AppState {
pub pool: PgPool,
@@ -53,15 +55,8 @@ pub struct AppState {
impl AppState {
/// Init the metrics for the app state.
pub fn init_metrics(&mut self) {
// XXX: do we want to put that somewhere else?
let scope = opentelemetry::InstrumentationScope::builder(env!("CARGO_PKG_NAME"))
.with_version(env!("CARGO_PKG_VERSION"))
.with_schema_url(opentelemetry_semantic_conventions::SCHEMA_URL)
.build();
let meter = opentelemetry::global::meter_with_scope(scope);
let pool = self.pool.clone();
meter
METER
.i64_observable_up_down_counter("db.connections.usage")
.with_description("The number of connections that are currently in `state` described by the state attribute.")
.with_unit("{connection}")
@@ -74,7 +69,7 @@ impl AppState {
.build();
let pool = self.pool.clone();
meter
METER
.i64_observable_up_down_counter("db.connections.max")
.with_description("The maximum number of open connections allowed.")
.with_unit("{connection}")
@@ -85,7 +80,7 @@ impl AppState {
.build();
// Track the connection acquisition time
let histogram = meter
let histogram = METER
.u64_histogram("db.client.connections.create_time")
.with_description("The time it took to create a new connection.")
.with_unit("ms")
+3 -3
View File
@@ -119,11 +119,11 @@ async fn try_main() -> anyhow::Result<ExitCode> {
});
// Setup OpenTelemetry tracing and metrics
let tracer = telemetry::setup(&telemetry_config).context("failed to setup OpenTelemetry")?;
self::telemetry::setup(&telemetry_config).context("failed to setup OpenTelemetry")?;
let telemetry_layer = tracer.map(|tracer| {
let telemetry_layer = self::telemetry::TRACER.get().map(|tracer| {
tracing_opentelemetry::layer()
.with_tracer(tracer)
.with_tracer(tracer.clone())
.with_tracked_inactivity(false)
.with_filter(LevelFilter::INFO)
});
+42 -28
View File
@@ -4,7 +4,10 @@
// SPDX-License-Identifier: AGPL-3.0-only
// Please see LICENSE in the repository root for full details.
use std::time::Duration;
use std::{
sync::{LazyLock, OnceLock},
time::Duration,
};
use anyhow::Context as _;
use bytes::Bytes;
@@ -15,7 +18,7 @@ use mas_config::{
TracingExporterKind,
};
use opentelemetry::{
global,
metrics::Meter,
propagation::{TextMapCompositePropagator, TextMapPropagator},
trace::TracerProvider as _,
InstrumentationScope, KeyValue,
@@ -23,7 +26,6 @@ use opentelemetry::{
use opentelemetry_otlp::{WithExportConfig, WithHttpConfig};
use opentelemetry_prometheus::PrometheusExporter;
use opentelemetry_sdk::{
self,
metrics::{ManualReader, PeriodicReader, SdkMeterProvider},
propagation::{BaggagePropagator, TraceContextPropagator},
trace::{Sampler, Tracer, TracerProvider},
@@ -31,29 +33,38 @@ use opentelemetry_sdk::{
};
use opentelemetry_semantic_conventions as semcov;
use prometheus::Registry;
use tokio::sync::OnceCell;
use url::Url;
static METER_PROVIDER: OnceCell<SdkMeterProvider> = OnceCell::const_new();
static PROMETHEUS_REGISTRY: OnceCell<Registry> = OnceCell::const_new();
static SCOPE: LazyLock<InstrumentationScope> = LazyLock::new(|| {
InstrumentationScope::builder(env!("CARGO_PKG_NAME"))
.with_version(env!("CARGO_PKG_VERSION"))
.with_schema_url(semcov::SCHEMA_URL)
.build()
});
pub fn setup(config: &TelemetryConfig) -> anyhow::Result<Option<Tracer>> {
pub static METER: LazyLock<Meter> =
LazyLock::new(|| opentelemetry::global::meter_with_scope(SCOPE.clone()));
pub static TRACER: OnceLock<Tracer> = OnceLock::new();
static METER_PROVIDER: OnceLock<SdkMeterProvider> = OnceLock::new();
static PROMETHEUS_REGISTRY: OnceLock<Registry> = OnceLock::new();
pub fn setup(config: &TelemetryConfig) -> anyhow::Result<()> {
let propagator = propagator(&config.tracing.propagators);
// The CORS filter needs to know what headers it should whitelist for
// CORS-protected requests.
mas_http::set_propagator(&propagator);
global::set_text_map_propagator(propagator);
let tracer = tracer(&config.tracing).context("Failed to configure traces exporter")?;
opentelemetry::global::set_text_map_propagator(propagator);
init_tracer(&config.tracing).context("Failed to configure traces exporter")?;
init_meter(&config.metrics).context("Failed to configure metrics exporter")?;
Ok(tracer)
Ok(())
}
pub fn shutdown() {
global::shutdown_tracer_provider();
opentelemetry::global::shutdown_tracer_provider();
if let Some(meter_provider) = METER_PROVIDER.get() {
meter_provider.shutdown().unwrap();
@@ -93,32 +104,30 @@ fn otlp_tracer_provider(endpoint: Option<&Url>) -> anyhow::Result<TracerProvider
.build()
.context("Failed to configure OTLP trace exporter")?;
let tracer = opentelemetry_sdk::trace::TracerProvider::builder()
let tracer_provider = opentelemetry_sdk::trace::TracerProvider::builder()
.with_batch_exporter(exporter, opentelemetry_sdk::runtime::Tokio)
.with_resource(resource())
.with_sampler(Sampler::AlwaysOn)
.build();
Ok(tracer)
Ok(tracer_provider)
}
fn tracer(config: &TracingConfig) -> anyhow::Result<Option<Tracer>> {
fn init_tracer(config: &TracingConfig) -> anyhow::Result<()> {
let tracer_provider = match config.exporter {
TracingExporterKind::None => return Ok(None),
TracingExporterKind::None => return Ok(()),
TracingExporterKind::Stdout => stdout_tracer_provider(),
TracingExporterKind::Otlp => otlp_tracer_provider(config.endpoint.as_ref())?,
};
let scope = InstrumentationScope::builder(env!("CARGO_PKG_NAME"))
.with_version(env!("CARGO_PKG_VERSION"))
.with_schema_url(semcov::SCHEMA_URL)
.build();
let tracer = tracer_provider.tracer_with_scope(SCOPE.clone());
TRACER
.set(tracer)
.map_err(|_| anyhow::anyhow!("TRACER was set twice"))?;
let tracer = tracer_provider.tracer_with_scope(scope);
opentelemetry::global::set_tracer_provider(tracer_provider);
global::set_tracer_provider(tracer_provider);
Ok(Some(tracer))
Ok(())
}
fn otlp_metric_reader(endpoint: Option<&url::Url>) -> anyhow::Result<PeriodicReader> {
@@ -175,7 +184,7 @@ fn prometheus_service_fn<T>(_req: T) -> PromServiceFuture {
}
pub fn prometheus_service<T>() -> tower::util::ServiceFn<fn(T) -> PromServiceFuture> {
if !PROMETHEUS_REGISTRY.initialized() {
if PROMETHEUS_REGISTRY.get().is_none() {
tracing::warn!("A Prometheus resource was mounted on a listener, but the Prometheus exporter was not setup in the config");
}
@@ -184,7 +193,10 @@ pub fn prometheus_service<T>() -> tower::util::ServiceFn<fn(T) -> PromServiceFut
fn prometheus_metric_reader() -> anyhow::Result<PrometheusExporter> {
let registry = Registry::new();
PROMETHEUS_REGISTRY.set(registry.clone())?;
PROMETHEUS_REGISTRY
.set(registry.clone())
.map_err(|_| anyhow::anyhow!("PROMETHEUS_REGISTRY was set twice"))?;
let exporter = opentelemetry_prometheus::exporter()
.with_registry(registry)
@@ -209,8 +221,10 @@ fn init_meter(config: &MetricsConfig) -> anyhow::Result<()> {
let meter_provider = meter_provider_builder.with_resource(resource()).build();
METER_PROVIDER.set(meter_provider.clone())?;
global::set_meter_provider(meter_provider.clone());
METER_PROVIDER
.set(meter_provider.clone())
.map_err(|_| anyhow::anyhow!("METER_PROVIDER was set twice"))?;
opentelemetry::global::set_meter_provider(meter_provider.clone());
Ok(())
}