ckb_sentry_core/scope/
real.rs1use std::borrow::Cow;
2use std::collections::{HashMap, VecDeque};
3use std::fmt;
4use std::sync::{Arc, Mutex, PoisonError, RwLock};
5
6use crate::protocol::{Breadcrumb, Context, Event, Level, User, Value};
7use crate::session::Session;
8use crate::Client;
9
10#[derive(Debug)]
11pub struct Stack {
12 layers: Vec<StackLayer>,
13}
14
15pub type EventProcessor = Box<dyn Fn(Event<'static>) -> Option<Event<'static>> + Send + Sync>;
16
17#[derive(Clone)]
36pub struct Scope {
37 pub(crate) level: Option<Level>,
38 pub(crate) fingerprint: Option<Arc<Vec<Cow<'static, str>>>>,
39 pub(crate) transaction: Option<Arc<String>>,
40 pub(crate) breadcrumbs: VecDeque<Breadcrumb>,
41 pub(crate) user: Option<Arc<User>>,
42 pub(crate) extra: HashMap<String, Value>,
43 pub(crate) tags: HashMap<String, String>,
44 pub(crate) contexts: HashMap<String, Context>,
45 pub(crate) event_processors: VecDeque<Arc<EventProcessor>>,
46 pub(crate) session: Arc<Mutex<Option<Session>>>,
47}
48
49impl fmt::Debug for Scope {
50 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
51 f.debug_struct("Scope")
52 .field("level", &self.level)
53 .field("fingerprint", &self.fingerprint)
54 .field("transaction", &self.transaction)
55 .field("breadcrumbs", &self.breadcrumbs)
56 .field("user", &self.user)
57 .field("extra", &self.extra)
58 .field("tags", &self.tags)
59 .field("contexts", &self.contexts)
60 .field("event_processors", &self.event_processors.len())
61 .field("session", &self.session)
62 .finish()
63 }
64}
65
66impl Default for Scope {
67 fn default() -> Scope {
68 Scope {
69 level: None,
70 fingerprint: None,
71 transaction: None,
72 breadcrumbs: Default::default(),
73 user: None,
74 extra: Default::default(),
75 tags: Default::default(),
76 contexts: Default::default(),
77 event_processors: Default::default(),
78 session: Default::default(),
79 }
80 }
81}
82
83#[derive(Debug, Clone)]
84pub struct StackLayer {
85 pub client: Option<Arc<Client>>,
86 pub scope: Arc<Scope>,
87}
88
89impl Stack {
90 pub fn from_client_and_scope(client: Option<Arc<Client>>, scope: Arc<Scope>) -> Stack {
91 Stack {
92 layers: vec![StackLayer { client, scope }],
93 }
94 }
95
96 pub fn push(&mut self) {
97 let layer = self.layers[self.layers.len() - 1].clone();
98 self.layers.push(layer);
99 }
100
101 pub fn pop(&mut self) {
102 if self.layers.len() <= 1 {
103 panic!("Pop from empty stack");
104 }
105 self.layers.pop().unwrap();
106 }
107
108 pub fn top(&self) -> &StackLayer {
109 &self.layers[self.layers.len() - 1]
110 }
111
112 pub fn top_mut(&mut self) -> &mut StackLayer {
113 let top = self.layers.len() - 1;
114 &mut self.layers[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 mut stack = stack.write().unwrap_or_else(PoisonError::into_inner);
141 if stack.depth() != depth {
142 panic!("Tried to pop guards out of order");
143 }
144 stack.pop();
145 }
146 }
147}
148
149impl Scope {
150 pub fn clear(&mut self) {
156 *self = Default::default();
157 }
158
159 pub fn clear_breadcrumbs(&mut self) {
161 self.breadcrumbs.clear();
162 }
163
164 pub fn set_level(&mut self, level: Option<Level>) {
166 self.level = level;
167 }
168
169 pub fn set_fingerprint(&mut self, fingerprint: Option<&[&str]>) {
171 self.fingerprint = fingerprint
172 .map(|fp| Arc::new(fp.iter().map(|x| Cow::Owned((*x).to_string())).collect()))
173 }
174
175 pub fn set_transaction(&mut self, transaction: Option<&str>) {
177 self.transaction = transaction.map(|txn| Arc::new(txn.to_string()));
178 }
179
180 pub fn set_user(&mut self, user: Option<User>) {
182 self.user = user.map(Arc::new);
183 }
184
185 #[allow(clippy::needless_pass_by_value)]
187 pub fn set_tag<V: ToString>(&mut self, key: &str, value: V) {
188 self.tags.insert(key.to_string(), value.to_string());
189 }
190
191 pub fn remove_tag(&mut self, key: &str) {
193 self.tags.remove(key);
194 }
195
196 pub fn set_context<C: Into<Context>>(&mut self, key: &str, value: C) {
198 self.contexts.insert(key.to_string(), value.into());
199 }
200
201 pub fn remove_context(&mut self, key: &str) {
203 self.contexts.remove(key);
204 }
205
206 pub fn set_extra(&mut self, key: &str, value: Value) {
208 self.extra.insert(key.to_string(), value);
209 }
210
211 pub fn remove_extra(&mut self, key: &str) {
213 self.extra.remove(key);
214 }
215
216 pub fn add_event_processor(
218 &mut self,
219 f: Box<dyn Fn(Event<'static>) -> Option<Event<'static>> + Send + Sync>,
220 ) {
221 self.event_processors.push_back(Arc::new(f));
222 }
223
224 #[allow(clippy::cognitive_complexity)]
226 pub fn apply_to_event(&self, mut event: Event<'static>) -> Option<Event<'static>> {
227 if self.level.is_some() {
229 event.level = self.level.unwrap();
230 }
231
232 if event.user.is_none() {
233 if let Some(ref user) = self.user {
234 event.user = Some((**user).clone());
235 }
236 }
237
238 event.breadcrumbs.extend(self.breadcrumbs.clone());
239 event.extra.extend(self.extra.clone());
240 event.tags.extend(self.tags.clone());
241 event.contexts.extend(self.contexts.clone());
242
243 if event.transaction.is_none() {
244 if let Some(ref txn) = self.transaction {
245 event.transaction = Some((**txn).clone());
246 }
247 }
248
249 if event.fingerprint.len() == 1
250 && (event.fingerprint[0] == "{{ default }}" || event.fingerprint[0] == "{{default}}")
251 {
252 if let Some(ref fp) = self.fingerprint {
253 event.fingerprint = Cow::Owned((**fp).clone());
254 }
255 }
256
257 for processor in &self.event_processors {
258 let id = event.event_id;
259 event = match processor(event) {
260 Some(event) => event,
261 None => {
262 sentry_debug!("event processor dropped event {}", id);
263 return None;
264 }
265 }
266 }
267
268 Some(event)
269 }
270
271 pub(crate) fn update_session_from_event(&self, event: &Event<'static>) {
272 if let Some(session) = self.session.lock().unwrap().as_mut() {
273 session.update_from_event(event);
274 }
275 }
276}