Skip to content

Commit 768851b

Browse files
committed
middleware::log_request: Split request timing code into dedicated middleware
1 parent ed87d18 commit 768851b

File tree

3 files changed

+76
-46
lines changed

3 files changed

+76
-46
lines changed

src/middleware.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ use self::ember_html::EmberHtml;
1010
use self::head::Head;
1111
use self::known_error_to_json::KnownErrorToJson;
1212
use self::log_connection_pool_status::LogConnectionPoolStatus;
13+
use self::request_timing::RequestTiming;
1314
use self::static_or_continue::StaticOrContinue;
1415
use self::update_metrics::UpdateMetrics;
1516

@@ -24,6 +25,7 @@ mod known_error_to_json;
2425
mod log_connection_pool_status;
2526
pub mod log_request;
2627
mod normalize_path;
28+
mod request_timing;
2729
mod require_user_agent;
2830
mod static_or_continue;
2931
mod update_metrics;
@@ -48,6 +50,8 @@ pub fn build_middleware(app: Arc<App>, endpoints: RouteBuilder) -> MiddlewareBui
4850
m.add(log_request::LogRequests::default());
4951
}
5052

53+
m.add(RequestTiming::default());
54+
5155
if env == Env::Development {
5256
// Optionally print debug information for each request
5357
// To enable, set the environment variable: `RUST_LOG=cargo_registry::middleware=debug`

src/middleware/log_request.rs

Lines changed: 6 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,8 @@ use conduit::{header, Host, RequestExt, Scheme, StatusCode};
88
use conduit_cookie::RequestSession;
99
use sentry::Level;
1010

11+
use crate::middleware::request_timing::ResponseTime;
1112
use std::fmt::{self, Display, Formatter};
12-
use std::time::{SystemTime, UNIX_EPOCH};
1313

1414
const SLOW_REQUEST_THRESHOLD_MS: u64 = 1000;
1515

@@ -28,30 +28,7 @@ impl Middleware for LogRequests {
2828
}
2929

3030
fn after(&self, req: &mut dyn RequestExt, res: AfterResult) -> AfterResult {
31-
let response_time =
32-
if let Ok(start_ms) = request_header(req, "x-request-start").parse::<u128>() {
33-
let current_ms = SystemTime::now()
34-
.duration_since(UNIX_EPOCH)
35-
.expect("Time went way backwards")
36-
.as_millis();
37-
38-
if current_ms > start_ms {
39-
// The result cannot be negative
40-
current_ms - start_ms
41-
} else {
42-
// Because our nginx proxy and app run on the same dyno in production, we
43-
// shouldn't have to worry about clock drift. But if something goes wrong,
44-
// calculate the response time based on when the request reached this app.
45-
fallback_response_time(req)
46-
}
47-
} else {
48-
// X-Request-Start header couldn't be parsed.
49-
// We are probably running locally and not behind nginx.
50-
fallback_response_time(req)
51-
};
52-
53-
// This will only trucate for requests lasting > 500 million years
54-
let response_time = response_time as u64;
31+
let response_time = req.extensions().find::<ResponseTime>().unwrap();
5532

5633
println!(
5734
"{}",
@@ -62,19 +39,12 @@ impl Middleware for LogRequests {
6239
}
6340
);
6441

65-
report_to_sentry(req, &res, response_time);
42+
report_to_sentry(req, &res, response_time.as_millis());
6643

6744
res
6845
}
6946
}
7047

71-
/// Calculate the response time based on when the request reached the in-app web server.
72-
///
73-
/// This serves as a fallback in case the `X-Request-Start` header is missing or invalid.
74-
fn fallback_response_time(req: &mut dyn RequestExt) -> u128 {
75-
req.elapsed().as_millis()
76-
}
77-
7848
struct CustomMetadata {
7949
entries: Vec<(&'static str, String)>,
8050
}
@@ -191,7 +161,7 @@ pub(crate) fn get_log_message(req: &dyn RequestExt, key: &'static str) -> String
191161
struct RequestLine<'r> {
192162
req: &'r dyn RequestExt,
193163
res: &'r AfterResult,
194-
response_time: u64,
164+
response_time: &'r ResponseTime,
195165
}
196166

197167
impl Display for RequestLine<'_> {
@@ -224,7 +194,7 @@ impl Display for RequestLine<'_> {
224194
}
225195

