#![warn(missing_docs, clippy::missing_panics_doc)]
use tracing::level_filters::LevelFilter;
#[cfg(debug_assertions)]
use tracing_subscriber::fmt::format::FmtSpan;
use tracing_subscriber::layer::SubscriberExt;
use opentelemetry::trace::TracerProvider as _;
pub use opentelemetry::{Array, Context, Key, KeyValue, StringValue, Value, global};
pub use opentelemetry_sdk::{
Resource,
error::OTelSdkError,
resource::{EnvResourceDetector, ResourceDetector, TelemetryResourceDetector},
trace::SdkTracerProvider as TracerProvider,
};
pub use opentelemetry_semantic_conventions::attribute as semconv;
pub use tracing_opentelemetry::{OpenTelemetryLayer, OpenTelemetrySpanExt};
pub mod fmt;
pub mod http;
pub mod instrumentations;
pub mod middleware;
pub mod otlp;
pub mod propagation;
#[cfg(feature = "axum")]
pub use tracing_opentelemetry_instrumentation_sdk;
#[cfg(feature = "test")]
pub mod test;
#[cfg(feature = "future")]
pub mod future;
mod filter;
mod util;
#[derive(Debug, Default)]
pub struct DetectResource {
fallback_service_name: &'static str,
fallback_service_version: &'static str,
}
impl DetectResource {
pub fn new(
fallback_service_name: &'static str,
fallback_service_version: &'static str,
) -> Self {
DetectResource {
fallback_service_name,
fallback_service_version,
}
}
pub fn build(self) -> Resource {
let env_detector = EnvResourceDetector::new();
let env_resource = env_detector.detect();
let read_from_env = |key| util::env_var(key).map(Into::into);
let service_name_key = Key::new(semconv::SERVICE_NAME);
let service_name_value = read_from_env("OTEL_SERVICE_NAME")
.or_else(|| env_resource.get(&service_name_key))
.or_else(|| read_from_env("SERVICE_NAME"))
.or_else(|| read_from_env("APP_NAME"))
.unwrap_or_else(|| self.fallback_service_name.into());
let service_version_key = Key::new(semconv::SERVICE_VERSION);
let service_version_value = read_from_env("OTEL_SERVICE_VERSION")
.or_else(|| env_resource.get(&service_version_key))
.or_else(|| read_from_env("SERVICE_VERSION"))
.or_else(|| read_from_env("APP_VERSION"))
.unwrap_or_else(|| self.fallback_service_version.into());
let resource = Resource::builder_empty()
.with_detectors(&[
Box::new(TelemetryResourceDetector),
Box::new(env_detector),
])
.with_attributes([
KeyValue::new(service_name_key, service_name_value),
KeyValue::new(service_version_key, service_version_value),
])
.build();
resource.iter().for_each(
|kv| tracing::debug!(target: "otel::setup::resource", key = %kv.0, value = %kv.1),
);
resource
}
}
macro_rules! fmt_layer {
() => {{
let layer = tracing_subscriber::fmt::layer();
#[cfg(debug_assertions)]
let layer = layer.compact().with_span_events(FmtSpan::CLOSE);
#[cfg(not(debug_assertions))]
let layer = layer.json().event_format(fmt::JsonFormat);
layer.with_writer(std::io::stdout)
}};
}
pub fn init_tracing_with_fallbacks(
log_level: tracing::Level,
fallback_service_name: &'static str,
fallback_service_version: &'static str,
) -> TracerProvider {
let setup_subscriber = tracing_subscriber::registry()
.with(Into::<LevelFilter>::into(log_level))
.with(fmt_layer!());
let _guard = tracing::subscriber::set_default(setup_subscriber);
tracing::info!("init logging & tracing");
let otel_rsrc =
DetectResource::new(fallback_service_name, fallback_service_version).build();
let tracer_provider =
otlp::init_tracer(otel_rsrc, otlp::identity).expect("TracerProvider setup");
global::set_tracer_provider(tracer_provider.clone());
global::set_text_map_propagator(
propagation::TextMapSplitPropagator::from_env().expect("TextMapPropagator setup"),
);
let otel_layer =
OpenTelemetryLayer::new(tracer_provider.tracer(env!("CARGO_PKG_NAME")));
let subscriber = tracing_subscriber::registry()
.with(Into::<filter::TracingFilter>::into(log_level))
.with(fmt_layer!())
.with(otel_layer);
tracing::subscriber::set_global_default(subscriber).unwrap();
tracer_provider
}
#[macro_export]
macro_rules! init_tracing {
($log_level:expr) => {
$crate::init_tracing_with_fallbacks(
$log_level,
env!("CARGO_PKG_NAME"),
env!("CARGO_PKG_VERSION"),
)
};
}
#[inline]
pub fn shutdown_tracer_provider(provider: &TracerProvider) {
if let Err(err) = provider.force_flush() {
tracing::warn!(?err, "failed to flush tracer provider");
}
if let Err(err) = provider.shutdown() {
tracing::warn!(?err, "failed to shutdown tracer provider");
} else {
tracing::info!("tracer provider is shutdown")
}
}