Skip to content
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,14 @@

## Unreleased

### Breaking changes

- fix(actix): capture only server errors ([#877](https://github.com/getsentry/sentry-rust/pull/877))
- 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).
- 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.
- With this change, we capture errors returned by middleware only if those errors can be classified as server errors.
- 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.

## Features

- feat(core): add Response context ([#874](https://github.com/getsentry/sentry-rust/pull/874)) by @lcian
Expand Down
69 changes: 66 additions & 3 deletions sentry-actix/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -351,7 +351,9 @@ where
let mut res: Self::Response = match fut.await {
Ok(res) => res,
Err(e) => {
if inner.capture_server_errors {
// Errors returned by middleware, and possibly other lower level errors
if inner.capture_server_errors && e.error_response().status().is_server_error()
{
hub.capture_error(&e);
}

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

use actix_web::body::BoxBody;
use actix_web::test::{call_service, init_service, TestRequest};
use actix_web::{get, web, App, HttpRequest, HttpResponse};
use futures::executor::block_on;
Expand Down Expand Up @@ -622,9 +625,9 @@ mod tests {
}
}

/// Ensures client errors (4xx) are not captured.
/// Ensures client errors (4xx) returned by service are not captured.
#[actix_web::test]
async fn test_client_errors_discarded() {
async fn test_service_client_errors_discarded() {
let events = sentry::test::with_captured_events(|| {
block_on(async {
let service = HttpResponse::NotFound;
Expand All @@ -645,6 +648,66 @@ mod tests {
assert!(events.is_empty());
}

/// Ensures client errors (4xx) returned by middleware are not captured.
#[actix_web::test]
async fn test_middleware_client_errors_discarded() {
let events = sentry::test::with_captured_events(|| {
block_on(async {
async fn hello_world() -> HttpResponse {
HttpResponse::Ok().body("Hello, world!")
}

let app = init_service(
App::new()
.wrap_fn(|_, _| async {
Err(actix_web::error::ErrorNotFound("Not found"))
as Result<ServiceResponse<BoxBody>, _>
})
.wrap(Sentry::builder().with_hub(Hub::current()).finish())
.service(web::resource("/test").to(hello_world)),
)
.await;

let req = TestRequest::get().uri("/test").to_request();
let res = app.call(req).await;
assert!(res.is_err());
assert!(res.unwrap_err().error_response().status().is_client_error());
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[nit] I would check the exact status code, not just that it is a client error here

})
});

assert!(events.is_empty());
}

/// Ensures server errors (5xx) returned by middleware are captured.
#[actix_web::test]
async fn test_middleware_server_errors_captured() {
let events = sentry::test::with_captured_events(|| {
block_on(async {
async fn hello_world() -> HttpResponse {
HttpResponse::Ok().body("Hello, world!")
}

let app = init_service(
App::new()
.wrap_fn(|_, _| async {
Err(actix_web::error::ErrorInternalServerError("Server error"))
as Result<ServiceResponse<BoxBody>, _>
})
.wrap(Sentry::builder().with_hub(Hub::current()).finish())
.service(web::resource("/test").to(hello_world)),
)
.await;

let req = TestRequest::get().uri("/test").to_request();
let res = app.call(req).await;
assert!(res.is_err());
assert!(res.unwrap_err().error_response().status().is_server_error());
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[nit] also would check exact status code here

})
});

assert_eq!(events.len(), 1);
}

/// Ensures transaction name can be overridden in handler scope.
#[actix_web::test]
async fn test_override_transaction_name() {
Expand Down
Loading