1#![allow(unused)]
2
3use std::cell::{Cell, UnsafeCell};
4use std::error::Error;
5use std::mem::drop;
6use std::sync::atomic::{AtomicBool, Ordering};
7use std::sync::{Arc, Mutex, PoisonError, RwLock, TryLockError};
8use std::thread;
9use std::time::Duration;
10
11use crate::protocol::{Breadcrumb, Event, Level, SessionStatus};
12use crate::types::Uuid;
13use crate::{event_from_error, Integration, IntoBreadcrumbs, Scope, ScopeGuard};
14#[cfg(feature = "client")]
15use crate::{scope::Stack, session::Session, Client, Envelope};
16
17#[cfg(feature = "client")]
18lazy_static::lazy_static! {
19 static ref PROCESS_HUB: (Arc<Hub>, thread::ThreadId) = (
20 Arc::new(Hub::new(None, Arc::new(Default::default()))),
21 thread::current().id()
22 );
23}
24
25#[cfg(feature = "client")]
26thread_local! {
27 static THREAD_HUB: UnsafeCell<Arc<Hub>> = UnsafeCell::new(
28 Arc::new(Hub::new_from_top(&PROCESS_HUB.0)));
29 static USE_PROCESS_HUB: Cell<bool> = Cell::new(PROCESS_HUB.1 == thread::current().id());
30}
31
32#[cfg(feature = "client")]
33#[derive(Debug)]
34pub(crate) struct HubImpl {
35 stack: Arc<RwLock<Stack>>,
36}
37
38#[cfg(feature = "client")]
39impl HubImpl {
40 pub(crate) fn with<F: FnOnce(&Stack) -> R, R>(&self, f: F) -> R {
41 let guard = self.stack.read().unwrap_or_else(PoisonError::into_inner);
42 f(&*guard)
43 }
44
45 fn with_mut<F: FnOnce(&mut Stack) -> R, R>(&self, f: F) -> R {
46 let mut guard = self.stack.write().unwrap_or_else(PoisonError::into_inner);
47 f(&mut *guard)
48 }
49
50 fn is_active_and_usage_safe(&self) -> bool {
51 let guard = match self.stack.read() {
52 Err(err) => err.into_inner(),
53 Ok(guard) => guard,
54 };
55
56 guard
57 .top()
58 .client
59 .as_ref()
60 .map_or(false, |c| c.is_enabled())
61 }
62}
63
64#[derive(Debug)]
90pub struct Hub {
91 #[cfg(feature = "client")]
92 pub(crate) inner: HubImpl,
93 last_event_id: RwLock<Option<Uuid>>,
94}
95
96impl Hub {
97 #[cfg(feature = "client")]
99 pub fn new(client: Option<Arc<Client>>, scope: Arc<Scope>) -> Hub {
100 Hub {
101 inner: HubImpl {
102 stack: Arc::new(RwLock::new(Stack::from_client_and_scope(client, scope))),
103 },
104 last_event_id: RwLock::new(None),
105 }
106 }
107
108 #[cfg(feature = "client")]
110 pub fn new_from_top<H: AsRef<Hub>>(other: H) -> Hub {
111 let hub = other.as_ref();
112 hub.inner.with(|stack| {
113 let top = stack.top();
114 Hub::new(top.client.clone(), top.scope.clone())
115 })
116 }
117
118 #[cfg(feature = "client")]
127 pub fn current() -> Arc<Hub> {
128 Hub::with(Arc::clone)
129 }
130
131 #[cfg(feature = "client")]
136 pub fn main() -> Arc<Hub> {
137 PROCESS_HUB.0.clone()
138 }
139
140 #[cfg(feature = "client")]
145 pub fn with<F, R>(f: F) -> R
146 where
147 F: FnOnce(&Arc<Hub>) -> R,
148 {
149 if USE_PROCESS_HUB.with(Cell::get) {
150 f(&PROCESS_HUB.0)
151 } else {
152 THREAD_HUB.with(|stack| unsafe {
156 let ptr = stack.get();
157 f(&*ptr)
158 })
159 }
160 }
161
162 pub fn with_active<F, R>(f: F) -> R
168 where
169 F: FnOnce(&Arc<Hub>) -> R,
170 R: Default,
171 {
172 with_client_impl! {{
173 Hub::with(|hub| {
174 if hub.is_active_and_usage_safe() {
175 f(hub)
176 } else {
177 Default::default()
178 }
179 })
180 }}
181 }
182
183 #[cfg(feature = "client")]
185 pub fn run<F: FnOnce() -> R, R>(hub: Arc<Hub>, f: F) -> R {
186 let mut restore_process_hub = false;
187 let did_switch = THREAD_HUB.with(|ctx| unsafe {
188 let ptr = ctx.get();
189 if &**ptr as *const _ == &*hub as *const _ {
190 None
191 } else {
192 USE_PROCESS_HUB.with(|x| {
193 if x.get() {
194 restore_process_hub = true;
195 x.set(false);
196 }
197 });
198 let old = (*ptr).clone();
199 *ptr = hub.clone();
200 Some(old)
201 }
202 });
203
204 match did_switch {
205 None => {
206 f()
209 }
210 Some(old_hub) => {
211 use std::panic;
212
213 let rv = panic::catch_unwind(panic::AssertUnwindSafe(f));
217 THREAD_HUB.with(|ctx| unsafe { *ctx.get() = old_hub });
218 if restore_process_hub {
219 USE_PROCESS_HUB.with(|x| x.set(true));
220 }
221 match rv {
222 Err(err) => panic::resume_unwind(err),
223 Ok(rv) => rv,
224 }
225 }
226 }
227 }
228
229 pub fn with_integration<I, F, R>(&self, f: F) -> R
237 where
238 I: Integration,
239 F: FnOnce(&I) -> R,
240 R: Default,
241 {
242 with_client_impl! {{
243 if let Some(client) = self.client() {
244 if let Some(integration) = client.get_integration::<I>() {
245 return f(integration);
246 }
247 }
248 Default::default()
249 }}
250 }
251
252 pub fn last_event_id(&self) -> Option<Uuid> {
254 *self.last_event_id.read().unwrap()
255 }
256
257 pub fn capture_event(&self, event: Event<'static>) -> Uuid {
264 with_client_impl! {{
265 self.inner.with(|stack| {
266 let top = stack.top();
267 if let Some(ref client) = top.client {
268 let event_id = client.capture_event(event, Some(&top.scope));
269 *self.last_event_id.write().unwrap() = Some(event_id);
270 event_id
271 } else {
272 Default::default()
273 }
274 })
275 }}
276 }
277
278 pub fn capture_message(&self, msg: &str, level: Level) -> Uuid {
283 with_client_impl! {{
284 self.inner.with(|stack| {
285 let top = stack.top();
286 if let Some(ref client) = top.client {
287 let mut event = Event {
288 message: Some(msg.to_string()),
289 level,
290 ..Default::default()
291 };
292 self.capture_event(event)
293 } else {
294 Uuid::nil()
295 }
296 })
297 }}
298 }
299
300 #[cfg(feature = "client")]
302 pub fn client(&self) -> Option<Arc<Client>> {
303 self.inner.with(|stack| stack.top().client.clone())
304 }
305
306 #[cfg(feature = "client")]
308 pub fn bind_client(&self, client: Option<Arc<Client>>) {
309 self.inner.with_mut(|stack| {
310 stack.top_mut().client = client;
311 })
312 }
313
314 pub fn start_session(&self) {
319 with_client_impl! {{
320 self.inner.with_mut(|stack| {
321 let top = stack.top_mut();
322 if let Some(session) = Session::from_stack(top) {
323 let mut scope = Arc::make_mut(&mut top.scope);
326 scope.session = Arc::new(Mutex::new(Some(session)));
327 }
328 })
329 }}
330 }
331
332 pub fn end_session(&self) {
337 self.end_session_with_status(SessionStatus::Exited)
338 }
339 pub fn end_session_with_status(&self, status: SessionStatus) {
344 with_client_impl! {{
345 self.inner.with_mut(|stack| {
346 let top = stack.top_mut();
347 if let Some(mut session) = top.scope.session.lock().unwrap().take() {
349 session.close(status);
350 }
351 })
352 }}
353 }
354
355 pub fn push_scope(&self) -> ScopeGuard {
359 with_client_impl! {{
360 self.inner.with_mut(|stack| {
361 stack.push();
362 ScopeGuard(Some((self.inner.stack.clone(), stack.depth())))
363 })
364 }}
365 }
366
367 pub fn with_scope<C, F, R>(&self, scope_config: C, callback: F) -> R
372 where
373 C: FnOnce(&mut Scope),
374 F: FnOnce() -> R,
375 {
376 #[cfg(feature = "client")]
377 {
378 let _guard = self.push_scope();
379 self.configure_scope(scope_config);
380 callback()
381 }
382 #[cfg(not(feature = "client"))]
383 {
384 let _scope_config = scope_config;
385 callback()
386 }
387 }
388
389 pub fn configure_scope<F, R>(&self, f: F) -> R
394 where
395 R: Default,
396 F: FnOnce(&mut Scope) -> R,
397 {
398 with_client_impl! {{
399 let mut new_scope = self.with_current_scope(|scope| scope.clone());
400 let rv = f(&mut new_scope);
401 self.with_current_scope_mut(|ptr| *ptr = new_scope);
402 rv
403 }}
404 }
405
406 pub fn add_breadcrumb<B: IntoBreadcrumbs>(&self, breadcrumb: B) {
411 with_client_impl! {{
412 self.inner.with_mut(|stack| {
413 let top = stack.top_mut();
414 if let Some(ref client) = top.client {
415 let scope = Arc::make_mut(&mut top.scope);
416 let options = client.options();
417 for breadcrumb in breadcrumb.into_breadcrumbs() {
418 let breadcrumb_opt = match options.before_breadcrumb {
419 Some(ref callback) => callback(breadcrumb),
420 None => Some(breadcrumb)
421 };
422 if let Some(breadcrumb) = breadcrumb_opt {
423 scope.breadcrumbs.push_back(breadcrumb);
424 }
425 while scope.breadcrumbs.len() > options.max_breadcrumbs {
426 scope.breadcrumbs.pop_front();
427 }
428 }
429 }
430 })
431 }}
432 }
433
434 #[cfg(feature = "client")]
435 pub(crate) fn is_active_and_usage_safe(&self) -> bool {
436 self.inner.is_active_and_usage_safe()
437 }
438
439 #[cfg(feature = "client")]
440 pub(crate) fn with_current_scope<F: FnOnce(&Scope) -> R, R>(&self, f: F) -> R {
441 self.inner.with(|stack| f(&stack.top().scope))
442 }
443
444 #[cfg(feature = "client")]
445 pub(crate) fn with_current_scope_mut<F: FnOnce(&mut Scope) -> R, R>(&self, f: F) -> R {
446 self.inner
447 .with_mut(|stack| f(Arc::make_mut(&mut stack.top_mut().scope)))
448 }
449}