pub use tracing_core::span::{Attributes, Id, Record};
use crate::stdlib::{
cmp, fmt,
hash::{Hash, Hasher},
};
use crate::{
dispatcher::{self, Dispatch},
field, Metadata,
};
pub trait AsId: crate::sealed::Sealed {
fn as_id(&self) -> Option<&Id>;
}
#[derive(Clone)]
pub struct Span {
inner: Option<Inner>,
meta: Option<&'static Metadata<'static>>,
}
#[derive(Debug)]
pub(crate) struct Inner {
id: Id,
subscriber: Dispatch,
}
#[derive(Debug)]
#[must_use = "once a span has been entered, it should be exited"]
pub struct Entered<'a> {
span: &'a Span,
}
#[cfg(feature = "log")]
const LIFECYCLE_LOG_TARGET: &str = "tracing::span";
#[cfg(feature = "log")]
const ACTIVITY_LOG_TARGET: &str = "tracing::span::active";
impl Span {
pub fn new(meta: &'static Metadata<'static>, values: &field::ValueSet<'_>) -> Span {
dispatcher::get_default(|dispatch| Self::new_with(meta, values, dispatch))
}
#[inline]
#[doc(hidden)]
pub fn new_with(
meta: &'static Metadata<'static>,
values: &field::ValueSet<'_>,
dispatch: &Dispatch,
) -> Span {
let new_span = Attributes::new(meta, values);
Self::make_with(meta, new_span, dispatch)
}
pub fn new_root(meta: &'static Metadata<'static>, values: &field::ValueSet<'_>) -> Span {
dispatcher::get_default(|dispatch| Self::new_root_with(meta, values, dispatch))
}
#[inline]
#[doc(hidden)]
pub fn new_root_with(
meta: &'static Metadata<'static>,
values: &field::ValueSet<'_>,
dispatch: &Dispatch,
) -> Span {
let new_span = Attributes::new_root(meta, values);
Self::make_with(meta, new_span, dispatch)
}
pub fn child_of(
parent: impl Into<Option<Id>>,
meta: &'static Metadata<'static>,
values: &field::ValueSet<'_>,
) -> Span {
let mut parent = parent.into();
dispatcher::get_default(move |dispatch| {
Self::child_of_with(Option::take(&mut parent), meta, values, dispatch)
})
}
#[inline]
#[doc(hidden)]
pub fn child_of_with(
parent: impl Into<Option<Id>>,
meta: &'static Metadata<'static>,
values: &field::ValueSet<'_>,
dispatch: &Dispatch,
) -> Span {
let new_span = match parent.into() {
Some(parent) => Attributes::child_of(parent, meta, values),
None => Attributes::new_root(meta, values),
};
Self::make_with(meta, new_span, dispatch)
}
#[inline(always)]
pub fn new_disabled(meta: &'static Metadata<'static>) -> Span {
Self {
inner: None,
meta: Some(meta),
}
}
#[inline(always)]
pub const fn none() -> Span {
Self {
inner: None,
meta: None,
}
}
pub fn current() -> Span {
dispatcher::get_default(|dispatch| {
if let Some((id, meta)) = dispatch.current_span().into_inner() {
let id = dispatch.clone_span(&id);
Self {
inner: Some(Inner::new(id, dispatch)),
meta: Some(meta),
}
} else {
Self::none()
}
})
}
fn make_with(
meta: &'static Metadata<'static>,
new_span: Attributes<'_>,
dispatch: &Dispatch,
) -> Span {
let attrs = &new_span;
let id = dispatch.new_span(attrs);
let inner = Some(Inner::new(id, dispatch));
let span = Self {
inner,
meta: Some(meta),
};
if_log_enabled! {{
let target = if attrs.is_empty() {
LIFECYCLE_LOG_TARGET
} else {
meta.target()
};
span.log(target, level_to_log!(*meta.level()), format_args!("++ {}{}", meta.name(), FmtAttrs(attrs)));
}}
span
}
pub fn enter(&self) -> Entered<'_> {
if let Some(ref inner) = self.inner.as_ref() {
inner.subscriber.enter(&inner.id);
}
if_log_enabled! {{
if let Some(ref meta) = self.meta {
self.log(ACTIVITY_LOG_TARGET, log::Level::Trace, format_args!("-> {}", meta.name()));
}
}}
Entered { span: self }
}
pub fn in_scope<F: FnOnce() -> T, T>(&self, f: F) -> T {
let _enter = self.enter();
f()
}
pub fn field<Q: ?Sized>(&self, field: &Q) -> Option<field::Field>
where
Q: field::AsField,
{
self.metadata().and_then(|meta| field.as_field(meta))
}
#[inline]
pub fn has_field<Q: ?Sized>(&self, field: &Q) -> bool
where
Q: field::AsField,
{
self.field(field).is_some()
}
pub fn record<Q: ?Sized, V>(&self, field: &Q, value: &V) -> &Self
where
Q: field::AsField,
V: field::Value,
{
if let Some(ref meta) = self.meta {
if let Some(field) = field.as_field(meta) {
self.record_all(
&meta
.fields()
.value_set(&[(&field, Some(value as &dyn field::Value))]),
);
}
}
self
}
pub fn record_all(&self, values: &field::ValueSet<'_>) -> &Self {
let record = Record::new(values);
if let Some(ref inner) = self.inner {
inner.record(&record);
}
if_log_enabled! {{
if let Some(ref meta) = self.meta {
let target = if record.is_empty() {
LIFECYCLE_LOG_TARGET
} else {
meta.target()
};
self.log(target, level_to_log!(*meta.level()), format_args!("{}{}", meta.name(), FmtValues(&record)));
}
}}
self
}
#[inline]
pub fn is_disabled(&self) -> bool {
self.inner.is_none()
}
#[inline]
pub fn is_none(&self) -> bool {
self.is_disabled() && self.meta.is_none()
}
pub fn follows_from(&self, from: impl Into<Option<Id>>) -> &Self {
if let Some(ref inner) = self.inner {
if let Some(from) = from.into() {
inner.follows_from(&from);
}
}
self
}
pub fn id(&self) -> Option<Id> {
self.inner.as_ref().map(Inner::id)
}
pub fn metadata(&self) -> Option<&'static Metadata<'static>> {
self.meta
}
#[cfg(feature = "log")]
#[inline]
fn log(&self, target: &str, level: log::Level, message: fmt::Arguments<'_>) {
if let Some(ref meta) = self.meta {
if level_to_log!(*meta.level()) <= log::max_level() {
let logger = log::logger();
let log_meta = log::Metadata::builder().level(level).target(target).build();
if logger.enabled(&log_meta) {
if let Some(ref inner) = self.inner {
logger.log(
&log::Record::builder()
.metadata(log_meta)
.module_path(meta.module_path())
.file(meta.file())
.line(meta.line())
.args(format_args!("{}; span={}", message, inner.id.into_u64()))
.build(),
);
} else {
logger.log(
&log::Record::builder()
.metadata(log_meta)
.module_path(meta.module_path())
.file(meta.file())
.line(meta.line())
.args(message)
.build(),
);
}
}
}
}
}
pub fn with_subscriber<T>(&self, f: impl FnOnce((&Id, &Dispatch)) -> T) -> Option<T> {
self.inner
.as_ref()
.map(|inner| f((&inner.id, &inner.subscriber)))
}
}
impl cmp::PartialEq for Span {
fn eq(&self, other: &Self) -> bool {
match (&self.meta, &other.meta) {
(Some(this), Some(that)) => {
this.callsite() == that.callsite() && self.inner == other.inner
}
_ => false,
}
}
}
impl Hash for Span {
fn hash<H: Hasher>(&self, hasher: &mut H) {
self.inner.hash(hasher);
}
}
impl fmt::Debug for Span {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let mut span = f.debug_struct("Span");
if let Some(ref meta) = self.meta {
span.field("name", &meta.name())
.field("level", &meta.level())
.field("target", &meta.target());
if let Some(ref inner) = self.inner {
span.field("id", &inner.id());
} else {
span.field("disabled", &true);
}
if let Some(ref path) = meta.module_path() {
span.field("module_path", &path);
}
if let Some(ref line) = meta.line() {
span.field("line", &line);
}
if let Some(ref file) = meta.file() {
span.field("file", &file);
}
} else {
span.field("none", &true);
}
span.finish()
}
}
impl<'a> Into<Option<&'a Id>> for &'a Span {
fn into(self) -> Option<&'a Id> {
self.inner.as_ref().map(|inner| &inner.id)
}
}
impl<'a> Into<Option<Id>> for &'a Span {
fn into(self) -> Option<Id> {
self.inner.as_ref().map(Inner::id)
}
}
impl Into<Option<Id>> for Span {
fn into(self) -> Option<Id> {
self.inner.as_ref().map(Inner::id)
}
}
impl Drop for Span {
fn drop(&mut self) {
if let Some(Inner {
ref id,
ref subscriber,
}) = self.inner
{
subscriber.try_close(id.clone());
}
if_log_enabled!({
if let Some(ref meta) = self.meta {
self.log(
LIFECYCLE_LOG_TARGET,
log::Level::Trace,
format_args!("-- {}", meta.name()),
);
}
})
}
}
impl Inner {
fn follows_from(&self, from: &Id) {
self.subscriber.record_follows_from(&self.id, &from)
}
fn id(&self) -> Id {
self.id.clone()
}
fn record(&self, values: &Record<'_>) {
self.subscriber.record(&self.id, values)
}
fn new(id: Id, subscriber: &Dispatch) -> Self {
Inner {
id,
subscriber: subscriber.clone(),
}
}
}
impl cmp::PartialEq for Inner {
fn eq(&self, other: &Self) -> bool {
self.id == other.id
}
}
impl Hash for Inner {
fn hash<H: Hasher>(&self, state: &mut H) {
self.id.hash(state);
}
}
impl Clone for Inner {
fn clone(&self) -> Self {
Inner {
id: self.subscriber.clone_span(&self.id),
subscriber: self.subscriber.clone(),
}
}
}
impl<'a> Drop for Entered<'a> {
#[inline]
fn drop(&mut self) {
if let Some(inner) = self.span.inner.as_ref() {
inner.subscriber.exit(&inner.id);
}
if_log_enabled! {{
if let Some(ref meta) = self.span.meta {
self.span.log(ACTIVITY_LOG_TARGET, log::Level::Trace, format_args!("<- {}", meta.name()));
}
}}
}
}
#[cfg(feature = "log")]
struct FmtValues<'a>(&'a Record<'a>);
#[cfg(feature = "log")]
impl<'a> fmt::Display for FmtValues<'a> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let mut res = Ok(());
let mut is_first = true;
self.0.record(&mut |k: &field::Field, v: &dyn fmt::Debug| {
res = write!(f, "{} {}={:?}", if is_first { ";" } else { "" }, k, v);
is_first = false;
});
res
}
}
#[cfg(feature = "log")]
struct FmtAttrs<'a>(&'a Attributes<'a>);
#[cfg(feature = "log")]
impl<'a> fmt::Display for FmtAttrs<'a> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let mut res = Ok(());
let mut is_first = true;
self.0.record(&mut |k: &field::Field, v: &dyn fmt::Debug| {
res = write!(f, "{} {}={:?}", if is_first { ";" } else { "" }, k, v);
is_first = false;
});
res
}
}
#[cfg(test)]
mod test {
use super::*;
trait AssertSend: Send {}
impl AssertSend for Span {}
trait AssertSync: Sync {}
impl AssertSync for Span {}
}