Skip to content

Commit d9ab811

Browse files
committed
refactor request logging to allow adding new fields more easily
1 parent 3e9191f commit d9ab811

File tree

1 file changed

+108
-38
lines changed

1 file changed

+108
-38
lines changed

src/middleware/log_request.rs

Lines changed: 108 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,11 @@
44
use super::prelude::*;
55
use crate::util::request_header;
66
use conduit::Request;
7-
use std::fmt;
7+
use std::fmt::{self, Display, Formatter};
88
use std::time::Instant;
99

10+
const SLOW_REQUEST_THRESHOLD_MS: u64 = 1000;
11+
1012
#[allow(missing_debug_implementations)] // We can't
1113
#[derive(Default)]
1214
pub struct LogRequests {
@@ -23,61 +25,129 @@ impl Handler for LogRequests {
2325
fn call(&self, req: &mut dyn Request) -> Result<Response> {
2426
let request_start = Instant::now();
2527
let res = self.handler.as_ref().unwrap().call(req);
26-
let (level, response_code) = match res {
27-
Ok(ref r) => ("info", r.status.0),
28-
Err(_) => ("error", 500),
29-
};
3028
let response_time = request_start.elapsed();
3129
let response_time =
3230
response_time.as_secs() * 1000 + u64::from(response_time.subsec_nanos()) / 1_000_000;
3331

34-
let metadata_length = req
35-
.extensions()
36-
.find::<u64>()
37-
.map_or(String::new(), |l| format!(" metadata_length={}", l));
32+
println!(
33+
"{}",
34+
RequestLine {
35+
req,
36+
res: &res,
37+
response_time,
38+
}
39+
);
3840

39-
let slow_request = if response_time > 1000 {
40-
" SLOW REQUEST"
41-
} else {
42-
""
43-
};
41+
res
42+
}
43+
}
44+
45+
struct RequestLine<'r> {
46+
req: &'r dyn Request,
47+
res: &'r Result<Response>,
48+
response_time: u64,
49+
}
50+
51+
impl Display for RequestLine<'_> {
52+
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
53+
let mut line = LogLine::new(f);
4454

45-
let error = if let Err(ref e) = res {
46-
format!(" error=\"{}\"", e)
47-
} else {
48-
String::new()
55+
let (at, status) = match self.res {
56+
Ok(resp) => ("info", resp.status.0),
57+
Err(_) => ("error", 500),
4958
};
5059

51-
println!(
52-
"at={level} method={method} path=\"{path}\" \
53-
request_id={request_id} fwd=\"{ip}\" service={time_ms}ms \
54-
status={status} user_agent=\"{user_agent}\"\
55-
{metadata_length}{error}{slow_request}",
56-
level = level,
57-
method = req.method(),
58-
path = FullPath(req),
59-
ip = request_header(req, "X-Real-Ip"),
60-
time_ms = response_time,
61-
user_agent = request_header(req, "User-Agent"),
62-
request_id = request_header(req, "X-Request-Id"),
63-
status = response_code,
64-
metadata_length = metadata_length,
65-
error = error,
66-
slow_request = slow_request,
67-
);
60+
line.add_field("at", at)?;
61+
line.add_field("method", self.req.method())?;
62+
line.add_quoted_field("path", FullPath(self.req))?;
63+
line.add_field("request_id", request_header(self.req, "X-Request-Id"))?;
64+
line.add_quoted_field("fwd", request_header(self.req, "X-Real-Ip"))?;
65+
line.add_field("service", TimeMs(self.response_time))?;
66+
line.add_field("status", status)?;
67+
line.add_quoted_field("user_agent", request_header(self.req, "User-Agent"))?;
6868

69-
res
69+
if let Some(len) = self.req.extensions().find::<u64>() {
70+
line.add_field("metadata_length", len)?;
71+
}
72+
73+
if let Err(err) = self.res {
74+
line.add_quoted_field("error", err)?;
75+
}
76+
77+
if self.response_time > SLOW_REQUEST_THRESHOLD_MS {
78+
line.add_marker("SLOW REQUEST")?;
79+
}
80+
81+
Ok(())
7082
}
7183
}
7284

7385
struct FullPath<'a>(&'a dyn Request);
7486

75-
impl<'a> fmt::Display for FullPath<'a> {
76-
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
87+
impl<'a> Display for FullPath<'a> {
88+
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
7789
write!(f, "{}", self.0.path())?;
7890
if let Some(q_string) = self.0.query_string() {
7991
write!(f, "?{}", q_string)?;
8092
}
8193
Ok(())
8294
}
8395
}
96+
97+
struct TimeMs(u64);
98+
99+
impl Display for TimeMs {
100+
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
101+
self.0.fmt(f)?;
102+
f.write_str("ms")?;
103+
Ok(())
104+
}
105+
}
106+
107+
struct LogLine<'f, 'g> {
108+
f: &'f mut Formatter<'g>,
109+
first: bool,
110+
}
111+
112+
impl<'f, 'g> LogLine<'f, 'g> {
113+
fn new(f: &'f mut Formatter<'g>) -> Self {
114+
Self { f, first: true }
115+
}
116+
117+
fn add_field<K: Display, V: Display>(&mut self, key: K, value: V) -> fmt::Result {
118+
self.start_item()?;
119+
120+
key.fmt(self.f)?;
121+
self.f.write_str("=")?;
122+
value.fmt(self.f)?;
123+
124+
Ok(())
125+
}
126+
127+
fn add_quoted_field<K: Display, V: Display>(&mut self, key: K, value: V) -> fmt::Result {
128+
self.start_item()?;
129+
130+
key.fmt(self.f)?;
131+
self.f.write_str("=\"")?;
132+
value.fmt(self.f)?;
133+
self.f.write_str("\"")?;
134+
135+
Ok(())
136+
}
137+
138+
fn add_marker<M: Display>(&mut self, marker: M) -> fmt::Result {
139+
self.start_item()?;
140+
141+
marker.fmt(self.f)?;
142+
143+
Ok(())
144+
}
145+
146+
fn start_item(&mut self) -> fmt::Result {
147+
if !self.first {
148+
self.f.write_str(" ")?;
149+
}
150+
self.first = false;
151+
Ok(())
152+
}
153+
}

0 commit comments

Comments
 (0)