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::{
10 Attachment, Breadcrumb, Context, Event, Level, TraceContext, Transaction, User, Value,
11};
12#[cfg(feature = "logs")]
13use crate::protocol::{Log, LogAttribute};
14#[cfg(feature = "release-health")]
15use crate::session::Session;
16use crate::{Client, SentryTrace, TraceHeader, TraceHeadersIter};
17
18#[derive(Debug)]
19pub struct Stack {
20 top: StackLayer,
21 layers: Vec<StackLayer>,
22}
23
24pub type EventProcessor = Arc<dyn Fn(Event<'static>) -> Option<Event<'static>> + Send + Sync>;
25
26#[derive(Clone, Default)]
45pub struct Scope {
46 pub(crate) level: Option<Level>,
47 pub(crate) fingerprint: Option<Arc<[Cow<'static, str>]>>,
48 pub(crate) transaction: Option<Arc<str>>,
49 pub(crate) breadcrumbs: Arc<VecDeque<Breadcrumb>>,
50 pub(crate) user: Option<Arc<User>>,
51 pub(crate) extra: Arc<HashMap<String, Value>>,
52 pub(crate) tags: Arc<HashMap<String, String>>,
53 pub(crate) contexts: Arc<HashMap<String, Context>>,
54 pub(crate) event_processors: Arc<Vec<EventProcessor>>,
55 #[cfg(feature = "release-health")]
56 pub(crate) session: Arc<Mutex<Option<Session>>>,
57 pub(crate) span: Arc<Option<TransactionOrSpan>>,
58 pub(crate) attachments: Arc<Vec<Attachment>>,
59 pub(crate) propagation_context: SentryTrace,
60}
61
62impl fmt::Debug for Scope {
63 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
64 let mut debug_struct = f.debug_struct("Scope");
65 debug_struct
66 .field("level", &self.level)
67 .field("fingerprint", &self.fingerprint)
68 .field("transaction", &self.transaction)
69 .field("breadcrumbs", &self.breadcrumbs)
70 .field("user", &self.user)
71 .field("extra", &self.extra)
72 .field("tags", &self.tags)
73 .field("contexts", &self.contexts)
74 .field("event_processors", &self.event_processors.len());
75
76 #[cfg(feature = "release-health")]
77 debug_struct.field("session", &self.session);
78
79 debug_struct
80 .field("span", &self.span)
81 .field("attachments", &self.attachments.len())
82 .field("propagation_context", &self.propagation_context)
83 .finish()
84 }
85}
86
87#[derive(Debug, Clone)]
88pub struct StackLayer {
89 pub client: Option<Arc<Client>>,
90 pub scope: Arc<Scope>,
91}
92
93impl Stack {
94 pub fn from_client_and_scope(client: Option<Arc<Client>>, scope: Arc<Scope>) -> Stack {
95 Stack {
96 top: StackLayer { client, scope },
97 layers: vec![],
98 }
99 }
100
101 pub fn push(&mut self) {
102 let layer = self.top.clone();
103 self.layers.push(layer);
104 }
105
106 pub fn pop(&mut self) {
107 if self.layers.is_empty() {
108 panic!("Pop from empty stack");
109 }
110 self.top = self.layers.pop().unwrap();
111 }
112
113 #[inline(always)]
114 pub fn top(&self) -> &StackLayer {
115 &self.top
116 }
117
118 #[inline(always)]
119 pub fn top_mut(&mut self) -> &mut StackLayer {
120 &mut self.top
121 }
122
123 pub fn depth(&self) -> usize {
124 self.layers.len()
125 }
126}
127
128#[derive(Default)]
135pub struct ScopeGuard(pub(crate) Option<(Arc<RwLock<Stack>>, usize)>);
136
137impl fmt::Debug for ScopeGuard {
138 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
139 write!(f, "ScopeGuard")
140 }
141}
142
143impl Drop for ScopeGuard {
144 fn drop(&mut self) {
145 if let Some((stack, depth)) = self.0.take() {
146 let popped_depth = {
147 let mut stack = stack.write().unwrap_or_else(PoisonError::into_inner);
148 let popped_depth = stack.depth();
149 stack.pop();
150 popped_depth
151 };
152 if popped_depth != depth {
162 panic!("Popped scope guard out of order");
163 }
164 }
165 }
166}
167
168impl Scope {
169 pub fn clear(&mut self) {
175 *self = Default::default();
176 }
177
178 pub fn clear_breadcrumbs(&mut self) {
180 self.breadcrumbs = Default::default();
181 }
182
183 pub fn set_level(&mut self, level: Option<Level>) {
185 self.level = level;
186 }
187
188 pub fn set_fingerprint(&mut self, fingerprint: Option<&[&str]>) {
190 self.fingerprint =
191 fingerprint.map(|fp| fp.iter().map(|s| Cow::Owned((*s).into())).collect())
192 }
193
194 pub fn set_transaction(&mut self, transaction: Option<&str>) {
196 self.transaction = transaction.map(Arc::from);
197 if let Some(name) = transaction {
198 let trx = match self.span.as_ref() {
199 Some(TransactionOrSpan::Span(span)) => &span.transaction,
200 Some(TransactionOrSpan::Transaction(trx)) => &trx.inner,
201 _ => return,
202 };
203
204 if let Some(trx) = trx.lock().unwrap().transaction.as_mut() {
205 trx.name = Some(name.into());
206 }
207 }
208 }
209
210 pub fn set_user(&mut self, user: Option<User>) {
212 self.user = user.map(Arc::new);
213 }
214
215 pub fn user(&self) -> Option<&User> {
217 self.user.as_deref()
218 }
219
220 pub fn set_tag<V: ToString>(&mut self, key: &str, value: V) {
222 Arc::make_mut(&mut self.tags).insert(key.to_string(), value.to_string());
223 }
224
225 pub fn remove_tag(&mut self, key: &str) {
229 Arc::make_mut(&mut self.tags).remove(key);
230 }
231
232 pub fn set_context<C: Into<Context>>(&mut self, key: &str, value: C) {
234 Arc::make_mut(&mut self.contexts).insert(key.to_string(), value.into());
235 }
236
237 pub fn remove_context(&mut self, key: &str) {
239 Arc::make_mut(&mut self.contexts).remove(key);
240 }
241
242 pub fn set_extra(&mut self, key: &str, value: Value) {
244 Arc::make_mut(&mut self.extra).insert(key.to_string(), value);
245 }
246
247 pub fn remove_extra(&mut self, key: &str) {
249 Arc::make_mut(&mut self.extra).remove(key);
250 }
251
252 pub fn add_event_processor<F>(&mut self, f: F)
254 where
255 F: Fn(Event<'static>) -> Option<Event<'static>> + Send + Sync + 'static,
256 {
257 Arc::make_mut(&mut self.event_processors).push(Arc::new(f));
258 }
259
260 pub fn add_attachment(&mut self, attachment: Attachment) {
262 Arc::make_mut(&mut self.attachments).push(attachment);
263 }
264
265 pub fn clear_attachments(&mut self) {
267 Arc::make_mut(&mut self.attachments).clear();
268 }
269
270 pub fn apply_to_event(&self, mut event: Event<'static>) -> Option<Event<'static>> {
272 if self.level.is_some() {
274 event.level = self.level.unwrap();
275 }
276
277 if event.user.is_none() {
278 if let Some(user) = self.user.as_deref() {
279 event.user = Some(user.clone());
280 }
281 }
282
283 event.breadcrumbs.extend(self.breadcrumbs.iter().cloned());
284 event
285 .extra
286 .extend(self.extra.iter().map(|(k, v)| (k.to_owned(), v.to_owned())));
287 event
288 .tags
289 .extend(self.tags.iter().map(|(k, v)| (k.to_owned(), v.to_owned())));
290 event.contexts.extend(
291 self.contexts
292 .iter()
293 .map(|(k, v)| (k.to_owned(), v.to_owned())),
294 );
295
296 if let Some(span) = self.span.as_ref() {
297 span.apply_to_event(&mut event);
298 } else {
299 self.apply_propagation_context(&mut event);
300 }
301
302 if event.transaction.is_none() {
303 if let Some(txn) = self.transaction.as_deref() {
304 event.transaction = Some(txn.to_owned());
305 }
306 }
307
308 if event.fingerprint.len() == 1
309 && (event.fingerprint[0] == "{{ default }}" || event.fingerprint[0] == "{{default}}")
310 {
311 if let Some(fp) = self.fingerprint.as_deref() {
312 event.fingerprint = Cow::Owned(fp.to_owned());
313 }
314 }
315
316 for processor in self.event_processors.as_ref() {
317 let id = event.event_id;
318 event = match processor(event) {
319 Some(event) => event,
320 None => {
321 sentry_debug!("event processor dropped event {}", id);
322 return None;
323 }
324 }
325 }
326
327 Some(event)
328 }
329
330 pub fn apply_to_transaction(&self, transaction: &mut Transaction<'static>) {
332 if transaction.user.is_none() {
333 if let Some(user) = self.user.as_deref() {
334 transaction.user = Some(user.clone());
335 }
336 }
337
338 transaction
339 .extra
340 .extend(self.extra.iter().map(|(k, v)| (k.to_owned(), v.to_owned())));
341 transaction
342 .tags
343 .extend(self.tags.iter().map(|(k, v)| (k.to_owned(), v.to_owned())));
344 transaction.contexts.extend(
345 self.contexts
346 .iter()
347 .map(|(k, v)| (k.to_owned(), v.to_owned())),
348 );
349 }
350
351 #[cfg(feature = "logs")]
354 pub fn apply_to_log(&self, log: &mut Log) {
355 if let Some(span) = self.span.as_ref() {
356 log.trace_id = Some(span.get_trace_context().trace_id);
357 } else {
358 log.trace_id = Some(self.propagation_context.trace_id);
359 }
360
361 if !log.attributes.contains_key("sentry.trace.parent_span_id") {
362 if let Some(span) = self.get_span() {
363 let span_id = match span {
364 crate::TransactionOrSpan::Transaction(transaction) => {
365 transaction.get_trace_context().span_id
366 }
367 crate::TransactionOrSpan::Span(span) => span.get_span_id(),
368 };
369 log.attributes.insert(
370 "parent_span_id".to_owned(),
371 LogAttribute(span_id.to_string().into()),
372 );
373 }
374 }
375
376 if let Some(user) = self.user.as_ref() {
377 if !log.attributes.contains_key("user.id") {
378 if let Some(id) = user.id.as_ref() {
379 log.attributes
380 .insert("user.id".to_owned(), LogAttribute(id.to_owned().into()));
381 }
382 }
383
384 if !log.attributes.contains_key("user.name") {
385 if let Some(name) = user.username.as_ref() {
386 log.attributes
387 .insert("user.name".to_owned(), LogAttribute(name.to_owned().into()));
388 }
389 }
390
391 if !log.attributes.contains_key("user.email") {
392 if let Some(email) = user.email.as_ref() {
393 log.attributes.insert(
394 "user.email".to_owned(),
395 LogAttribute(email.to_owned().into()),
396 );
397 }
398 }
399 }
400 }
401
402 pub fn set_span(&mut self, span: Option<TransactionOrSpan>) {
404 self.span = Arc::new(span);
405 }
406
407 pub fn get_span(&self) -> Option<TransactionOrSpan> {
409 self.span.as_ref().clone()
410 }
411
412 #[allow(unused_variables)]
413 pub(crate) fn update_session_from_event(&self, event: &Event<'static>) {
414 #[cfg(feature = "release-health")]
415 if let Some(session) = self.session.lock().unwrap().as_mut() {
416 session.update_from_event(event);
417 }
418 }
419
420 pub(crate) fn apply_propagation_context(&self, event: &mut Event<'_>) {
421 if event.contexts.contains_key("trace") {
422 return;
423 }
424
425 let context = TraceContext {
426 trace_id: self.propagation_context.trace_id,
427 span_id: self.propagation_context.span_id,
428 ..Default::default()
429 };
430 event.contexts.insert("trace".into(), context.into());
431 }
432
433 pub fn iter_trace_propagation_headers(&self) -> impl Iterator<Item = TraceHeader> {
435 if let Some(span) = self.get_span() {
436 span.iter_headers()
437 } else {
438 let data = SentryTrace::new(
439 self.propagation_context.trace_id,
440 self.propagation_context.span_id,
441 None,
442 );
443 TraceHeadersIter::new(data.to_string())
444 }
445 }
446}