LCOV - code coverage report
Current view: top level - libs/tracing-utils/src - http.rs (source / functions) Coverage Total Hit
Test: e402c46de0a007db6b48dddbde450ddbb92e6ceb.info Lines: 0.0 % 39 0
Test Date: 2024-06-25 10:31:23 Functions: 0.0 % 10 0

            Line data    Source code
       1              : //! Tracing wrapper for Hyper HTTP server
       2              : 
       3              : use hyper::HeaderMap;
       4              : use hyper::{Body, Request, Response};
       5              : use std::future::Future;
       6              : use tracing::Instrument;
       7              : use tracing_opentelemetry::OpenTelemetrySpanExt;
       8              : 
       9              : /// Configuration option for what to use as the "otel.name" field in the traces.
      10              : pub enum OtelName<'a> {
      11              :     /// Use a constant string
      12              :     Constant(&'a str),
      13              : 
      14              :     /// Use the path from the request.
      15              :     ///
      16              :     /// That's very useful information, but is not appropriate if the
      17              :     /// path contains parameters that differ on ever request, or worse,
      18              :     /// sensitive information like usernames or email addresses.
      19              :     ///
      20              :     /// See <https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/trace/semantic_conventions/http.md#name>
      21              :     UriPath,
      22              : }
      23              : 
      24              : /// Handle an incoming HTTP request using the given handler function,
      25              : /// with OpenTelemetry tracing.
      26              : ///
      27              : /// This runs 'handler' on the request in a new span, with fields filled in
      28              : /// from the request. Notably, if the request contains tracing information,
      29              : /// it is propagated to the span, so that this request is traced as part of
      30              : /// the same trace.
      31              : ///
      32              : /// XXX: Usually, this is handled by existing libraries, or built
      33              : /// directly into HTTP servers. However, I couldn't find one for Hyper,
      34              : /// so I had to write our own. OpenTelemetry website has a registry of
      35              : /// instrumentation libraries at:
      36              : /// <https://opentelemetry.io/registry/?language=rust&component=instrumentation>
      37              : /// If a Hyper crate appears, consider switching to that.
      38            0 : pub async fn tracing_handler<F, R>(
      39            0 :     req: Request<Body>,
      40            0 :     handler: F,
      41            0 :     otel_name: OtelName<'_>,
      42            0 : ) -> Response<Body>
      43            0 : where
      44            0 :     F: Fn(Request<Body>) -> R,
      45            0 :     R: Future<Output = Response<Body>>,
      46            0 : {
      47              :     // Create a tracing span, with context propagated from the incoming
      48              :     // request if any.
      49              :     //
      50              :     // See list of standard fields defined for HTTP requests at
      51              :     // https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/trace/semantic_conventions/http.md
      52              :     // We only fill in a few of the most useful ones here.
      53            0 :     let otel_name = match otel_name {
      54            0 :         OtelName::Constant(s) => s,
      55            0 :         OtelName::UriPath => req.uri().path(),
      56              :     };
      57              : 
      58            0 :     let span = tracing::info_span!(
      59              :         "http request",
      60              :         otel.name= %otel_name,
      61            0 :         http.method = %req.method(),
      62              :         http.status_code = tracing::field::Empty,
      63              :     );
      64            0 :     let parent_ctx = extract_remote_context(req.headers());
      65            0 :     span.set_parent(parent_ctx);
      66              : 
      67              :     // Handle the request within the span
      68            0 :     let response = handler(req).instrument(span.clone()).await;
      69              : 
      70              :     // Fill in the fields from the response code
      71            0 :     let status = response.status();
      72            0 :     span.record("http.status_code", status.as_str());
      73            0 :     span.record(
      74            0 :         "otel.status_code",
      75            0 :         if status.is_success() { "OK" } else { "ERROR" },
      76              :     );
      77              : 
      78            0 :     response
      79            0 : }
      80              : 
      81              : // Extract remote tracing context from the HTTP headers
      82            0 : fn extract_remote_context(headers: &HeaderMap) -> opentelemetry::Context {
      83            0 :     struct HeaderExtractor<'a>(&'a HeaderMap);
      84            0 : 
      85            0 :     impl<'a> opentelemetry::propagation::Extractor for HeaderExtractor<'a> {
      86            0 :         fn get(&self, key: &str) -> Option<&str> {
      87            0 :             self.0.get(key).and_then(|value| value.to_str().ok())
      88            0 :         }
      89            0 : 
      90            0 :         fn keys(&self) -> Vec<&str> {
      91            0 :             self.0.keys().map(|value| value.as_str()).collect()
      92            0 :         }
      93            0 :     }
      94            0 :     let extractor = HeaderExtractor(headers);
      95            0 :     opentelemetry::global::get_text_map_propagator(|propagator| propagator.extract(&extractor))
      96            0 : }
        

Generated by: LCOV version 2.1-beta