miden_node_utils/
logging.rs1use std::str::FromStr;
2
3use opentelemetry::trace::TracerProvider as _;
4use opentelemetry_otlp::WithTonicConfig;
5use opentelemetry_sdk::propagation::TraceContextPropagator;
6use opentelemetry_sdk::trace::SdkTracerProvider;
7use tracing::subscriber::Subscriber;
8use tracing_opentelemetry::OpenTelemetryLayer;
9use tracing_subscriber::layer::{Filter, SubscriberExt};
10use tracing_subscriber::{Layer, Registry};
11
12#[derive(Clone, Copy)]
14pub enum OpenTelemetry {
15 Enabled,
16 Disabled,
17}
18
19impl OpenTelemetry {
20 fn is_enabled(self) -> bool {
21 matches!(self, OpenTelemetry::Enabled)
22 }
23}
24
25pub struct OtelGuard {
28 tracer_provider: SdkTracerProvider,
29}
30
31impl Drop for OtelGuard {
32 fn drop(&mut self) {
33 if let Err(err) = self.tracer_provider.shutdown() {
34 eprintln!("{err:?}");
35 }
36 }
37}
38
39pub fn setup_tracing(otel: OpenTelemetry) -> anyhow::Result<Option<OtelGuard>> {
52 if otel.is_enabled() {
53 opentelemetry::global::set_text_map_propagator(TraceContextPropagator::new());
54 }
55
56 let tracer_provider = if otel.is_enabled() {
60 Some(init_tracer_provider()?)
61 } else {
62 None
63 };
64 let otel_layer = tracer_provider.as_ref().map(|provider| {
65 OpenTelemetryLayer::new(provider.tracer("tracing-otel-subscriber")).boxed()
66 });
67
68 let subscriber = Registry::default()
69 .with(stdout_layer().with_filter(env_or_default_filter()))
70 .with(otel_layer.with_filter(env_or_default_filter()));
71 tracing::subscriber::set_global_default(subscriber).map_err(Into::<anyhow::Error>::into)?;
72
73 std::panic::set_hook(Box::new(|info| {
75 tracing::error!(panic = true, "{info}");
76 }));
77
78 Ok(tracer_provider.map(|tracer_provider| OtelGuard { tracer_provider }))
79}
80
81fn init_tracer_provider() -> anyhow::Result<SdkTracerProvider> {
82 let exporter = opentelemetry_otlp::SpanExporter::builder()
83 .with_tonic()
84 .with_tls_config(tonic::transport::ClientTlsConfig::new().with_native_roots())
85 .build()?;
86
87 Ok(opentelemetry_sdk::trace::SdkTracerProvider::builder()
88 .with_batch_exporter(exporter)
89 .build())
90}
91
92#[cfg(feature = "testing")]
101pub fn setup_test_tracing() -> anyhow::Result<(
102 tokio::sync::mpsc::UnboundedReceiver<opentelemetry_sdk::trace::SpanData>,
103 tokio::sync::mpsc::UnboundedReceiver<()>,
104)> {
105 let (exporter, rx_export, rx_shutdown) =
106 opentelemetry_sdk::testing::trace::new_tokio_test_exporter();
107
108 let tracer_provider = opentelemetry_sdk::trace::SdkTracerProvider::builder()
109 .with_batch_exporter(exporter)
110 .build();
111 let otel_layer =
112 OpenTelemetryLayer::new(tracer_provider.tracer("tracing-otel-subscriber")).boxed();
113 let subscriber = Registry::default()
114 .with(stdout_layer().with_filter(env_or_default_filter()))
115 .with(otel_layer.with_filter(env_or_default_filter()));
116 tracing::subscriber::set_global_default(subscriber)?;
117 Ok((rx_export, rx_shutdown))
118}
119
120#[cfg(not(feature = "tracing-forest"))]
121fn stdout_layer<S>() -> Box<dyn tracing_subscriber::Layer<S> + Send + Sync + 'static>
122where
123 S: Subscriber,
124 for<'a> S: tracing_subscriber::registry::LookupSpan<'a>,
125{
126 use tracing_subscriber::fmt::format::FmtSpan;
127
128 tracing_subscriber::fmt::layer()
129 .pretty()
130 .compact()
131 .with_level(true)
132 .with_file(true)
133 .with_line_number(true)
134 .with_target(true)
135 .with_span_events(FmtSpan::CLOSE)
136 .boxed()
137}
138
139#[cfg(feature = "tracing-forest")]
140fn stdout_layer<S>() -> Box<dyn tracing_subscriber::Layer<S> + Send + Sync + 'static>
141where
142 S: Subscriber,
143 for<'a> S: tracing_subscriber::registry::LookupSpan<'a>,
144{
145 tracing_forest::ForestLayer::default().boxed()
146}
147
148fn env_or_default_filter<S>() -> Box<dyn Filter<S> + Send + Sync + 'static> {
154 use tracing::level_filters::LevelFilter;
155 use tracing_subscriber::EnvFilter;
156 use tracing_subscriber::filter::{FilterExt, Targets};
157
158 match std::env::var(EnvFilter::DEFAULT_ENV) {
161 Ok(rust_log) => FilterExt::boxed(
162 EnvFilter::from_str(&rust_log)
163 .expect("RUST_LOG should contain a valid filter configuration"),
164 ),
165 Err(std::env::VarError::NotUnicode(_)) => panic!("RUST_LOG contained non-unicode"),
166 Err(std::env::VarError::NotPresent) => {
167 FilterExt::boxed(
169 Targets::new()
170 .with_default(LevelFilter::INFO)
171 .with_target("axum::rejection", LevelFilter::TRACE),
172 )
173 },
174 }
175}