sentry_core/scope/
real.rs

1use std::borrow::Cow;
2use std::collections::{HashMap, VecDeque};
3use std::fmt;
4#[cfg(feature = "release-health")]
5use std::sync::Mutex;
6use std::sync::{Arc, PoisonError, RwLock};
7
8use crate::performance::TransactionOrSpan;
9use crate::protocol::{Attachment, Breadcrumb, Context, Event, Level, Transaction, User, Value};
10#[cfg(feature = "release-health")]
11use crate::session::Session;
12use crate::Client;
13
14#[derive(Debug)]
15pub struct Stack {
16    top: StackLayer,
17    layers: Vec<StackLayer>,
18}
19
20pub type EventProcessor = Arc<dyn Fn(Event<'static>) -> Option<Event<'static>> + Send + Sync>;
21
22/// Holds contextual data for the current scope.
23///
24/// The scope is an object that can be cloned efficiently and stores data that
25/// is locally relevant to an event.  For instance the scope will hold recorded
26/// breadcrumbs and similar information.
27///
28/// The scope can be interacted with in two ways:
29///
30/// 1. the scope is routinely updated with information by functions such as
31///    [`add_breadcrumb`] which will modify the currently top-most scope.
32/// 2. the topmost scope can also be configured through the [`configure_scope`]
33///    method.
34///
35/// Note that the scope can only be modified but not inspected.  Only the
36/// client can use the scope to extract information currently.
37///
38/// [`add_breadcrumb`]: fn.add_breadcrumb.html
39/// [`configure_scope`]: fn.configure_scope.html
40#[derive(Clone, Default)]
41pub struct Scope {
42    pub(crate) level: Option<Level>,
43    pub(crate) fingerprint: Option<Arc<[Cow<'static, str>]>>,
44    pub(crate) transaction: Option<Arc<str>>,
45    pub(crate) breadcrumbs: Arc<VecDeque<Breadcrumb>>,
46    pub(crate) user: Option<Arc<User>>,
47    pub(crate) extra: Arc<HashMap<String, Value>>,
48    pub(crate) tags: Arc<HashMap<String, String>>,
49    pub(crate) contexts: Arc<HashMap<String, Context>>,
50    pub(crate) event_processors: Arc<Vec<EventProcessor>>,
51    #[cfg(feature = "release-health")]
52    pub(crate) session: Arc<Mutex<Option<Session>>>,
53    pub(crate) span: Arc<Option<TransactionOrSpan>>,
54    pub(crate) attachments: Arc<Vec<Attachment>>,
55}
56
57impl fmt::Debug for Scope {
58    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
59        let mut debug_struct = f.debug_struct("Scope");
60        debug_struct
61            .field("level", &self.level)
62            .field("fingerprint", &self.fingerprint)
63            .field("transaction", &self.transaction)
64            .field("breadcrumbs", &self.breadcrumbs)
65            .field("user", &self.user)
66            .field("extra", &self.extra)
67            .field("tags", &self.tags)
68            .field("contexts", &self.contexts)
69            .field("event_processors", &self.event_processors.len());
70
71        #[cfg(feature = "release-health")]
72        debug_struct.field("session", &self.session);
73
74        debug_struct
75            .field("span", &self.span)
76            .field("attachments", &self.attachments.len())
77            .finish()
78    }
79}
80
81#[derive(Debug, Clone)]
82pub struct StackLayer {
83    pub client: Option<Arc<Client>>,
84    pub scope: Arc<Scope>,
85}
86
87impl Stack {
88    pub fn from_client_and_scope(client: Option<Arc<Client>>, scope: Arc<Scope>) -> Stack {
89        Stack {
90            top: StackLayer { client, scope },
91            layers: vec![],
92        }
93    }
94
95    pub fn push(&mut self) {
96        let layer = self.top.clone();
97        self.layers.push(layer);
98    }
99
100    pub fn pop(&mut self) {
101        if self.layers.is_empty() {
102            panic!("Pop from empty stack");
103        }
104        self.top = self.layers.pop().unwrap();
105    }
106
107    #[inline(always)]
108    pub fn top(&self) -> &StackLayer {
109        &self.top
110    }
111
112    #[inline(always)]
113    pub fn top_mut(&mut self) -> &mut StackLayer {
114        &mut self.top
115    }
116
117    pub fn depth(&self) -> usize {
118        self.layers.len()
119    }
120}
121
122/// A scope guard.
123///
124/// This is returned from [`Hub::push_scope`] and will automatically pop the
125/// scope on drop.
126///
127/// [`Hub::push_scope`]: struct.Hub.html#method.with_scope
128#[derive(Default)]
129pub struct ScopeGuard(pub(crate) Option<(Arc<RwLock<Stack>>, usize)>);
130
131impl fmt::Debug for ScopeGuard {
132    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
133        write!(f, "ScopeGuard")
134    }
135}
136
137impl Drop for ScopeGuard {
138    fn drop(&mut self) {
139        if let Some((stack, depth)) = self.0.take() {
140            let popped_depth = {
141                let mut stack = stack.write().unwrap_or_else(PoisonError::into_inner);
142                let popped_depth = stack.depth();
143                stack.pop();
144                popped_depth
145            };
146            // NOTE: We need to drop the `stack` lock before panicking, as the
147            // `PanicIntegration` will want to lock the `stack` itself
148            // (through `capture_event` -> `HubImpl::with`), and would thus
149            // result in a deadlock.
150            // Though that deadlock itself is detected by the `RwLock` (on macOS)
151            // and results in its own panic: `rwlock read lock would result in deadlock`.
152            // However that panic happens in the panic handler and will thus
153            // ultimately result in a `thread panicked while processing panic. aborting.`
154            // Long story short, we should not panic while holding the lock :-)
155            if popped_depth != depth {
156                panic!("Popped scope guard out of order");
157            }
158        }
159    }
160}
161
162impl Scope {
163    /// Clear the scope.
164    ///
165    /// By default a scope will inherit all values from the higher scope.
166    /// In some situations this might not be what a user wants.  Calling
167    /// this method will wipe all data contained within.
168    pub fn clear(&mut self) {
169        *self = Default::default();
170    }
171
172    /// Deletes current breadcrumbs from the scope.
173    pub fn clear_breadcrumbs(&mut self) {
174        self.breadcrumbs = Default::default();
175    }
176
177    /// Sets a level override.
178    pub fn set_level(&mut self, level: Option<Level>) {
179        self.level = level;
180    }
181
182    /// Sets the fingerprint.
183    pub fn set_fingerprint(&mut self, fingerprint: Option<&[&str]>) {
184        self.fingerprint =
185            fingerprint.map(|fp| fp.iter().map(|s| Cow::Owned((*s).into())).collect())
186    }
187
188    /// Sets the transaction.
189    pub fn set_transaction(&mut self, transaction: Option<&str>) {
190        self.transaction = transaction.map(Arc::from);
191        if let Some(name) = transaction {
192            let trx = match self.span.as_ref() {
193                Some(TransactionOrSpan::Span(span)) => &span.transaction,
194                Some(TransactionOrSpan::Transaction(trx)) => &trx.inner,
195                _ => return,
196            };
197
198            if let Some(trx) = trx.lock().unwrap().transaction.as_mut() {
199                trx.name = Some(name.into());
200            }
201        }
202    }
203
204    /// Sets the user for the current scope.
205    pub fn set_user(&mut self, user: Option<User>) {
206        self.user = user.map(Arc::new);
207    }
208
209    /// Retrieves the user of the current scope.
210    pub fn user(&self) -> Option<&User> {
211        self.user.as_deref()
212    }
213
214    /// Sets a tag to a specific value.
215    pub fn set_tag<V: ToString>(&mut self, key: &str, value: V) {
216        Arc::make_mut(&mut self.tags).insert(key.to_string(), value.to_string());
217    }
218
219    /// Removes a tag.
220    ///
221    /// If the tag is not set, does nothing.
222    pub fn remove_tag(&mut self, key: &str) {
223        Arc::make_mut(&mut self.tags).remove(key);
224    }
225
226    /// Sets a context for a key.
227    pub fn set_context<C: Into<Context>>(&mut self, key: &str, value: C) {
228        Arc::make_mut(&mut self.contexts).insert(key.to_string(), value.into());
229    }
230
231    /// Removes a context for a key.
232    pub fn remove_context(&mut self, key: &str) {
233        Arc::make_mut(&mut self.contexts).remove(key);
234    }
235
236    /// Sets a extra to a specific value.
237    pub fn set_extra(&mut self, key: &str, value: Value) {
238        Arc::make_mut(&mut self.extra).insert(key.to_string(), value);
239    }
240
241    /// Removes a extra.
242    pub fn remove_extra(&mut self, key: &str) {
243        Arc::make_mut(&mut self.extra).remove(key);
244    }
245
246    /// Add an event processor to the scope.
247    pub fn add_event_processor<F>(&mut self, f: F)
248    where
249        F: Fn(Event<'static>) -> Option<Event<'static>> + Send + Sync + 'static,
250    {
251        Arc::make_mut(&mut self.event_processors).push(Arc::new(f));
252    }
253
254    /// Adds an attachment to the scope
255    pub fn add_attachment(&mut self, attachment: Attachment) {
256        Arc::make_mut(&mut self.attachments).push(attachment);
257    }
258
259    /// Clears attachments from the scope
260    pub fn clear_attachments(&mut self) {
261        Arc::make_mut(&mut self.attachments).clear();
262    }
263
264    /// Applies the contained scoped data to fill an event.
265    pub fn apply_to_event(&self, mut event: Event<'static>) -> Option<Event<'static>> {
266        // TODO: event really should have an optional level
267        if self.level.is_some() {
268            event.level = self.level.unwrap();
269        }
270
271        if event.user.is_none() {
272            if let Some(user) = self.user.as_deref() {
273                event.user = Some(user.clone());
274            }
275        }
276
277        event.breadcrumbs.extend(self.breadcrumbs.iter().cloned());
278        event
279            .extra
280            .extend(self.extra.iter().map(|(k, v)| (k.to_owned(), v.to_owned())));
281        event
282            .tags
283            .extend(self.tags.iter().map(|(k, v)| (k.to_owned(), v.to_owned())));
284        event.contexts.extend(
285            self.contexts
286                .iter()
287                .map(|(k, v)| (k.to_owned(), v.to_owned())),
288        );
289
290        if let Some(span) = self.span.as_ref() {
291            span.apply_to_event(&mut event);
292        }
293
294        if event.transaction.is_none() {
295            if let Some(txn) = self.transaction.as_deref() {
296                event.transaction = Some(txn.to_owned());
297            }
298        }
299
300        if event.fingerprint.len() == 1
301            && (event.fingerprint[0] == "{{ default }}" || event.fingerprint[0] == "{{default}}")
302        {
303            if let Some(fp) = self.fingerprint.as_deref() {
304                event.fingerprint = Cow::Owned(fp.to_owned());
305            }
306        }
307
308        for processor in self.event_processors.as_ref() {
309            let id = event.event_id;
310            event = match processor(event) {
311                Some(event) => event,
312                None => {
313                    sentry_debug!("event processor dropped event {}", id);
314                    return None;
315                }
316            }
317        }
318
319        Some(event)
320    }
321
322    /// Applies the contained scoped data to fill a transaction.
323    pub fn apply_to_transaction(&self, transaction: &mut Transaction<'static>) {
324        if transaction.user.is_none() {
325            if let Some(user) = self.user.as_deref() {
326                transaction.user = Some(user.clone());
327            }
328        }
329
330        transaction
331            .extra
332            .extend(self.extra.iter().map(|(k, v)| (k.to_owned(), v.to_owned())));
333        transaction
334            .tags
335            .extend(self.tags.iter().map(|(k, v)| (k.to_owned(), v.to_owned())));
336        transaction.contexts.extend(
337            self.contexts
338                .iter()
339                .map(|(k, v)| (k.to_owned(), v.to_owned())),
340        );
341    }
342
343    /// Set the given [`TransactionOrSpan`] as the active span for this scope.
344    pub fn set_span(&mut self, span: Option<TransactionOrSpan>) {
345        self.span = Arc::new(span);
346    }
347
348    /// Returns the currently active span.
349    pub fn get_span(&self) -> Option<TransactionOrSpan> {
350        self.span.as_ref().clone()
351    }
352
353    #[allow(unused_variables)]
354    pub(crate) fn update_session_from_event(&self, event: &Event<'static>) {
355        #[cfg(feature = "release-health")]
356        if let Some(session) = self.session.lock().unwrap().as_mut() {
357            session.update_from_event(event);
358        }
359    }
360}