pub use tokio_trace_core::span::{Attributes, Id, Record};
use std::{
cmp, fmt,
hash::{Hash, Hasher},
};
use {dispatcher::Dispatch, field, Metadata};
pub trait AsId: ::sealed::Sealed {
fn as_id(&self) -> Option<&Id>;
}
#[derive(Clone)]
pub struct Span {
inner: Option<Inner>,
meta: &'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"]
struct Entered<'a> {
inner: &'a Inner,
}
impl Span {
#[inline]
pub fn new(meta: &'static Metadata<'static>, values: &field::ValueSet) -> Span {
let new_span = Attributes::new(meta, values);
Self::make(meta, new_span)
}
#[inline]
pub fn new_root(meta: &'static Metadata<'static>, values: &field::ValueSet) -> Span {
Self::make(meta, Attributes::new_root(meta, values))
}
pub fn child_of<I>(
parent: I,
meta: &'static Metadata<'static>,
values: &field::ValueSet,
) -> Span
where
I: AsId,
{
let new_span = match parent.as_id() {
Some(parent) => Attributes::child_of(parent.clone(), meta, values),
None => Attributes::new_root(meta, values),
};
Self::make(meta, new_span)
}
#[inline(always)]
pub fn new_disabled(meta: &'static Metadata<'static>) -> Span {
Span { inner: None, meta }
}
fn make(meta: &'static Metadata<'static>, new_span: Attributes) -> Span {
let attrs = &new_span;
let inner = ::dispatcher::get_default(move |dispatch| {
let id = dispatch.new_span(attrs);
Some(Inner::new(id, dispatch))
});
let span = Self { inner, meta };
span.log(format_args!("{}; {}", meta.name(), FmtAttrs(&new_span)));
span
}
pub fn enter<F: FnOnce() -> T, T>(&self, f: F) -> T {
self.log(format_args!("-> {}", self.meta.name));
let _enter = self.inner.as_ref().map(Inner::enter);
let result = f();
self.log(format_args!("<- {}", self.meta.name));
result
}
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(field) = field.as_field(self.meta) {
self.record_all(
&self
.meta
.fields()
.value_set(&[(&field, Some(value as &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);
}
self.log(format_args!("{}; {}", self.meta.name(), FmtValues(&record)));
self
}
#[inline]
pub fn is_disabled(&self) -> bool {
self.inner.is_none()
}
pub fn follows_from<I>(&self, from: I) -> &Self
where
I: AsId,
{
if let Some(ref inner) = self.inner {
if let Some(from) = from.as_id() {
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>> {
if self.inner.is_some() {
Some(self.meta)
} else {
None
}
}
#[cfg(feature = "log")]
#[inline]
fn log(&self, message: fmt::Arguments) {
use log;
let logger = log::logger();
let log_meta = log::Metadata::builder()
.level(level_to_log!(self.meta.level))
.target(self.meta.target)
.build();
if logger.enabled(&log_meta) {
logger.log(
&log::Record::builder()
.metadata(log_meta)
.module_path(self.meta.module_path)
.file(self.meta.file)
.line(self.meta.line)
.args(message)
.build(),
);
}
}
#[cfg(not(feature = "log"))]
#[inline]
fn log(&self, _: fmt::Arguments) {}
}
impl cmp::PartialEq for Span {
fn eq(&self, other: &Self) -> bool {
self.meta.callsite() == other.meta.callsite() && self.inner == other.inner
}
}
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");
span.field("name", &self.meta.name())
.field("level", &self.meta.level())
.field("target", &self.meta.target());
if let Some(ref inner) = self.inner {
span.field("id", &inner.id());
} else {
span.field("disabled", &true);
}
if let Some(ref path) = self.meta.module_path() {
span.field("module_path", &path);
}
if let Some(ref line) = self.meta.line() {
span.field("line", &line);
}
if let Some(ref file) = self.meta.file() {
span.field("file", &file);
}
span.finish()
}
}
impl Inner {
#[inline]
fn enter(&self) -> Entered {
self.subscriber.enter(&self.id);
Entered { inner: self }
}
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 Drop for Inner {
fn drop(&mut self) {
self.subscriber.drop_span(self.id.clone());
}
}
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) {
self.inner.subscriber.exit(&self.inner.id);
}
}
struct FmtValues<'a>(&'a Record<'a>);
impl<'a> fmt::Display for FmtValues<'a> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let mut res = Ok(());
self.0.record(&mut |k: &field::Field, v: &fmt::Debug| {
res = write!(f, "{}={:?} ", k, v);
});
res
}
}
struct FmtAttrs<'a>(&'a Attributes<'a>);
impl<'a> fmt::Display for FmtAttrs<'a> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let mut res = Ok(());
self.0.record(&mut |k: &field::Field, v: &fmt::Debug| {
res = write!(f, "{}={:?} ", k, v);
});
res
}
}
impl ::sealed::Sealed for Span {}
impl AsId for Span {
fn as_id(&self) -> Option<&Id> {
self.inner.as_ref().map(|inner| &inner.id)
}
}
impl<'a> ::sealed::Sealed for &'a Span {}
impl<'a> AsId for &'a Span {
fn as_id(&self) -> Option<&Id> {
self.inner.as_ref().map(|inner| &inner.id)
}
}
impl ::sealed::Sealed for Id {}
impl AsId for Id {
fn as_id(&self) -> Option<&Id> {
Some(self)
}
}
impl<'a> ::sealed::Sealed for &'a Id {}
impl<'a> AsId for &'a Id {
fn as_id(&self) -> Option<&Id> {
Some(self)
}
}
impl ::sealed::Sealed for Option<Id> {}
impl AsId for Option<Id> {
fn as_id(&self) -> Option<&Id> {
self.as_ref()
}
}
impl<'a> ::sealed::Sealed for &'a Option<Id> {}
impl<'a> AsId for &'a Option<Id> {
fn as_id(&self) -> Option<&Id> {
self.as_ref()
}
}
#[cfg(test)]
mod test {
use super::*;
trait AssertSend: Send {}
impl AssertSend for Span {}
trait AssertSync: Sync {}
impl AssertSync for Span {}
}