1use opentelemetry_sdk::{
6 resource::{EnvResourceDetector, ResourceDetector},
7 Resource,
8};
9use tracing::level_filters::LevelFilter;
10#[cfg(debug_assertions)]
11use tracing_subscriber::fmt::format::FmtSpan;
12use tracing_subscriber::layer::SubscriberExt;
13
14use opentelemetry::trace::TracerProvider as _;
15pub use opentelemetry::{global, Array, Context, Key, KeyValue, StringValue, Value};
16pub use opentelemetry_sdk::trace::TracerProvider;
17pub use opentelemetry_semantic_conventions::attribute as semconv;
18pub use tracing_opentelemetry::{OpenTelemetryLayer, OpenTelemetrySpanExt};
19
20pub mod http;
21pub mod middleware;
22pub mod otlp;
23pub mod propagation;
24
25#[cfg(feature = "axum")]
26pub use tracing_opentelemetry_instrumentation_sdk;
27
28#[cfg(feature = "test")]
29pub mod test;
30
31#[cfg(feature = "future")]
32pub mod future;
33
34mod filter;
35mod util;
36
37#[derive(Debug, Default)]
38pub struct DetectResource {
39 fallback_service_name: &'static str,
40 fallback_service_version: &'static str,
41}
42
43impl DetectResource {
44 pub fn new(
48 fallback_service_name: &'static str,
49 fallback_service_version: &'static str,
50 ) -> Self {
51 DetectResource {
52 fallback_service_name,
53 fallback_service_version,
54 }
55 }
56
57 pub fn build(self) -> Resource {
58 let base = Resource::default();
59 let fallback = Resource::from_detectors(
60 std::time::Duration::from_secs(0),
61 vec![
62 Box::new(ServiceInfoDetector {
63 fallback_service_name: self.fallback_service_name,
64 fallback_service_version: self.fallback_service_version,
65 }),
66 Box::new(EnvResourceDetector::new()),
67 ],
68 );
69 let rsrc = base.merge(&fallback); rsrc.iter().for_each(
73 |kv| tracing::debug!(target: "otel::setup::resource", key = %kv.0, value = %kv.1),
74 );
75
76 rsrc
77 }
78}
79
80#[derive(Debug)]
81pub struct ServiceInfoDetector {
82 fallback_service_name: &'static str,
83 fallback_service_version: &'static str,
84}
85
86impl ResourceDetector for ServiceInfoDetector {
87 fn detect(&self, _timeout: std::time::Duration) -> Resource {
88 let service_name = util::env_var("OTEL_SERVICE_NAME")
89 .or_else(|| util::env_var("SERVICE_NAME"))
90 .or_else(|| util::env_var("APP_NAME"))
91 .or_else(|| Some(self.fallback_service_name.to_string()))
92 .map(|v| KeyValue::new(semconv::SERVICE_NAME, v));
93 let service_version = util::env_var("OTEL_SERVICE_VERSION")
94 .or_else(|| util::env_var("SERVICE_VERSION"))
95 .or_else(|| util::env_var("APP_VERSION"))
96 .or_else(|| Some(self.fallback_service_version.to_string()))
97 .map(|v| KeyValue::new(semconv::SERVICE_VERSION, v));
98 Resource::new(vec![service_name, service_version].into_iter().flatten())
99 }
100}
101
102macro_rules! fmt_layer {
103 () => {{
104 let layer = tracing_subscriber::fmt::layer();
105
106 #[cfg(debug_assertions)]
107 let layer = layer.compact().with_span_events(FmtSpan::CLOSE);
108 #[cfg(not(debug_assertions))]
109 let layer = layer
110 .json()
111 .flatten_event(true)
112 .with_current_span(false)
113 .with_span_list(true);
114
115 layer.with_writer(std::io::stdout)
116 }};
117}
118
119pub fn init_tracing_with_fallbacks(
120 log_level: tracing::Level,
121 fallback_service_name: &'static str,
122 fallback_service_version: &'static str,
123) -> TracerProvider {
124 let setup_subscriber = tracing_subscriber::registry()
126 .with(Into::<LevelFilter>::into(log_level))
127 .with(fmt_layer!());
128 let _guard = tracing::subscriber::set_default(setup_subscriber);
129 tracing::info!("init logging & tracing");
130
131 let otel_rsrc =
132 DetectResource::new(fallback_service_name, fallback_service_version).build();
133 let tracer_provider =
134 otlp::init_tracer(otel_rsrc, otlp::identity).expect("TracerProvider setup");
135
136 global::set_tracer_provider(tracer_provider.clone());
137 global::set_text_map_propagator(
138 propagation::TextMapSplitPropagator::from_env().expect("TextMapPropagator setup"),
139 );
140
141 let otel_layer =
142 OpenTelemetryLayer::new(tracer_provider.tracer(env!("CARGO_PKG_NAME")));
143 let subscriber = tracing_subscriber::registry()
144 .with(Into::<filter::TracingFilter>::into(log_level))
145 .with(fmt_layer!())
146 .with(otel_layer);
147 tracing::subscriber::set_global_default(subscriber).unwrap();
148
149 tracer_provider
150}
151
152#[macro_export]
153macro_rules! init_tracing {
154 ($log_level:expr) => {
155 $crate::init_tracing_with_fallbacks(
156 $log_level,
157 env!("CARGO_PKG_NAME"),
158 env!("CARGO_PKG_VERSION"),
159 )
160 };
161}
162
163#[inline]
164pub fn shutdown_signal() {
165 std::thread::spawn(global::shutdown_tracer_provider)
166 .join()
167 .unwrap();
168}