sentry_core/scope/
real.rs

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