1use std::{
16 fmt::{self, Display, Formatter},
17 sync::Arc,
18};
19
20use opentelemetry::{KeyValue, global};
21use opentelemetry_otlp::{ExporterBuildError, Protocol, WithExportConfig as _};
22use opentelemetry_sdk::{Resource, metrics::SdkMeterProvider};
23use opentelemetry_semantic_conventions::resource::SERVICE_NAME;
24use tracing::debug;
25use url::{ParseError, Url};
26
27#[derive(Clone, Debug, thiserror::Error)]
28pub enum Error {
29 ExporterBuild(Arc<ExporterBuildError>),
30 Parse(#[from] ParseError),
31}
32
33impl From<ExporterBuildError> for Error {
34 fn from(value: ExporterBuildError) -> Self {
35 Self::ExporterBuild(Arc::new(value))
36 }
37}
38
39impl Display for Error {
40 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
41 write!(f, "{self:?}")
42 }
43}
44
45pub type Result<T, E = Error> = std::result::Result<T, E>;
46
47pub fn meter_provider(
48 otlp_endpoint_url: Url,
49 service_name: impl Into<String>,
50) -> Result<SdkMeterProvider> {
51 otlp_endpoint_url
52 .join("v1/metrics")
53 .inspect(|endpoint| debug!(%endpoint))
54 .map_err(Into::into)
55 .and_then(|endpoint| {
56 opentelemetry_otlp::MetricExporter::builder()
57 .with_http()
58 .with_protocol(Protocol::HttpBinary)
59 .with_endpoint(endpoint.to_string())
60 .build()
61 .map_err(Into::into)
62 })
63 .map(|exporter| {
64 let meter_provider = SdkMeterProvider::builder()
65 .with_periodic_exporter(exporter)
66 .with_resource(
67 Resource::builder_empty()
68 .with_attributes([KeyValue::new(SERVICE_NAME, service_name.into())])
69 .build(),
70 )
71 .build();
72
73 global::set_meter_provider(meter_provider.clone());
74
75 meter_provider
76 })
77}