telemetry_rust/
lib.rs

1// Initialization logic was retired from https://github.com/davidB/tracing-opentelemetry-instrumentation-sdk/
2// which is licensed under CC0 1.0 Universal
3// https://github.com/davidB/tracing-opentelemetry-instrumentation-sdk/blob/d3609ac2cc699d3a24fbf89754053cc8e938e3bf/LICENSE
4
5use 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    /// `service.name` is first extracted from environment variables
45    /// (in this order) `OTEL_SERVICE_NAME`, `SERVICE_NAME`, `APP_NAME`.
46    /// But a default value can be provided with this method.
47    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); // base has lower priority
70
71        // Debug
72        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    // set to debug to log detected resources, configuration read and infered
125    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}