tower_http_tracing/
opentelemetry.rs

1//! opentelemetry integration
2pub use opentelemetry::*;
3pub use opentelemetry_sdk as sdk;
4pub use tracing_opentelemetry;
5
6use opentelemetry_sdk::propagation::TraceContextPropagator;
7use opentelemetry::propagation::text_map_propagator::TextMapPropagator;
8
9///Opentelemetry extractor for [http::HeaderMap](https://docs.rs/http/latest/http/header/struct.HeaderMap.html)
10pub struct HeaderMapExtractor<'a, T: AsRef<[u8]>>(pub &'a http::HeaderMap<T>);
11
12impl<T: AsRef<[u8]>> opentelemetry::propagation::Extractor for HeaderMapExtractor<'_, T> {
13    #[inline]
14    fn get(&self, key: &str) -> Option<&str> {
15        self.0.get(key).and_then(|value| core::str::from_utf8(value.as_ref()).ok())
16    }
17
18    #[inline]
19    fn keys(&self) -> Vec<&str> {
20        Vec::new()
21    }
22
23    #[inline]
24    fn get_all(&self, key: &str) -> Option<Vec<&str>> {
25        let values: Vec<_> = self.0.get_all(key).iter().filter_map(|value| core::str::from_utf8(value.as_ref()).ok()).collect();
26        if values.is_empty() {
27            None
28        } else {
29            Some(values)
30        }
31    }
32}
33
34///Opentelemetry injector for [http::HeaderMap](https://docs.rs/http/latest/http/header/struct.HeaderMap.html)
35pub struct HeaderMapInjector<'a, T: TryFrom<String>>(pub &'a mut http::HeaderMap<T>);
36
37impl<T: TryFrom<String>> opentelemetry::propagation::Injector for HeaderMapInjector<'_, T> {
38    #[inline]
39    fn set(&mut self, key: &str, value: String) {
40        let key = match http::HeaderName::from_bytes(key.as_bytes()) {
41            Ok(key) => key,
42            Err(_) => unreachable!()
43        };
44        match value.try_into() {
45            Ok(value) => self.0.insert(key, value),
46            Err(_) => unreachable!(),
47        };
48    }
49}
50
51#[inline(always)]
52///Extracts OTEL context from `request` propagating it as `span`'s parent
53///
54///Note that this can only be done once for single span
55pub fn on_request<T>(span: &tracing::Span, request: &http::Request<T>) {
56    use tracing_opentelemetry::OpenTelemetrySpanExt;
57
58    let propagator = TraceContextPropagator::new();
59    let context = propagator.extract(&HeaderMapExtractor(request.headers()));
60
61    if let Err(error) = span.set_parent(context) {
62        tracing::warn!("Unable to propagate parent context: {error}");
63    }
64}
65
66#[inline(always)]
67///Propagates success into `span` context and then export context headers into response
68pub fn on_response_ok<T>(span: &tracing::Span, response: &mut http::Response<T>) {
69    use tracing_opentelemetry::OpenTelemetrySpanExt;
70
71    span.set_status(trace::Status::Ok);
72
73    let propagator = TraceContextPropagator::new();
74    let context = span.context();
75    propagator.inject_context(&context, &mut HeaderMapInjector(response.headers_mut()));
76}
77
78#[inline(always)]
79///Propagates error into `span` context
80pub fn on_response_error(span: &tracing::Span, error: &impl std::error::Error) {
81    use tracing_opentelemetry::OpenTelemetrySpanExt;
82
83    let error = trace::Status::Error {
84        description: error.to_string().into()
85    };
86    span.set_status(error);
87}