use opentelemetry::trace::{TraceError, TracerProvider};
use opentelemetry_sdk::trace::{SdkTracerProvider, Tracer};
use tracing::{info, level_filters::LevelFilter, Subscriber};
use tracing_opentelemetry::OpenTelemetryLayer;
use tracing_subscriber::{filter::EnvFilter, layer::SubscriberExt, registry::LookupSpan, Layer};
use crate::Error;
#[cfg(not(feature = "logfmt"))]
#[must_use]
pub fn build_logger_text<S>() -> Box<dyn Layer<S> + Send + Sync + 'static>
where
S: Subscriber + for<'a> LookupSpan<'a>,
{
use tracing_subscriber::fmt::format::FmtSpan;
if cfg!(debug_assertions) {
Box::new(
tracing_subscriber::fmt::layer()
.pretty()
.with_line_number(true)
.with_thread_names(true)
.with_span_events(FmtSpan::NEW | FmtSpan::CLOSE)
.with_timer(tracing_subscriber::fmt::time::uptime()),
)
} else {
Box::new(
tracing_subscriber::fmt::layer()
.json()
.with_timer(tracing_subscriber::fmt::time::uptime()),
)
}
}
#[cfg(feature = "logfmt")]
#[must_use]
pub fn build_logger_text<S>() -> Box<dyn Layer<S> + Send + Sync + 'static>
where
S: Subscriber + for<'a> LookupSpan<'a>,
{
Box::new(tracing_logfmt::layer())
}
#[must_use]
#[deprecated = "replaced by the configurable build_level_filter_layer(\"\")"]
pub fn build_loglevel_filter_layer() -> EnvFilter {
build_level_filter_layer("").unwrap_or_default()
}
pub fn build_level_filter_layer(log_directives: &str) -> Result<EnvFilter, Error> {
let dirs = if log_directives.is_empty() {
std::env::var("RUST_LOG")
.or_else(|_| std::env::var("OTEL_LOG_LEVEL"))
.unwrap_or_else(|_| "info".to_string())
} else {
log_directives.to_string()
};
let directive_to_allow_otel_trace = "otel::tracing=trace".parse()?;
Ok(EnvFilter::builder()
.with_default_directive(LevelFilter::INFO.into())
.parse_lossy(dirs)
.add_directive(directive_to_allow_otel_trace))
}
pub fn build_otel_layer<S>() -> Result<(OpenTelemetryLayer<S, Tracer>, TracingGuard), TraceError>
where
S: Subscriber + for<'a> LookupSpan<'a>,
{
use crate::{
init_propagator, otlp,
resource::DetectResource,
};
use opentelemetry::global;
let otel_rsrc = DetectResource::default()
.build();
let tracer_provider = otlp::init_tracerprovider(otel_rsrc, otlp::identity)?;
init_propagator()?;
let layer = tracing_opentelemetry::layer()
.with_error_records_to_exceptions(true)
.with_tracer(tracer_provider.tracer(""));
global::set_tracer_provider(tracer_provider.clone());
Ok((layer, TracingGuard { tracer_provider }))
}
#[must_use = "Recommend holding with 'let _guard = ' pattern to ensure final traces are sent to the server"]
pub struct TracingGuard {
tracer_provider: SdkTracerProvider,
}
impl TracingGuard {
#[must_use]
pub fn tracer_provider(&self) -> &impl TracerProvider {
&self.tracer_provider
}
}
impl Drop for TracingGuard {
fn drop(&mut self) {
#[allow(unused_must_use)]
let _ = self.tracer_provider.force_flush();
let _ = self.tracer_provider.shutdown();
}
}
pub fn init_subscribers() -> Result<TracingGuard, Error> {
init_subscribers_and_loglevel("")
}
pub fn init_subscribers_and_loglevel(log_directives: &str) -> Result<TracingGuard, Error> {
let subscriber = tracing_subscriber::registry()
.with(build_level_filter_layer(log_directives)?)
.with(build_logger_text());
let _guard = tracing::subscriber::set_default(subscriber);
info!("init logging & tracing");
let (layer, guard) = build_otel_layer()?;
let subscriber = tracing_subscriber::registry()
.with(layer)
.with(build_level_filter_layer(log_directives)?)
.with(build_logger_text());
tracing::subscriber::set_global_default(subscriber)?;
Ok(guard)
}