//! The meter provider a swarm service pushes its OTLP metrics through. //! //! Built from the standard `OTEL_*` environment: the endpoint from //! `OTEL_EXPORTER_OTLP_ENDPOINT` and the resource from //! `OTEL_RESOURCE_ATTRIBUTES`. With no endpoint a service exports nothing, so //! a run outside the swarm (a test, a dev shell) needs no collector. use std::time::Duration; use opentelemetry_otlp::{MetricExporter, Protocol, WithExportConfig}; use opentelemetry_sdk::metrics::{PeriodicReader, SdkMeterProvider}; /// How often the metrics are exported. const INTERVAL: Duration = Duration::from_mins(1); /// The provider for the service `cert` names in the `bao.client_cert.*` gauges /// it also registers on a meter called `meter`, or `None` when no collector is /// configured or the exporter failed to build. /// /// The caller keeps the provider alive: it exports only while it lives. #[must_use] pub fn from_env(meter: &'static str, cert: &'static str) -> Option { use opentelemetry::metrics::MeterProvider as _; if !std::env::var("OTEL_EXPORTER_OTLP_ENDPOINT").is_ok_and(|s| !s.trim().is_empty()) { return None; } // The blocking client: `PeriodicReader` exports from a thread of its own // with no Tokio reactor. let exporter = match MetricExporter::builder() .with_http() .with_protocol(Protocol::HttpJson) .build() { Ok(exporter) => exporter, Err(e) => { tracing::warn!(error = %e, meter, "building the metric exporter failed; no metrics are exported"); return None; } }; let reader = PeriodicReader::builder(exporter) .with_interval(INTERVAL) .build(); let provider = SdkMeterProvider::builder().with_reader(reader).build(); crate::cert_metrics::register(&provider.meter(meter), cert); Some(provider) }