Skip to content

Commit 2801a64

Browse files
committed
add support for custom log metadata
1 parent d9ab811 commit 2801a64

File tree

3 files changed

+54
-2
lines changed

3 files changed

+54
-2
lines changed

src/controllers.rs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,17 @@ mod prelude {
7777
format!("?{}", query_string)
7878
}
7979
}
80+
81+
pub trait ResponseUtils {
82+
fn with_log_metadata<V: std::fmt::Display>(self, key: &str, value: V) -> Self;
83+
}
84+
85+
impl ResponseUtils for Response {
86+
fn with_log_metadata<V: std::fmt::Display>(mut self, key: &str, value: V) -> Self {
87+
crate::middleware::log_request::add_custom_metadata(&mut self, key, value);
88+
self
89+
}
90+
}
8091
}
8192

8293
pub mod helpers;

src/middleware.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ mod ember_index_rewrite;
2525
mod ensure_well_formed_500;
2626
mod head;
2727
mod log_connection_pool_status;
28-
mod log_request;
28+
pub mod log_request;
2929
mod require_user_agent;
3030
mod static_or_continue;
3131

src/middleware/log_request.rs

Lines changed: 42 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,16 @@ use conduit::Request;
77
use std::fmt::{self, Display, Formatter};
88
use std::time::Instant;
99

10+
const LOG_METADATA_HEADER_PREFIX: &str = "X-CratesIo-Log-Metadata-";
1011
const SLOW_REQUEST_THRESHOLD_MS: u64 = 1000;
1112

13+
pub fn add_custom_metadata<K: Display, V: Display>(resp: &mut Response, key: K, value: V) {
14+
resp.headers.insert(
15+
format!("{}{}", LOG_METADATA_HEADER_PREFIX, key),
16+
vec![value.to_string()],
17+
);
18+
}
19+
1220
#[allow(missing_debug_implementations)] // We can't
1321
#[derive(Default)]
1422
pub struct LogRequests {
@@ -24,17 +32,45 @@ impl AroundMiddleware for LogRequests {
2432
impl Handler for LogRequests {
2533
fn call(&self, req: &mut dyn Request) -> Result<Response> {
2634
let request_start = Instant::now();
27-
let res = self.handler.as_ref().unwrap().call(req);
35+
let mut res = self.handler.as_ref().unwrap().call(req);
2836
let response_time = request_start.elapsed();
2937
let response_time =
3038
response_time.as_secs() * 1000 + u64::from(response_time.subsec_nanos()) / 1_000_000;
3139

40+
// To aid debugging issues in production, some routes attach extra metadata to the log
41+
// line. An example of this is the crate publishing route, which adds the published crate
42+
// name and version to the line.
43+
//
44+
// This is implemented as special HTTP headers:
45+
//
46+
// X-CratesIo-Log-Metadata-{key}: {value}
47+
//
48+
// When this middleware finds those headers in the response, it removes them (so the user
49+
// can't see them) and adds the key/value pair at the end of the log line.
50+
let mut custom_metadata = Vec::new();
51+
if let Ok(resp) = &mut res {
52+
resp.headers.retain(|key, value| {
53+
if key.starts_with(LOG_METADATA_HEADER_PREFIX) {
54+
if let Some(value) = value.get(0) {
55+
custom_metadata.push((
56+
key[LOG_METADATA_HEADER_PREFIX.len()..].to_string(),
57+
value.clone(),
58+
));
59+
}
60+
false
61+
} else {
62+
true
63+
}
64+
});
65+
}
66+
3267
println!(
3368
"{}",
3469
RequestLine {
3570
req,
3671
res: &res,
3772
response_time,
73+
custom_metadata,
3874
}
3975
);
4076

@@ -45,6 +81,7 @@ impl Handler for LogRequests {
4581
struct RequestLine<'r> {
4682
req: &'r dyn Request,
4783
res: &'r Result<Response>,
84+
custom_metadata: Vec<(String, String)>,
4885
response_time: u64,
4986
}
5087

@@ -66,6 +103,10 @@ impl Display for RequestLine<'_> {
66103
line.add_field("status", status)?;
67104
line.add_quoted_field("user_agent", request_header(self.req, "User-Agent"))?;
68105

106+
for (key, value) in &self.custom_metadata {
107+
line.add_quoted_field(key, value)?;
108+
}
109+
69110
if let Some(len) = self.req.extensions().find::<u64>() {
70111
line.add_field("metadata_length", len)?;
71112
}

0 commit comments

Comments
 (0)