extern crate alloc;
use alloc::string::ToString;
use alloc::sync::{Arc, Weak};
use alloc::vec::Vec;
use core::error::Error;
use core::hash::{Hash, Hasher};
use core::marker::{Send, Sync};
use core::ops::Index;
use core::sync::atomic::{self, AtomicBool, Ordering};
use core::{cmp, fmt};
#[cfg(feature = "std")]
type AtomicHash = portable_atomic::AtomicU64;
#[cfg(not(feature = "std"))]
type AtomicHash = portable_atomic::AtomicU32;
use crate::data::{Id, Metadata};
use crate::dlog;
use crate::downcast::Downcasted;
use crate::extensions::{Extension, Extensions, ExtensionsInner, ExtensionsMut};
use crate::extract::{Extract, Extractable};
use crate::global::{for_each_subscriber, get_formatter};
use crate::location::Location;
#[derive(Clone)]
pub struct TraceRecord {
pub location: Location,
pub name: &'static str,
pub target: &'static str,
pub target_id: Id,
pub id: Id,
pub is_transparent: bool,
pub(crate) inner: Option<Weak<dyn Error + Send + Sync>>,
pub(crate) format_span: Arc<AtomicBool>,
}
impl fmt::Debug for TraceRecord {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("TraceRecord")
.field("location", &self.location.to_string())
.field("name", &self.name)
.field("target", &self.target)
.field("target_id", &self.target_id)
.field("id", &self.id)
.field("is_transparent", &self.is_transparent)
.finish()
}
}
impl fmt::Display for TraceRecord {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.format_span.store(false, atomic::Ordering::Relaxed);
let r = get_formatter().format_record(self, f);
self.format_span.store(true, atomic::Ordering::Relaxed);
r
}
}
impl TraceRecord {
#[track_caller]
pub(crate) fn new<T>(error: &T, ctx: &TraceContext) -> Self
where
T: Metadata + Traceable,
{
Self {
location: core::panic::Location::caller().into(),
name: error.name(),
target: error.target(),
target_id: *error.target_id(),
id: *error.id(),
is_transparent: error.is_transparent(),
inner: Some(Arc::downgrade(&error.inner())),
format_span: ctx.format_span.clone(),
}
}
#[inline]
pub fn error_ref(&self) -> Option<Arc<dyn Error + Send + Sync>> {
self.inner
.as_ref()
.expect("Weak reference upgrade should never fail. Please open an issue.")
.upgrade()
}
}
impl Hash for TraceRecord {
#[inline]
fn hash<H: Hasher>(&self, state: &mut H) {
self.location.hash(state);
self.name.hash(state);
}
}
impl Eq for TraceRecord {}
impl PartialEq for TraceRecord {
#[inline]
fn eq(&self, other: &TraceRecord) -> bool {
self.location == other.location && self.name == other.name
}
}
impl Ord for TraceRecord {
#[inline]
fn cmp(&self, other: &TraceRecord) -> cmp::Ordering {
match Ord::cmp(&self.location, &other.location) {
cmp::Ordering::Equal => Ord::cmp(&self.name, &other.name),
cmp => cmp,
}
}
}
impl PartialOrd for TraceRecord {
#[inline]
fn partial_cmp(&self, other: &TraceRecord) -> Option<cmp::Ordering> {
match PartialOrd::partial_cmp(&self.location, &other.location) {
Option::Some(cmp::Ordering::Equal) => PartialOrd::partial_cmp(&self.name, &other.name),
cmp => cmp,
}
}
}
pub struct TraceContext {
pub(crate) trace: Vec<TraceRecord>,
pub(crate) last_record: Arc<AtomicHash>,
pub(crate) format_span: Arc<AtomicBool>,
pub(crate) dropped: Arc<AtomicBool>,
pub(crate) extensions: ExtensionsInner,
}
impl fmt::Debug for TraceContext {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("TraceContext")
.field("trace", &self.trace)
.field("format_span", &self.format_span)
.finish()
}
}
impl fmt::Display for TraceContext {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
if self.trace.is_empty() {
return write!(f, "TraceContext empty");
}
self.format_span.store(false, atomic::Ordering::Relaxed);
let r = get_formatter().format_trace(self, f);
self.format_span.store(true, atomic::Ordering::Relaxed);
r
}
}
impl Drop for TraceContext {
fn drop(&mut self) {
dlog!("TraceContext");
if !self.dropped.load(Ordering::Relaxed) {
for_each_subscriber(|s| s.on_end(self));
}
}
}
#[doc(hidden)]
#[derive(Default)]
pub struct TraceRecordIterator<'a> {
index: Option<&'a Vec<TraceRecord>>,
pos: usize,
len: usize,
}
impl<'a> TraceRecordIterator<'a> {
#[inline]
pub fn len(&self) -> usize {
match self.index {
Some(index) => index.len(),
None => 0,
}
}
}
impl<'a> Iterator for TraceRecordIterator<'a> {
type Item = &'a TraceRecord;
fn next(&mut self) -> Option<Self::Item> {
let pos = self.pos;
self.pos += 1;
match self.index {
Some(index) => index.get(pos),
None => None,
}
}
}
impl<'a> DoubleEndedIterator for TraceRecordIterator<'a> {
fn next_back(&mut self) -> Option<Self::Item> {
self.pos += 1;
if self.pos > self.len {
return None;
}
match self.index {
Some(index) => index.get(self.len - self.pos),
None => None,
}
}
}
impl<'a> Index<usize> for TraceRecordIterator<'a> {
type Output = TraceRecord;
#[inline]
fn index(&self, index: usize) -> &Self::Output {
self.index.unwrap().get(index).unwrap()
}
}
impl<'a> IntoIterator for &'a TraceContext {
type Item = &'a TraceRecord;
type IntoIter = TraceRecordIterator<'a>;
#[inline]
fn into_iter(self) -> Self::IntoIter {
self.iter()
}
}
impl TraceContext {
#[inline]
pub fn first(&self) -> &TraceRecord {
self.trace
.first()
.expect("Context must have at least one or more records. Please open an issue.")
}
#[inline]
pub fn last(&self) -> &TraceRecord {
self.trace
.last()
.expect("Context must have at least one or more records. Please open an issue.")
}
#[doc(hidden)]
#[inline]
pub fn insert(&mut self, record: TraceRecord) -> bool {
#[cfg(feature = "std")]
let hash = {
let mut hasher = std::hash::DefaultHasher::new();
record.hash(&mut hasher);
hasher.finish()
};
#[cfg(not(feature = "std"))]
let hash = {
use hash32::Hasher;
let mut hasher = hash32::FnvHasher::default();
record.hash(&mut hasher);
hasher.finish32()
};
if self.last_record.swap(hash, Ordering::Relaxed) != hash {
self.trace.push(record);
return true;
}
return false;
}
}
pub struct TraceContextBuilder {
trace: Vec<TraceRecord>,
pub(crate) format_span: Arc<AtomicBool>,
extensions: ExtensionsInner,
}
impl TraceContextBuilder {
#[inline]
pub(crate) fn new() -> Self {
Self {
trace: Vec::new(),
format_span: Arc::new(AtomicBool::new(true)),
extensions: ExtensionsInner::new(),
}
}
#[inline]
pub fn reserve_trace(&mut self, additional: usize) -> &mut Self {
self.trace.reserve(additional);
self
}
#[inline]
pub fn reserve_extensions(&mut self, additional: usize) -> &mut Self {
self.extensions.reserve(additional);
self
}
pub(crate) fn build(self) -> TraceContext {
TraceContext {
trace: self.trace,
last_record: Arc::new(AtomicHash::new(0)),
format_span: self.format_span,
dropped: Arc::new(AtomicBool::new(false)),
extensions: self.extensions,
}
}
}
impl Extension for TraceContextBuilder {
#[inline]
fn extensions(&self) -> Extensions<'_> {
Extensions::new(&self.extensions)
}
#[inline]
fn extensions_mut(&mut self) -> ExtensionsMut<'_> {
ExtensionsMut::new(&mut self.extensions)
}
}
pub trait TraceAccess {
fn id(&self) -> Id;
fn len(&self) -> usize;
fn iter(&self) -> TraceRecordIterator;
}
impl TraceAccess for TraceContext {
#[inline]
fn id(&self) -> Id {
return (core::ptr::addr_of!(self) as u64).into();
}
#[inline]
fn len(&self) -> usize {
self.trace.len()
}
#[inline]
fn iter(&self) -> TraceRecordIterator {
TraceRecordIterator {
index: Some(&self.trace),
pos: 0,
len: self.trace.len(),
}
}
}
impl Extension for TraceContext {
#[inline]
fn extensions(&self) -> Extensions<'_> {
Extensions::new(&self.extensions)
}
#[inline]
fn extensions_mut(&mut self) -> ExtensionsMut<'_> {
ExtensionsMut::new(&mut self.extensions)
}
}
impl Extract for TraceContext {
#[inline]
fn get<'a, E>(&'a self) -> Option<Downcasted<'a, E>>
where
E: Error + Extractable + 'static,
{
for e in self {
let origin = match e.error_ref() {
Some(v) => v,
None => continue,
};
if origin.is::<E>() {
return Some(Downcasted::<E>::new(origin));
}
}
return None;
}
#[inline]
fn has<'a, E>(&'a self) -> bool
where
E: Error + Extractable + 'static,
{
for e in self {
let origin = match e.error_ref() {
Some(v) => v,
None => continue,
};
if origin.is::<E>() {
return true;
}
}
return false;
}
}
pub trait Traceable {
fn trace(&self) -> &TraceContext;
#[doc(hidden)]
fn trace_ref(&self) -> Option<&TraceContext>;
#[doc(hidden)]
fn take_trace(&mut self) -> Option<TraceContext>;
#[doc(hidden)]
fn inner(&self) -> Arc<dyn Error + Send + Sync>;
#[doc(hidden)]
fn insert(&mut self, record: TraceRecord) -> bool;
}