Skip to content

Commit 44fa0cf

Browse files
authored
fix(actix): process request in other middleware using correct Hub (#758)
* feat(actix): pass request Hub in request extensions * Update lib.rs * improve * improve * fixes * import
1 parent 5dd246d commit 44fa0cf

File tree

1 file changed

+57
-4
lines changed

1 file changed

+57
-4
lines changed

sentry-actix/src/lib.rs

Lines changed: 57 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,7 @@
33
//!
44
//! To use this middleware just configure Sentry and then add it to your actix web app as a
55
//! middleware. Because actix is generally working with non sendable objects and highly concurrent
6-
//! this middleware creates a new hub per request. As a result many of the sentry integrations
7-
//! such as breadcrumbs do not work unless you bind the actix hub.
6+
//! this middleware creates a new Hub per request.
87
//!
98
//! # Example
109
//!
@@ -59,11 +58,15 @@
5958
//! # Reusing the Hub
6059
//!
6160
//! This integration will automatically create a new per-request Hub from the main Hub, and update the
62-
//! current Hub instance. For example, the following will capture a message in the current request's Hub:
61+
//! current Hub instance. For example, the following in the handler or in any of the subsequent
62+
//! middleware will capture a message in the current request's Hub:
6363
//!
6464
//! ```
6565
//! sentry::capture_message("Something is not well", sentry::Level::Warning);
6666
//! ```
67+
//!
68+
//! It is recommended to register the Sentry middleware as the last, i.e. the first to be executed
69+
//! when processing a request, so that the rest of the processing will run with the correct Hub.
6770
6871
#![doc(html_favicon_url = "https://sentry-brand.storage.googleapis.com/favicon.ico")]
6972
#![doc(html_logo_url = "https://sentry-brand.storage.googleapis.com/sentry-glyph-black.png")]
@@ -276,6 +279,7 @@ where
276279
let hub = Arc::new(Hub::new_from_top(
277280
inner.hub.clone().unwrap_or_else(Hub::main),
278281
));
282+
279283
let client = hub.client();
280284
let track_sessions = client.as_ref().is_some_and(|client| {
281285
let options = client.options();
@@ -332,7 +336,8 @@ where
332336
scope.add_event_processor(move |event| Some(process_event(event, &sentry_req)));
333337
parent_span
334338
});
335-
let fut = svc.call(req).bind_hub(hub.clone());
339+
340+
let fut = Hub::run(hub.clone(), || svc.call(req)).bind_hub(hub.clone());
336341
let mut res: Self::Response = match fut.await {
337342
Ok(res) => res,
338343
Err(e) => {
@@ -461,6 +466,7 @@ mod tests {
461466
use actix_web::{get, web, App, HttpRequest, HttpResponse};
462467
use futures::executor::block_on;
463468

469+
use futures::future::join_all;
464470
use sentry::Level;
465471

466472
use super::*;
@@ -703,4 +709,51 @@ mod tests {
703709
}
704710
assert_eq!(items.next(), None);
705711
}
712+
713+
/// Tests that the per-request Hub is used in the handler and both sides of the roundtrip
714+
/// through middleware
715+
#[actix_web::test]
716+
async fn test_middleware_and_handler_use_correct_hub() {
717+
sentry::test::with_captured_events(|| {
718+
block_on(async {
719+
sentry::capture_message("message outside", Level::Error);
720+
721+
let handler = || {
722+
// an event was captured in the middleware
723+
assert!(Hub::current().last_event_id().is_some());
724+
sentry::capture_message("second message", Level::Error);
725+
HttpResponse::Ok()
726+
};
727+
728+
let app = init_service(
729+
App::new()
730+
.wrap_fn(|req, srv| {
731+
// the event captured outside the per-request Hub is not there
732+
assert!(Hub::current().last_event_id().is_none());
733+
734+
let event_id = sentry::capture_message("first message", Level::Error);
735+
736+
srv.call(req).map(move |res| {
737+
// a different event was captured in the handler
738+
assert!(Hub::current().last_event_id().is_some());
739+
assert_ne!(Some(event_id), Hub::current().last_event_id());
740+
res
741+
})
742+
})
743+
.wrap(Sentry::builder().with_hub(Hub::current()).finish())
744+
.service(web::resource("/test").to(handler)),
745+
)
746+
.await;
747+
748+
// test with multiple requests in parallel
749+
let mut futures = Vec::new();
750+
for _ in 0..16 {
751+
let req = TestRequest::get().uri("/test").to_request();
752+
futures.push(call_service(&app, req));
753+
}
754+
755+
join_all(futures).await;
756+
})
757+
});
758+
}
706759
}

0 commit comments

Comments
 (0)