Skip to content

Commit cd933d3

Browse files
authored
fix(actix): capture only server errors (#877)
1 parent c29c01f commit cd933d3

File tree

2 files changed

+74
-3
lines changed

2 files changed

+74
-3
lines changed

CHANGELOG.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,14 @@
22

33
## Unreleased
44

5+
### Breaking changes
6+
7+
- fix(actix): capture only server errors ([#877](https://github.com/getsentry/sentry-rust/pull/877))
8+
- The Actix integration now properly honors the `capture_server_errors` option (enabled by default), capturing errors returned by middleware only if they are server errors (HTTP status code 5xx).
9+
- Previously, if a middleware were to process the request after the Sentry middleware and return an error, our middleware would always capture it and send it to Sentry, regardless if it was a client, server or some other kind of error.
10+
- With this change, we capture errors returned by middleware only if those errors can be classified as server errors.
11+
- There is no change in behavior when it comes to errors returned by services, in which case the Sentry middleware only captures server errors exclusively.
12+
513
### Features
614

715
- feat(core): add Response context ([#874](https://github.com/getsentry/sentry-rust/pull/874)) by @lcian

sentry-actix/src/lib.rs

Lines changed: 66 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -351,7 +351,9 @@ where
351351
let mut res: Self::Response = match fut.await {
352352
Ok(res) => res,
353353
Err(e) => {
354-
if inner.capture_server_errors {
354+
// Errors returned by middleware, and possibly other lower level errors
355+
if inner.capture_server_errors && e.error_response().status().is_server_error()
356+
{
355357
hub.capture_error(&e);
356358
}
357359

@@ -474,6 +476,7 @@ fn process_event(mut event: Event<'static>, request: &Request) -> Event<'static>
474476
mod tests {
475477
use std::io;
476478

479+
use actix_web::body::BoxBody;
477480
use actix_web::test::{call_service, init_service, TestRequest};
478481
use actix_web::{get, web, App, HttpRequest, HttpResponse};
479482
use futures::executor::block_on;
@@ -622,9 +625,9 @@ mod tests {
622625
}
623626
}
624627

625-
/// Ensures client errors (4xx) are not captured.
628+
/// Ensures client errors (4xx) returned by service are not captured.
626629
#[actix_web::test]
627-
async fn test_client_errors_discarded() {
630+
async fn test_service_client_errors_discarded() {
628631
let events = sentry::test::with_captured_events(|| {
629632
block_on(async {
630633
let service = HttpResponse::NotFound;
@@ -645,6 +648,66 @@ mod tests {
645648
assert!(events.is_empty());
646649
}
647650

651+
/// Ensures client errors (4xx) returned by middleware are not captured.
652+
#[actix_web::test]
653+
async fn test_middleware_client_errors_discarded() {
654+
let events = sentry::test::with_captured_events(|| {
655+
block_on(async {
656+
async fn hello_world() -> HttpResponse {
657+
HttpResponse::Ok().body("Hello, world!")
658+
}
659+
660+
let app = init_service(
661+
App::new()
662+
.wrap_fn(|_, _| async {
663+
Err(actix_web::error::ErrorNotFound("Not found"))
664+
as Result<ServiceResponse<BoxBody>, _>
665+
})
666+
.wrap(Sentry::builder().with_hub(Hub::current()).finish())
667+
.service(web::resource("/test").to(hello_world)),
668+
)
669+
.await;
670+
671+
let req = TestRequest::get().uri("/test").to_request();
672+
let res = app.call(req).await;
673+
assert!(res.is_err());
674+
assert!(res.unwrap_err().error_response().status().is_client_error());
675+
})
676+
});
677+
678+
assert!(events.is_empty());
679+
}
680+
681+
/// Ensures server errors (5xx) returned by middleware are captured.
682+
#[actix_web::test]
683+
async fn test_middleware_server_errors_captured() {
684+
let events = sentry::test::with_captured_events(|| {
685+
block_on(async {
686+
async fn hello_world() -> HttpResponse {
687+
HttpResponse::Ok().body("Hello, world!")
688+
}
689+
690+
let app = init_service(
691+
App::new()
692+
.wrap_fn(|_, _| async {
693+
Err(actix_web::error::ErrorInternalServerError("Server error"))
694+
as Result<ServiceResponse<BoxBody>, _>
695+
})
696+
.wrap(Sentry::builder().with_hub(Hub::current()).finish())
697+
.service(web::resource("/test").to(hello_world)),
698+
)
699+
.await;
700+
701+
let req = TestRequest::get().uri("/test").to_request();
702+
let res = app.call(req).await;
703+
assert!(res.is_err());
704+
assert!(res.unwrap_err().error_response().status().is_server_error());
705+
})
706+
});
707+
708+
assert_eq!(events.len(), 1);
709+
}
710+
648711
/// Ensures transaction name can be overridden in handler scope.
649712
#[actix_web::test]
650713
async fn test_override_transaction_name() {

0 commit comments

Comments
 (0)