mirror of
https://github.com/neondatabase/neon.git
synced 2026-01-13 00:12:54 +00:00
Migrates the remaining crates to edition 2024. We like to stay on the latest edition if possible. There is no functional changes, however some code changes had to be done to accommodate the edition's breaking changes. Like the previous migration PRs, this is comprised of three commits: * the first does the edition update and makes `cargo check`/`cargo clippy` pass. we had to update bindgen to make its output [satisfy the requirements of edition 2024](https://doc.rust-lang.org/edition-guide/rust-2024/unsafe-extern.html) * the second commit does a `cargo fmt` for the new style edition. * the third commit reorders imports as a one-off change. As before, it is entirely optional. Part of #10918
97 lines
3.4 KiB
Rust
97 lines
3.4 KiB
Rust
//! Tracing wrapper for Hyper HTTP server
|
|
|
|
use std::future::Future;
|
|
|
|
use hyper0::{Body, HeaderMap, Request, Response};
|
|
use tracing::Instrument;
|
|
use tracing_opentelemetry::OpenTelemetrySpanExt;
|
|
|
|
/// Configuration option for what to use as the "otel.name" field in the traces.
|
|
pub enum OtelName<'a> {
|
|
/// Use a constant string
|
|
Constant(&'a str),
|
|
|
|
/// Use the path from the request.
|
|
///
|
|
/// That's very useful information, but is not appropriate if the
|
|
/// path contains parameters that differ on ever request, or worse,
|
|
/// sensitive information like usernames or email addresses.
|
|
///
|
|
/// See <https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/trace/semantic_conventions/http.md#name>
|
|
UriPath,
|
|
}
|
|
|
|
/// Handle an incoming HTTP request using the given handler function,
|
|
/// with OpenTelemetry tracing.
|
|
///
|
|
/// This runs 'handler' on the request in a new span, with fields filled in
|
|
/// from the request. Notably, if the request contains tracing information,
|
|
/// it is propagated to the span, so that this request is traced as part of
|
|
/// the same trace.
|
|
///
|
|
/// XXX: Usually, this is handled by existing libraries, or built
|
|
/// directly into HTTP servers. However, I couldn't find one for Hyper,
|
|
/// so I had to write our own. OpenTelemetry website has a registry of
|
|
/// instrumentation libraries at:
|
|
/// <https://opentelemetry.io/registry/?language=rust&component=instrumentation>
|
|
/// If a Hyper crate appears, consider switching to that.
|
|
pub async fn tracing_handler<F, R>(
|
|
req: Request<Body>,
|
|
handler: F,
|
|
otel_name: OtelName<'_>,
|
|
) -> Response<Body>
|
|
where
|
|
F: Fn(Request<Body>) -> R,
|
|
R: Future<Output = Response<Body>>,
|
|
{
|
|
// Create a tracing span, with context propagated from the incoming
|
|
// request if any.
|
|
//
|
|
// See list of standard fields defined for HTTP requests at
|
|
// https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/trace/semantic_conventions/http.md
|
|
// We only fill in a few of the most useful ones here.
|
|
let otel_name = match otel_name {
|
|
OtelName::Constant(s) => s,
|
|
OtelName::UriPath => req.uri().path(),
|
|
};
|
|
|
|
let span = tracing::info_span!(
|
|
"http request",
|
|
otel.name= %otel_name,
|
|
http.method = %req.method(),
|
|
http.status_code = tracing::field::Empty,
|
|
);
|
|
let parent_ctx = extract_remote_context(req.headers());
|
|
span.set_parent(parent_ctx);
|
|
|
|
// Handle the request within the span
|
|
let response = handler(req).instrument(span.clone()).await;
|
|
|
|
// Fill in the fields from the response code
|
|
let status = response.status();
|
|
span.record("http.status_code", status.as_str());
|
|
span.record(
|
|
"otel.status_code",
|
|
if status.is_success() { "OK" } else { "ERROR" },
|
|
);
|
|
|
|
response
|
|
}
|
|
|
|
// Extract remote tracing context from the HTTP headers
|
|
fn extract_remote_context(headers: &HeaderMap) -> opentelemetry::Context {
|
|
struct HeaderExtractor<'a>(&'a HeaderMap);
|
|
|
|
impl opentelemetry::propagation::Extractor for HeaderExtractor<'_> {
|
|
fn get(&self, key: &str) -> Option<&str> {
|
|
self.0.get(key).and_then(|value| value.to_str().ok())
|
|
}
|
|
|
|
fn keys(&self) -> Vec<&str> {
|
|
self.0.keys().map(|value| value.as_str()).collect()
|
|
}
|
|
}
|
|
let extractor = HeaderExtractor(headers);
|
|
opentelemetry::global::get_text_map_propagator(|propagator| propagator.extract(&extractor))
|
|
}
|