Skip to content

Commit 5437c24

Browse files
committed
middleware::log_request: Split request timing code into dedicated middleware
1 parent 61c1a7a commit 5437c24

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

@@ -23,6 +24,7 @@ mod known_error_to_json;
2324
mod log_connection_pool_status;
2425
pub mod log_request;
2526
mod normalize_path;
27+
mod request_timing;
2628
mod require_user_agent;
2729
mod static_or_continue;
2830
mod update_metrics;
@@ -48,6 +50,8 @@ pub fn build_middleware(app: Arc<App>, endpoints: RouteBuilder) -> MiddlewareBui
4850
m.around(SentryMiddleware::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
@@ -7,8 +7,8 @@ use crate::util::request_header;
77
use conduit::{header, RequestExt, StatusCode};
88
use conduit_cookie::RequestSession;
99

10+
use crate::middleware::request_timing::ResponseTime;
1011
use std::fmt::{self, Display, Formatter};
11-
use std::time::{SystemTime, UNIX_EPOCH};
1212

1313
const SLOW_REQUEST_THRESHOLD_MS: u64 = 1000;
1414

@@ -25,30 +25,7 @@ impl Middleware for LogRequests {
2525
}
2626

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

5330
println!(
5431
"{}",
@@ -59,19 +36,12 @@ impl Middleware for LogRequests {
5936
}
6037
);
6138

62-
report_to_sentry(req, &res, response_time);
39+
report_to_sentry(req, &res, response_time.as_millis());
6340

6441
res
6542
}
6643
}
6744

68-
/// Calculate the response time based on when the request reached the in-app web server.
69-
///
70-
/// This serves as a fallback in case the `X-Request-Start` header is missing or invalid.
71-
fn fallback_response_time(req: &mut dyn RequestExt) -> u128 {
72-
req.elapsed().as_millis()
73-
}
74-
7545
struct CustomMetadata {
7646
entries: Vec<(&'static str, String)>,
7747
}
@@ -142,7 +112,7 @@ pub(crate) fn get_log_message(req: &dyn RequestExt, key: &'static str) -> String
142112
struct RequestLine<'r> {
143113
req: &'r dyn RequestExt,
144114
res: &'r AfterResult,
145-
response_time: u64,
115+
response_time: &'r ResponseTime,
146116
}
147117

148118
impl Display for RequestLine<'_> {
@@ -175,7 +145,7 @@ impl Display for RequestLine<'_> {
175145
}
176146

177147
line.add_quoted_field("fwd", request_header(self.req, "x-real-ip"))?;
178-
line.add_field("service", TimeMs(self.response_time))?;
148+
line.add_field("service", self.response_time)?;
179149
line.add_field("status", status.as_str())?;
180150
line.add_quoted_field("user_agent", request_header(self.req, header::USER_AGENT))?;
181151

@@ -189,7 +159,7 @@ impl Display for RequestLine<'_> {
189159
line.add_quoted_field("error", err)?;
190160
}
191161

192-
if self.response_time > SLOW_REQUEST_THRESHOLD_MS {
162+
if self.response_time.as_millis() > SLOW_REQUEST_THRESHOLD_MS {
193163
line.add_marker("SLOW REQUEST")?;
194164
}
195165

@@ -214,16 +184,6 @@ impl<'a> Display for FullPath<'a> {
214184
}
215185
}
216186

217-
struct TimeMs(u64);
218-
219-
impl Display for TimeMs {
220-
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
221-
self.0.fmt(f)?;
222-
f.write_str("ms")?;
223-
Ok(())
224-
}
225-
}
226-
227187
struct LogLine<'f, 'g> {
228188
f: &'f mut Formatter<'g>,
229189
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)