use std::collections::HashMap;
use opentelemetry::KeyValue;
use opentelemetry::trace::TracerProvider as _;
use opentelemetry_otlp::{SpanExporter, WithExportConfig, WithHttpConfig};
use opentelemetry_sdk::Resource;
use opentelemetry_sdk::trace::SdkTracerProvider;
#[derive(Debug, Clone)]
pub struct OtlpConfig {
pub(crate) endpoint: String,
pub(crate) service_name: Option<String>,
pub(crate) headers: HashMap<String, String>,
pub(crate) resource_attributes: Vec<(String, String)>,
}
impl OtlpConfig {
#[must_use]
pub fn new(endpoint: impl Into<String>) -> Self {
Self {
endpoint: endpoint.into(),
service_name: None,
headers: HashMap::new(),
resource_attributes: Vec::new(),
}
}
#[must_use]
pub fn with_service_name(mut self, name: impl Into<String>) -> Self {
self.service_name = Some(name.into());
self
}
#[must_use]
pub fn with_header(mut self, key: impl Into<String>, value: impl Into<String>) -> Self {
self.headers.insert(key.into(), value.into());
self
}
#[must_use]
pub fn with_resource_attribute(mut self, key: impl Into<String>, value: impl Into<String>) -> Self {
self.resource_attributes.push((key.into(), value.into()));
self
}
}
pub(crate) fn build_layer<S>(
config: &OtlpConfig,
) -> Result<tracing_opentelemetry::OpenTelemetryLayer<S, opentelemetry_sdk::trace::Tracer>, crate::Error>
where
S: tracing::Subscriber + for<'a> tracing_subscriber::registry::LookupSpan<'a>,
{
let mut builder = SpanExporter::builder().with_http().with_endpoint(&config.endpoint);
if !config.headers.is_empty() {
builder = builder.with_headers(config.headers.clone());
}
let exporter = builder.build().map_err(|e| crate::Error::OtlpInit(e.to_string()))?;
let mut attrs: Vec<KeyValue> =
config.resource_attributes.iter().map(|(k, v)| KeyValue::new(k.clone(), v.clone())).collect();
if let Some(name) = config.service_name.as_deref() {
attrs.push(KeyValue::new("service.name", name.to_owned()));
}
let resource = Resource::builder().with_attributes(attrs).build();
let provider =
SdkTracerProvider::builder().with_simple_exporter(exporter).with_resource(resource).build();
let tracer = provider.tracer("kamu-logging");
Ok(tracing_opentelemetry::layer().with_tracer(tracer))
}