extern crate alloc;
use alloc::string::ToString;
use alloc::sync::Arc;
use core::error::Error;
use core::marker::{Send, Sync};
use core::sync::atomic::AtomicBool;
use core::{fmt, sync::atomic::Ordering};
use crate::data::{Id, Metadata};
use crate::dlog;
use crate::downcast::Downcasted;
use crate::extensions::{Extension, Extensions, ExtensionsMut};
use crate::extract::{Extract, Extractable};
use crate::global::{for_each_subscriber, get_formatter};
use crate::trace::{
TraceAccess, TraceContext, TraceContextBuilder, TraceRecord, TraceRecordIterator,
};
pub struct Span<T>
where
T: Error + Metadata,
{
format_span: Arc<AtomicBool>,
#[doc(hidden)]
pub ctx: Option<TraceContext>,
pub(crate) record: TraceRecord,
#[doc(hidden)]
pub inner: Arc<T>,
}
#[derive(Debug)]
pub struct SpanContext<'a> {
ctx: &'a mut TraceContext,
pub record: &'a TraceRecord,
}
impl<'a> SpanContext<'a> {
#[doc(hidden)]
#[inline]
pub fn new(ctx: &'a mut TraceContext, record: &'a TraceRecord) -> Self {
Self { ctx, record }
}
}
impl<'a> fmt::Display for SpanContext<'a> {
#[inline]
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::Display::fmt(self.record, f)
}
}
impl<'a> TraceAccess for SpanContext<'a> {
#[inline]
fn id(&self) -> Id {
self.ctx.id()
}
#[inline]
fn len(&self) -> usize {
self.ctx.len()
}
#[inline]
fn iter(&self) -> TraceRecordIterator {
self.ctx.iter()
}
}
impl<'a> Extension for SpanContext<'a> {
#[inline]
fn extensions(&self) -> Extensions<'_> {
self.ctx.extensions()
}
#[inline]
fn extensions_mut(&mut self) -> ExtensionsMut<'_> {
self.ctx.extensions_mut()
}
}
impl<'s> Extract for SpanContext<'s> {
#[inline]
fn get<'a, E>(&'a self) -> Option<Downcasted<'a, E>>
where
E: Error + Extractable + 'static,
{
self.ctx.get::<E>()
}
#[inline]
fn has<'a, E>(&'a self) -> bool
where
E: Error + Extractable + 'static,
{
self.ctx.has::<E>()
}
}
impl<T> fmt::Debug for Span<T>
where
T: Error + Metadata,
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("Span")
.field(
"records",
&self.ctx.as_ref().map(|f| f.trace.len()).unwrap_or(0),
)
.field("location", &self.record.location.to_string())
.field("name", &self.record.name)
.field("target", &self.record.target)
.field("target_id", &self.record.target_id)
.field("id", &self.record.id)
.field("is_transparent", &self.record.is_transparent)
.finish()
}
}
impl<T> Drop for Span<T>
where
T: Error + Metadata,
{
fn drop(&mut self) {
dlog!("{:#?}", self);
if let Some(ctx) = self.ctx.as_mut() {
dlog!("{}", self.record.id);
ctx.dropped.store(true, Ordering::Relaxed);
for_each_subscriber(|s| s.on_end(ctx));
}
}
}
impl<T> Span<T>
where
T: Error + Metadata + 'static + Send + Sync,
{
#[doc(hidden)]
#[track_caller]
pub fn new(ctx: Option<TraceContext>, inner: T) -> Self {
let inner_owned = Arc::new(inner);
let inner_ref = Arc::downgrade(&inner_owned);
let insert;
let (mut ctx, record) = match ctx {
Some(v) => {
insert = false;
let record = TraceRecord {
location: core::panic::Location::caller().into(),
name: inner_owned.name(),
target: inner_owned.target(),
target_id: *inner_owned.target_id(),
id: *inner_owned.id(),
is_transparent: inner_owned.is_transparent(),
inner: Some(inner_ref),
format_span: v.format_span.clone(),
};
(v, record)
}
None => {
insert = true;
let mut builder = TraceContextBuilder::new();
let record = TraceRecord {
location: core::panic::Location::caller().into(),
name: inner_owned.name(),
target: inner_owned.target(),
target_id: *inner_owned.target_id(),
id: *inner_owned.id(),
is_transparent: inner_owned.is_transparent(),
inner: Some(inner_ref),
format_span: builder.format_span.clone(),
};
for_each_subscriber(|s| s.on_start(&mut builder, &record));
let ctx = builder.build();
(ctx, record)
}
};
dlog!("span {:#?}", record);
let mut span_ctx = SpanContext::new(&mut ctx, &record);
for_each_subscriber(|s| s.on_new_span(&mut span_ctx));
if insert {
dlog!(
"insert ({})\n\tin {}",
span_ctx.record.name,
span_ctx.record.location
);
for_each_subscriber(|s| s.on_try_record(&mut span_ctx));
if ctx.insert(record.clone()) {
for_each_subscriber(|s| s.on_record(&mut ctx));
}
}
Self {
format_span: ctx.format_span.clone(),
ctx: Some(ctx),
record,
inner: inner_owned,
}
}
}
impl<'a, T> IntoIterator for &'a Span<T>
where
T: Error + Metadata + 'static,
{
type Item = &'a TraceRecord;
type IntoIter = TraceRecordIterator<'a>;
#[inline]
fn into_iter(self) -> Self::IntoIter {
match &self.ctx {
Some(ctx) => ctx.iter(),
None => Self::IntoIter::default(),
}
}
}
impl<T> Metadata for Span<T>
where
T: Error + Metadata + 'static,
{
#[inline]
fn name(&self) -> &'static str {
self.inner.name()
}
#[inline]
fn id(&self) -> &'static Id {
self.inner.id()
}
#[inline]
fn target(&self) -> &'static str {
self.inner.target()
}
#[inline]
fn target_id(&self) -> &'static Id {
self.inner.target_id()
}
#[inline]
fn display(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.inner)
}
#[inline]
fn is_transparent(&self) -> bool {
self.inner.is_transparent()
}
}
impl<T> fmt::Display for Span<T>
where
T: Error + Metadata + 'static,
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
if !self.format_span.load(Ordering::Relaxed) {
return self.display(f);
}
if let Some(ctx) = &self.ctx {
dlog!(
"format_span transparent={} Span<{}>",
self.is_transparent() as i32,
core::any::type_name::<T>()
);
get_formatter().format_span(self, ctx, f)
} else {
dlog!(
"display transparent={} Span<{}>",
self.is_transparent() as i32,
core::any::type_name::<T>()
);
self.display(f)
}
}
}
impl<T> Extract for Span<T>
where
T: Error + Metadata + 'static,
{
#[inline]
fn get<'a, E>(&'a self) -> Option<Downcasted<'a, E>>
where
E: Error + Extractable + 'static,
{
self.ctx.as_ref().map(|f| f.get::<E>()).unwrap_or(None)
}
#[inline]
fn has<'a, E>(&'a self) -> bool
where
E: Error + Extractable + 'static,
{
self.ctx.as_ref().map(|f| f.has::<E>()).unwrap_or(false)
}
}
impl<T> Error for Span<T> where T: Error + Metadata + 'static {}