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#[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#[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 if popped_depth != depth {
156 panic!("Popped scope guard out of order");
157 }
158 }
159 }
160}
161
162impl Scope {
163 pub fn clear(&mut self) {
169 *self = Default::default();
170 }
171
172 pub fn clear_breadcrumbs(&mut self) {
174 self.breadcrumbs = Default::default();
175 }
176
177 pub fn set_level(&mut self, level: Option<Level>) {
179 self.level = level;
180 }
181
182 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 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 pub fn set_user(&mut self, user: Option<User>) {
206 self.user = user.map(Arc::new);
207 }
208
209 pub fn user(&self) -> Option<&User> {
211 self.user.as_deref()
212 }
213
214 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 pub fn remove_tag(&mut self, key: &str) {
223 Arc::make_mut(&mut self.tags).remove(key);
224 }
225
226 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 pub fn remove_context(&mut self, key: &str) {
233 Arc::make_mut(&mut self.contexts).remove(key);
234 }
235
236 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 pub fn remove_extra(&mut self, key: &str) {
243 Arc::make_mut(&mut self.extra).remove(key);
244 }
245
246 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 pub fn add_attachment(&mut self, attachment: Attachment) {
256 Arc::make_mut(&mut self.attachments).push(attachment);
257 }
258
259 pub fn clear_attachments(&mut self) {
261 Arc::make_mut(&mut self.attachments).clear();
262 }
263
264 pub fn apply_to_event(&self, mut event: Event<'static>) -> Option<Event<'static>> {
266 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 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 pub fn set_span(&mut self, span: Option<TransactionOrSpan>) {
345 self.span = Arc::new(span);
346 }
347
348 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}