init_tracing_opentelemetry/
tracing_subscriber_ext.rs

1use opentelemetry::trace::TracerProvider;
2#[cfg(feature = "metrics")]
3use opentelemetry_sdk::metrics::SdkMeterProvider;
4use opentelemetry_sdk::{
5    trace::{SdkTracerProvider, Tracer},
6    Resource,
7};
8use tracing::{level_filters::LevelFilter, Subscriber};
9#[cfg(feature = "metrics")]
10use tracing_opentelemetry::MetricsLayer;
11use tracing_opentelemetry::OpenTelemetryLayer;
12use tracing_subscriber::{filter::EnvFilter, layer::SubscriberExt, registry::LookupSpan, Layer};
13
14use crate::{
15    config::TracingConfig,
16    init_propagator, //stdio,
17    otlp,
18    otlp::OtelGuard,
19    resource::DetectResource,
20    Error,
21};
22
23#[must_use]
24#[deprecated(
25    since = "0.31.0",
26    note = "Use `TracingConfig::default().build_layer()` instead"
27)]
28/// # Panics
29/// Panics if the logger layer cannot be built.
30pub fn build_logger_text<S>() -> Box<dyn Layer<S> + Send + Sync + 'static>
31where
32    S: Subscriber + for<'a> LookupSpan<'a>,
33{
34    TracingConfig::default()
35        .build_layer()
36        .expect("Failed to build logger layer")
37}
38
39#[must_use]
40#[deprecated = "replaced by the configurable build_level_filter_layer(\"\")"]
41pub fn build_loglevel_filter_layer() -> EnvFilter {
42    build_level_filter_layer("").unwrap_or_default()
43}
44
45/// Read the configuration from (first non empty used, priority top to bottom):
46///
47/// - from parameter `directives`
48/// - from environment variable `RUST_LOG`
49/// - from environment variable `OTEL_LOG_LEVEL`
50/// - default to `Level::INFO`
51///
52/// And add directive to:
53///
54/// - `otel::tracing` should be a level info to emit opentelemetry trace & span
55///
56/// You can customize parameter "directives", by adding:
57///
58/// - `otel::setup=debug` set to debug to log detected resources, configuration read (optional)
59///
60/// see [Directives syntax](https://docs.rs/tracing-subscriber/latest/tracing_subscriber/filter/struct.EnvFilter.html#directives)
61pub fn build_level_filter_layer(log_directives: &str) -> Result<EnvFilter, Error> {
62    let dirs = if log_directives.is_empty() {
63        std::env::var("RUST_LOG")
64            .or_else(|_| std::env::var("OTEL_LOG_LEVEL"))
65            .unwrap_or_else(|_| "info".to_string())
66    } else {
67        log_directives.to_string()
68    };
69    let directive_to_allow_otel_trace = "otel::tracing=trace".parse()?;
70
71    Ok(EnvFilter::builder()
72        .with_default_directive(LevelFilter::INFO.into())
73        .parse_lossy(dirs)
74        .add_directive(directive_to_allow_otel_trace))
75}
76
77pub fn regiter_otel_layers<S>(
78    subscriber: S,
79) -> Result<(impl Subscriber + for<'span> LookupSpan<'span>, OtelGuard), Error>
80where
81    S: Subscriber + for<'a> LookupSpan<'a>,
82{
83    register_otel_layers_with_resource(subscriber, DetectResource::default().build())
84}
85
86pub fn register_otel_layers_with_resource<S>(
87    subscriber: S,
88    otel_rsrc: Resource,
89) -> Result<(impl Subscriber + for<'span> LookupSpan<'span>, OtelGuard), Error>
90where
91    S: Subscriber + for<'a> LookupSpan<'a>,
92{
93    #[cfg(feature = "metrics")]
94    let (metrics_layer, meter_provider) = build_metrics_layer_with_resource(otel_rsrc.clone())?;
95    let (trace_layer, tracer_provider) = build_tracer_layer_with_resource(otel_rsrc)?;
96    let subscriber = subscriber.with(trace_layer);
97    #[cfg(feature = "metrics")]
98    let subscriber = subscriber.with(metrics_layer);
99    Ok((
100        subscriber,
101        OtelGuard {
102            #[cfg(feature = "metrics")]
103            meter_provider,
104            tracer_provider,
105        },
106    ))
107}
108
109/// change (version 0.31): no longer set the glabal tracer
110pub fn build_tracer_layer<S>() -> Result<(OpenTelemetryLayer<S, Tracer>, SdkTracerProvider), Error>
111where
112    S: Subscriber + for<'span> LookupSpan<'span>,
113{
114    build_tracer_layer_with_resource(
115        DetectResource::default()
116            //.with_fallback_service_name(env!("CARGO_PKG_NAME"))
117            //.with_fallback_service_version(env!("CARGO_PKG_VERSION"))
118            .build(),
119    )
120}
121
122pub fn build_tracer_layer_with_resource<S>(
123    otel_rsrc: Resource,
124) -> Result<(OpenTelemetryLayer<S, Tracer>, SdkTracerProvider), Error>
125where
126    S: Subscriber + for<'span> LookupSpan<'span>,
127{
128    let tracer_provider = otlp::traces::init_tracerprovider(otel_rsrc, otlp::traces::identity)?;
129    // to not send trace somewhere, but continue to create and propagate,...
130    // then send them to `init_tracing_opentelemetry::stdio::WriteNoWhere::default()`
131    // or to `std::io::stdout()` to print
132    //
133    // let otel_tracer = stdio::init_tracer(
134    //     otel_rsrc,
135    //     stdio::identity::<stdio::WriteNoWhere>,
136    //     stdio::WriteNoWhere::default(),
137    // )?;
138    init_propagator()?;
139    let layer = tracing_opentelemetry::layer()
140        .with_error_records_to_exceptions(true)
141        .with_tracer(tracer_provider.tracer(""));
142    opentelemetry::global::set_tracer_provider(tracer_provider.clone());
143    Ok((layer, tracer_provider))
144}
145
146#[cfg(feature = "metrics")]
147pub fn build_metrics_layer<S>(
148) -> Result<(MetricsLayer<S, SdkMeterProvider>, SdkMeterProvider), Error>
149where
150    S: Subscriber + for<'span> LookupSpan<'span>,
151{
152    build_metrics_layer_with_resource(DetectResource::default().build())
153}
154
155#[cfg(feature = "metrics")]
156pub fn build_metrics_layer_with_resource<S>(
157    otel_rsrc: Resource,
158) -> Result<(MetricsLayer<S, SdkMeterProvider>, SdkMeterProvider), Error>
159where
160    S: Subscriber + for<'a> LookupSpan<'a>,
161{
162    let meter_provider = otlp::metrics::init_meterprovider(otel_rsrc, otlp::metrics::identity)?;
163    let layer = MetricsLayer::new(meter_provider.clone());
164    opentelemetry::global::set_meter_provider(meter_provider.clone());
165    Ok((layer, meter_provider))
166}
167
168/// Initialize subscribers with default configuration
169///
170/// This is a convenience function that uses production-ready defaults.
171/// For more control, use `TracingConfig::production().init_subscriber()`.
172#[deprecated(
173    since = "0.31.0",
174    note = "Use `TracingConfig::production()...` instead"
175)]
176pub fn init_subscribers() -> Result<OtelGuard, Error> {
177    let guard = TracingConfig::production().init_subscriber()?;
178    match guard.otel_guard {
179        Some(otel_guard) => {
180            // For backward compatibility, we leak the default_guard since the caller
181            // only expects an OtelGuard and won't hold onto the DefaultGuard
182            if let Some(default_guard) = guard.default_guard {
183                std::mem::forget(default_guard);
184            }
185            Ok(otel_guard)
186        }
187        None => Err(std::io::Error::new(
188            std::io::ErrorKind::Unsupported,
189            "OpenTelemetry is disabled but OtelGuard was requested",
190        )
191        .into()),
192    }
193}
194
195/// Initialize subscribers with custom log directives
196///
197/// See [`build_level_filter_layer`] for the syntax of `log_directives`.
198/// For more control, use `TracingConfig::production().with_log_directives(log_directives).init_subscriber()`.
199#[deprecated(
200    since = "0.31.0",
201    note = "Use `TracingConfig::production().with_log_directives(log_directives)...` instead"
202)]
203pub fn init_subscribers_and_loglevel(log_directives: &str) -> Result<OtelGuard, Error> {
204    let guard = TracingConfig::production()
205        .with_log_directives(log_directives)
206        .init_subscriber()?;
207    match guard.otel_guard {
208        Some(otel_guard) => {
209            // For backward compatibility, we leak the default_guard since the caller
210            // only expects an OtelGuard and won't hold onto the DefaultGuard
211            if let Some(default_guard) = guard.default_guard {
212                std::mem::forget(default_guard);
213            }
214            Ok(otel_guard)
215        }
216        None => Err(std::io::Error::new(
217            std::io::ErrorKind::Unsupported,
218            "OpenTelemetry is disabled but OtelGuard was requested",
219        )
220        .into()),
221    }
222}