#[cfg(feature = "opentelemetry")] pub mod telemetry; #[cfg(feature = "opentelemetry")] pub use opentelemetry_sdk::trace::SdkTracerProvider; #[cfg(feature = "tracing-loki")] mod loki; use tracing_subscriber::{ EnvFilter, Layer, Registry, layer::SubscriberExt, util::SubscriberInitExt, }; /// Telemetry handle #[derive(bon::Builder)] #[builder(finish_fn(vis = "", name = build_internal))] pub struct Tracing { #[builder(field = vec![tracing_subscriber::fmt::layer().boxed()])] layers: Vec + Sync + Send>>, #[cfg(feature = "tracing-loki")] #[builder(setters(vis = "", name = loki_internal))] pub loki_task: tracing_loki::BackgroundTask, #[cfg(feature = "opentelemetry")] #[builder(setters(vis = "", name = otel_internal))] pub otel_provider: opentelemetry_sdk::trace::SdkTracerProvider, } // Define a custom finishing function as a method on the `UserBuilder`. // The builder's state must implement the `IsComplete` trait. // See details about it in the tip below this example. impl TracingBuilder { pub fn build(self, config: &crate::Monitoring) -> Tracing { // Delegate to `build_internal()` to get the instance of user. let mut tracing = self.build_internal(); let layers = std::mem::take(&mut tracing.layers); tracing_subscriber::registry() .with(layers) .with( EnvFilter::try_from_default_env() .unwrap_or_else(|_| config.log_level.to_string().into()), ) .try_init() .ok(); tracing } } #[cfg(test)] mod tests { use crate::{AppConfig, Environment, Monitoring}; use super::*; #[tokio::test] async fn build() { let config = Monitoring { log_level: "error".to_string(), opentelemetry_endpoint: "http://localhost:4317".into(), loki_endpoint: "http://localhost:3100".into(), }; let app_config = AppConfig { name: "test".into(), version: "1.0.0".into(), env: Environment::Development, port: 6969, }; let tracing = Tracing::builder().opentelemetry(&app_config, &config); assert!(tracing.is_ok()); let tracing = tracing.unwrap().loki(&app_config, &config); assert!(tracing.is_ok()); } }