LCOV - differential code coverage report
Current view: top level - libs/tracing-utils/src - http.rs (source / functions) Coverage Total Hit UBC CBC
Current: f6946e90941b557c917ac98cd5a7e9506d180f3e.info Lines: 92.3 % 39 36 3 36
Current Date: 2023-10-19 02:04:12 Functions: 50.0 % 10 5 5 5
Baseline: c8637f37369098875162f194f92736355783b050.info
Baseline Date: 2023-10-18 20:25:20

           TLA  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 CBC        1454 : pub async fn tracing_handler<F, R>(
      39            1454 :     req: Request<Body>,
      40            1454 :     handler: F,
      41            1454 :     otel_name: OtelName<'_>,
      42            1454 : ) -> Response<Body>
      43            1454 : where
      44            1454 :     F: Fn(Request<Body>) -> R,
      45            1454 :     R: Future<Output = Response<Body>>,
      46            1454 : {
      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            1454 :     let otel_name = match otel_name {
      54 UBC           0 :         OtelName::Constant(s) => s,
      55 CBC        1454 :         OtelName::UriPath => req.uri().path(),
      56                 :     };
      57                 : 
      58            1454 :     let span = tracing::info_span!(
      59                 :         "http request",
      60                 :         otel.name= %otel_name,
      61            1454 :         http.method = %req.method(),
      62                 :         http.status_code = tracing::field::Empty,
      63                 :     );
      64            1454 :     let parent_ctx = extract_remote_context(req.headers());
      65            1454 :     span.set_parent(parent_ctx);
      66                 : 
      67                 :     // Handle the request within the span
      68            1454 :     let response = handler(req).instrument(span.clone()).await;
      69                 : 
      70                 :     // Fill in the fields from the response code
      71            1454 :     let status = response.status();
      72            1454 :     span.record("http.status_code", status.as_str());
      73            1454 :     span.record(
      74            1454 :         "otel.status_code",
      75            1454 :         if status.is_success() { "OK" } else { "ERROR" },
      76                 :     );
      77                 : 
      78            1454 :     response
      79            1454 : }
      80                 : 
      81                 : // Extract remote tracing context from the HTTP headers
      82            1454 : fn extract_remote_context(headers: &HeaderMap) -> opentelemetry::Context {
      83            1454 :     struct HeaderExtractor<'a>(&'a HeaderMap);
      84            1454 : 
      85            1454 :     impl<'a> opentelemetry::propagation::Extractor for HeaderExtractor<'a> {
      86            1454 :         fn get(&self, key: &str) -> Option<&str> {
      87            1454 :             self.0.get(key).and_then(|value| value.to_str().ok())
      88            1454 :         }
      89            1454 : 
      90            1454 :         fn keys(&self) -> Vec<&str> {
      91 UBC           0 :             self.0.keys().map(|value| value.as_str()).collect()
      92               0 :         }
      93 CBC        1454 :     }
      94            1454 :     let extractor = HeaderExtractor(headers);
      95            1454 :     opentelemetry::global::get_text_map_propagator(|propagator| propagator.extract(&extractor))
      96            1454 : }
        

Generated by: LCOV version 2.1-beta