Skip to content

Commit e63fd2e

Browse files
committed
Auto merge of #2235 - jtgeibel:log-inner-chained-errors, r=pietroalbini
Log inner chained errors This builds on the logging infrastructure recently added by @pietroalbini. Whenever a user facing error is chained around an internal error, the router now captures that message and adds it to the log metadata. In particular, this should allow us to identify errors sent to cargo via `cargo_err` which are sent via a status 200 response and left no indication in the logs that an error has occurred. r? @pietroalbini
2 parents e566283 + 5deaedc commit e63fd2e

File tree

5 files changed

+59
-23
lines changed

5 files changed

+59
-23
lines changed

src/controllers.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@ mod frontend_prelude {
88
pub use crate::util::errors::{bad_request, server_error};
99
}
1010

11+
pub(crate) use prelude::RequestUtils;
12+
1113
mod prelude {
1214
pub use super::helpers::ok_true;
1315
pub use diesel::prelude::*;

src/controllers/krate/publish.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -232,7 +232,7 @@ pub fn publish(req: &mut dyn Request) -> AppResult<Response> {
232232
fn parse_new_headers(req: &mut dyn Request) -> AppResult<EncodableCrateUpload> {
233233
// Read the json upload request
234234
let metadata_length = u64::from(read_le_u32(req.body())?);
235-
req.mut_extensions().insert(metadata_length);
235+
req.log_metadata("metadata_length", metadata_length);
236236

237237
let max = req.app().config.max_upload_size;
238238
if metadata_length > max {

src/middleware/log_request.rs

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,16 @@ pub fn add_custom_metadata<V: Display>(req: &mut dyn Request, key: &'static str,
5858
}
5959
}
6060

61+
#[cfg(test)]
62+
pub(crate) fn get_log_message(req: &dyn Request, key: &'static str) -> String {
63+
for (k, v) in &req.extensions().find::<CustomMetadata>().unwrap().entries {
64+
if key == *k {
65+
return v.clone();
66+
}
67+
}
68+
panic!("expected log message for {} not found", key);
69+
}
70+
6171
struct RequestLine<'r> {
6272
req: &'r dyn Request,
6373
res: &'r Result<Response>,
@@ -88,10 +98,6 @@ impl Display for RequestLine<'_> {
8898
}
8999
}
90100

91-
if let Some(len) = self.req.extensions().find::<u64>() {
92-
line.add_field("metadata_length", len)?;
93-
}
94-
95101
if let Err(err) = self.res {
96102
line.add_quoted_field("error", err)?;
97103
}

src/router.rs

Lines changed: 28 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -143,10 +143,15 @@ impl Handler for C {
143143
let C(f) = *self;
144144
match f(req) {
145145
Ok(resp) => Ok(resp),
146-
Err(e) => match e.response() {
147-
Some(response) => Ok(response),
148-
None => Err(std_error(e)),
149-
},
146+
Err(e) => {
147+
if let Some(cause) = e.cause() {
148+
req.log_metadata("cause", cause.to_string())
149+
};
150+
match e.response() {
151+
Some(response) => Ok(response),
152+
None => Err(std_error(e)),
153+
}
154+
}
150155
}
151156
}
152157
}
@@ -181,7 +186,9 @@ impl Handler for R404 {
181186
#[cfg(test)]
182187
mod tests {
183188
use super::*;
184-
use crate::util::errors::{bad_request, cargo_err, internal, AppError, NotFound, Unauthorized};
189+
use crate::util::errors::{
190+
bad_request, cargo_err, internal, AppError, ChainError, NotFound, Unauthorized,
191+
};
185192

186193
use conduit_test::MockRequest;
187194
use diesel::result::Error as DieselError;
@@ -219,6 +226,22 @@ mod tests {
219226
200
220227
);
221228

229+
// Inner errors are captured for logging when wrapped by a user facing error
230+
let response = C(|_| {
231+
Err("-1"
232+
.parse::<u8>()
233+
.chain_error(|| internal("middle error"))
234+
.chain_error(|| bad_request("outer user facing error"))
235+
.unwrap_err())
236+
})
237+
.call(&mut req)
238+
.unwrap();
239+
assert_eq!(response.status.0, 400);
240+
assert_eq!(
241+
crate::middleware::log_request::get_log_message(&req, "cause"),
242+
"middle error caused by invalid digit found in string"
243+
);
244+
222245
// All other error types are propogated up the middleware, eventually becoming status 500
223246
assert!(C(|_| Err(internal(""))).call(&mut req).is_err());
224247
assert!(

src/util/errors.rs

Lines changed: 18 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,16 @@ pub trait AppError: Send + fmt::Display + fmt::Debug + 'static {
7777
/// where it is eventually logged and turned into a status 500 response.
7878
fn response(&self) -> Option<Response>;
7979

80+
/// The cause of an error response
81+
///
82+
/// If present, an error provided to the `LogRequests` middleware.
83+
///
84+
/// This is intended for use with the `ChainError` trait, where a user facing
85+
/// error may wrap an internal error we still want to log.
86+
fn cause(&self) -> Option<&dyn AppError> {
87+
None
88+
}
89+
8090
fn get_type_id(&self) -> TypeId {
8191
TypeId::of::<Self>()
8292
}
@@ -108,6 +118,10 @@ impl AppError for Box<dyn AppError> {
108118
fn response(&self) -> Option<Response> {
109119
(**self).response()
110120
}
121+
122+
fn cause(&self) -> Option<&dyn AppError> {
123+
(**self).cause()
124+
}
111125
}
112126

113127
pub type AppResult<T> = Result<T, Box<dyn AppError>>;
@@ -128,19 +142,6 @@ struct ChainedError<E> {
128142
cause: Box<dyn AppError>,
129143
}
130144

131-
impl<T, F> ChainError<T> for F
132-
where
133-
F: FnOnce() -> AppResult<T>,
134-
{
135-
fn chain_error<E, C>(self, callback: C) -> AppResult<T>
136-
where
137-
E: AppError,
138-
C: FnOnce() -> E,
139-
{
140-
self().chain_error(callback)
141-
}
142-
}
143-
144145
impl<T, E: AppError> ChainError<T> for Result<T, E> {
145146
fn chain_error<E2, C>(self, callback: C) -> AppResult<T>
146147
where
@@ -173,6 +174,10 @@ impl<E: AppError> AppError for ChainedError<E> {
173174
fn response(&self) -> Option<Response> {
174175
self.error.response()
175176
}
177+
178+
fn cause(&self) -> Option<&dyn AppError> {
179+
Some(&*self.cause)
180+
}
176181
}
177182

178183
impl<E: AppError> fmt::Display for ChainedError<E> {

0 commit comments

Comments
 (0)