use std::borrow::Cow;
use opentelemetry::KeyValue;
use opentelemetry_sdk::{Resource, resource::ResourceDetector};
use opentelemetry_semantic_conventions::resource;
#[derive(Debug, Default, Clone)]
pub struct DetectResource {
fallback_service_name: Option<Cow<'static, str>>,
fallback_service_version: Option<Cow<'static, str>>,
}
impl DetectResource {
#[must_use]
pub fn with_fallback_service_name(
mut self,
fallback_service_name: impl Into<Cow<'static, str>>,
) -> Self {
self.fallback_service_name = Some(fallback_service_name.into());
self
}
#[must_use]
pub fn with_fallback_service_version(
mut self,
fallback_service_version: impl Into<Cow<'static, str>>,
) -> Self {
self.fallback_service_version = Some(fallback_service_version.into());
self
}
#[must_use]
pub fn build(self) -> Resource {
let rsrc = Resource::builder()
.with_detector(Box::new(ServiceInfoDetector {
fallback_service_name: self.fallback_service_name,
fallback_service_version: self.fallback_service_version,
}))
.build();
debug_resource(&rsrc);
rsrc
}
}
pub fn debug_resource(rsrc: &Resource) {
rsrc.iter().for_each(
|kv| tracing::debug!(target: "otel::setup::resource", key = %kv.0, value = %kv.1),
);
}
#[derive(Debug)]
pub struct ServiceInfoDetector {
fallback_service_name: Option<Cow<'static, str>>,
fallback_service_version: Option<Cow<'static, str>>,
}
impl ResourceDetector for ServiceInfoDetector {
fn detect(&self) -> Resource {
let service_name = std::env::var("OTEL_SERVICE_NAME")
.or_else(|_| std::env::var("SERVICE_NAME"))
.or_else(|_| std::env::var("APP_NAME"))
.ok()
.or_else(|| self.fallback_service_name.clone().map(|v| v.to_string()))
.map(|v| KeyValue::new(resource::SERVICE_NAME, v));
let service_version = std::env::var("SERVICE_VERSION")
.or_else(|_| std::env::var("APP_VERSION"))
.ok()
.or_else(|| {
self.fallback_service_version
.clone()
.map(std::borrow::Cow::into_owned)
})
.map(|v| KeyValue::new(resource::SERVICE_VERSION, v));
let mut resource = Resource::builder_empty();
if let Some(service_name) = service_name {
resource = resource.with_attribute(service_name);
}
if let Some(service_version) = service_version {
resource = resource.with_attribute(service_version);
}
resource.build()
}
}