#![deny(unsafe_code)]
#![deny(missing_docs)]
#![cfg_attr(
not(test),
deny(clippy::unwrap_used, clippy::expect_used, clippy::panic, clippy::indexing_slicing)
)]
mod config;
mod error;
mod logging;
mod trace;
#[cfg(feature = "metrics")]
pub mod metrics;
#[cfg(feature = "otel")]
mod otel;
pub use config::{LogConfig, LogFormat, MetricsConfig, OtelConfig, TelemetryConfig};
pub use error::ObservabilityError;
#[cfg(feature = "otel")]
pub use trace::propagation;
pub use trace::{RecordContext, request_span};
pub mod prelude {
pub use crate::{
LogConfig, LogFormat, MetricsConfig, ObservabilityError, OtelConfig, RecordContext,
TelemetryConfig, init, request_span,
};
}
use tracing_subscriber::Registry;
use tracing_subscriber::prelude::*;
pub struct Telemetry {
_guard: Guard,
#[cfg(feature = "metrics")]
metrics: Option<metrics::MetricsHandle>,
}
impl Telemetry {
#[cfg(feature = "metrics")]
pub fn metrics(&self) -> Option<&metrics::MetricsHandle> {
self.metrics.as_ref()
}
}
pub fn init(config: &TelemetryConfig) -> Result<Telemetry, ObservabilityError> {
#[cfg(feature = "metrics")]
let metrics = if config.metrics.enabled { Some(metrics::install()?) } else { None };
#[cfg_attr(not(feature = "otel"), allow(unused_mut))]
let mut layers: Vec<logging::BoxedLayer> = vec![logging::fmt_layer(&config.log)];
#[cfg(feature = "otel")]
let tracer_provider = if config.otel.enabled {
let (layer, provider) = otel::trace_layer(config)?;
layers.push(layer);
Some(provider)
} else {
None
};
Registry::default()
.with(layers)
.with(logging::env_filter(&config.log))
.try_init()
.map_err(|e| ObservabilityError::Subscriber(e.to_string()))?;
Ok(Telemetry {
_guard: Guard {
#[cfg(feature = "otel")]
tracer_provider,
},
#[cfg(feature = "metrics")]
metrics,
})
}
struct Guard {
#[cfg(feature = "otel")]
tracer_provider: Option<opentelemetry_sdk::trace::SdkTracerProvider>,
}
impl Drop for Guard {
fn drop(&mut self) {
#[cfg(feature = "otel")]
if let Some(provider) = self.tracer_provider.take() {
let _ = provider.shutdown();
}
}
}