From b43817e66caf14bccc52525488017fa30ae0cb6b Mon Sep 17 00:00:00 2001 From: Quentin Gliech Date: Thu, 7 Apr 2022 11:52:43 +0200 Subject: [PATCH] Attach remote and local address to HTTP server/client spans --- Cargo.lock | 1 + crates/cli/src/commands/server.rs | 2 +- crates/http/Cargo.toml | 1 + .../http/src/layers/otel/make_span_builder.rs | 33 +++++++++++++++++-- crates/http/src/layers/otel/mod.rs | 27 +++++++++++++++ crates/http/src/layers/otel/on_response.rs | 14 +++++++- crates/http/src/layers/server.rs | 2 +- 7 files changed, 75 insertions(+), 5 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 9e8e408b..c1cb4313 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2136,6 +2136,7 @@ dependencies = [ name = "mas-http" version = "0.1.0" dependencies = [ + "axum", "bytes 1.1.0", "futures-util", "http", diff --git a/crates/cli/src/commands/server.rs b/crates/cli/src/commands/server.rs index 9a0b2ee5..c6429313 100644 --- a/crates/cli/src/commands/server.rs +++ b/crates/cli/src/commands/server.rs @@ -222,7 +222,7 @@ impl Options { info!("Listening on http://{}", listener.local_addr().unwrap()); Server::from_tcp(listener)? - .serve(router.into_make_service()) + .serve(router.into_make_service_with_connect_info::()) .with_graceful_shutdown(shutdown_signal()) .await?; diff --git a/crates/http/Cargo.toml b/crates/http/Cargo.toml index 2852314b..2d832f1f 100644 --- a/crates/http/Cargo.toml +++ b/crates/http/Cargo.toml @@ -6,6 +6,7 @@ edition = "2021" license = "Apache-2.0" [dependencies] +axum = "0.5.1" bytes = "1.1.0" futures-util = "0.3.21" http = "0.2.6" diff --git a/crates/http/src/layers/otel/make_span_builder.rs b/crates/http/src/layers/otel/make_span_builder.rs index fe3586a6..6a7be882 100644 --- a/crates/http/src/layers/otel/make_span_builder.rs +++ b/crates/http/src/layers/otel/make_span_builder.rs @@ -12,13 +12,14 @@ // See the License for the specific language governing permissions and // limitations under the License. -use std::borrow::Cow; +use std::{borrow::Cow, net::SocketAddr}; +use axum::extract::{ConnectInfo, MatchedPath}; use http::{Method, Request, Version}; use hyper::client::connect::dns::Name; use opentelemetry::trace::{SpanBuilder, SpanKind}; use opentelemetry_semantic_conventions::trace::{ - HTTP_FLAVOR, HTTP_METHOD, HTTP_URL, NET_HOST_NAME, + HTTP_FLAVOR, HTTP_METHOD, HTTP_URL, NET_HOST_NAME, NET_PEER_IP, NET_PEER_PORT, }; pub trait MakeSpanBuilder { @@ -125,6 +126,34 @@ impl MakeSpanBuilder> for SpanFromHttpRequest { } } +#[derive(Debug, Clone)] +pub struct SpanFromAxumRequest; + +impl MakeSpanBuilder> for SpanFromAxumRequest { + fn make_span_builder(&self, request: &Request) -> SpanBuilder { + let mut attributes = vec![ + HTTP_METHOD.string(http_method_str(request.method())), + HTTP_FLAVOR.string(http_flavor(request.version())), + HTTP_URL.string(request.uri().to_string()), + ]; + + if let Some(ConnectInfo(addr)) = request.extensions().get::>() { + attributes.push(NET_PEER_IP.string(addr.ip().to_string())); + attributes.push(NET_PEER_PORT.i64(addr.port().into())); + } + + let path = if let Some(path) = request.extensions().get::() { + path.as_str() + } else { + request.uri().path() + }; + + SpanBuilder::from_name(path.to_string()) + .with_kind(SpanKind::Server) + .with_attributes(attributes) + } +} + #[derive(Debug, Clone, Copy, Default)] pub struct SpanFromDnsRequest; diff --git a/crates/http/src/layers/otel/mod.rs b/crates/http/src/layers/otel/mod.rs index 32fb9330..fdf556bc 100644 --- a/crates/http/src/layers/otel/mod.rs +++ b/crates/http/src/layers/otel/mod.rs @@ -37,6 +37,23 @@ pub type TraceHttpServer = Trace< S, >; +pub type TraceAxumServerLayer = TraceLayer< + ExtractFromHttpRequest, + DefaultInjectContext, + SpanFromAxumRequest, + OnHttpResponse, + DefaultOnError, +>; + +pub type TraceAxumServer = Trace< + ExtractFromHttpRequest, + DefaultInjectContext, + SpanFromAxumRequest, + OnHttpResponse, + DefaultOnError, + S, +>; + pub type TraceHttpClientLayer = TraceLayer< DefaultExtractContext, InjectInHttpRequest, @@ -81,6 +98,16 @@ impl TraceHttpServerLayer { } } +impl TraceAxumServerLayer { + #[must_use] + pub fn axum() -> Self { + TraceLayer::default() + .make_span_builder(SpanFromAxumRequest) + .on_response(OnHttpResponse) + .extract_context(ExtractFromHttpRequest) + } +} + impl TraceHttpClientLayer { #[must_use] pub fn http_client(operation: &'static str) -> Self { diff --git a/crates/http/src/layers/otel/on_response.rs b/crates/http/src/layers/otel/on_response.rs index ae96bb55..33d26af2 100644 --- a/crates/http/src/layers/otel/on_response.rs +++ b/crates/http/src/layers/otel/on_response.rs @@ -13,8 +13,11 @@ // limitations under the License. use http::Response; +use hyper::client::connect::HttpInfo; use opentelemetry::trace::SpanRef; -use opentelemetry_semantic_conventions::trace::HTTP_STATUS_CODE; +use opentelemetry_semantic_conventions::trace::{ + HTTP_STATUS_CODE, NET_HOST_IP, NET_HOST_PORT, NET_PEER_IP, NET_PEER_PORT, +}; pub trait OnResponse { fn on_response(&self, span: &SpanRef<'_>, response: &R); @@ -33,5 +36,14 @@ pub struct OnHttpResponse; impl OnResponse> for OnHttpResponse { fn on_response(&self, span: &SpanRef<'_>, response: &Response) { span.set_attribute(HTTP_STATUS_CODE.i64(i64::from(response.status().as_u16()))); + + // Get local and remote address from hyper's HttpInfo injected by the + // HttpConnector + if let Some(info) = response.extensions().get::() { + span.set_attribute(NET_PEER_IP.string(info.remote_addr().ip().to_string())); + span.set_attribute(NET_PEER_PORT.i64(info.remote_addr().port().into())); + span.set_attribute(NET_HOST_IP.string(info.local_addr().ip().to_string())); + span.set_attribute(NET_HOST_PORT.i64(info.local_addr().port().into())); + } } } diff --git a/crates/http/src/layers/server.rs b/crates/http/src/layers/server.rs index 4a136ddf..c0a344bd 100644 --- a/crates/http/src/layers/server.rs +++ b/crates/http/src/layers/server.rs @@ -39,7 +39,7 @@ where fn layer(&self, inner: S) -> Self::Service { ServiceBuilder::new() .compression() - .layer(TraceLayer::http_server()) + .layer(TraceLayer::axum()) .service(inner) .boxed_clone() }