#![allow(unused)]
use std::cell::{Cell, UnsafeCell};
use std::error::Error;
use std::mem::drop;
use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::{Arc, Mutex, PoisonError, RwLock, TryLockError};
use std::thread;
use std::time::Duration;
use crate::protocol::{Breadcrumb, Event, Level, SessionStatus};
use crate::session::Session;
use crate::types::Uuid;
use crate::{event_from_error, Integration, IntoBreadcrumbs, Scope, ScopeGuard};
#[cfg(feature = "client")]
use crate::{scope::Stack, Client, Envelope};
#[cfg(feature = "client")]
lazy_static::lazy_static! {
static ref PROCESS_HUB: (Arc<Hub>, thread::ThreadId) = (
Arc::new(Hub::new(None, Arc::new(Default::default()))),
thread::current().id()
);
}
#[cfg(feature = "client")]
thread_local! {
static THREAD_HUB: UnsafeCell<Arc<Hub>> = UnsafeCell::new(
Arc::new(Hub::new_from_top(&PROCESS_HUB.0)));
static USE_PROCESS_HUB: Cell<bool> = Cell::new(PROCESS_HUB.1 == thread::current().id());
}
#[cfg(feature = "client")]
#[derive(Debug)]
pub(crate) struct HubImpl {
stack: Arc<RwLock<Stack>>,
}
#[cfg(feature = "client")]
impl HubImpl {
pub(crate) fn with<F: FnOnce(&Stack) -> R, R>(&self, f: F) -> R {
let guard = self.stack.read().unwrap_or_else(PoisonError::into_inner);
f(&*guard)
}
fn with_mut<F: FnOnce(&mut Stack) -> R, R>(&self, f: F) -> R {
let mut guard = self.stack.write().unwrap_or_else(PoisonError::into_inner);
f(&mut *guard)
}
fn is_active_and_usage_safe(&self) -> bool {
let guard = match self.stack.read() {
Err(err) => err.into_inner(),
Ok(guard) => guard,
};
guard
.top()
.client
.as_ref()
.map_or(false, |c| c.is_enabled())
}
}
#[derive(Debug)]
pub struct Hub {
#[cfg(feature = "client")]
pub(crate) inner: HubImpl,
last_event_id: RwLock<Option<Uuid>>,
}
impl Hub {
#[cfg(feature = "client")]
pub fn new(client: Option<Arc<Client>>, scope: Arc<Scope>) -> Hub {
Hub {
inner: HubImpl {
stack: Arc::new(RwLock::new(Stack::from_client_and_scope(client, scope))),
},
last_event_id: RwLock::new(None),
}
}
#[cfg(feature = "client")]
pub fn new_from_top<H: AsRef<Hub>>(other: H) -> Hub {
let hub = other.as_ref();
hub.inner.with(|stack| {
let top = stack.top();
Hub::new(top.client.clone(), top.scope.clone())
})
}
#[cfg(feature = "client")]
pub fn current() -> Arc<Hub> {
Hub::with(Arc::clone)
}
#[cfg(feature = "client")]
pub fn main() -> Arc<Hub> {
PROCESS_HUB.0.clone()
}
#[cfg(feature = "client")]
pub fn with<F, R>(f: F) -> R
where
F: FnOnce(&Arc<Hub>) -> R,
{
if USE_PROCESS_HUB.with(Cell::get) {
f(&PROCESS_HUB.0)
} else {
THREAD_HUB.with(|stack| unsafe {
let ptr = stack.get();
f(&*ptr)
})
}
}
pub fn with_active<F, R>(f: F) -> R
where
F: FnOnce(&Arc<Hub>) -> R,
R: Default,
{
with_client_impl! {{
Hub::with(|hub| {
if hub.is_active_and_usage_safe() {
f(hub)
} else {
Default::default()
}
})
}}
}
#[cfg(feature = "client")]
pub fn run<F: FnOnce() -> R, R>(hub: Arc<Hub>, f: F) -> R {
let mut restore_process_hub = false;
let did_switch = THREAD_HUB.with(|ctx| unsafe {
let ptr = ctx.get();
if &**ptr as *const _ == &*hub as *const _ {
None
} else {
USE_PROCESS_HUB.with(|x| {
if x.get() {
restore_process_hub = true;
x.set(false);
}
});
let old = (*ptr).clone();
*ptr = hub.clone();
Some(old)
}
});
match did_switch {
None => {
f()
}
Some(old_hub) => {
use std::panic;
let rv = panic::catch_unwind(panic::AssertUnwindSafe(f));
THREAD_HUB.with(|ctx| unsafe { *ctx.get() = old_hub });
if restore_process_hub {
USE_PROCESS_HUB.with(|x| x.set(true));
}
match rv {
Err(err) => panic::resume_unwind(err),
Ok(rv) => rv,
}
}
}
}
pub fn with_integration<I, F, R>(&self, f: F) -> R
where
I: Integration,
F: FnOnce(&I) -> R,
R: Default,
{
with_client_impl! {{
if let Some(client) = self.client() {
if let Some(integration) = client.get_integration::<I>() {
return f(integration);
}
}
Default::default()
}}
}
pub fn last_event_id(&self) -> Option<Uuid> {
*self.last_event_id.read().unwrap()
}
pub fn capture_event(&self, event: Event<'static>) -> Uuid {
with_client_impl! {{
self.inner.with(|stack| {
let top = stack.top();
if let Some(ref client) = top.client {
let event_id = client.capture_event(event, Some(&top.scope));
*self.last_event_id.write().unwrap() = Some(event_id);
event_id
} else {
Default::default()
}
})
}}
}
pub fn capture_message(&self, msg: &str, level: Level) -> Uuid {
with_client_impl! {{
self.inner.with(|stack| {
let top = stack.top();
if let Some(ref client) = top.client {
let mut event = Event {
message: Some(msg.to_string()),
level,
..Default::default()
};
self.capture_event(event)
} else {
Uuid::nil()
}
})
}}
}
#[cfg(feature = "client")]
pub fn client(&self) -> Option<Arc<Client>> {
self.inner.with(|stack| stack.top().client.clone())
}
#[cfg(feature = "client")]
pub fn bind_client(&self, client: Option<Arc<Client>>) {
self.inner.with_mut(|stack| {
stack.top_mut().client = client;
})
}
pub fn start_session(&self) {
with_client_impl! {{
self.inner.with_mut(|stack| {
let top = stack.top_mut();
if let Some(session) = Session::from_stack(top) {
let mut scope = Arc::make_mut(&mut top.scope);
scope.session = Arc::new(Mutex::new(Some(session)));
}
})
}}
}
pub fn end_session(&self) {
with_client_impl! {{
self.inner.with_mut(|stack| {
let top = stack.top_mut();
if let Some(mut session) = top.scope.session.lock().unwrap().take() {
session.close();
if let Some(item) = session.create_envelope_item() {
let mut envelope = Envelope::new();
envelope.add_item(item);
if let Some(ref client) = top.client {
client.capture_envelope(envelope);
}
}
}
})
}}
}
pub fn push_scope(&self) -> ScopeGuard {
with_client_impl! {{
self.inner.with_mut(|stack| {
stack.push();
ScopeGuard(Some((self.inner.stack.clone(), stack.depth())))
})
}}
}
pub fn with_scope<C, F, R>(&self, scope_config: C, callback: F) -> R
where
C: FnOnce(&mut Scope),
F: FnOnce() -> R,
{
#[cfg(feature = "client")]
{
let _guard = self.push_scope();
self.configure_scope(scope_config);
callback()
}
#[cfg(not(feature = "client"))]
{
let _scope_config = scope_config;
callback()
}
}
pub fn configure_scope<F, R>(&self, f: F) -> R
where
R: Default,
F: FnOnce(&mut Scope) -> R,
{
with_client_impl! {{
let mut new_scope = self.with_current_scope(|scope| scope.clone());
let rv = f(&mut new_scope);
self.with_current_scope_mut(|ptr| *ptr = new_scope);
rv
}}
}
pub fn add_breadcrumb<B: IntoBreadcrumbs>(&self, breadcrumb: B) {
with_client_impl! {{
self.inner.with_mut(|stack| {
let top = stack.top_mut();
if let Some(ref client) = top.client {
let scope = Arc::make_mut(&mut top.scope);
let options = client.options();
for breadcrumb in breadcrumb.into_breadcrumbs() {
let breadcrumb_opt = match options.before_breadcrumb {
Some(ref callback) => callback(breadcrumb),
None => Some(breadcrumb)
};
if let Some(breadcrumb) = breadcrumb_opt {
scope.breadcrumbs.push_back(breadcrumb);
}
while scope.breadcrumbs.len() > options.max_breadcrumbs {
scope.breadcrumbs.pop_front();
}
}
}
})
}}
}
#[cfg(feature = "client")]
pub(crate) fn is_active_and_usage_safe(&self) -> bool {
self.inner.is_active_and_usage_safe()
}
#[cfg(feature = "client")]
pub(crate) fn with_current_scope<F: FnOnce(&Scope) -> R, R>(&self, f: F) -> R {
self.inner.with(|stack| f(&stack.top().scope))
}
#[cfg(feature = "client")]
pub(crate) fn with_current_scope_mut<F: FnOnce(&mut Scope) -> R, R>(&self, f: F) -> R {
self.inner
.with_mut(|stack| f(Arc::make_mut(&mut stack.top_mut().scope)))
}
}