#[cfg(all(with_testing, feature = "opentelemetry"))]
use opentelemetry_sdk::trace::InMemorySpanExporter;
use tracing_chrome::ChromeLayerBuilder;
use tracing_subscriber::{layer::SubscriberExt as _, util::SubscriberInitExt as _};
#[cfg(feature = "opentelemetry")]
use {
opentelemetry::{global, propagation::TextMapCompositePropagator, trace::TracerProvider},
opentelemetry_otlp::{SpanExporter, WithExportConfig},
opentelemetry_sdk::{
propagation::{BaggagePropagator, TraceContextPropagator},
trace::SdkTracerProvider,
Resource,
},
tracing_opentelemetry::OpenTelemetryLayer,
tracing_subscriber::{
filter::{filter_fn, FilterFn},
layer::Layer,
},
};
#[cfg(feature = "opentelemetry")]
fn opentelemetry_skip_filter() -> FilterFn<impl Fn(&tracing::Metadata<'_>) -> bool> {
filter_fn(|metadata| {
if !metadata.is_span() {
return false;
}
metadata.fields().field("opentelemetry.skip").is_none()
})
}
#[cfg(feature = "opentelemetry")]
fn init_with_tracer_provider(log_name: &str, tracer_provider: SdkTracerProvider) {
global::set_tracer_provider(tracer_provider.clone());
let tracer = tracer_provider.tracer("linera");
let opentelemetry_layer =
OpenTelemetryLayer::new(tracer).with_filter(opentelemetry_skip_filter());
let config = crate::tracing::get_env_config(log_name);
let maybe_log_file_layer = config.maybe_log_file_layer();
let stderr_layer = config.stderr_layer();
tracing_subscriber::registry()
.with(opentelemetry_layer)
.with(config.env_filter)
.with(maybe_log_file_layer)
.with(stderr_layer)
.init();
}
#[cfg(all(with_testing, feature = "opentelemetry"))]
pub fn build_opentelemetry_layer_with_test_exporter(
log_name: &str,
) -> (
impl tracing_subscriber::Layer<tracing_subscriber::Registry>,
InMemorySpanExporter,
SdkTracerProvider,
) {
let exporter = InMemorySpanExporter::default();
let exporter_clone = exporter.clone();
let resource = Resource::builder()
.with_service_name(log_name.to_string())
.build();
let tracer_provider = SdkTracerProvider::builder()
.with_resource(resource)
.with_simple_exporter(exporter)
.with_sampler(opentelemetry_sdk::trace::Sampler::AlwaysOn)
.build();
global::set_tracer_provider(tracer_provider.clone());
let tracer = tracer_provider.tracer("linera");
let opentelemetry_layer =
OpenTelemetryLayer::new(tracer).with_filter(opentelemetry_skip_filter());
(opentelemetry_layer, exporter_clone, tracer_provider)
}
#[cfg(feature = "opentelemetry")]
pub fn init_with_opentelemetry(log_name: &str, otlp_endpoint: Option<&str>) {
let propagator = TextMapCompositePropagator::new(vec![
Box::new(TraceContextPropagator::new()),
Box::new(BaggagePropagator::new()),
]);
global::set_text_map_propagator(propagator);
let endpoint = match otlp_endpoint {
Some(ep) if !ep.is_empty() => ep.to_string(),
_ => match std::env::var("LINERA_OTLP_EXPORTER_ENDPOINT") {
Ok(ep) if !ep.is_empty() => ep,
_ => {
crate::tracing::init(log_name);
return;
}
},
};
let resource = Resource::builder()
.with_service_name(log_name.to_string())
.build();
let exporter = SpanExporter::builder()
.with_tonic()
.with_endpoint(endpoint)
.build()
.expect("Failed to create OTLP exporter");
let tracer_provider = SdkTracerProvider::builder()
.with_resource(resource)
.with_batch_exporter(exporter)
.with_sampler(opentelemetry_sdk::trace::Sampler::AlwaysOn)
.build();
init_with_tracer_provider(log_name, tracer_provider);
}
#[cfg(not(feature = "opentelemetry"))]
pub fn init_with_opentelemetry(log_name: &str, otlp_endpoint: Option<&str>) {
crate::tracing::init(log_name);
let endpoint_requested = matches!(otlp_endpoint, Some(ep) if !ep.is_empty())
|| matches!(std::env::var("LINERA_OTLP_EXPORTER_ENDPOINT"), Ok(ep) if !ep.is_empty());
if endpoint_requested {
tracing::warn!(
"OTLP export requires the 'opentelemetry' feature to be enabled. \
Falling back to default tracing initialization."
);
}
}
pub type ChromeTraceGuard = tracing_chrome::FlushGuard;
pub fn build_chrome_trace_layer_with_exporter<W>(
log_name: &str,
writer: W,
) -> (impl tracing::Subscriber + Send + Sync, ChromeTraceGuard)
where
W: std::io::Write + Send + 'static,
{
let (chrome_layer, guard) = ChromeLayerBuilder::new().writer(writer).build();
let config = crate::tracing::get_env_config(log_name);
let maybe_log_file_layer = config.maybe_log_file_layer();
let stderr_layer = config.stderr_layer();
let subscriber = tracing_subscriber::registry()
.with(chrome_layer)
.with(config.env_filter)
.with(maybe_log_file_layer)
.with(stderr_layer);
(subscriber, guard)
}
pub fn init_with_chrome_trace_exporter<W>(log_name: &str, writer: W) -> ChromeTraceGuard
where
W: std::io::Write + Send + 'static,
{
let (subscriber, guard) = build_chrome_trace_layer_with_exporter(log_name, writer);
let _ = subscriber.try_init();
guard
}