Add TraceLayer to quire-server matching quire-ci
Adds tower-http as a dependency and wires up TraceLayer with the same
make_span_with (MatchedPath + method) and on_response (RequestError
extension logging) pattern used in quire-ci/src/server.rs.
diff --git a/Cargo.lock b/Cargo.lock
index 340a2a2..87263bb 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -2718,6 +2718,7 @@ dependencies = [
"thiserror",
"tokio",
"tower",
+ "tower-http",
"tracing",
"tracing-opentelemetry",
"uuid",
diff --git a/quire-server/Cargo.toml b/quire-server/Cargo.toml
index 953e502..7ccfcb3 100644
--- a/quire-server/Cargo.toml
+++ b/quire-server/Cargo.toml
@@ -35,6 +35,7 @@ tracing-opentelemetry = { workspace = true }
askama = "*"
axum = "*"
axum-extra = { version = "*", features = ["typed-header"] }
+tower-http = { workspace = true }
clap = { workspace = true }
clap_complete = "*"
rand = "*"
diff --git a/quire-server/src/bin/quire/server.rs b/quire-server/src/bin/quire/server.rs
index 3b9edae..4c41bb6 100644
--- a/quire-server/src/bin/quire/server.rs
+++ b/quire-server/src/bin/quire/server.rs
@@ -1,12 +1,22 @@
use std::net::SocketAddr;
use std::os::unix::net::UnixListener as StdUnixListener;
+use std::time::Duration;
use axum::Router;
+use axum::extract::MatchedPath;
+use axum::http::Request;
+use axum::response::Response;
use axum::routing::get;
use miette::{Context, IntoDiagnostic, Result};
use quire::Quire;
use quire::ci;
use quire::event::PushEvent;
+use tower_http::trace::TraceLayer;
+use tracing::info_span;
+
+/// Carries an error message through response extensions so TraceLayer can log it.
+#[derive(Clone)]
+pub struct RequestError(pub String);
async fn health() -> &'static str {
"ok"
@@ -56,7 +66,26 @@ pub async fn run(quire: &Quire, web_routes: axum::Router, api_routes: axum::Rout
.route("/health", get(health))
.route("/", get(index))
.merge(web_routes)
- .nest("/api", api_routes);
+ .nest("/api", api_routes)
+ .layer(
+ TraceLayer::new_for_http()
+ .make_span_with(|request: &Request<_>| {
+ let matched_path = request
+ .extensions()
+ .get::<MatchedPath>()
+ .map(MatchedPath::as_str);
+ info_span!("http_request", method = ?request.method(), matched_path)
+ })
+ .on_response(|response: &Response, _: Duration, _: &tracing::Span| {
+ if let Some(RequestError(error)) = response.extensions().get::<RequestError>() {
+ if response.status().is_server_error() {
+ tracing::error!(%error);
+ } else {
+ tracing::warn!(%error);
+ }
+ }
+ }),
+ );
tracing::info!(%addr, "starting HTTP server");