#[allow(unused)]
use std::cell::{Cell, UnsafeCell};
use std::iter;
#[allow(unused)]
use std::sync::{Arc, Mutex, RwLock, RwLockReadGuard, RwLockWriteGuard, TryLockError};
#[allow(unused)]
use std::thread;
use std::time::Duration;
#[cfg(feature = "with_client_implementation")]
use fragile::SemiSticky;
#[allow(unused)]
use std::sync::atomic::{AtomicBool, Ordering};
#[cfg(feature = "with_client_implementation")]
use client::Client;
use protocol::{Breadcrumb, Event, Level};
use scope::{Scope, ScopeGuard};
#[cfg(feature = "with_client_implementation")]
use scope::{Stack, StackLayerToken};
use uuid::Uuid;
#[cfg(feature = "with_client_implementation")]
lazy_static! {
static ref PROCESS_HUB: (Arc<Hub>, thread::ThreadId) = (
Arc::new(Hub::new(None, Arc::new(Default::default()))),
thread::current().id()
);
}
#[cfg(feature = "with_client_implementation")]
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());
}
pub trait IntoBreadcrumbs {
type Output: Iterator<Item = Breadcrumb>;
fn into_breadcrumbs(self) -> Self::Output;
}
impl IntoBreadcrumbs for Breadcrumb {
type Output = iter::Once<Breadcrumb>;
fn into_breadcrumbs(self) -> Self::Output {
return iter::once(self);
}
}
impl IntoBreadcrumbs for Vec<Breadcrumb> {
type Output = ::std::vec::IntoIter<Breadcrumb>;
fn into_breadcrumbs(self) -> Self::Output {
self.into_iter()
}
}
impl IntoBreadcrumbs for Option<Breadcrumb> {
type Output = ::std::option::IntoIter<Breadcrumb>;
fn into_breadcrumbs(self) -> Self::Output {
self.into_iter()
}
}
impl<F: FnOnce() -> I, I: IntoBreadcrumbs> IntoBreadcrumbs for F {
type Output = I::Output;
fn into_breadcrumbs(self) -> Self::Output {
self().into_breadcrumbs()
}
}
#[cfg(feature = "with_client_implementation")]
pub(crate) trait EventProcessorFactoryFn {
fn call(self: Box<Self>) -> Box<Fn(&mut Event) + Send + Sync>;
}
#[cfg(feature = "with_client_implementation")]
pub(crate) enum PendingProcessor {
Send(Box<EventProcessorFactoryFn + Send + Sync>),
NonSend(SemiSticky<Box<EventProcessorFactoryFn>>),
}
#[cfg(feature = "with_client_implementation")]
impl<F: 'static + FnOnce() -> Box<Fn(&mut Event) + Send + Sync>> EventProcessorFactoryFn for F {
fn call(self: Box<Self>) -> Box<Fn(&mut Event) + Send + Sync> {
let this: Self = *self;
this()
}
}
#[cfg(feature = "with_client_implementation")]
impl PendingProcessor {
fn is_safe_call(&self) -> bool {
match *self {
PendingProcessor::Send(..) => true,
PendingProcessor::NonSend(ref f) => f.is_valid(),
}
}
fn call(self) -> Box<Fn(&mut Event) + Send + Sync> {
match self {
PendingProcessor::Send(f) => f.call(),
PendingProcessor::NonSend(f) => f.into_inner().call(),
}
}
}
#[cfg(feature = "with_client_implementation")]
struct HubImpl {
stack: RwLock<Stack>,
pending_processors: Mutex<Vec<PendingProcessor>>,
has_pending_processors: AtomicBool,
}
#[cfg(feature = "with_client_implementation")]
impl HubImpl {
fn with<F: FnOnce(&Stack) -> R, R>(&self, f: F) -> R {
let guard = self.stack.read().unwrap_or_else(|x| x.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(|x| x.into_inner());
f(&mut *guard)
}
fn with_processors_mut<F: FnOnce(&mut Vec<PendingProcessor>) -> R, R>(&self, f: F) -> R {
f(&mut *self.pending_processors
.lock()
.unwrap_or_else(|x| x.into_inner()))
}
fn is_active_and_usage_safe(&self) -> bool {
let guard = match self.stack.try_read() {
Err(TryLockError::Poisoned(err)) => err.into_inner(),
Err(TryLockError::WouldBlock) => return false,
Ok(guard) => guard,
};
guard.top().client.is_some()
}
}
pub struct Hub {
#[cfg(feature = "with_client_implementation")]
inner: HubImpl,
}
impl Hub {
#[cfg(feature = "with_client_implementation")]
pub fn new(client: Option<Arc<Client>>, scope: Arc<Scope>) -> Hub {
Hub {
inner: HubImpl {
stack: RwLock::new(Stack::from_client_and_scope(client, scope)),
pending_processors: Mutex::new(vec![]),
has_pending_processors: AtomicBool::new(false),
},
}
}
#[cfg(feature = "with_client_implementation")]
pub fn new_from_top<H: AsRef<Hub>>(other: H) -> Hub {
let hub = other.as_ref();
hub.flush_pending_processors();
hub.inner.with(|stack| {
let top = stack.top();
Hub::new(top.client.clone(), top.scope.clone())
})
}
#[cfg(feature = "with_client_implementation")]
pub fn current() -> Arc<Hub> {
Hub::with(|hub| hub.clone())
}
#[cfg(feature = "with_client_implementation")]
pub fn main() -> Arc<Hub> {
PROCESS_HUB.0.clone()
}
#[cfg(feature = "with_client_implementation")]
pub fn with<F, R>(f: F) -> R
where
F: FnOnce(&Arc<Hub>) -> R,
{
if USE_PROCESS_HUB.with(|x| x.get()) {
f(&PROCESS_HUB.0)
} else {
THREAD_HUB.with(|stack| unsafe {
let ptr = stack.get();
f(&*ptr)
})
}
}
#[allow(unused_variables)]
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 = "with_client_implementation")]
pub fn run<F: FnOnce() -> R, R>(hub: Arc<Hub>, f: F) -> R {
hub.flush_pending_processors();
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,
}
}
}
}
#[allow(unused_variables)]
pub fn capture_event(&self, event: Event<'static>) -> Uuid {
self.flush_pending_processors();
with_client_impl! {{
self.inner.with(|stack| {
let top = stack.top();
if let Some(ref client) = top.client {
client.capture_event(event, Some(&top.scope))
} else {
Default::default()
}
})
}}
}
pub fn capture_message(&self, msg: &str, level: Level) -> Uuid {
self.capture_event(Event {
message: Some(msg.to_string()),
level,
..Default::default()
})
}
#[allow(unused_variables)]
pub fn drain_events(&self, timeout: Option<Duration>) {
with_client_impl! {{
if let Some(ref client) = self.client() {
client.drain_events(timeout);
}
}}
}
#[cfg(feature = "with_client_implementation")]
pub fn client(&self) -> Option<Arc<Client>> {
with_client_impl! {{
self.inner.with(|stack| {
stack.top().client.clone()
})
}}
}
#[cfg(feature = "with_client_implementation")]
pub fn bind_client(&self, client: Option<Arc<Client>>) {
with_client_impl! {{
self.inner.with_mut(|stack| {
stack.top_mut().client = client;
})
}}
}
pub fn push_scope(&self) -> ScopeGuard {
self.flush_pending_processors();
with_client_impl! {{
self.inner.with_mut(|stack| {
stack.push();
ScopeGuard(Some(stack.layer_token()))
})
}}
}
#[allow(unused_variables)]
pub fn configure_scope<F, R>(&self, f: F) -> R
where
R: Default,
F: FnOnce(&mut Scope) -> R,
{
with_client_impl! {{
let (new_scope, rv) = self.with_scope(|scope| {
let mut new_scope = (**scope).clone();
let rv = f(&mut new_scope);
(new_scope, rv)
});
self.with_scope_mut(|ptr| *ptr = new_scope);
rv
}}
}
#[allow(unused_variables)]
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 limit = client.options().max_breadcrumbs;
for breadcrumb in breadcrumb.into_breadcrumbs() {
scope.breadcrumbs = scope.breadcrumbs.push_back(breadcrumb);
while scope.breadcrumbs.len() > limit {
if let Some((_, new)) = scope.breadcrumbs.pop_front() {
scope.breadcrumbs = new;
}
}
}
}
})
}}
}
#[allow(unused)]
pub fn add_event_processor<F: FnOnce() -> Box<Fn(&mut Event) + Send + Sync> + 'static>(
&self,
f: F,
) {
with_client_impl! {{
self.inner.with_processors_mut(|pending| {
pending.push(PendingProcessor::NonSend(SemiSticky::new(
Box::new(f) as Box<EventProcessorFactoryFn>)));
});
self.inner.has_pending_processors.store(true, Ordering::Release);
}}
}
#[allow(unused)]
pub fn add_send_event_processor<
F: FnOnce() -> Box<Fn(&mut Event) + Send + Sync> + Send + Sync + 'static,
>(
&self,
f: F,
) {
with_client_impl! {{
use std::mem;
self.inner.with_processors_mut(|pending| {
pending.push(PendingProcessor::Send(
Box::new(f) as Box<EventProcessorFactoryFn + Send + Sync>));
});
self.inner.has_pending_processors.store(true, Ordering::Release);
}}
}
fn flush_pending_processors(&self) {
with_client_impl! {{
if !self.inner.has_pending_processors.load(Ordering::Acquire) {
return;
}
let mut new_processors = vec![];
let any_left = self.inner.with_processors_mut(|vec| {
let mut i = 0;
while i < vec.len() {
if !vec[i].is_safe_call() {
i += 1;
} else {
new_processors.push(vec.remove(i).call());
}
}
!vec.is_empty()
});
self.inner.has_pending_processors.store(any_left, Ordering::Release);
if !new_processors.is_empty() {
self.configure_scope(|scope| {
for func in new_processors.into_iter() {
scope.event_processors = scope.event_processors.push_back(func);
}
});
}
}}
}
#[cfg(feature = "with_client_implementation")]
pub(crate) fn is_active_and_usage_safe(&self) -> bool {
self.inner.is_active_and_usage_safe()
}
#[cfg(feature = "with_client_implementation")]
pub(crate) fn with_scope<F: FnOnce(&Arc<Scope>) -> R, R>(&self, f: F) -> R {
self.inner.with(|stack| f(&stack.top().scope))
}
#[cfg(feature = "with_client_implementation")]
pub(crate) fn with_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)))
}
#[cfg(feature = "with_client_implementation")]
pub(crate) fn pop_scope(&self, token: StackLayerToken) {
with_client_impl! {{
self.inner.with_mut(|stack| {
if stack.layer_token() != token {
panic!("Current active stack does not match scope guard");
}
stack.pop();
})
}}
}
}