#![warn(missing_docs)]
use std::{
num::NonZeroU64,
sync::{Arc, Mutex, MutexGuard},
thread::{self, ThreadId},
};
use forrust_fire_tree::{
ashes::Ashes,
fire::{self, ForestFire},
};
use thread_local::ThreadLocal;
use tracing::{
Subscriber,
field::{self, ValueSet},
span,
};
pub mod providers;
#[cfg(test)]
mod test;
#[derive(Debug)]
pub enum Fields<'a> {
Full(&'a field::FieldSet),
Iter(field::Iter),
}
#[derive(Debug)]
#[non_exhaustive]
pub struct EventInfo<'a> {
pub is_span: bool,
pub fields: Fields<'a>,
pub metadata: &'static tracing::Metadata<'static>,
pub values_early: Option<&'a ValueSet<'a>>,
}
pub trait EventProvider: 'static {
type Event: Sized;
fn make_event(&mut self, id: usize, info: EventInfo) -> Self::Event;
#[inline]
fn span_enter(&mut self, id: usize, event: &mut Self::Event) {
let _ = (id, event);
}
#[inline]
fn should_span_enter() -> bool {
true
}
#[inline]
fn span_exit(&mut self, id: usize, event: &mut Self::Event) {
let _ = (id, event);
}
#[inline]
fn should_span_exit() -> bool {
true
}
#[inline]
fn make_visitor(&mut self, id: usize, event: &mut Self::Event) -> impl field::Visit {
let _ = (id, event);
struct VisitNoop;
impl field::Visit for VisitNoop {
#[inline]
fn record_debug(&mut self, _: &field::Field, _: &dyn std::fmt::Debug) {}
}
VisitNoop
}
#[inline]
fn should_use_visitor() -> bool {
true
}
fn should_use_visitor_if_values_given(&self) -> bool {
Self::should_use_visitor()
}
}
struct Local {
stack: Vec<fire::BranchId>,
last_using_thread: ThreadId,
}
impl Default for Local {
fn default() -> Self {
Self {
stack: Default::default(),
last_using_thread: thread::current().id(),
}
}
}
struct Inner<P: EventProvider> {
forest: ForestFire<P::Event>,
provider: P,
}
pub struct ForestFireSubscriber<P: EventProvider> {
inner: Mutex<Inner<P>>,
stack: ThreadLocal<Mutex<Local>>,
}
impl<P: EventProvider> ForestFireSubscriber<P> {
pub const fn new(forest: ForestFire<P::Event>, provider: P) -> Self {
Self {
inner: Mutex::new(Inner {
forest,
provider,
}),
stack: ThreadLocal::new(),
}
}
pub fn burn(self) -> AshTrayce<P> {
let inner = mutex_into_inner_ignore_poison(self.inner);
let ash = inner.forest.burn();
AshTrayce {
ash,
provider: inner.provider, }
}
pub fn node_count(&self) -> usize {
self.inner().forest.node_count()
}
pub fn node_count_ex(&mut self) -> usize {
mutex_get_mut_ignore_poison(&mut self.inner).forest.node_count()
}
fn local<'this>(&'this self) -> MutexGuard<'this, Local> {
let mut local = mutex_lock_ignore_poison(self.stack.get_or_default());
let current = thread::current().id();
if local.last_using_thread != current {
local.last_using_thread = current;
local.stack.clear();
}
local
}
fn inner<'this>(&'this self) -> MutexGuard<'this, Inner<P>> {
mutex_lock_ignore_poison(&self.inner)
}
}
impl<P: EventProvider + Default> Default for ForestFireSubscriber<P> {
fn default() -> Self {
Self::new(ForestFire::default(), P::default())
}
}
fn br2sp(branch: fire::BranchId) -> span::Id {
debug_assert!(!branch.is_root());
let v = branch
.value()
.try_into()
.ok()
.and_then(|x: u64| x.checked_add(1))
.map(|x|
unsafe { NonZeroU64::new_unchecked(x)
})
.unwrap_or_else(||
panic!(
"ID overflow: cannot convert branch ID {} (max = {}) to span ID",
branch.value(),
u64::MAX - 1 )
);
span::Id::from_non_zero_u64(v)
}
fn sp2br(span: &span::Id) -> fire::BranchId {
let v = span.into_non_zero_u64().get() - 1; let v = usize::try_from(v)
.unwrap_or_else(|_| panic!("ID overflow: cannot convert span ID {v} (after converting to zero-indexing) to branch ID (max = {})", usize::MAX));
fire::BranchId::new(v)
}
fn ensure_normal<T>(forest: &ForestFire<T>, original: &span::Id, id: fire::BranchId) {
if !forest.exists(id) {
panic!("the provided span ({}) does not refer to an existing node", original.into_u64())
}
if id.is_root() {
panic!("the provided span ({}) refers to root", original.into_u64());
}
}
fn assign_parent(local: &Local, explicit_parent: Option<&span::Id>) -> fire::BranchId {
explicit_parent
.map(sp2br)
.or_else(|| local.stack.last().copied())
.unwrap_or(fire::BranchId::ROOT)
}
impl<P: EventProvider> Subscriber for ForestFireSubscriber<P> {
fn enabled(&self, _: &tracing::Metadata<'_>) -> bool {
true
}
fn new_span(&self, span: &span::Attributes<'_>) -> span::Id {
let local = self.local();
let mut inner = self.inner();
let parent = assign_parent(&local, span.parent());
let id = inner.forest.next_id();
let event = inner.provider.make_event(
id.value(),
EventInfo {
is_span: true,
fields: Fields::Full(span.fields()),
metadata: span.metadata(),
values_early: Some(span.values()),
},
);
inner.forest.branch(parent, event);
if inner.provider.should_use_visitor_if_values_given() {
let inner = &mut *inner; let payload = inner.forest.payload_mut(id);
span.values()
.record(&mut inner.provider.make_visitor(id.value(), payload));
}
br2sp(id)
}
fn record(&self, span: &span::Id, values: &span::Record<'_>) {
if !P::should_use_visitor() {
return;
}
let id = sp2br(span);
let mut inner = self.inner();
let inner = &mut *inner;
ensure_normal(&inner.forest, span, id);
let payload = inner.forest.payload_mut(id);
values.record(&mut inner.provider.make_visitor(id.value(), payload));
}
fn record_follows_from(&self, _span: &span::Id, _follows: &span::Id) {
}
fn event(&self, event: &tracing::Event<'_>) {
let local = self.local();
let mut inner = self.inner();
let id = inner.forest.next_id();
let payload = inner.provider.make_event(
id.value(),
EventInfo {
is_span: false,
fields: Fields::Iter(event.fields()),
metadata: event.metadata(),
values_early: None,
},
);
let parent = assign_parent(&local, event.parent());
inner.forest.branch(parent, payload);
if P::should_use_visitor() {
let inner = &mut *inner;
let payload = inner.forest.payload_mut(id);
event.record(&mut inner.provider.make_visitor(id.value(), payload));
}
}
fn enter(&self, span: &span::Id) {
let br = sp2br(span);
let mut local = self.local();
local.stack.push(br);
drop(local);
if P::should_span_enter() {
let mut inner = self.inner();
ensure_normal(&inner.forest, span, br);
let payload = inner.forest.payload_mut(br);
self.inner().provider.span_enter(br.value(), payload);
}
}
fn exit(&self, span: &span::Id) {
let br = sp2br(span);
let mut local = self.local();
while let Some(removed) = local.stack.pop() {
if removed == br {
break;
}
}
drop(local);
if P::should_span_exit() {
let mut inner = self.inner();
ensure_normal(&inner.forest, span, br);
let payload = inner.forest.payload_mut(br);
self.inner().provider.span_exit(br.value(), payload);
}
}
}
#[non_exhaustive]
#[derive(Debug)]
pub struct AshTrayce<P: EventProvider> {
pub ash: Ashes<P::Event>,
pub provider: P,
}
pub fn nothread_run_forest_ret<Provider: EventProvider + Send, R>(
provider: Provider,
func: impl FnOnce() -> R,
) -> (R, AshTrayce<Provider>)
where
Provider::Event: Send,
{
let fire = ForestFireSubscriber::new(ForestFire::default(), provider);
let fire = Arc::new(fire);
let out = tracing::subscriber::with_default(Arc::clone(&fire), func);
let fire = Arc::into_inner(fire).unwrap_or_else(|| panic!("forest fire escaped"));
let ash = fire.burn();
(out, ash)
}
pub fn nothread_local_run_forest<Provider: EventProvider + Send>(
provider: Provider,
func: impl FnOnce(),
) -> AshTrayce<Provider>
where
Provider::Event: Send,
{
let ((), trayce) = nothread_run_forest_ret(provider, func);
trayce
}
fn mutex_get_mut_ignore_poison<T>(mutex: &mut Mutex<T>) -> &mut T {
match mutex.get_mut() {
Ok(r) => r,
Err(err) => {
err.into_inner()
},
}
}
fn mutex_lock_ignore_poison<T>(mutex: &Mutex<T>) -> MutexGuard<'_, T> {
match mutex.lock() {
Ok(v) => v,
Err(err) => {
mutex.clear_poison();
err.into_inner()
}
}
}
fn mutex_into_inner_ignore_poison<T>(mutex: Mutex<T>) -> T {
match mutex.into_inner() {
Ok(x) => x,
Err(e) => e.into_inner(),
}
}