use std::sync::OnceLock;
use crate::telemetry::TracingConfig;
#[cfg(feature = "otel")]
use {
crate::telemetry::config::ExporterKind,
anyhow::Context,
opentelemetry_otlp::{Protocol, WithExportConfig, WithHttpConfig, WithTonicConfig},
opentelemetry_sdk::metrics::SdkMeterProvider,
};
#[cfg(feature = "otel")]
static METRICS_INIT: OnceLock<Result<(), String>> = OnceLock::new();
#[cfg(feature = "otel")]
pub fn init_metrics_provider(cfg: &TracingConfig) -> anyhow::Result<()> {
METRICS_INIT
.get_or_init(|| do_init_metrics_provider(cfg).map_err(|e| e.to_string()))
.clone()
.map_err(|e| anyhow::anyhow!("{e}"))
}
#[cfg(feature = "otel")]
fn do_init_metrics_provider(cfg: &TracingConfig) -> anyhow::Result<()> {
let metrics_cfg = &cfg.metrics;
let (kind, endpoint, timeout) = {
let (k, ep) = metrics_cfg.exporter.as_ref().map_or_else(
|| (ExporterKind::OtlpGrpc, "http://127.0.0.1:4317".to_owned()),
|e| {
(
e.kind,
e.endpoint
.clone()
.unwrap_or_else(|| "http://127.0.0.1:4317".to_owned()),
)
},
);
let t = metrics_cfg
.exporter
.as_ref()
.and_then(|e| e.timeout_ms)
.map(std::time::Duration::from_millis);
(k, ep, t)
};
let exporter = if matches!(kind, ExporterKind::OtlpHttp) {
let mut b = opentelemetry_otlp::MetricExporter::builder()
.with_http()
.with_protocol(Protocol::HttpBinary)
.with_endpoint(&endpoint);
if let Some(t) = timeout {
b = b.with_timeout(t);
}
if let Some(headers) =
crate::telemetry::init::build_headers_from_cfg_and_env(metrics_cfg.exporter.as_ref())
{
b = b.with_headers(headers);
}
b.build().context("build OTLP HTTP metric exporter")?
} else {
let mut b = opentelemetry_otlp::MetricExporter::builder()
.with_tonic()
.with_endpoint(&endpoint);
if let Some(t) = timeout {
b = b.with_timeout(t);
}
if let Some(md) =
crate::telemetry::init::build_metadata_from_cfg_and_env(metrics_cfg.exporter.as_ref())
{
b = b.with_metadata(md);
}
b.build().context("build OTLP gRPC metric exporter")?
};
let resource = crate::telemetry::init::build_resource(cfg);
let mut builder = SdkMeterProvider::builder()
.with_periodic_exporter(exporter)
.with_resource(resource);
if let Some(limit) = metrics_cfg.cardinality_limit {
builder = builder.with_view(move |_: &opentelemetry_sdk::metrics::Instrument| {
opentelemetry_sdk::metrics::Stream::builder()
.with_cardinality_limit(limit)
.build()
.ok()
});
}
let provider = builder.build();
crate::telemetry::init::init_metrics(metrics_cfg, provider)?;
Ok(())
}
#[cfg(not(feature = "otel"))]
pub fn init_metrics_provider(_cfg: &TracingConfig) -> anyhow::Result<()> {
Err(anyhow::anyhow!("otel feature is disabled"))
}