1use std::borrow::Cow;
2use std::collections::{HashMap, VecDeque};
3use std::fmt;
4use std::panic::RefUnwindSafe;
5#[cfg(feature = "release-health")]
6use std::sync::Mutex;
7use std::sync::{Arc, PoisonError, RwLock};
8
9#[cfg(feature = "metrics")]
10use crate::metrics::{IntoProtocolMetric, MetricTraceInfo};
11use crate::performance::TransactionOrSpan;
12#[cfg(feature = "logs")]
13use crate::protocol::Log;
14#[cfg(any(feature = "logs", feature = "metrics"))]
15use crate::protocol::LogAttribute;
16#[cfg(feature = "metrics")]
17use crate::protocol::Metric;
18use crate::protocol::{
19 Attachment, Breadcrumb, Context, Event, Level, TraceContext, Transaction, User, Value,
20};
21#[cfg(feature = "release-health")]
22use crate::session::Session;
23use crate::{Client, SentryTrace, TraceHeader, TraceHeadersIter};
24
25#[derive(Debug)]
26pub struct Stack {
27 top: StackLayer,
28 layers: Vec<StackLayer>,
29}
30
31type EventProcessor =
32 Arc<dyn Fn(Event<'static>) -> Option<Event<'static>> + Send + Sync + RefUnwindSafe>;
33
34#[derive(Clone, Default)]
53pub struct Scope {
54 pub(crate) level: Option<Level>,
55 pub(crate) fingerprint: Option<Arc<[Cow<'static, str>]>>,
56 pub(crate) transaction: Option<Arc<str>>,
57 pub(crate) breadcrumbs: Arc<VecDeque<Breadcrumb>>,
58 pub(crate) user: Option<Arc<User>>,
59 pub(crate) extra: Arc<HashMap<String, Value>>,
60 pub(crate) tags: Arc<HashMap<String, String>>,
61 pub(crate) contexts: Arc<HashMap<String, Context>>,
62 pub(crate) event_processors: Arc<Vec<EventProcessor>>,
63 #[cfg(feature = "release-health")]
64 pub(crate) session: Arc<Mutex<Option<Session>>>,
65 pub(crate) span: Arc<Option<TransactionOrSpan>>,
66 pub(crate) attachments: Arc<Vec<Attachment>>,
67 pub(crate) propagation_context: SentryTrace,
68}
69
70impl fmt::Debug for Scope {
71 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
72 let mut debug_struct = f.debug_struct("Scope");
73 debug_struct
74 .field("level", &self.level)
75 .field("fingerprint", &self.fingerprint)
76 .field("transaction", &self.transaction)
77 .field("breadcrumbs", &self.breadcrumbs)
78 .field("user", &self.user)
79 .field("extra", &self.extra)
80 .field("tags", &self.tags)
81 .field("contexts", &self.contexts)
82 .field("event_processors", &self.event_processors.len());
83
84 #[cfg(feature = "release-health")]
85 debug_struct.field("session", &self.session);
86
87 debug_struct
88 .field("span", &self.span)
89 .field("attachments", &self.attachments.len())
90 .field("propagation_context", &self.propagation_context)
91 .finish()
92 }
93}
94
95#[derive(Debug, Clone)]
96pub struct StackLayer {
97 pub client: Option<Arc<Client>>,
98 pub scope: Arc<Scope>,
99}
100
101impl Stack {
102 pub fn from_client_and_scope(client: Option<Arc<Client>>, scope: Arc<Scope>) -> Stack {
103 Stack {
104 top: StackLayer { client, scope },
105 layers: vec![],
106 }
107 }
108
109 pub fn push(&mut self) {
110 let layer = self.top.clone();
111 self.layers.push(layer);
112 }
113
114 pub fn pop(&mut self) {
115 if self.layers.is_empty() {
116 panic!("Pop from empty stack");
117 }
118 self.top = self.layers.pop().unwrap();
119 }
120
121 #[inline(always)]
122 pub fn top(&self) -> &StackLayer {
123 &self.top
124 }
125
126 #[inline(always)]
127 pub fn top_mut(&mut self) -> &mut StackLayer {
128 &mut self.top
129 }
130
131 pub fn depth(&self) -> usize {
132 self.layers.len()
133 }
134}
135
136#[derive(Default)]
143pub struct ScopeGuard(pub(crate) Option<(Arc<RwLock<Stack>>, usize)>);
144
145impl fmt::Debug for ScopeGuard {
146 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
147 write!(f, "ScopeGuard")
148 }
149}
150
151impl Drop for ScopeGuard {
152 fn drop(&mut self) {
153 if let Some((stack, depth)) = self.0.take() {
154 let popped_depth = {
155 let mut stack = stack.write().unwrap_or_else(PoisonError::into_inner);
156 let popped_depth = stack.depth();
157 stack.pop();
158 popped_depth
159 };
160 if popped_depth != depth {
170 panic!("Popped scope guard out of order");
171 }
172 }
173 }
174}
175
176impl Scope {
177 pub fn clear(&mut self) {
183 *self = Default::default();
184 }
185
186 pub fn clear_breadcrumbs(&mut self) {
188 self.breadcrumbs = Default::default();
189 }
190
191 pub fn set_level(&mut self, level: Option<Level>) {
193 self.level = level;
194 }
195
196 pub fn set_fingerprint(&mut self, fingerprint: Option<&[&str]>) {
198 self.fingerprint =
199 fingerprint.map(|fp| fp.iter().map(|s| Cow::Owned((*s).into())).collect())
200 }
201
202 pub fn set_transaction(&mut self, transaction: Option<&str>) {
204 self.transaction = transaction.map(Arc::from);
205 if let Some(name) = transaction {
206 let trx = match self.span.as_ref() {
207 Some(TransactionOrSpan::Span(span)) => &span.transaction,
208 Some(TransactionOrSpan::Transaction(trx)) => &trx.inner,
209 _ => return,
210 };
211
212 if let Some(trx) = trx.lock().unwrap().transaction.as_mut() {
213 trx.name = Some(name.into());
214 }
215 }
216 }
217
218 pub fn set_user(&mut self, user: Option<User>) {
220 self.user = user.map(Arc::new);
221 }
222
223 pub fn user(&self) -> Option<&User> {
225 self.user.as_deref()
226 }
227
228 pub fn set_tag<V: ToString>(&mut self, key: &str, value: V) {
230 Arc::make_mut(&mut self.tags).insert(key.to_string(), value.to_string());
231 }
232
233 pub fn remove_tag(&mut self, key: &str) {
237 Arc::make_mut(&mut self.tags).remove(key);
238 }
239
240 pub fn set_context<C: Into<Context>>(&mut self, key: &str, value: C) {
242 Arc::make_mut(&mut self.contexts).insert(key.to_string(), value.into());
243 }
244
245 pub fn remove_context(&mut self, key: &str) {
247 Arc::make_mut(&mut self.contexts).remove(key);
248 }
249
250 pub fn set_extra(&mut self, key: &str, value: Value) {
252 Arc::make_mut(&mut self.extra).insert(key.to_string(), value);
253 }
254
255 pub fn remove_extra(&mut self, key: &str) {
257 Arc::make_mut(&mut self.extra).remove(key);
258 }
259
260 pub fn add_event_processor<F>(&mut self, f: F)
262 where
263 F: Fn(Event<'static>) -> Option<Event<'static>> + Send + Sync + RefUnwindSafe + 'static,
264 {
265 Arc::make_mut(&mut self.event_processors).push(Arc::new(f));
266 }
267
268 pub fn add_attachment(&mut self, attachment: Attachment) {
270 Arc::make_mut(&mut self.attachments).push(attachment);
271 }
272
273 pub fn clear_attachments(&mut self) {
275 Arc::make_mut(&mut self.attachments).clear();
276 }
277
278 pub fn apply_to_event(&self, mut event: Event<'static>) -> Option<Event<'static>> {
280 if let Some(level) = self.level {
282 event.level = level;
283 }
284
285 if event.user.is_none() {
286 if let Some(user) = self.user.as_deref() {
287 event.user = Some(user.clone());
288 }
289 }
290
291 event.breadcrumbs.extend(self.breadcrumbs.iter().cloned());
292 event
293 .extra
294 .extend(self.extra.iter().map(|(k, v)| (k.to_owned(), v.to_owned())));
295 event
296 .tags
297 .extend(self.tags.iter().map(|(k, v)| (k.to_owned(), v.to_owned())));
298 event.contexts.extend(
299 self.contexts
300 .iter()
301 .map(|(k, v)| (k.to_owned(), v.to_owned())),
302 );
303
304 if let Some(span) = self.span.as_ref() {
305 span.apply_to_event(&mut event);
306 } else {
307 self.apply_propagation_context(&mut event);
308 }
309
310 if event.transaction.is_none() {
311 if let Some(txn) = self.transaction.as_deref() {
312 event.transaction = Some(txn.to_owned());
313 }
314 }
315
316 if event.fingerprint.len() == 1
317 && (event.fingerprint[0] == "{{ default }}" || event.fingerprint[0] == "{{default}}")
318 {
319 if let Some(fp) = self.fingerprint.as_deref() {
320 event.fingerprint = Cow::Owned(fp.to_owned());
321 }
322 }
323
324 for processor in self.event_processors.as_ref() {
325 let id = event.event_id;
326 event = match processor(event) {
327 Some(event) => event,
328 None => {
329 sentry_debug!("event processor dropped event {}", id);
330 return None;
331 }
332 }
333 }
334
335 Some(event)
336 }
337
338 pub fn apply_to_transaction(&self, transaction: &mut Transaction<'static>) {
340 if transaction.user.is_none() {
341 if let Some(user) = self.user.as_deref() {
342 transaction.user = Some(user.clone());
343 }
344 }
345
346 transaction
347 .extra
348 .extend(self.extra.iter().map(|(k, v)| (k.to_owned(), v.to_owned())));
349 transaction
350 .tags
351 .extend(self.tags.iter().map(|(k, v)| (k.to_owned(), v.to_owned())));
352 transaction.contexts.extend(
353 self.contexts
354 .iter()
355 .map(|(k, v)| (k.to_owned(), v.to_owned())),
356 );
357 }
358
359 #[cfg(feature = "logs")]
362 pub fn apply_to_log(&self, log: &mut Log) {
363 if let Some(span) = self.span.as_ref() {
364 log.trace_id = Some(span.get_trace_context().trace_id);
365 } else {
366 log.trace_id = Some(self.propagation_context.trace_id);
367 }
368
369 if !log.attributes.contains_key("sentry.trace.parent_span_id") {
370 if let Some(span) = self.get_span() {
371 let span_id = match span {
372 crate::TransactionOrSpan::Transaction(transaction) => {
373 transaction.get_trace_context().span_id
374 }
375 crate::TransactionOrSpan::Span(span) => span.get_span_id(),
376 };
377 log.attributes.insert(
378 "parent_span_id".to_owned(),
379 LogAttribute(span_id.to_string().into()),
380 );
381 }
382 }
383
384 if let Some(user) = self.user.as_ref() {
385 if !log.attributes.contains_key("user.id") {
386 if let Some(id) = user.id.as_ref() {
387 log.attributes
388 .insert("user.id".to_owned(), LogAttribute(id.to_owned().into()));
389 }
390 }
391
392 if !log.attributes.contains_key("user.name") {
393 if let Some(name) = user.username.as_ref() {
394 log.attributes
395 .insert("user.name".to_owned(), LogAttribute(name.to_owned().into()));
396 }
397 }
398
399 if !log.attributes.contains_key("user.email") {
400 if let Some(email) = user.email.as_ref() {
401 log.attributes.insert(
402 "user.email".to_owned(),
403 LogAttribute(email.to_owned().into()),
404 );
405 }
406 }
407 }
408 }
409
410 #[cfg(feature = "metrics")]
418 pub(crate) fn apply_to_metric<M>(&self, metric: M, send_default_pii: bool) -> Metric
419 where
420 M: IntoProtocolMetric,
421 {
422 let (trace_id, span_id) = match self.get_span() {
423 Some(span) => {
424 let trace_context = span.get_trace_context();
425 let span_id = match span {
426 TransactionOrSpan::Span(span) => span.get_span_id(),
427 TransactionOrSpan::Transaction(_) => trace_context.span_id,
428 };
429
430 (trace_context.trace_id, Some(span_id))
431 }
432 None => (self.propagation_context.trace_id, None),
433 };
434
435 let trace = MetricTraceInfo { trace_id, span_id };
436 let mut metric = metric.into_protocol_metric(trace);
437 let should_add_user_attributes = send_default_pii && !metric.has_any_user_attributes();
438
439 if let Some(user) = should_add_user_attributes
440 .then_some(self.user.as_deref())
441 .flatten()
442 {
443 metric.apply_user_attributes(user);
444 }
445
446 metric
447 }
448
449 pub fn set_span(&mut self, span: Option<TransactionOrSpan>) {
451 self.span = Arc::new(span);
452 }
453
454 pub fn get_span(&self) -> Option<TransactionOrSpan> {
456 self.span.as_ref().clone()
457 }
458
459 #[allow(unused_variables)]
460 pub(crate) fn update_session_from_event(&self, event: &Event<'static>) {
461 #[cfg(feature = "release-health")]
462 if let Some(session) = self.session.lock().unwrap().as_mut() {
463 session.update_from_event(event);
464 }
465 }
466
467 pub(crate) fn apply_propagation_context(&self, event: &mut Event<'_>) {
468 if event.contexts.contains_key("trace") {
469 return;
470 }
471
472 let context = TraceContext {
473 trace_id: self.propagation_context.trace_id,
474 span_id: self.propagation_context.span_id,
475 ..Default::default()
476 };
477 event.contexts.insert("trace".into(), context.into());
478 }
479
480 pub fn iter_trace_propagation_headers(&self) -> impl Iterator<Item = TraceHeader> {
482 if let Some(span) = self.get_span() {
483 span.iter_headers()
484 } else {
485 let data = SentryTrace::new(
486 self.propagation_context.trace_id,
487 self.propagation_context.span_id,
488 None,
489 );
490 TraceHeadersIter::new(data.to_string())
491 }
492 }
493}
494
495#[cfg(feature = "metrics")]
496trait MetricExt {
497 fn insert_attribute<K, V>(&mut self, key: K, value: V)
498 where
499 K: Into<Cow<'static, str>>,
500 V: Into<Value>;
501
502 fn attribute(&self, key: &str) -> Option<&LogAttribute>;
503
504 fn apply_user_attributes(&mut self, user: &User) {
506 [
507 ("user.id", user.id.as_deref()),
508 ("user.name", user.username.as_deref()),
509 ("user.email", user.email.as_deref()),
510 ]
511 .into_iter()
512 .flat_map(|(attribute, value)| value.map(|v| (attribute, v)))
513 .for_each(|(attribute, value)| self.insert_attribute(attribute, value));
514 }
515
516 fn has_any_user_attributes(&self) -> bool {
518 ["user.id", "user.name", "user.email"]
519 .into_iter()
520 .any(|key| self.attribute(key).is_some())
521 }
522}
523
524#[cfg(feature = "metrics")]
525impl MetricExt for Metric {
526 fn insert_attribute<K, V>(&mut self, key: K, value: V)
527 where
528 K: Into<Cow<'static, str>>,
529 V: Into<Value>,
530 {
531 self.attributes
532 .insert(key.into(), LogAttribute(value.into()));
533 }
534
535 fn attribute(&self, key: &str) -> Option<&LogAttribute> {
536 self.attributes.get(key)
537 }
538}