mirror of
https://github.com/tokio-rs/tracing.git
synced 2025-10-02 07:20:35 +00:00
314 lines
8.1 KiB
Rust
314 lines
8.1 KiB
Rust
extern crate futures;
|
|
extern crate log;
|
|
#[macro_use]
|
|
extern crate lazy_static;
|
|
|
|
pub use self::subscriber::Subscriber;
|
|
pub use log::Level;
|
|
|
|
use std::{cell::RefCell, cmp, fmt, slice, sync::Arc, time::Instant};
|
|
|
|
use self::dedup::IteratorDedup;
|
|
|
|
#[doc(hidden)]
|
|
#[macro_export]
|
|
macro_rules! static_meta {
|
|
($($k:ident),*) => (
|
|
static_meta!(@ None, $crate::Level::Trace, $($k),* )
|
|
);
|
|
(level: $lvl:expr, $($k:ident),*) => (
|
|
static_meta!(@ None, $lvl, $($k),* )
|
|
);
|
|
(target: $target:expr, level: $lvl:expr, $($k:ident),*) => (
|
|
static_meta!(@ Some($target), $lvl, $($k),* )
|
|
);
|
|
(target: $target:expr, $($k:ident),*) => (
|
|
static_meta!(@ Some($target), $crate::Level::Trace, $($k),* )
|
|
);
|
|
(@ $target:expr, $lvl:expr, $($k:ident),*) => (
|
|
$crate::StaticMeta {
|
|
target: $target,
|
|
level: $lvl,
|
|
module_path: module_path!(),
|
|
file: file!(),
|
|
line: line!(),
|
|
field_names: &[ $(stringify!($k)),* ],
|
|
}
|
|
)
|
|
}
|
|
|
|
#[macro_export]
|
|
macro_rules! span {
|
|
($name:expr, $($k:ident = $val:expr),*) => {
|
|
$crate::Span::new(
|
|
Some($name),
|
|
::std::time::Instant::now(),
|
|
$crate::Span::current(),
|
|
&static_meta!( $($k),* ),
|
|
vec![ $(Box::new($val)),* ], // todo: wish this wasn't double-boxed...
|
|
)
|
|
}
|
|
}
|
|
|
|
#[macro_export]
|
|
macro_rules! event {
|
|
(target: $target:expr, $lvl:expr, { $($k:ident = $val:expr),* }, $($arg:tt)+ ) => ({
|
|
{ let field_values: &[& dyn $crate::Value] = &[ $( & $val),* ];
|
|
$crate::Event {
|
|
timestamp: ::std::time::Instant::now(),
|
|
parent: $crate::Span::current(),
|
|
follows_from: &[],
|
|
static_meta: &static_meta!(@ $target, $lvl, $($k),* ),
|
|
field_values: &field_values[..],
|
|
message: format_args!( $($arg)+ ),
|
|
};
|
|
}
|
|
|
|
});
|
|
($lvl:expr, { $($k:ident = $val:expr),* }, $($arg:tt)+ ) => (event!(target: None, $lvl, { $($k = $val),* }, $($arg)+))
|
|
}
|
|
|
|
lazy_static! {
|
|
static ref ROOT_SPAN: Span = Span {
|
|
inner: Arc::new(SpanInner {
|
|
name: Some("root"),
|
|
opened_at: Instant::now(),
|
|
parent: None,
|
|
static_meta: &static_meta!(),
|
|
field_values: Vec::new(),
|
|
})
|
|
};
|
|
}
|
|
|
|
thread_local! {
|
|
static CURRENT_SPAN: RefCell<Span> = RefCell::new(ROOT_SPAN.clone());
|
|
}
|
|
|
|
mod dedup;
|
|
mod dispatcher;
|
|
pub mod subscriber;
|
|
pub mod instrument;
|
|
|
|
pub use dispatcher::{Builder as DispatcherBuilder, Dispatcher};
|
|
|
|
// XXX: im using fmt::Debug for prototyping purposes, it should probably leave.
|
|
pub trait Value: fmt::Debug + Send + Sync {
|
|
// ... ?
|
|
}
|
|
|
|
impl<T> Value for T where T: fmt::Debug + Send + Sync {}
|
|
|
|
pub struct Event<'event> {
|
|
pub timestamp: Instant,
|
|
|
|
pub parent: Span,
|
|
pub follows_from: &'event [Span],
|
|
|
|
pub static_meta: &'event StaticMeta,
|
|
// TODO: agh box
|
|
pub field_values: &'event [&'event dyn Value],
|
|
pub message: fmt::Arguments<'event>,
|
|
}
|
|
|
|
#[derive(Clone, Debug, Eq, PartialEq)]
|
|
pub struct StaticMeta {
|
|
pub target: Option<&'static str>,
|
|
pub level: log::Level,
|
|
|
|
pub module_path: &'static str,
|
|
pub file: &'static str,
|
|
pub line: u32,
|
|
|
|
pub field_names: &'static [&'static str],
|
|
}
|
|
|
|
#[derive(Clone, PartialEq)]
|
|
pub struct Span {
|
|
inner: Arc<SpanInner>,
|
|
}
|
|
|
|
#[derive(Debug)]
|
|
struct SpanInner {
|
|
pub name: Option<&'static str>,
|
|
pub opened_at: Instant,
|
|
|
|
pub parent: Option<Span>,
|
|
|
|
pub static_meta: &'static StaticMeta,
|
|
|
|
pub field_values: Vec<Box<dyn Value>>,
|
|
// ...
|
|
}
|
|
|
|
/// Iterator over the parents of a span or event
|
|
pub struct Parents<'a> {
|
|
next: Option<&'a Span>,
|
|
}
|
|
|
|
// ===== impl Event =====
|
|
|
|
impl<'event> Event<'event> {
|
|
pub fn field_names(&self) -> slice::Iter<&'static str> {
|
|
self.static_meta.field_names.iter()
|
|
}
|
|
|
|
pub fn fields(&'event self) -> impl Iterator<Item = (&'static str, &'event dyn Value)> {
|
|
self.field_names()
|
|
.enumerate()
|
|
.filter_map(move |(idx, &name)| self.field_values.get(idx).map(|&val| (name, val)))
|
|
}
|
|
|
|
pub fn debug_fields(&'event self) -> DebugFields<'event, Self> {
|
|
DebugFields(self)
|
|
}
|
|
|
|
pub fn parents<'a>(&'a self) -> Parents<'a> {
|
|
Parents {
|
|
next: Some(&self.parent),
|
|
}
|
|
}
|
|
|
|
pub fn all_fields<'a>(&'a self) -> impl Iterator<Item = (&'static str, &'a dyn Value)> {
|
|
self.fields()
|
|
.chain(self.parents().flat_map(|parent| parent.fields()))
|
|
.dedup_by(|(k, _)| k)
|
|
}
|
|
}
|
|
|
|
impl<'event> Drop for Event<'event> {
|
|
fn drop(&mut self) {
|
|
Dispatcher::current().observe_event(self);
|
|
}
|
|
}
|
|
|
|
// ===== impl Span =====
|
|
|
|
impl Span {
|
|
pub fn new(
|
|
name: Option<&'static str>,
|
|
opened_at: Instant,
|
|
parent: Span,
|
|
static_meta: &'static StaticMeta,
|
|
field_values: Vec<Box<dyn Value>>,
|
|
) -> Self {
|
|
Span {
|
|
inner: Arc::new(SpanInner {
|
|
name,
|
|
opened_at,
|
|
parent: Some(parent),
|
|
static_meta,
|
|
field_values,
|
|
}),
|
|
}
|
|
}
|
|
|
|
pub fn current() -> Self {
|
|
CURRENT_SPAN.with(|span| span.borrow().clone())
|
|
}
|
|
|
|
pub fn name(&self) -> Option<&'static str> {
|
|
self.inner.name
|
|
}
|
|
|
|
pub fn parent(&self) -> Option<&Span> {
|
|
self.inner.parent.as_ref()
|
|
}
|
|
|
|
pub fn meta(&self) -> &'static StaticMeta {
|
|
self.inner.static_meta
|
|
}
|
|
|
|
pub fn field_names(&self) -> slice::Iter<&'static str> {
|
|
self.inner.static_meta.field_names.iter()
|
|
}
|
|
|
|
pub fn fields<'a>(&'a self) -> impl Iterator<Item = (&'static str, &'a dyn Value)> {
|
|
self.field_names()
|
|
.enumerate()
|
|
.map(move |(idx, &name)| (name, self.inner.field_values[idx].as_ref()))
|
|
}
|
|
|
|
pub fn enter<F: FnOnce() -> T, T>(&self, f: F) -> T {
|
|
CURRENT_SPAN.with(|current_span| {
|
|
current_span.replace(self.clone());
|
|
Dispatcher::current().enter(&self, Instant::now());
|
|
|
|
let result = f();
|
|
|
|
if let Some(parent) = self.parent() {
|
|
current_span.replace(parent.clone());
|
|
}
|
|
|
|
Dispatcher::current().exit(&self, Instant::now());
|
|
result
|
|
})
|
|
}
|
|
|
|
pub fn debug_fields<'a>(&'a self) -> DebugFields<'a, Self> {
|
|
DebugFields(self)
|
|
}
|
|
|
|
pub fn parents<'a>(&'a self) -> Parents<'a> {
|
|
Parents { next: Some(self) }
|
|
}
|
|
}
|
|
|
|
impl cmp::PartialEq for SpanInner {
|
|
fn eq(&self, other: &SpanInner) -> bool {
|
|
self.opened_at == other.opened_at
|
|
&& self.name == other.name
|
|
&& self.static_meta == other.static_meta
|
|
}
|
|
}
|
|
|
|
impl<'a> IntoIterator for &'a Span {
|
|
type Item = (&'static str, &'a dyn Value);
|
|
type IntoIter = Box<Iterator<Item = (&'static str, &'a dyn Value)> + 'a>; // TODO: unbox
|
|
fn into_iter(self) -> Self::IntoIter {
|
|
Box::new(self.fields())
|
|
}
|
|
}
|
|
|
|
impl<'a> IntoIterator for &'a Event<'a> {
|
|
type Item = (&'static str, &'a dyn Value);
|
|
type IntoIter = Box<Iterator<Item = (&'static str, &'a dyn Value)> + 'a>; // TODO: unbox
|
|
fn into_iter(self) -> Self::IntoIter {
|
|
Box::new(self.fields())
|
|
}
|
|
}
|
|
|
|
pub struct DebugFields<'a, I: 'a>(&'a I)
|
|
where
|
|
&'a I: IntoIterator<Item = (&'static str, &'a dyn Value)>;
|
|
|
|
impl<'a, I: 'a> fmt::Debug for DebugFields<'a, I>
|
|
where
|
|
&'a I: IntoIterator<Item = (&'static str, &'a dyn Value)>,
|
|
{
|
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
|
f.debug_map().entries(self.0.into_iter()).finish()
|
|
}
|
|
}
|
|
|
|
impl fmt::Debug for Span {
|
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
|
f.debug_struct("Span")
|
|
.field("name", &self.inner.name)
|
|
.field("opened_at", &self.inner.opened_at)
|
|
.field("parent", &self.parent().unwrap_or(self).name())
|
|
.field("fields", &self.debug_fields())
|
|
.field("meta", &self.meta())
|
|
.finish()
|
|
}
|
|
}
|
|
|
|
// ===== impl Parents =====
|
|
|
|
impl<'a> Iterator for Parents<'a> {
|
|
type Item = &'a Span;
|
|
fn next(&mut self) -> Option<Self::Item> {
|
|
self.next = self.next.and_then(Span::parent);
|
|
self.next
|
|
}
|
|
}
|