linera_base/
tracing_opentelemetry.rs1use tracing_chrome::ChromeLayerBuilder;
7use tracing_subscriber::{layer::SubscriberExt as _, util::SubscriberInitExt as _};
8#[cfg(feature = "opentelemetry")]
9use {
10 opentelemetry::{global, trace::TracerProvider},
11 opentelemetry_otlp::{SpanExporter, WithExportConfig},
12 opentelemetry_sdk::{
13 trace::{InMemorySpanExporter, SdkTracerProvider},
14 Resource,
15 },
16 tracing_opentelemetry::OpenTelemetryLayer,
17 tracing_subscriber::{
18 filter::{filter_fn, FilterFn},
19 layer::Layer,
20 },
21};
22
23#[cfg(feature = "opentelemetry")]
43fn opentelemetry_skip_filter() -> FilterFn<impl Fn(&tracing::Metadata<'_>) -> bool> {
44 filter_fn(|metadata| {
45 if !metadata.is_span() {
46 return false;
47 }
48 metadata.fields().field("opentelemetry.skip").is_none()
49 })
50}
51
52#[cfg(feature = "opentelemetry")]
56fn init_with_tracer_provider(log_name: &str, tracer_provider: SdkTracerProvider) {
57 global::set_tracer_provider(tracer_provider.clone());
58 let tracer = tracer_provider.tracer("linera");
59
60 let opentelemetry_layer =
61 OpenTelemetryLayer::new(tracer).with_filter(opentelemetry_skip_filter());
62
63 let config = crate::tracing::get_env_config(log_name);
64 let maybe_log_file_layer = config.maybe_log_file_layer();
65 let stderr_layer = config.stderr_layer();
66
67 tracing_subscriber::registry()
68 .with(opentelemetry_layer)
69 .with(config.env_filter)
70 .with(maybe_log_file_layer)
71 .with(stderr_layer)
72 .init();
73}
74
75#[cfg(all(with_testing, feature = "opentelemetry"))]
80pub fn build_opentelemetry_layer_with_test_exporter(
81 log_name: &str,
82) -> (
83 impl tracing_subscriber::Layer<tracing_subscriber::Registry>,
84 InMemorySpanExporter,
85 SdkTracerProvider,
86) {
87 let exporter = InMemorySpanExporter::default();
88 let exporter_clone = exporter.clone();
89
90 let resource = Resource::builder()
91 .with_service_name(log_name.to_string())
92 .build();
93
94 let tracer_provider = SdkTracerProvider::builder()
95 .with_resource(resource)
96 .with_simple_exporter(exporter)
97 .with_sampler(opentelemetry_sdk::trace::Sampler::AlwaysOn)
98 .build();
99
100 global::set_tracer_provider(tracer_provider.clone());
101 let tracer = tracer_provider.tracer("linera");
102 let opentelemetry_layer =
103 OpenTelemetryLayer::new(tracer).with_filter(opentelemetry_skip_filter());
104
105 (opentelemetry_layer, exporter_clone, tracer_provider)
106}
107
108#[cfg(feature = "opentelemetry")]
115pub fn init_with_opentelemetry(log_name: &str, otlp_endpoint: Option<&str>) {
116 let endpoint = match otlp_endpoint {
118 Some(ep) if !ep.is_empty() => ep.to_string(),
119 _ => match std::env::var("LINERA_OTLP_EXPORTER_ENDPOINT") {
120 Ok(ep) if !ep.is_empty() => ep,
121 _ => {
122 eprintln!(
123 "LINERA_OTLP_EXPORTER_ENDPOINT not set and no endpoint provided. \
124 Falling back to standard tracing without OpenTelemetry support."
125 );
126 crate::tracing::init(log_name);
127 return;
128 }
129 },
130 };
131
132 let resource = Resource::builder()
133 .with_service_name(log_name.to_string())
134 .build();
135
136 let exporter = SpanExporter::builder()
137 .with_tonic()
138 .with_endpoint(endpoint)
139 .build()
140 .expect("Failed to create OTLP exporter");
141
142 let tracer_provider = SdkTracerProvider::builder()
143 .with_resource(resource)
144 .with_batch_exporter(exporter)
145 .with_sampler(opentelemetry_sdk::trace::Sampler::AlwaysOn)
146 .build();
147
148 init_with_tracer_provider(log_name, tracer_provider);
149}
150
151#[cfg(not(feature = "opentelemetry"))]
153pub fn init_with_opentelemetry(log_name: &str, _otlp_endpoint: Option<&str>) {
154 eprintln!(
155 "OTLP export requires the 'opentelemetry' feature to be enabled! Falling back to default tracing initialization."
156 );
157 crate::tracing::init(log_name);
158}
159
160pub type ChromeTraceGuard = tracing_chrome::FlushGuard;
165
166pub fn build_chrome_trace_layer_with_exporter<W>(
171 log_name: &str,
172 writer: W,
173) -> (impl tracing::Subscriber + Send + Sync, ChromeTraceGuard)
174where
175 W: std::io::Write + Send + 'static,
176{
177 let (chrome_layer, guard) = ChromeLayerBuilder::new().writer(writer).build();
178
179 let config = crate::tracing::get_env_config(log_name);
180 let maybe_log_file_layer = config.maybe_log_file_layer();
181 let stderr_layer = config.stderr_layer();
182
183 let subscriber = tracing_subscriber::registry()
184 .with(chrome_layer)
185 .with(config.env_filter)
186 .with(maybe_log_file_layer)
187 .with(stderr_layer);
188
189 (subscriber, guard)
190}
191
192pub fn init_with_chrome_trace_exporter<W>(log_name: &str, writer: W) -> ChromeTraceGuard
204where
205 W: std::io::Write + Send + 'static,
206{
207 let (subscriber, guard) = build_chrome_trace_layer_with_exporter(log_name, writer);
208 let _ = subscriber.try_init();
209 guard
210}