use thiserror::Error;
use tracing_subscriber::{EnvFilter, fmt};
use crate::observability::{OpenTelemetryConfig, TraceExporter};
pub type ObservabilityResult<T> = Result<T, ObservabilityError>;
#[derive(Debug, Error, PartialEq, Eq)]
pub enum ObservabilityError {
#[error("tracing subscriber is already initialized")]
SubscriberAlreadyInitialized,
#[error("otlp exporter endpoint is required")]
MissingOtlpEndpoint,
}
pub fn init_opentelemetry_tracing(config: OpenTelemetryConfig) -> ObservabilityResult<()> {
match config.exporter {
TraceExporter::Disabled => Ok(()),
TraceExporter::Stdout => {
let filter =
EnvFilter::try_new(config.filter).unwrap_or_else(|_| EnvFilter::new("info"));
fmt()
.with_env_filter(filter)
.try_init()
.map_err(|_| ObservabilityError::SubscriberAlreadyInitialized)
}
TraceExporter::Otlp { endpoint } => {
if endpoint.trim().is_empty() {
Err(ObservabilityError::MissingOtlpEndpoint)
} else {
Ok(())
}
}
}
}
#[cfg(test)]
mod tests {
use super::{ObservabilityError, init_opentelemetry_tracing};
use crate::observability::{OpenTelemetryConfig, TraceExporter};
#[test]
fn disabled_exporter_does_not_install_subscriber() {
init_opentelemetry_tracing(OpenTelemetryConfig::default()).expect("disabled");
}
#[test]
fn otlp_requires_endpoint() {
let error = init_opentelemetry_tracing(OpenTelemetryConfig {
exporter: TraceExporter::Otlp {
endpoint: String::new(),
},
..OpenTelemetryConfig::default()
})
.expect_err("missing endpoint");
assert_eq!(error, ObservabilityError::MissingOtlpEndpoint);
}
}