226196
line.add_quoted_field("fwd", request_header(self.req, "x-real-ip"))?;
227-
line.add_field("service", TimeMs(self.response_time))?;
197+
line.add_field("service", self.response_time)?;
228198
line.add_field("status", status.as_str())?;
229199
line.add_quoted_field("user_agent", request_header(self.req, header::USER_AGENT))?;
230200

@@ -238,7 +208,7 @@ impl Display for RequestLine<'_> {
238208
line.add_quoted_field("error", err)?;
239209
}
240210

241-
if self.response_time > SLOW_REQUEST_THRESHOLD_MS {
211+
if self.response_time.as_millis() > SLOW_REQUEST_THRESHOLD_MS {
242212
line.add_marker("SLOW REQUEST")?;
243213
}
244214

@@ -263,16 +233,6 @@ impl<'a> Display for FullPath<'a> {
263233
}
264234
}
265235

266-
struct TimeMs(u64);
267-
268-
impl Display for TimeMs {
269-
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
270-
self.0.fmt(f)?;
271-
f.write_str("ms")?;
272-
Ok(())
273-
}
274-
}
275-
276236
struct LogLine<'f, 'g> {
277237
f: &'f mut Formatter<'g>,
278238
first: bool,

src/middleware/request_timing.rs

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
use super::prelude::*;
2+
use crate::util::request_header;
3+
4+
use conduit::RequestExt;
5+
6+
use std::fmt::{self, Display, Formatter};
7+
use std::time::{SystemTime, UNIX_EPOCH};
8+
9+
#[derive(Default)]
10+
pub struct RequestTiming();
11+
12+
pub struct ResponseTime(u64);
13+
14+
impl ResponseTime {
15+
pub fn as_millis(&self) -> u64 {
16+
self.0
17+
}
18+
}
19+
20+
impl Display for ResponseTime {
21+
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
22+
self.0.fmt(f)?;
23+
f.write_str("ms")?;
24+
Ok(())
25+
}
26+
}
27+
28+
impl Middleware for RequestTiming {
29+
fn after(&self, req: &mut dyn RequestExt, res: AfterResult) -> AfterResult {
30+
let response_time =
31+
if let Ok(start_ms) = request_header(req, "x-request-start").parse::<u128>() {
32+
let current_ms = SystemTime::now()
33+
.duration_since(UNIX_EPOCH)
34+
.expect("Time went way backwards")
35+
.as_millis();
36+
37+
if current_ms > start_ms {
38+
// The result cannot be negative
39+
current_ms - start_ms
40+
} else {
41+
// Because our nginx proxy and app run on the same dyno in production, we
42+
// shouldn't have to worry about clock drift. But if something goes wrong,
43+
// calculate the response time based on when the request reached this app.
44+
fallback_response_time(req)
45+
}
46+
} else {
47+
// X-Request-Start header couldn't be parsed.
48+
// We are probably running locally and not behind nginx.
49+
fallback_response_time(req)
50+
};
51+
52+
// This will only trucate for requests lasting > 500 million years
53+
let response_time = response_time as u64;
54+
55+
req.mut_extensions().insert(ResponseTime(response_time));
56+
57+
res
58+
}
59+
}
60+
61+
/// Calculate the response time based on when the request reached the in-app web server.
62+
///
63+
/// This serves as a fallback in case the `X-Request-Start` header is missing or invalid.
64+
fn fallback_response_time(req: &mut dyn RequestExt) -> u128 {
65+
req.elapsed().as_millis()
66+
}

0 commit comments

Comments
 (0)