use std::borrow::Cow;
use std::collections::{HashMap, VecDeque};
use std::fmt;
#[cfg(feature = "release-health")]
use std::sync::Mutex;
use std::sync::{Arc, PoisonError, RwLock};
use crate::performance::TransactionOrSpan;
use crate::protocol::{
Attachment, Breadcrumb, Context, Event, Level, TraceContext, Transaction, User, Value,
};
#[cfg(feature = "logs")]
use crate::protocol::{Log, LogAttribute};
#[cfg(feature = "release-health")]
use crate::session::Session;
use crate::{Client, SentryTrace, TraceHeader, TraceHeadersIter};
#[derive(Debug)]
pub struct Stack {
top: StackLayer,
layers: Vec<StackLayer>,
}
pub type EventProcessor = Arc<dyn Fn(Event<'static>) -> Option<Event<'static>> + Send + Sync>;
#[derive(Clone, Default)]
pub struct Scope {
pub(crate) level: Option<Level>,
pub(crate) fingerprint: Option<Arc<[Cow<'static, str>]>>,
pub(crate) transaction: Option<Arc<str>>,
pub(crate) breadcrumbs: Arc<VecDeque<Breadcrumb>>,
pub(crate) user: Option<Arc<User>>,
pub(crate) extra: Arc<HashMap<String, Value>>,
pub(crate) tags: Arc<HashMap<String, String>>,
pub(crate) contexts: Arc<HashMap<String, Context>>,
pub(crate) event_processors: Arc<Vec<EventProcessor>>,
#[cfg(feature = "release-health")]
pub(crate) session: Arc<Mutex<Option<Session>>>,
pub(crate) span: Arc<Option<TransactionOrSpan>>,
pub(crate) attachments: Arc<Vec<Attachment>>,
pub(crate) propagation_context: SentryTrace,
}
impl fmt::Debug for Scope {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let mut debug_struct = f.debug_struct("Scope");
debug_struct
.field("level", &self.level)
.field("fingerprint", &self.fingerprint)
.field("transaction", &self.transaction)
.field("breadcrumbs", &self.breadcrumbs)
.field("user", &self.user)
.field("extra", &self.extra)
.field("tags", &self.tags)
.field("contexts", &self.contexts)
.field("event_processors", &self.event_processors.len());
#[cfg(feature = "release-health")]
debug_struct.field("session", &self.session);
debug_struct
.field("span", &self.span)
.field("attachments", &self.attachments.len())
.field("propagation_context", &self.propagation_context)
.finish()
}
}
#[derive(Debug, Clone)]
pub struct StackLayer {
pub client: Option<Arc<Client>>,
pub scope: Arc<Scope>,
}
impl Stack {
pub fn from_client_and_scope(client: Option<Arc<Client>>, scope: Arc<Scope>) -> Stack {
Stack {
top: StackLayer { client, scope },
layers: vec![],
}
}
pub fn push(&mut self) {
let layer = self.top.clone();
self.layers.push(layer);
}
pub fn pop(&mut self) {
if self.layers.is_empty() {
panic!("Pop from empty stack");
}
self.top = self.layers.pop().unwrap();
}
#[inline(always)]
pub fn top(&self) -> &StackLayer {
&self.top
}
#[inline(always)]
pub fn top_mut(&mut self) -> &mut StackLayer {
&mut self.top
}
pub fn depth(&self) -> usize {
self.layers.len()
}
}
#[derive(Default)]
pub struct ScopeGuard(pub(crate) Option<(Arc<RwLock<Stack>>, usize)>);
impl fmt::Debug for ScopeGuard {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "ScopeGuard")
}
}
impl Drop for ScopeGuard {
fn drop(&mut self) {
if let Some((stack, depth)) = self.0.take() {
let popped_depth = {
let mut stack = stack.write().unwrap_or_else(PoisonError::into_inner);
let popped_depth = stack.depth();
stack.pop();
popped_depth
};
if popped_depth != depth {
panic!("Popped scope guard out of order");
}
}
}
}
impl Scope {
pub fn clear(&mut self) {
*self = Default::default();
}
pub fn clear_breadcrumbs(&mut self) {
self.breadcrumbs = Default::default();
}
pub fn set_level(&mut self, level: Option<Level>) {
self.level = level;
}
pub fn set_fingerprint(&mut self, fingerprint: Option<&[&str]>) {
self.fingerprint =
fingerprint.map(|fp| fp.iter().map(|s| Cow::Owned((*s).into())).collect())
}
pub fn set_transaction(&mut self, transaction: Option<&str>) {
self.transaction = transaction.map(Arc::from);
if let Some(name) = transaction {
let trx = match self.span.as_ref() {
Some(TransactionOrSpan::Span(span)) => &span.transaction,
Some(TransactionOrSpan::Transaction(trx)) => &trx.inner,
_ => return,
};
if let Some(trx) = trx.lock().unwrap().transaction.as_mut() {
trx.name = Some(name.into());
}
}
}
pub fn set_user(&mut self, user: Option<User>) {
self.user = user.map(Arc::new);
}
pub fn user(&self) -> Option<&User> {
self.user.as_deref()
}
pub fn set_tag<V: ToString>(&mut self, key: &str, value: V) {
Arc::make_mut(&mut self.tags).insert(key.to_string(), value.to_string());
}
pub fn remove_tag(&mut self, key: &str) {
Arc::make_mut(&mut self.tags).remove(key);
}
pub fn set_context<C: Into<Context>>(&mut self, key: &str, value: C) {
Arc::make_mut(&mut self.contexts).insert(key.to_string(), value.into());
}
pub fn remove_context(&mut self, key: &str) {
Arc::make_mut(&mut self.contexts).remove(key);
}
pub fn set_extra(&mut self, key: &str, value: Value) {
Arc::make_mut(&mut self.extra).insert(key.to_string(), value);
}
pub fn remove_extra(&mut self, key: &str) {
Arc::make_mut(&mut self.extra).remove(key);
}
pub fn add_event_processor<F>(&mut self, f: F)
where
F: Fn(Event<'static>) -> Option<Event<'static>> + Send + Sync + 'static,
{
Arc::make_mut(&mut self.event_processors).push(Arc::new(f));
}
pub fn add_attachment(&mut self, attachment: Attachment) {
Arc::make_mut(&mut self.attachments).push(attachment);
}
pub fn clear_attachments(&mut self) {
Arc::make_mut(&mut self.attachments).clear();
}
pub fn apply_to_event(&self, mut event: Event<'static>) -> Option<Event<'static>> {
if let Some(level) = self.level {
event.level = level;
}
if event.user.is_none() {
if let Some(user) = self.user.as_deref() {
event.user = Some(user.clone());
}
}
event.breadcrumbs.extend(self.breadcrumbs.iter().cloned());
event
.extra
.extend(self.extra.iter().map(|(k, v)| (k.to_owned(), v.to_owned())));
event
.tags
.extend(self.tags.iter().map(|(k, v)| (k.to_owned(), v.to_owned())));
event.contexts.extend(
self.contexts
.iter()
.map(|(k, v)| (k.to_owned(), v.to_owned())),
);
if let Some(span) = self.span.as_ref() {
span.apply_to_event(&mut event);
} else {
self.apply_propagation_context(&mut event);
}
if event.transaction.is_none() {
if let Some(txn) = self.transaction.as_deref() {
event.transaction = Some(txn.to_owned());
}
}
if event.fingerprint.len() == 1
&& (event.fingerprint[0] == "{{ default }}" || event.fingerprint[0] == "{{default}}")
{
if let Some(fp) = self.fingerprint.as_deref() {
event.fingerprint = Cow::Owned(fp.to_owned());
}
}
for processor in self.event_processors.as_ref() {
let id = event.event_id;
event = match processor(event) {
Some(event) => event,
None => {
sentry_debug!("event processor dropped event {}", id);
return None;
}
}
}
Some(event)
}
pub fn apply_to_transaction(&self, transaction: &mut Transaction<'static>) {
if transaction.user.is_none() {
if let Some(user) = self.user.as_deref() {
transaction.user = Some(user.clone());
}
}
transaction
.extra
.extend(self.extra.iter().map(|(k, v)| (k.to_owned(), v.to_owned())));
transaction
.tags
.extend(self.tags.iter().map(|(k, v)| (k.to_owned(), v.to_owned())));
transaction.contexts.extend(
self.contexts
.iter()
.map(|(k, v)| (k.to_owned(), v.to_owned())),
);
}
#[cfg(feature = "logs")]
pub fn apply_to_log(&self, log: &mut Log) {
if let Some(span) = self.span.as_ref() {
log.trace_id = Some(span.get_trace_context().trace_id);
} else {
log.trace_id = Some(self.propagation_context.trace_id);
}
if !log.attributes.contains_key("sentry.trace.parent_span_id") {
if let Some(span) = self.get_span() {
let span_id = match span {
crate::TransactionOrSpan::Transaction(transaction) => {
transaction.get_trace_context().span_id
}
crate::TransactionOrSpan::Span(span) => span.get_span_id(),
};
log.attributes.insert(
"parent_span_id".to_owned(),
LogAttribute(span_id.to_string().into()),
);
}
}
if let Some(user) = self.user.as_ref() {
if !log.attributes.contains_key("user.id") {
if let Some(id) = user.id.as_ref() {
log.attributes
.insert("user.id".to_owned(), LogAttribute(id.to_owned().into()));
}
}
if !log.attributes.contains_key("user.name") {
if let Some(name) = user.username.as_ref() {
log.attributes
.insert("user.name".to_owned(), LogAttribute(name.to_owned().into()));
}
}
if !log.attributes.contains_key("user.email") {
if let Some(email) = user.email.as_ref() {
log.attributes.insert(
"user.email".to_owned(),
LogAttribute(email.to_owned().into()),
);
}
}
}
}
pub fn set_span(&mut self, span: Option<TransactionOrSpan>) {
self.span = Arc::new(span);
}
pub fn get_span(&self) -> Option<TransactionOrSpan> {
self.span.as_ref().clone()
}
#[allow(unused_variables)]
pub(crate) fn update_session_from_event(&self, event: &Event<'static>) {
#[cfg(feature = "release-health")]
if let Some(session) = self.session.lock().unwrap().as_mut() {
session.update_from_event(event);
}
}
pub(crate) fn apply_propagation_context(&self, event: &mut Event<'_>) {
if event.contexts.contains_key("trace") {
return;
}
let context = TraceContext {
trace_id: self.propagation_context.trace_id,
span_id: self.propagation_context.span_id,
..Default::default()
};
event.contexts.insert("trace".into(), context.into());
}
pub fn iter_trace_propagation_headers(&self) -> impl Iterator<Item = TraceHeader> {
if let Some(span) = self.get_span() {
span.iter_headers()
} else {
let data = SentryTrace::new(
self.propagation_context.trace_id,
self.propagation_context.span_id,
None,
);
TraceHeadersIter::new(data.to_string())
}
}
}