diff --git a/sentry-core/src/hub.rs b/sentry-core/src/hub.rs index 27461d8c..79146892 100644 --- a/sentry-core/src/hub.rs +++ b/sentry-core/src/hub.rs @@ -427,17 +427,14 @@ impl Hub { if let Some(ref client) = top.client { let scope = Arc::make_mut(&mut top.scope); let options = client.options(); - let breadcrumbs = Arc::make_mut(&mut scope.breadcrumbs); for breadcrumb in breadcrumb.into_breadcrumbs() { let breadcrumb_opt = match options.before_breadcrumb { Some(ref callback) => callback(breadcrumb), None => Some(breadcrumb) }; + if let Some(breadcrumb) = breadcrumb_opt { - breadcrumbs.push_back(breadcrumb); - } - while breadcrumbs.len() > options.max_breadcrumbs { - breadcrumbs.pop_front(); + scope.add_breadcrumb(breadcrumb, options.max_breadcrumbs); } } } diff --git a/sentry-core/src/lib.rs b/sentry-core/src/lib.rs index 93a7cff8..1d9431a5 100644 --- a/sentry-core/src/lib.rs +++ b/sentry-core/src/lib.rs @@ -77,7 +77,7 @@ pub use crate::hub::Hub; pub use crate::integration::Integration; pub use crate::intodsn::IntoDsn; pub use crate::performance::*; -pub use crate::scope::{Scope, ScopeGuard}; +pub use crate::scope::{Scope, ScopeGuard, ScopeUpdate}; pub use crate::transport::{Transport, TransportFactory}; // client feature diff --git a/sentry-core/src/scope/mod.rs b/sentry-core/src/scope/mod.rs index c2d0ebed..d1462487 100644 --- a/sentry-core/src/scope/mod.rs +++ b/sentry-core/src/scope/mod.rs @@ -1,3 +1,6 @@ +use sentry_types::protocol::v7::{Breadcrumb, User, Value}; +use serde::{Deserialize, Serialize}; + #[cfg(feature = "client")] mod real; @@ -9,3 +12,15 @@ pub use self::real::*; #[cfg(not(feature = "client"))] pub use self::noop::*; + +#[non_exhaustive] +#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)] +pub enum ScopeUpdate { + AddBreadcrumb(Breadcrumb), + ClearBreadcrumbs, + User(Option), + SetExtra(String, Value), + RemoveExtra(String), + SetTag(String, String), + RemoveTag(String), +} diff --git a/sentry-core/src/scope/real.rs b/sentry-core/src/scope/real.rs index eb92621e..26edc79b 100644 --- a/sentry-core/src/scope/real.rs +++ b/sentry-core/src/scope/real.rs @@ -8,12 +8,15 @@ use crate::protocol::{Attachment, Breadcrumb, Context, Event, Level, User, Value use crate::session::Session; use crate::Client; +use super::ScopeUpdate; + #[derive(Debug)] pub struct Stack { layers: Vec, } pub type EventProcessor = Arc) -> Option> + Send + Sync>; +pub type ScopeListener = Arc; /// Holds contextual data for the current scope. /// @@ -44,6 +47,7 @@ pub struct Scope { pub(crate) tags: Arc>, pub(crate) contexts: Arc>, pub(crate) event_processors: Arc>, + pub(crate) scope_listeners: Arc>, pub(crate) session: Arc>>, pub(crate) span: Arc>, pub(crate) attachments: Arc>, @@ -146,6 +150,7 @@ impl Scope { /// Deletes current breadcrumbs from the scope. pub fn clear_breadcrumbs(&mut self) { + self.notify_scope_listeners(|| ScopeUpdate::ClearBreadcrumbs); self.breadcrumbs = Default::default(); } @@ -178,11 +183,13 @@ impl Scope { /// Sets the user for the current scope. pub fn set_user(&mut self, user: Option) { + self.notify_scope_listeners(|| ScopeUpdate::User(user.clone())); self.user = user.map(Arc::new); } /// Sets a tag to a specific value. pub fn set_tag(&mut self, key: &str, value: V) { + self.notify_scope_listeners(|| ScopeUpdate::SetTag(key.to_string(), value.to_string())); Arc::make_mut(&mut self.tags).insert(key.to_string(), value.to_string()); } @@ -190,6 +197,7 @@ impl Scope { /// /// If the tag is not set, does nothing. pub fn remove_tag(&mut self, key: &str) { + self.notify_scope_listeners(|| ScopeUpdate::RemoveTag(key.to_string())); Arc::make_mut(&mut self.tags).remove(key); } @@ -205,11 +213,13 @@ impl Scope { /// Sets a extra to a specific value. pub fn set_extra(&mut self, key: &str, value: Value) { + self.notify_scope_listeners(|| ScopeUpdate::SetExtra(key.to_string(), value.clone())); Arc::make_mut(&mut self.extra).insert(key.to_string(), value); } /// Removes a extra. pub fn remove_extra(&mut self, key: &str) { + self.notify_scope_listeners(|| ScopeUpdate::RemoveExtra(key.to_string())); Arc::make_mut(&mut self.extra).remove(key); } @@ -221,6 +231,14 @@ impl Scope { Arc::make_mut(&mut self.event_processors).push(Arc::new(f)); } + /// Add an scope listener to the scope. + pub fn add_scope_listener(&mut self, f: F) + where + F: Fn(&ScopeUpdate) + Send + Sync + 'static, + { + Arc::make_mut(&mut self.scope_listeners).push(Arc::new(f)); + } + /// Adds an attachment to the scope pub fn add_attachment(&mut self, attachment: Attachment) { Arc::make_mut(&mut self.attachments).push(attachment); @@ -304,4 +322,26 @@ impl Scope { session.update_from_event(event); } } + + pub(crate) fn add_breadcrumb(&mut self, breadcrumb: Breadcrumb, max_breadcrumbs: usize) { + self.notify_scope_listeners(|| ScopeUpdate::AddBreadcrumb(breadcrumb.clone())); + + let breadcrumbs = Arc::make_mut(&mut self.breadcrumbs); + breadcrumbs.push_back(breadcrumb); + + while breadcrumbs.len() > max_breadcrumbs { + breadcrumbs.pop_front(); + } + } + + fn notify_scope_listeners ScopeUpdate>(&mut self, update_fn: F) { + if self.scope_listeners.is_empty() { + return; + } + + let update = update_fn(); + for listener in self.scope_listeners.as_ref() { + listener(&update); + } + } } diff --git a/sentry/src/lib.rs b/sentry/src/lib.rs index 5cfe255f..a5230d9b 100644 --- a/sentry/src/lib.rs +++ b/sentry/src/lib.rs @@ -224,3 +224,4 @@ pub mod integrations { #[doc(inline)] pub use sentry_core::types::protocol::latest as protocol; +pub use sentry_core::ScopeUpdate;