chore: remove tokio-trace, add "Related Projects" to README (#1221)

## Motivation

The `tokio-trace` and `tokio-trace-core` crates have been renamed to
`tracing` and `tracing-core`, and moved to their own repository
(`tokio-rs/tracing`).

## Solution

This branch removes `tokio-trace` and `tokio-trace-core` from the
`tokio` repository. In addition, I've added a "Related Projects" section
to the root README, which lists `tracing` (as well as  `mio`, and
`bytes`) as other libraries maintained by the Tokio project. I thought
that this would help folks looking for `tokio-trace` here find it in its
new home.

In addition, it changes `tokio` to depend on `tracing-core` rather than
`tokio-trace-core`.

Closes #1159

Signed-off-by: Eliza Weisman <[email protected]>
This commit is contained in:
Eliza Weisman
2019-06-28 13:13:46 -07:00
committed by GitHub
parent e7488d983e
commit af46eac583
52 changed files with 20 additions and 11107 deletions
-2
View File
@@ -18,8 +18,6 @@ members = [
"tokio-timer",
"tokio-tcp",
# "tokio-tls",
# "tokio-trace",
# "tokio-trace/tokio-trace-core",
"tokio-udp",
# "tokio-uds",
]
+17
View File
@@ -168,6 +168,23 @@ The crates included as part of Tokio are:
[`tokio-udp`]: tokio-udp
[`tokio-uds`]: tokio-uds
## Related Projects
In addition to the crates in this repository, the Tokio project also maintains
several other libraries, including:
* [`tracing`] (formerly `tokio-trace`): A framework for application-level
tracing and async-aware diagnostics.
* [`mio`]: A low-level, cross-platform abstraction over OS I/O APIs that powers
`tokio`.
* [`bytes`]: Utilities for working with bytes, including efficient byte buffers.
[`tracing`]: https://github.com/tokio-rs/tracing
[`mio`]: https://github.com/tokio-rs/mio
[`bytes`]: https://github.com/tokio-rs/bytes
## Supported Rust Versions
Tokio is built against the latest stable, nightly, and beta Rust releases. The
-4
View File
@@ -54,10 +54,6 @@ jobs:
# - tokio-threadpool
- tokio-timer
- tokio-test
# - tokio-trace
# - tokio-trace/tokio-trace-core
# - tokio-trace/test-log-support
# - tokio-trace/test_static_max_level_features
# - template: ci/azure-cargo-check.yml
# parameters:
-2
View File
@@ -16,7 +16,5 @@ tokio-threadpool = { path = "tokio-threadpool" }
tokio-timer = { path = "tokio-timer" }
tokio-tcp = { path = "tokio-tcp" }
tokio-tls = { path = "tokio-tls" }
tokio-trace = { path = "tokio-trace" }
tokio-trace-core = { path = "tokio-trace/tokio-trace-core" }
tokio-udp = { path = "tokio-udp" }
tokio-uds = { path = "tokio-uds" }
-3
View File
@@ -1,3 +0,0 @@
# 0.1.0 (April 22, 2019)
- Initial release
-62
View File
@@ -1,62 +0,0 @@
[package]
name = "tokio-trace"
# When releasing to crates.io:
# - Remove path dependencies
# - Update html_root_url.
# - Update doc url
# - Cargo.toml
# - README.md
# - Update CHANGELOG.md.
# - Create "v0.1.x" git tag
version = "0.1.0"
authors = ["Tokio Contributors <[email protected]>"]
license = "MIT"
readme = "README.md"
repository = "https://github.com/tokio-rs/tokio"
homepage = "https://tokio.rs"
documentation = "https://docs.rs/tokio-trace/0.1.0/tokio_trace"
description = """
A scoped, structured logging and diagnostics system.
"""
categories = ["development-tools::debugging", "asynchronous"]
keywords = ["logging", "tracing"]
[dependencies]
tokio-trace-core = { path = "./tokio-trace-core" }
log = { version = "0.4", optional = true }
cfg-if = "0.1.7"
[dev-dependencies]
ansi_term = "0.11"
humantime = "1.1.1"
futures = "0.1"
log = "0.4"
criterion = { version = "0.2", default_features = false }
# These are used for the "basic" example from the tokio-trace-prototype repo,
# which is currently not included as it used the `tokio-trace-log` crate, and
# that crate is currently unstable.
# env_logger = "0.5"
# tokio-trace-log = { path = "../tokio-trace-log" }
[features]
max_level_off = []
max_level_error = []
max_level_warn = []
max_level_info = []
max_level_debug = []
max_level_trace = []
release_max_level_off = []
release_max_level_error = []
release_max_level_warn = []
release_max_level_info = []
release_max_level_debug = []
release_max_level_trace = []
[[bench]]
name = "subscriber"
harness = false
[[bench]]
name = "no_subscriber"
harness = false
-25
View File
@@ -1,25 +0,0 @@
Copyright (c) 2019 Tokio Contributors
Permission is hereby granted, free of charge, to any
person obtaining a copy of this software and associated
documentation files (the "Software"), to deal in the
Software without restriction, including without
limitation the rights to use, copy, modify, merge,
publish, distribute, sublicense, and/or sell copies of
the Software, and to permit persons to whom the Software
is furnished to do so, subject to the following
conditions:
The above copyright notice and this permission notice
shall be included in all copies or substantial portions
of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.
-192
View File
@@ -1,192 +0,0 @@
# tokio-trace
A scoped, structured logging and diagnostics system.
[Documentation](https://docs.rs/tokio-trace/0.1.0/tokio_trace/index.html)
## Overview
`tokio-trace` is a framework for instrumenting Rust programs to collect
structured, event-based diagnostic information.
In asynchronous systems like Tokio, interpreting traditional log messages can
often be quite challenging. Since individual tasks are multiplexed on the same
thread, associated events and log lines are intermixed making it difficult to
trace the logic flow. `tokio-trace` expands upon logging-style diagnostics by
allowing libraries and applications to record structured events with additional
information about *temporality* and *causality* — unlike a log message, a span
in `tokio-trace` has a beginning and end time, may be entered and exited by the
flow of execution, and may exist within a nested tree of similar spans. In
addition, `tokio-trace` spans are *structured*, with the ability to record typed
data as well as textual messages.
The `tokio-trace` crate provides the APIs necessary for instrumenting libraries
and applications to emit trace data.
## Usage
First, add this to your `Cargo.toml`:
```toml
[dependencies]
tokio-trace = "0.1"
```
Next, add this to your crate:
```rust
#[macro_use]
extern crate tokio_trace;
```
This crate provides macros for creating `Span`s and `Event`s, which represent
periods of time and momentary events within the execution of a program,
respectively.
As a rule of thumb, _spans_ should be used to represent discrete units of work
(e.g., a given request's lifetime in a server) or periods of time spent in a
given context (e.g., time spent interacting with an instance of an external
system, such as a database). In contrast, _events_ should be used to represent
points in time within a span — a request returned with a given status code,
_n_ new items were taken from a queue, and so on.
`Span`s are constructed using the `span!` macro, and then _entered_
to indicate that some code takes place within the context of that `Span`:
```rust
// Construct a new span named "my span".
let mut span = span!("my span");
span.in_scope(|| {
// Any trace events in this closure or code called by it will occur within
// the span.
});
// Dropping the span will close it, indicating that it has ended.
```
The `Event` type represent an event that occurs instantaneously, and is
essentially a `Span` that cannot be entered. They are created using the `event!`
macro:
```rust
use tokio_trace::Level;
event!(Level::INFO, "something has happened!");
```
Users of the [`log`] crate should note that `tokio-trace` exposes a set of macros for
creating `Event`s (`trace!`, `debug!`, `info!`, `warn!`, and `error!`) which may
be invoked with the same syntax as the similarly-named macros from the `log`
crate. Often, the process of converting a project to use `tokio-trace` can begin
with a simple drop-in replacement.
Let's consider the `log` crate's yak-shaving example:
```rust
#[macro_use]
extern crate tokio_trace;
use tokio_trace::field;
pub fn shave_the_yak(yak: &mut Yak) {
// Create a new span for this invocation of `shave_the_yak`, annotated
// with the yak being shaved as a *field* on the span.
span!("shave_the_yak", yak = field::debug(&yak)).in_scope(|| {
// Since the span is annotated with the yak, it is part of the context
// for everything happening inside the span. Therefore, we don't need
// to add it to the message for this event, as the `log` crate does.
info!(target: "yak_events", "Commencing yak shaving");
loop {
match find_a_razor() {
Ok(razor) => {
// We can add the razor as a field rather than formatting it
// as part of the message, allowing subscribers to consume it
// in a more structured manner:
info!({ razor = field::display(razor) }, "Razor located");
yak.shave(razor);
break;
}
Err(err) => {
// However, we can also create events with formatted messages,
// just as we would for log records.
warn!("Unable to locate a razor: {}, retrying", err);
}
}
}
})
}
```
You can find examples showing how to use this crate in the examples directory.
### In libraries
Libraries should link only to the `tokio-trace` crate, and use the provided
macros to record whatever information will be useful to downstream consumers.
### In executables
In order to record trace events, executables have to use a `Subscriber`
implementation compatible with `tokio-trace`. A `Subscriber` implements a way of
collecting trace data, such as by logging it to standard output.
There currently aren't too many subscribers to choose from. The best one to use right now
is probably [`tokio-trace-fmt`], which logs to the terminal.
The simplest way to use a subscriber is to call the `set_global_default` function:
```rust
#[macro_use]
extern crate tokio_trace;
let my_subscriber = FooSubscriber::new();
tokio_trace::subscriber::set_global_default(my_subscriber).expect("setting tokio_trace default failed");
```
This subscriber will be used as the default in all threads for the remainder of the duration
of the program, similar to how loggers work in the `log` crate.
Note: Libraries should *NOT* call `set_global_default()`! That will cause conflicts when
executables try to set the default later.
In addition, you can locally override the default subscriber, using the `tokio` pattern
of executing code in a context. For example:
```rust
#[macro_use]
extern crate tokio_trace;
let my_subscriber = FooSubscriber::new();
tokio_trace::subscriber::with_default(subscriber, || {
// Any trace events generated in this closure or by functions it calls
// will be collected by `my_subscriber`.
})
```
This approach allows trace data to be collected by multiple subscribers within
different contexts in the program. Note that the override only applies to the
currently executing thread; other threads will not see the change from with_default.
Any trace events generated outside the context of a
subscriber will not be collected.
The executable itself may use the `tokio-trace` crate to instrument itself as
well.
The [`tokio-trace-nursery`] repository contains less stable crates designed to
be used with the `tokio-trace` ecosystem. It includes a collection of
`Subscriber` implementations, as well as utility and adapter crates.
[`log`]: https://docs.rs/log/0.4.6/log/
[`tokio-trace-nursery`]: https://github.com/tokio-rs/tokio-trace-nursery
[`tokio-trace-fmt`]: https://github.com/tokio-rs/tokio-trace-nursery/tree/master/tokio-trace-fmt
## License
This project is licensed under the [MIT license](LICENSE).
### Contribution
Unless you explicitly state otherwise, any contribution intentionally submitted
for inclusion in Tokio by you, shall be licensed as MIT, without any additional
terms or conditions.
-42
View File
@@ -1,42 +0,0 @@
#[macro_use]
extern crate tokio_trace;
#[macro_use]
extern crate log;
#[macro_use]
extern crate criterion;
use criterion::Criterion;
use tokio_trace::Level;
fn criterion_benchmark(c: &mut Criterion) {
c.bench_function("span_no_subscriber", |b| {
b.iter(|| {
span!(Level::TRACE, "span");
})
});
c.bench_function("bench_log_no_logger", |b| {
b.iter(|| {
log!(log::Level::Info, "log");
});
});
c.bench_function("bench_costly_field_no_subscriber", |b| {
b.iter(|| {
span!(
Level::TRACE,
"span",
foo = tokio_trace::field::display(format!("bar {:?}", 2))
);
});
});
// This is just included as a baseline.
c.bench_function("bench_no_span_no_subscriber", |b| {
b.iter(|| {});
});
c.bench_function("bench_1_atomic_load", |b| {
use std::sync::atomic::{AtomicUsize, Ordering};
let foo = AtomicUsize::new(1);
b.iter(|| foo.load(Ordering::Relaxed));
});
}
criterion_group!(benches, criterion_benchmark);
criterion_main!(benches);
-158
View File
@@ -1,158 +0,0 @@
#[macro_use]
extern crate tokio_trace;
#[macro_use]
extern crate criterion;
use criterion::{black_box, Criterion};
use tokio_trace::Level;
use std::{
fmt,
sync::{Mutex, MutexGuard},
};
use tokio_trace::{field, span, Event, Id, Metadata};
/// A subscriber that is enabled but otherwise does nothing.
struct EnabledSubscriber;
impl tokio_trace::Subscriber for EnabledSubscriber {
fn new_span(&self, span: &span::Attributes) -> Id {
let _ = span;
Id::from_u64(0xDEADFACE)
}
fn event(&self, event: &Event) {
let _ = event;
}
fn record(&self, span: &Id, values: &span::Record) {
let _ = (span, values);
}
fn record_follows_from(&self, span: &Id, follows: &Id) {
let _ = (span, follows);
}
fn enabled(&self, metadata: &Metadata) -> bool {
let _ = metadata;
true
}
fn enter(&self, span: &Id) {
let _ = span;
}
fn exit(&self, span: &Id) {
let _ = span;
}
}
/// Simulates a subscriber that records span data.
struct VisitingSubscriber(Mutex<String>);
struct Visitor<'a>(MutexGuard<'a, String>);
impl<'a> field::Visit for Visitor<'a> {
fn record_debug(&mut self, _field: &field::Field, value: &fmt::Debug) {
use std::fmt::Write;
let _ = write!(&mut *self.0, "{:?}", value);
}
}
impl tokio_trace::Subscriber for VisitingSubscriber {
fn new_span(&self, span: &span::Attributes) -> Id {
let mut visitor = Visitor(self.0.lock().unwrap());
span.record(&mut visitor);
Id::from_u64(0xDEADFACE)
}
fn record(&self, _span: &Id, values: &span::Record) {
let mut visitor = Visitor(self.0.lock().unwrap());
values.record(&mut visitor);
}
fn event(&self, event: &Event) {
let mut visitor = Visitor(self.0.lock().unwrap());
event.record(&mut visitor);
}
fn record_follows_from(&self, span: &Id, follows: &Id) {
let _ = (span, follows);
}
fn enabled(&self, metadata: &Metadata) -> bool {
let _ = metadata;
true
}
fn enter(&self, span: &Id) {
let _ = span;
}
fn exit(&self, span: &Id) {
let _ = span;
}
}
const N_SPANS: usize = 100;
fn criterion_benchmark(c: &mut Criterion) {
c.bench_function("span_no_fields", |b| {
tokio_trace::subscriber::with_default(EnabledSubscriber, || {
b.iter(|| span!(Level::TRACE, "span"))
});
});
c.bench_function("enter_span", |b| {
tokio_trace::subscriber::with_default(EnabledSubscriber, || {
let span = span!(Level::TRACE, "span");
b.iter(|| black_box(span.in_scope(|| {})))
});
});
c.bench_function("span_repeatedly", |b| {
#[inline]
fn mk_span(i: u64) -> tokio_trace::Span {
span!(Level::TRACE, "span", i = i)
}
let n = black_box(N_SPANS);
tokio_trace::subscriber::with_default(EnabledSubscriber, || {
b.iter(|| (0..n).fold(mk_span(0), |_, i| mk_span(i as u64)))
});
});
c.bench_function("span_with_fields", |b| {
tokio_trace::subscriber::with_default(EnabledSubscriber, || {
b.iter(|| {
span!(
Level::TRACE,
"span",
foo = "foo",
bar = "bar",
baz = 3,
quuux = tokio_trace::field::debug(0.99)
)
})
});
});
c.bench_function("span_with_fields_record", |b| {
let subscriber = VisitingSubscriber(Mutex::new(String::from("")));
tokio_trace::subscriber::with_default(subscriber, || {
b.iter(|| {
span!(
Level::TRACE,
"span",
foo = "foo",
bar = "bar",
baz = 3,
quuux = tokio_trace::field::debug(0.99)
)
})
});
});
}
criterion_group!(benches, criterion_benchmark);
criterion_main!(benches);
-144
View File
@@ -1,144 +0,0 @@
#[macro_use]
extern crate tokio_trace;
use tokio_trace::{
field::{Field, Visit},
span,
subscriber::{self, Subscriber},
Event, Id, Level, Metadata,
};
use std::{
collections::HashMap,
fmt,
sync::{
atomic::{AtomicUsize, Ordering},
Arc, RwLock, RwLockReadGuard,
},
};
#[derive(Clone)]
struct Counters(Arc<RwLock<HashMap<String, AtomicUsize>>>);
struct CounterSubscriber {
ids: AtomicUsize,
counters: Counters,
}
struct Count<'a> {
counters: RwLockReadGuard<'a, HashMap<String, AtomicUsize>>,
}
impl<'a> Visit for Count<'a> {
fn record_i64(&mut self, field: &Field, value: i64) {
if let Some(counter) = self.counters.get(field.name()) {
if value > 0 {
counter.fetch_add(value as usize, Ordering::Release);
} else {
counter.fetch_sub((value * -1) as usize, Ordering::Release);
}
};
}
fn record_u64(&mut self, field: &Field, value: u64) {
if let Some(counter) = self.counters.get(field.name()) {
counter.fetch_add(value as usize, Ordering::Release);
};
}
fn record_bool(&mut self, _: &Field, _: bool) {}
fn record_str(&mut self, _: &Field, _: &str) {}
fn record_debug(&mut self, _: &Field, _: &fmt::Debug) {}
}
impl CounterSubscriber {
fn visitor(&self) -> Count {
Count {
counters: self.counters.0.read().unwrap(),
}
}
}
impl Subscriber for CounterSubscriber {
fn register_callsite(&self, meta: &Metadata) -> subscriber::Interest {
let mut interest = subscriber::Interest::never();
for key in meta.fields() {
let name = key.name();
if name.contains("count") {
self.counters
.0
.write()
.unwrap()
.entry(name.to_owned())
.or_insert_with(|| AtomicUsize::new(0));
interest = subscriber::Interest::always();
}
}
interest
}
fn new_span(&self, new_span: &span::Attributes) -> Id {
new_span.record(&mut self.visitor());
let id = self.ids.fetch_add(1, Ordering::SeqCst);
Id::from_u64(id as u64)
}
fn record_follows_from(&self, _span: &Id, _follows: &Id) {
// unimplemented
}
fn record(&self, _: &Id, values: &span::Record) {
values.record(&mut self.visitor())
}
fn event(&self, event: &Event) {
event.record(&mut self.visitor())
}
fn enabled(&self, metadata: &Metadata) -> bool {
metadata.fields().iter().any(|f| f.name().contains("count"))
}
fn enter(&self, _span: &Id) {}
fn exit(&self, _span: &Id) {}
}
impl Counters {
fn print_counters(&self) {
for (k, v) in self.0.read().unwrap().iter() {
println!("{}: {}", k, v.load(Ordering::Acquire));
}
}
fn new() -> (Self, CounterSubscriber) {
let counters = Counters(Arc::new(RwLock::new(HashMap::new())));
let subscriber = CounterSubscriber {
ids: AtomicUsize::new(1),
counters: counters.clone(),
};
(counters, subscriber)
}
}
fn main() {
let (counters, subscriber) = Counters::new();
tokio_trace::subscriber::with_default(subscriber, || {
let mut foo: u64 = 2;
span!(Level::TRACE, "my_great_span", foo_count = &foo).in_scope(|| {
foo += 1;
info!({ yak_shaved = true, yak_count = 1 }, "hi from inside my span");
span!(
Level::TRACE,
"my other span",
foo_count = &foo,
baz_count = 5
)
.in_scope(|| {
warn!({ yak_shaved = false, yak_count = -1 }, "failed to shave yak");
});
});
});
counters.print_counters();
}
-53
View File
@@ -1,53 +0,0 @@
//! A simple example demonstrating how one might implement a custom
//! subscriber.
//!
//! This subscriber implements a tree-structured logger similar to
//! the "compact" formatter in [`slog-term`]. The demo mimicks the
//! example output in the screenshot in the [`slog` README].
//!
//! Note that this logger isn't ready for actual production use.
//! Several corners were cut to make the example simple.
//!
//! [`slog-term`]: https://docs.rs/slog-term/2.4.0/slog_term/
//! [`slog` README]: https://github.com/slog-rs/slog#terminal-output-example
#[macro_use]
extern crate tokio_trace;
use tokio_trace::{field, Level};
mod sloggish_subscriber;
use self::sloggish_subscriber::SloggishSubscriber;
fn main() {
let subscriber = SloggishSubscriber::new(2);
tokio_trace::subscriber::with_default(subscriber, || {
span!(Level::TRACE, "", version = &field::display(5.0)).in_scope(|| {
span!(Level::TRACE, "server", host = "localhost", port = 8080).in_scope(|| {
info!("starting");
info!("listening");
let peer1 = span!(Level::TRACE, "conn", peer_addr = "82.9.9.9", port = 42381);
peer1.in_scope(|| {
debug!("connected");
debug!({ length = 2 }, "message received");
});
let peer2 = span!(Level::TRACE, "conn", peer_addr = "8.8.8.8", port = 18230);
peer2.in_scope(|| {
debug!("connected");
});
peer1.in_scope(|| {
warn!({ algo = "xor" }, "weak encryption requested");
debug!({ length = 8 }, "response sent");
debug!("disconnected");
});
peer2.in_scope(|| {
debug!({ length = 5 }, "message received");
debug!({ length = 8 }, "response sent");
debug!("disconnected");
});
warn!("internal error");
info!("exit");
})
});
});
}
@@ -1,285 +0,0 @@
//! A simple example demonstrating how one might implement a custom
//! subscriber.
//!
//! This subscriber implements a tree-structured logger similar to
//! the "compact" formatter in [`slog-term`]. The demo mimicks the
//! example output in the screenshot in the [`slog` README].
//!
//! Note that this logger isn't ready for actual production use.
//! Several corners were cut to make the example simple.
//!
//! [`slog-term`]: https://docs.rs/slog-term/2.4.0/slog_term/
//! [`slog` README]: https://github.com/slog-rs/slog#terminal-output-example
extern crate ansi_term;
extern crate humantime;
use self::ansi_term::{Color, Style};
use super::tokio_trace::{
self,
field::{Field, Visit},
Id, Level, Subscriber,
};
use std::{
cell::RefCell,
collections::HashMap,
fmt,
io::{self, Write},
sync::{
atomic::{AtomicUsize, Ordering},
Mutex,
},
thread,
time::SystemTime,
};
/// Tracks the currently executing span on a per-thread basis.
#[derive(Clone)]
pub struct CurrentSpanPerThread {
current: &'static thread::LocalKey<RefCell<Vec<Id>>>,
}
impl CurrentSpanPerThread {
pub fn new() -> Self {
thread_local! {
static CURRENT: RefCell<Vec<Id>> = RefCell::new(vec![]);
};
Self { current: &CURRENT }
}
/// Returns the [`Id`](::Id) of the span in which the current thread is
/// executing, or `None` if it is not inside of a span.
pub fn id(&self) -> Option<Id> {
self.current
.with(|current| current.borrow().last().cloned())
}
pub fn enter(&self, span: Id) {
self.current.with(|current| {
current.borrow_mut().push(span);
})
}
pub fn exit(&self) {
self.current.with(|current| {
let _ = current.borrow_mut().pop();
})
}
}
pub struct SloggishSubscriber {
// TODO: this can probably be unified with the "stack" that's used for
// printing?
current: CurrentSpanPerThread,
indent_amount: usize,
stderr: io::Stderr,
stack: Mutex<Vec<Id>>,
spans: Mutex<HashMap<Id, Span>>,
ids: AtomicUsize,
}
struct Span {
parent: Option<Id>,
kvs: Vec<(&'static str, String)>,
}
struct Event<'a> {
stderr: io::StderrLock<'a>,
comma: bool,
}
struct ColorLevel<'a>(&'a Level);
impl<'a> fmt::Display for ColorLevel<'a> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self.0 {
&Level::TRACE => Color::Purple.paint("TRACE"),
&Level::DEBUG => Color::Blue.paint("DEBUG"),
&Level::INFO => Color::Green.paint("INFO "),
&Level::WARN => Color::Yellow.paint("WARN "),
&Level::ERROR => Color::Red.paint("ERROR"),
}
.fmt(f)
}
}
impl Span {
fn new(parent: Option<Id>, attrs: &tokio_trace::span::Attributes) -> Self {
let mut span = Self {
parent,
kvs: Vec::new(),
};
attrs.record(&mut span);
span
}
}
impl Visit for Span {
fn record_debug(&mut self, field: &Field, value: &fmt::Debug) {
self.kvs.push((field.name(), format!("{:?}", value)))
}
}
impl<'a> Visit for Event<'a> {
fn record_debug(&mut self, field: &Field, value: &fmt::Debug) {
write!(
&mut self.stderr,
"{comma} ",
comma = if self.comma { "," } else { "" },
)
.unwrap();
let name = field.name();
if name == "message" {
write!(
&mut self.stderr,
"{}",
// Have to alloc here due to `ansi_term`'s API...
Style::new().bold().paint(format!("{:?}", value))
)
.unwrap();
self.comma = true;
} else {
write!(
&mut self.stderr,
"{}: {:?}",
Style::new().bold().paint(name),
value
)
.unwrap();
self.comma = true;
}
}
}
impl SloggishSubscriber {
pub fn new(indent_amount: usize) -> Self {
Self {
current: CurrentSpanPerThread::new(),
indent_amount,
stderr: io::stderr(),
stack: Mutex::new(vec![]),
spans: Mutex::new(HashMap::new()),
ids: AtomicUsize::new(1),
}
}
fn print_kvs<'a, I, K, V>(
&self,
writer: &mut impl Write,
kvs: I,
leading: &str,
) -> io::Result<()>
where
I: IntoIterator<Item = (K, V)>,
K: AsRef<str> + 'a,
V: fmt::Display + 'a,
{
let mut kvs = kvs.into_iter();
if let Some((k, v)) = kvs.next() {
write!(
writer,
"{}{}: {}",
leading,
Style::new().bold().paint(k.as_ref()),
v
)?;
}
for (k, v) in kvs {
write!(writer, ", {}: {}", Style::new().bold().paint(k.as_ref()), v)?;
}
Ok(())
}
fn print_indent(&self, writer: &mut impl Write, indent: usize) -> io::Result<()> {
for _ in 0..(indent * self.indent_amount) {
write!(writer, " ")?;
}
Ok(())
}
}
impl Subscriber for SloggishSubscriber {
fn enabled(&self, _metadata: &tokio_trace::Metadata) -> bool {
true
}
fn new_span(&self, span: &tokio_trace::span::Attributes) -> tokio_trace::Id {
let next = self.ids.fetch_add(1, Ordering::SeqCst) as u64;
let id = tokio_trace::Id::from_u64(next);
let span = Span::new(self.current.id(), span);
self.spans.lock().unwrap().insert(id.clone(), span);
id
}
fn record(&self, span: &tokio_trace::Id, values: &tokio_trace::span::Record) {
let mut spans = self.spans.lock().expect("mutex poisoned!");
if let Some(span) = spans.get_mut(span) {
values.record(span);
}
}
fn record_follows_from(&self, _span: &tokio_trace::Id, _follows: &tokio_trace::Id) {
// unimplemented
}
fn enter(&self, span_id: &tokio_trace::Id) {
self.current.enter(span_id.clone());
let mut stderr = self.stderr.lock();
let mut stack = self.stack.lock().unwrap();
let spans = self.spans.lock().unwrap();
let data = spans.get(span_id);
let parent = data.and_then(|span| span.parent.as_ref());
if stack.iter().any(|id| id == span_id) {
// We are already in this span, do nothing.
return;
} else {
let indent = if let Some(idx) = stack
.iter()
.position(|id| parent.map(|p| id == p).unwrap_or(false))
{
let idx = idx + 1;
stack.truncate(idx);
idx
} else {
stack.clear();
0
};
self.print_indent(&mut stderr, indent).unwrap();
stack.push(span_id.clone());
if let Some(data) = data {
self.print_kvs(&mut stderr, data.kvs.iter().map(|(k, v)| (k, v)), "")
.unwrap();
}
write!(&mut stderr, "\n").unwrap();
}
}
fn event(&self, event: &tokio_trace::Event) {
let mut stderr = self.stderr.lock();
let indent = self.stack.lock().unwrap().len();
self.print_indent(&mut stderr, indent).unwrap();
write!(
&mut stderr,
"{timestamp} {level} {target}",
timestamp = humantime::format_rfc3339_seconds(SystemTime::now()),
level = ColorLevel(event.metadata().level()),
target = &event.metadata().target(),
)
.unwrap();
let mut visitor = Event {
stderr,
comma: false,
};
event.record(&mut visitor);
write!(&mut visitor.stderr, "\n").unwrap();
}
#[inline]
fn exit(&self, _span: &tokio_trace::Id) {
// TODO: unify stack with current span
self.current.exit();
}
fn drop_span(&self, _id: tokio_trace::Id) {
// TODO: GC unneeded spans.
}
}
-56
View File
@@ -1,56 +0,0 @@
//! Structured data associated with `Span`s and `Event`s.
pub use tokio_trace_core::field::*;
use Metadata;
/// Trait implemented to allow a type to be used as a field key.
///
/// **Note**: Although this is implemented for both the [`Field`] type *and* any
/// type that can be borrowed as an `&str`, only `Field` allows _O_(1) access.
/// Indexing a field with a string results in an iterative search that performs
/// string comparisons. Thus, if possible, once the key for a field is known, it
/// should be used whenever possible.
///
/// [`Field`]: ../struct.Field.html
pub trait AsField: ::sealed::Sealed {
/// Attempts to convert `&self` into a `Field` with the specified `metadata`.
///
/// If `metadata` defines this field, then the field is returned. Otherwise,
/// this returns `None`.
fn as_field(&self, metadata: &Metadata) -> Option<Field>;
}
// ===== impl AsField =====
impl AsField for Field {
#[inline]
fn as_field(&self, metadata: &Metadata) -> Option<Field> {
if self.callsite() == metadata.callsite() {
Some(self.clone())
} else {
None
}
}
}
impl<'a> AsField for &'a Field {
#[inline]
fn as_field(&self, metadata: &Metadata) -> Option<Field> {
if self.callsite() == metadata.callsite() {
Some((*self).clone())
} else {
None
}
}
}
impl AsField for str {
#[inline]
fn as_field(&self, metadata: &Metadata) -> Option<Field> {
metadata.fields().field(&self)
}
}
impl ::sealed::Sealed for Field {}
impl<'a> ::sealed::Sealed for &'a Field {}
impl ::sealed::Sealed for str {}
-137
View File
@@ -1,137 +0,0 @@
//! Trace verbosity level filtering.
//!
//! # Compile time filters
//!
//! Trace verbosity levels can be statically disabled at compile time via Cargo
//! features, similar to the [`log` crate]. Trace instrumentation at disabled
//! levels will be skipped and will not even be present in the resulting binary
//! unless the verbosity level is specified dynamically. This level is
//! configured separately for release and debug builds. The features are:
//!
//! * `max_level_off`
//! * `max_level_error`
//! * `max_level_warn`
//! * `max_level_info`
//! * `max_level_debug`
//! * `max_level_trace`
//! * `release_max_level_off`
//! * `release_max_level_error`
//! * `release_max_level_warn`
//! * `release_max_level_info`
//! * `release_max_level_debug`
//! * `release_max_level_trace`
//!
//! These features control the value of the `STATIC_MAX_LEVEL` constant. The
//! instrumentation macros macros check this value before recording an event or
//! constructing a span. By default, no levels are disabled.
//!
//! For example, a crate can disable trace level instrumentation in debug builds
//! and trace, debug, and info level instrumentation in release builds with the
//! following configuration:
//!
//! ```toml
//! [dependencies]
//! tokio-trace = { version = "0.1", features = ["max_level_debug", "release_max_level_warn"] }
//! ```
//!
//! [`log` crate]: https://docs.rs/log/0.4.6/log/#compile-time-filters
use std::cmp::Ordering;
use tokio_trace_core::Level;
/// A filter comparable to trace verbosity `Level`.
///
/// If a `Level` is considered less than a `LevelFilter`, it should be
/// considered disabled; if greater than or equal to the `LevelFilter`, that
/// level is enabled.
///
/// Note that this is essentially identical to the `Level` type, but with the
/// addition of an `OFF` level that completely disables all trace
/// instrumentation.
#[derive(Clone, Debug, Eq, PartialEq, Ord, PartialOrd)]
pub struct LevelFilter(Option<Level>);
impl LevelFilter {
/// The "off" level.
///
/// Designates that trace instrumentation should be completely disabled.
pub const OFF: LevelFilter = LevelFilter(None);
/// The "error" level.
///
/// Designates very serious errors.
pub const ERROR: LevelFilter = LevelFilter(Some(Level::ERROR));
/// The "warn" level.
///
/// Designates hazardous situations.
pub const WARN: LevelFilter = LevelFilter(Some(Level::WARN));
/// The "info" level.
///
/// Designates useful information.
pub const INFO: LevelFilter = LevelFilter(Some(Level::INFO));
/// The "debug" level.
///
/// Designates lower priority information.
pub const DEBUG: LevelFilter = LevelFilter(Some(Level::DEBUG));
/// The "trace" level.
///
/// Designates very low priority, often extremely verbose, information.
pub const TRACE: LevelFilter = LevelFilter(Some(Level::TRACE));
}
impl PartialEq<LevelFilter> for Level {
fn eq(&self, other: &LevelFilter) -> bool {
match other.0 {
None => false,
Some(ref level) => self.eq(level),
}
}
}
impl PartialOrd<LevelFilter> for Level {
fn partial_cmp(&self, other: &LevelFilter) -> Option<Ordering> {
match other.0 {
None => Some(Ordering::Less),
Some(ref level) => self.partial_cmp(level),
}
}
}
/// The statically configured maximum trace level.
///
/// See the [module-level documentation] for information on how to configure
/// this.
///
/// This value is checked by the `event!` and `span!` macros. Code that
/// manually constructs events or spans via the `Event::record` function or
/// `Span` constructors should compare the level against this value to
/// determine if those spans or events are enabled.
///
/// [module-level documentation]: ../index.html#compile-time-filters
pub const STATIC_MAX_LEVEL: LevelFilter = MAX_LEVEL;
cfg_if! {
if #[cfg(all(not(debug_assertions), feature = "release_max_level_off"))] {
const MAX_LEVEL: LevelFilter = LevelFilter::OFF;
} else if #[cfg(all(not(debug_assertions), feature = "release_max_level_error"))] {
const MAX_LEVEL: LevelFilter = LevelFilter::ERROR;
} else if #[cfg(all(not(debug_assertions), feature = "release_max_level_warn"))] {
const MAX_LEVEL: LevelFilter = LevelFilter::WARN;
} else if #[cfg(all(not(debug_assertions), feature = "release_max_level_info"))] {
const MAX_LEVEL: LevelFilter = LevelFilter::INFO;
} else if #[cfg(all(not(debug_assertions), feature = "release_max_level_debug"))] {
const MAX_LEVEL: LevelFilter = LevelFilter::DEBUG;
} else if #[cfg(all(not(debug_assertions), feature = "release_max_level_trace"))] {
const MAX_LEVEL: LevelFilter = LevelFilter::TRACE;
} else if #[cfg(feature = "max_level_off")] {
const MAX_LEVEL: LevelFilter = LevelFilter::OFF;
} else if #[cfg(feature = "max_level_error")] {
const MAX_LEVEL: LevelFilter = LevelFilter::ERROR;
} else if #[cfg(feature = "max_level_warn")] {
const MAX_LEVEL: LevelFilter = LevelFilter::WARN;
} else if #[cfg(feature = "max_level_info")] {
const MAX_LEVEL: LevelFilter = LevelFilter::INFO;
} else if #[cfg(feature = "max_level_debug")] {
const MAX_LEVEL: LevelFilter = LevelFilter::DEBUG;
} else {
const MAX_LEVEL: LevelFilter = LevelFilter::TRACE;
}
}
-378
View File
@@ -1,378 +0,0 @@
#![doc(html_root_url = "https://docs.rs/tokio-trace/0.1.0")]
#![deny(missing_debug_implementations, missing_docs, unreachable_pub)]
#![cfg_attr(test, deny(warnings))]
//! A scoped, structured logging and diagnostics system.
//!
//! # Overview
//!
//! `tokio-trace` is a framework for instrumenting Rust programs to collect
//! structured, event-based diagnostic information.
//!
//! In asynchronous systems like Tokio, interpreting traditional log messages can
//! often be quite challenging. Since individual tasks are multiplexed on the same
//! thread, associated events and log lines are intermixed making it difficult to
//! trace the logic flow. `tokio-trace` expands upon logging-style diagnostics by
//! allowing libraries and applications to record structured events with additional
//! information about *temporality* and *causality* — unlike a log message, a span
//! in `tokio-trace` has a beginning and end time, may be entered and exited by the
//! flow of execution, and may exist within a nested tree of similar spans. In
//! addition, `tokio-trace` spans are *structured*, with the ability to record typed
//! data as well as textual messages.
//!
//! The `tokio-trace` crate provides the APIs necessary for instrumenting libraries
//! and applications to emit trace data.
//!
//! # Core Concepts
//!
//! The core of `tokio-trace`'s API is composed of _spans_, _events_ and
//! _subscribers_. We'll cover these in turn.
//!
//! ## Spans
//!
//! A [`span`] represents a _period of time_ during which a program was executing
//! in some context. A thread of execution is said to _enter_ a span when it
//! begins executing in that context, and to _exit_ the span when switching to
//! another context. The span in which a thread is currently executing is
//! referred to as the _current_ span.
//!
//! For example:
//! ```
//! #[macro_use]
//! extern crate tokio_trace;
//!
//! use tokio_trace::Level;
//!
//! # fn main() {
//! let span = span!(Level::TRACE, "my_span");
//! let _enter = span.enter();
//! // perform some work in the context of `my_span`...
//! # }
//!```
//!
//! The [`span` module]'s documentation provides further details on how to use spans.
//!
//! ## Events
//!
//! An [`Event`] represents a _point_ in time. It signifies something that
//! happened while the trace was executing. `Event`s are comparable to the log
//! records emitted by unstructured logging code, but unlike a typical log line,
//! an `Event` may occur within the context of a `Span`. Like a `Span`, it
//! may have fields, and implicitly inherits any of the fields present on its
//! parent span.
//!
//! For example:
//! ```
//! # #[macro_use] extern crate tokio_trace;
//! # use tokio_trace::Level;
//! # fn main() {
//! // records an event outside of any span context:
//! event!(Level::INFO, "something happened");
//!
//! span!(Level::INFO, "my_span").in_scope(|| {
//! // records an event within "my_span".
//! event!(Level::DEBUG, "something happened inside my_span");
//! });
//! # }
//!```
//!
//! Essentially, `Event`s exist to bridge the gap between traditional
//! unstructured logging and span-based tracing. Similar to log records, they
//! may be recorded at a number of levels, and can have unstructured,
//! human-readable messages; however, they also carry key-value data and exist
//! within the context of the tree of spans that comprise a trace. Thus,
//! individual log record-like events can be pinpointed not only in time, but
//! in the logical execution flow of the system.
//!
//! Events are represented as a special case of spans — they are created, they
//! may have fields added, and then they close immediately, without being
//! entered.
//!
//! In general, events should be used to represent points in time _within_ a
//! span — a request returned with a given status code, _n_ new items were
//! taken from a queue, and so on.
//!
//! ## `Subscriber`s
//!
//! As `Span`s and `Event`s occur, they are recorded or aggregated by
//! implementations of the [`Subscriber`] trait. `Subscriber`s are notified
//! when an `Event` takes place and when a `Span` is entered or exited. These
//! notifications are represented by the following `Subscriber` trait methods:
//! + [`observe_event`], called when an `Event` takes place,
//! + [`enter`], called when execution enters a `Span`,
//! + [`exit`], called when execution exits a `Span`
//!
//! In addition, subscribers may implement the [`enabled`] function to _filter_
//! the notifications they receive based on [metadata] describing each `Span`
//! or `Event`. If a call to `Subscriber::enabled` returns `false` for a given
//! set of metadata, that `Subscriber` will *not* be notified about the
//! corresponding `Span` or `Event`. For performance reasons, if no currently
//! active subscribers express interest in a given set of metadata by returning
//! `true`, then the corresponding `Span` or `Event` will never be constructed.
//!
//! # Usage
//!
//! First, add this to your `Cargo.toml`:
//!
//! ```toml
//! [dependencies]
//! tokio-trace = "0.1"
//! ```
//!
//! Next, add this to your crate:
//!
//! ```rust
//! #[macro_use]
//! extern crate tokio_trace;
//! # fn main() {}
//! ```
//!
//! `Span`s are constructed using the `span!` macro, and then _entered_
//! to indicate that some code takes place within the context of that `Span`:
//!
//! ```rust
//! # #[macro_use]
//! # extern crate tokio_trace;
//! # use tokio_trace::Level;
//! # fn main() {
//! // Construct a new span named "my span" with trace log level.
//! let span = span!(Level::TRACE, "my span");
//!
//! // Enter the span, returning a guard object.
//! let _enter = span.enter();
//!
//! // Any trace events that occur before the guard is dropped will occur
//! // within the span.
//!
//! // Dropping the guard will exit the span.
//! # }
//! ```
//!
//! `Event`s are created using the `event!` macro, and are recorded when the
//! event is dropped:
//!
//! ```rust
//! # #[macro_use]
//! # extern crate tokio_trace;
//! # fn main() {
//! use tokio_trace::Level;
//! event!(Level::INFO, "something has happened!");
//! # }
//! ```
//!
//! Users of the [`log`] crate should note that `tokio-trace` exposes a set of
//! macros for creating `Event`s (`trace!`, `debug!`, `info!`, `warn!`, and
//! `error!`) which may be invoked with the same syntax as the similarly-named
//! macros from the `log` crate. Often, the process of converting a project to
//! use `tokio-trace` can begin with a simple drop-in replacement.
//!
//! Let's consider the `log` crate's yak-shaving example:
//!
//! ```rust
//! #[macro_use]
//! extern crate tokio_trace;
//! use tokio_trace::Level;
//!
//! # #[derive(Debug)] pub struct Yak(String);
//! # impl Yak { fn shave(&mut self, _: u32) {} }
//! # fn find_a_razor() -> Result<u32, u32> { Ok(1) }
//! # fn main() {
//! pub fn shave_the_yak(yak: &mut Yak) {
//! let span = span!(Level::TRACE, "shave_the_yak", ?yak);
//! let _enter = span.enter();
//!
//! // Since the span is annotated with the yak, it is part of the context
//! // for everything happening inside the span. Therefore, we don't need
//! // to add it to the message for this event, as the `log` crate does.
//! info!(target: "yak_events", "Commencing yak shaving");
//! loop {
//! match find_a_razor() {
//! Ok(razor) => {
//! // We can add the razor as a field rather than formatting it
//! // as part of the message, allowing subscribers to consume it
//! // in a more structured manner:
//! info!({ %razor }, "Razor located");
//! yak.shave(razor);
//! break;
//! }
//! Err(err) => {
//! // However, we can also create events with formatted messages,
//! // just as we would for log records.
//! warn!("Unable to locate a razor: {}, retrying", err);
//! }
//! }
//! }
//! }
//! # }
//! ```
//!
//! You can find examples showing how to use this crate in the examples
//! directory.
//!
//! ## In libraries
//!
//! Libraries should link only to the `tokio-trace` crate, and use the provided
//! macros to record whatever information will be useful to downstream
//! consumers.
//!
//! ## In executables
//!
//! In order to record trace events, executables have to use a `Subscriber`
//! implementation compatible with `tokio-trace`. A `Subscriber` implements a
//! way of collecting trace data, such as by logging it to standard output.
//!
//! There currently aren't too many subscribers to choose from. The best one to use right now
//! is probably [`tokio-trace-fmt`], which logs to the terminal.
//! The simplest way to use a subscriber is to call the `set_global_default` function:
//!
//! ```no_build
//! #[macro_use]
//! extern crate tokio_trace;
//! let my_subscriber = FooSubscriber::new();
//! tokio_trace::subscriber::set_global_default(my_subscriber).expect("setting tokio_trace default failed");
//! ```
//!
//! Note: Libraries should *NOT* call `set_global_default()`! That will cause conflicts when
//! executables try to set the default later.
//!
//! This subscriber will be used as the default in all threads for the remainder of the duration
//! of the program, similar to how loggers work in the `log` crate.
//!
//! In addition, you can locally override the default subscriber, using the `tokio` pattern
//! of executing code in a context. For example:
//!
//! Unlike the `log` crate, `tokio-trace` does *not* use a global `Subscriber`
//! which is initialized once. Instead, it follows the `tokio` pattern of
//! executing code in a context. For example:
//!
//! ```rust
//! #[macro_use]
//! extern crate tokio_trace;
//! # pub struct FooSubscriber;
//! # use tokio_trace::{span::{Id, Attributes, Record}, Metadata};
//! # impl tokio_trace::Subscriber for FooSubscriber {
//! # fn new_span(&self, _: &Attributes) -> Id { Id::from_u64(0) }
//! # fn record(&self, _: &Id, _: &Record) {}
//! # fn event(&self, _: &tokio_trace::Event) {}
//! # fn record_follows_from(&self, _: &Id, _: &Id) {}
//! # fn enabled(&self, _: &Metadata) -> bool { false }
//! # fn enter(&self, _: &Id) {}
//! # fn exit(&self, _: &Id) {}
//! # }
//! # impl FooSubscriber {
//! # fn new() -> Self { FooSubscriber }
//! # }
//! # fn main() {
//!
//! let my_subscriber = FooSubscriber::new();
//!
//! tokio_trace::subscriber::with_default(my_subscriber, || {
//! // Any trace events generated in this closure or by functions it calls
//! // will be collected by `my_subscriber`.
//! })
//! # }
//! ```
//!
//! This approach allows trace data to be collected by multiple subscribers
//! within different contexts in the program. Note that the override only applies to the
//! currently executing thread; other threads will not see the change from with_default.
//! with that subscriber as the default.
//!
//! Any trace events generated outside the
//! context of a subscriber will not be collected.
//!
//! The executable itself may use the `tokio-trace` crate to instrument itself
//! as well.
//!
//! The [`tokio-trace-nursery`] repository contains less stable crates designed
//! to be used with the `tokio-trace` ecosystem. It includes a collection of
//! `Subscriber` implementations, as well as utility and adapter crates.
//!
//! In particular, the following `tokio-trace-nursery` crates are likely to be
//! of interest:
//!
//! - [`tokio-trace-futures`] provides a compatibility layer with the `futures`
//! crate, allowing spans to be attached to `Future`s, `Stream`s, and `Executor`s.
//! - [`tokio-trace-fmt`] provides a `Subscriber` implementation for
//! logging formatted trace data to stdout, with similar filtering and
//! formatting to the `env-logger` crate.
//! - [`tokio-trace-log`] provides a compatibility layer with the `log` crate,
//! allowing log `Record`s to be recorded as `tokio-trace` `Event`s within the
//! trace tree. This is useful when a project using `tokio-trace` have
//! dependencies which use `log`.
//!
//!
//! ## Crate Feature Flags
//!
//! The following crate feature flags are available:
//!
//! * A set of features controlling the [static verbosity level].
//! * `log` causes trace instrumentation points to emit [`log`] records as well
//! as trace events. This is inteded for use in libraries whose users may be
//! using either `tokio-trace` or `log`.
//!
//! ```toml
//! [dependencies]
//! tokio-trace = { version = "0.1", features = ["log"] }
//! ```
//!
//! [`log`]: https://docs.rs/log/0.4.6/log/
//! [`span`]: span/index.html
//! [`span` module]: span/index.html
//! [`in_scope`]: span/struct.Span.html#method.in_scope
//! [`Event`]: struct.Event.html
//! [`Subscriber`]: subscriber/trait.Subscriber.html
//! [`observe_event`]: subscriber/trait.Subscriber.html#tymethod.observe_event
//! [`enter`]: subscriber/trait.Subscriber.html#tymethod.enter
//! [`exit`]: subscriber/trait.Subscriber.html#tymethod.exit
//! [`enabled`]: subscriber/trait.Subscriber.html#tymethod.enabled
//! [metadata]: struct.Metadata.html
//! [`field::display`]: field/fn.display.html
//! [`field::debug`]: field/fn.debug.html
//! [`tokio-trace-nursery`]: https://github.com/tokio-rs/tokio-trace-nursery
//! [`tokio-trace-futures`]: https://github.com/tokio-rs/tokio-trace-nursery/tree/master/tokio-trace-futures
//! [`tokio-trace-fmt`]: https://github.com/tokio-rs/tokio-trace-nursery/tree/master/tokio-trace-fmt
//! [`tokio-trace-log`]: https://github.com/tokio-rs/tokio-trace-nursery/tree/master/tokio-trace-log
//! [static verbosity level]: level_filters/index.html#compile-time-filters
#[macro_use]
extern crate cfg_if;
extern crate tokio_trace_core;
#[cfg(feature = "log")]
#[doc(hidden)]
pub extern crate log;
// Somehow this `use` statement is necessary for us to re-export the `core`
// macros on Rust 1.26.0. I'm not sure how this makes it work, but it does.
#[allow(unused_imports)]
#[doc(hidden)]
use tokio_trace_core::*;
pub use self::{
dispatcher::Dispatch,
event::Event,
field::Value,
span::Span,
subscriber::Subscriber,
tokio_trace_core::{dispatcher, event, Level, Metadata},
};
#[doc(hidden)]
pub use self::{
span::Id,
tokio_trace_core::{
callsite::{self, Callsite},
metadata,
},
};
#[macro_use]
mod macros;
pub mod field;
pub mod level_filters;
pub mod span;
pub mod subscriber;
mod sealed {
pub trait Sealed {}
}
File diff suppressed because it is too large Load Diff
-858
View File
@@ -1,858 +0,0 @@
//! Spans represent periods of time in which a program was executing in a
//! particular context.
//!
//! A span consists of [fields], user-defined key-value pairs of arbitrary data
//! that describe the context the span represents, and [metadata], a fixed set
//! of attributes that describe all `tokio-trace` spans and events. Each span is
//! assigned an [`Id` ] by the subscriber that uniquely identifies it in relation
//! to other spans.
//!
//! # Creating Spans
//!
//! Spans are created using the [`span!`] macro. This macro is invoked with a
//! [verbosity level], followed by a set of attributes whose default values
//! the user whishes to override, a string literal providing the span's name,
//! and finally, between zero and 32 fields.
//!
//! For example:
//! ```rust
//! #[macro_use]
//! extern crate tokio_trace;
//! use tokio_trace::Level;
//!
//! # fn main() {
//! /// Construct a new span at the `INFO` level named "my_span", with a single
//! /// field named answer , with the value `42`.
//! let my_span = span!(Level::INFO, "my_span", answer = 42);
//! # }
//! ```
//!
//! The documentation for the [`span!`] macro provides additional examples of
//! the various options that exist when creating spans.
//!
//! The [`trace_span!`], [`debug_span!`], [`info_span!`], [`warn_span!`], and
//! [`error_span!`] exist as shorthand for constructing spans at various
//! verbosity levels.
//!
//! ## Recording Span Creation
//!
//! The [`Attributes`] type contains data associated with a span, and is
//! provided to the [`Subscriber`] when a new span is created. It contains
//! the span's metadata, the ID of the span's parent if one was explicitly set,
//! and any fields whose values were recorded when the span was constructed.
//! The subscriber may then choose to cache the data for future use, record
//! it in some manner, or discard it completely.
//!
//! # The Span Lifecycle
//!
//! ## Entering a Span
//!
//! A thread of execution is said to _enter_ a span when it begins executing,
//! and _exit_ the span when it switches to another context. Spans may be
//! entered through the [`enter`] and [`in_scope`] methods.
//!
//! The `enter` method enters a span, returning a [guard] that exits the span
//! when dropped
//! ```
//! # #[macro_use] extern crate tokio_trace;
//! # use tokio_trace::Level;
//! # fn main() {
//! let my_var: u64 = 5;
//! let my_span = span!(Level::TRACE, "my_span", my_var);
//!
//! // `my_span` exists but has not been entered.
//!
//! // Enter `my_span`...
//! let _enter = my_span.enter();
//!
//! // Perform some work inside of the context of `my_span`...
//! // Dropping the `_enter` guard will exit the span.
//! # }
//!```
//!
//! `in_scope` takes a closure or function pointer and executes it inside the
//! span.
//! ```
//! # #[macro_use] extern crate tokio_trace;
//! # use tokio_trace::Level;
//! # fn main() {
//! let my_var: u64 = 5;
//! let my_span = span!(Level::TRACE, "my_span", my_var = &my_var);
//!
//! my_span.in_scope(|| {
//! // perform some work in the context of `my_span`...
//! });
//!
//! // Perform some work outside of the context of `my_span`...
//!
//! my_span.in_scope(|| {
//! // Perform some more work in the context of `my_span`.
//! });
//! # }
//! ```
//!
//! **Note:** Since entering a span takes `&self`, and `Span`s are `Clone`,
//! `Send`, and `Sync`, it is entirely valid for multiple threads to enter the
//! same span concurrently.
//!
//! ## Span Relationships
//!
//! Spans form a tree structure — unless it is a root span, all spans have a
//! _parent_, and may have one or more _children_. When a new span is created,
//! the current span becomes the new span's parent. The total execution time of
//! a span consists of the time spent in that span and in the entire subtree
//! represented by its children. Thus, a parent span always lasts for at least
//! as long as the longest-executing span in its subtree.
//!
//! ```
//! # #[macro_use] extern crate tokio_trace;
//! # use tokio_trace::Level;
//! # fn main() {
//! // this span is considered the "root" of a new trace tree:
//! span!(Level::INFO, "root").in_scope(|| {
//! // since we are now inside "root", this span is considered a child
//! // of "root":
//! span!(Level::DEBUG, "outer_child").in_scope(|| {
//! // this span is a child of "outer_child", which is in turn a
//! // child of "root":
//! span!(Level::TRACE, "inner_child").in_scope(|| {
//! // and so on...
//! });
//! });
//! // another span created here would also be a child of "root".
//! });
//! # }
//!```
//!
//! In addition, the parent of a span may be explicitly specified in
//! the `span!` macro. For example:
//!
//! ```rust
//! # #[macro_use] extern crate tokio_trace;
//! # use tokio_trace::Level;
//! # fn main() {
//! // Create, but do not enter, a span called "foo".
//! let foo = span!(Level::INFO, "foo");
//!
//! // Create and enter a span called "bar".
//! let bar = span!(Level::INFO, "bar");
//! let _enter = bar.enter();
//!
//! // Although we have currently entered "bar", "baz"'s parent span
//! // will be "foo".
//! let baz = span!(Level::INFO, parent: &foo, "baz");
//! # }
//! ```
//!
//! A child span should typically be considered _part_ of its parent. For
//! example, if a subscriber is recording the length of time spent in various
//! spans, it should generally include the time spent in a span's children as
//! part of that span's duration.
//!
//! In addition to having zero or one parent, a span may also _follow from_ any
//! number of other spans. This indicates a causal relationship between the span
//! and the spans that it follows from, but a follower is *not* typically
//! considered part of the duration of the span it follows. Unlike the parent, a
//! span may record that it follows from another span after it is created, using
//! the [`follows_from`] method.
//!
//! As an example, consider a listener task in a server. As the listener accepts
//! incoming connections, it spawns new tasks that handle those connections. We
//! might want to have a span representing the listener, and instrument each
//! spawned handler task with its own span. We would want our instrumentation to
//! record that the handler tasks were spawned as a result of the listener task.
//! However, we might not consider the handler tasks to be _part_ of the time
//! spent in the listener task, so we would not consider those spans children of
//! the listener span. Instead, we would record that the handler tasks follow
//! from the listener, recording the causal relationship but treating the spans
//! as separate durations.
//!
//! ## Closing Spans
//!
//! Execution may enter and exit a span multiple times before that span is
//! _closed_. Consider, for example, a future which has an associated
//! span and enters that span every time it is polled:
//! ```rust
//! # extern crate tokio_trace;
//! # extern crate futures;
//! # use futures::{Future, Poll, Async};
//! struct MyFuture {
//! // data
//! span: tokio_trace::Span,
//! }
//!
//! impl Future for MyFuture {
//! type Item = ();
//! type Error = ();
//!
//! fn poll(&mut self) -> Poll<Self::Item, Self::Error> {
//! let _enter = self.span.enter();
//! // Do actual future work...
//! # Ok(Async::Ready(()))
//! }
//! }
//! ```
//!
//! If this future was spawned on an executor, it might yield one or more times
//! before `poll` returns `Ok(Async::Ready)`. If the future were to yield, then
//! the executor would move on to poll the next future, which may _also_ enter
//! an associated span or series of spans. Therefore, it is valid for a span to
//! be entered repeatedly before it completes. Only the time when that span or
//! one of its children was the current span is considered to be time spent in
//! that span. A span which is not executing and has not yet been closed is said
//! to be _idle_.
//!
//! Because spans may be entered and exited multiple times before they close,
//! [`Subscriber`]s have separate trait methods which are called to notify them
//! of span exits and when span handles are dropped. When execution exits a
//! span, [`exit`] will always be called with that span's ID to notify the
//! subscriber that the span has been exited. When span handles are dropped, the
//! [`drop_span`] method is called with that span's ID. The subscriber may use
//! this to determine whether or not the span will be entered again.
//!
//! If there is only a single handle with the capacity to exit a span, dropping
//! that handle "closes" the span, since the capacity to enter it no longer
//! exists. For example:
//! ```
//! # #[macro_use] extern crate tokio_trace;
//! # use tokio_trace::Level;
//! # fn main() {
//! {
//! span!(Level::TRACE, "my_span").in_scope(|| {
//! // perform some work in the context of `my_span`...
//! }); // --> Subscriber::exit(my_span)
//!
//! // The handle to `my_span` only lives inside of this block; when it is
//! // dropped, the subscriber will be informed via `drop_span`.
//!
//! } // --> Subscriber::drop_span(my_span)
//! # }
//! ```
//!
//! However, if multiple handles exist, the span can still be re-entered even if
//! one or more is dropped. For determining when _all_ handles to a span have
//! been dropped, `Subscriber`s have a [`clone_span`] method, which is called
//! every time a span handle is cloned. Combined with `drop_span`, this may be
//! used to track the number of handles to a given span — if `drop_span` has
//! been called one more time than the number of calls to `clone_span` for a
//! given ID, then no more handles to the span with that ID exist. The
//! subscriber may then treat it as closed.
//!
//! # When to use spans
//!
//! As a rule of thumb, spans should be used to represent discrete units of work
//! (e.g., a given request's lifetime in a server) or periods of time spent in a
//! given context (e.g., time spent interacting with an instance of an external
//! system, such as a database).
//!
//! Which scopes in a program correspond to new spans depend somewhat on user
//! intent. For example, consider the case of a loop in a program. Should we
//! construct one span and perform the entire loop inside of that span, like:
//!
//! ```rust
//! # #[macro_use] extern crate tokio_trace;
//! # use tokio_trace::Level;
//! # fn main() {
//! # let n = 1;
//! let span = span!(Level::TRACE, "my_loop");
//! let _enter = span.enter();
//! for i in 0..n {
//! # let _ = i;
//! // ...
//! }
//! # }
//! ```
//! Or, should we create a new span for each iteration of the loop, as in:
//! ```rust
//! # #[macro_use] extern crate tokio_trace;
//! # use tokio_trace::Level;
//! # fn main() {
//! # let n = 1u64;
//! for i in 0..n {
//! let span = span!(Level::TRACE, "my_loop", iteration = i);
//! let _enter = span.enter();
//! // ...
//! }
//! # }
//! ```
//!
//! Depending on the circumstances, we might want to do either, or both. For
//! example, if we want to know how long was spent in the loop overall, we would
//! create a single span around the entire loop; whereas if we wanted to know how
//! much time was spent in each individual iteration, we would enter a new span
//! on every iteration.
//!
//! [fields]: ../field/index.html
//! [metadata]: ../struct.Metadata.html
//! [`Id`]: struct.Id.html
//! [verbosity level]: ../struct.Level.html
//! [`span!`]: ../macro.span.html
//! [`trace_span!`]: ../macro.trace_span.html
//! [`debug_span!`]: ../macro.debug_span.html
//! [`info_span!`]: ../macro.info_span.html
//! [`warn_span!`]: ../macro.warn_span.html
//! [`error_span!`]: ../macro.error_span.html
//! [`clone_span`]: ../subscriber/trait.Subscriber.html#method.clone_span
//! [`drop_span`]: ../subscriber/trait.Subscriber.html#method.drop_span
//! [`exit`]: ../subscriber/trait.Subscriber.html#tymethod.exit
//! [`Subscriber`]: ../subscriber/trait.Subscriber.html
//! [`Attributes`]: struct.Attributes.html
//! [`enter`]: struct.Span.html#method.enter
//! [`in_scope`]: struct.Span.html#method.in_scope
//! [`follows_from`]: struct.Span.html#method.follows_from
//! [guard]: struct.Entered.html
pub use tokio_trace_core::span::{Attributes, Id, Record};
use std::{
cmp, fmt,
hash::{Hash, Hasher},
};
use {dispatcher::Dispatch, field, Metadata};
/// Trait implemented by types which have a span `Id`.
pub trait AsId: ::sealed::Sealed {
/// Returns the `Id` of the span that `self` corresponds to, or `None` if
/// this corresponds to a disabled span.
fn as_id(&self) -> Option<&Id>;
}
/// A handle representing a span, with the capability to enter the span if it
/// exists.
///
/// If the span was rejected by the current `Subscriber`'s filter, entering the
/// span will silently do nothing. Thus, the handle can be used in the same
/// manner regardless of whether or not the trace is currently being collected.
#[derive(Clone)]
pub struct Span {
/// A handle used to enter the span when it is not executing.
///
/// If this is `None`, then the span has either closed or was never enabled.
inner: Option<Inner>,
meta: &'static Metadata<'static>,
}
/// A handle representing the capacity to enter a span which is known to exist.
///
/// Unlike `Span`, this type is only constructed for spans which _have_ been
/// enabled by the current filter. This type is primarily used for implementing
/// span handles; users should typically not need to interact with it directly.
#[derive(Debug)]
pub(crate) struct Inner {
/// The span's ID, as provided by `subscriber`.
id: Id,
/// The subscriber that will receive events relating to this span.
///
/// This should be the same subscriber that provided this span with its
/// `id`.
subscriber: Dispatch,
}
/// A guard representing a span which has been entered and is currently
/// executing.
///
/// When the guard is dropped, the span will be exited.
///
/// This is returned by the [`Span::enter`] function.
///
/// [`Span::enter`]: ../struct.Span.html#method.enter
#[derive(Debug)]
#[must_use = "once a span has been entered, it should be exited"]
pub struct Entered<'a> {
span: &'a Span,
}
// ===== impl Span =====
impl Span {
/// Constructs a new `Span` with the given [metadata] and set of
/// [field values].
///
/// The new span will be constructed by the currently-active [`Subscriber`],
/// with the current span as its parent (if one exists).
///
/// After the span is constructed, [field values] and/or [`follows_from`]
/// annotations may be added to it.
///
/// [metadata]: ../metadata
/// [`Subscriber`]: ../subscriber/trait.Subscriber.html
/// [field values]: ../field/struct.ValueSet.html
/// [`follows_from`]: ../struct.Span.html#method.follows_from
#[inline]
pub fn new(meta: &'static Metadata<'static>, values: &field::ValueSet) -> Span {
let new_span = Attributes::new(meta, values);
Self::make(meta, new_span)
}
/// Constructs a new `Span` as the root of its own trace tree, with the
/// given [metadata] and set of [field values].
///
/// After the span is constructed, [field values] and/or [`follows_from`]
/// annotations may be added to it.
///
/// [metadata]: ../metadata
/// [field values]: ../field/struct.ValueSet.html
/// [`follows_from`]: ../struct.Span.html#method.follows_from
#[inline]
pub fn new_root(meta: &'static Metadata<'static>, values: &field::ValueSet) -> Span {
Self::make(meta, Attributes::new_root(meta, values))
}
/// Constructs a new `Span` as child of the given parent span, with the
/// given [metadata] and set of [field values].
///
/// After the span is constructed, [field values] and/or [`follows_from`]
/// annotations may be added to it.
///
/// [metadata]: ../metadata
/// [field values]: ../field/struct.ValueSet.html
/// [`follows_from`]: ../struct.Span.html#method.follows_from
pub fn child_of(
parent: impl Into<Option<Id>>,
meta: &'static Metadata<'static>,
values: &field::ValueSet,
) -> Span {
let new_span = match parent.into() {
Some(parent) => Attributes::child_of(parent, meta, values),
None => Attributes::new_root(meta, values),
};
Self::make(meta, new_span)
}
/// Constructs a new disabled 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
}
/// Enters this span, returning a guard that will exit the span when dropped.
///
/// If this span is enabled by the current subscriber, then this function will
/// call [`Subscriber::enter`] with the span's [`Id`], and dropping the guard
/// will call [`Subscriber::exit`]. If the span is disabled, this does nothing.
///
/// # Examples
///
/// ```
/// #[macro_use] extern crate tokio_trace;
/// # use tokio_trace::Level;
/// # fn main() {
/// let span = span!(Level::INFO, "my_span");
/// let guard = span.enter();
///
/// // code here is within the span
///
/// drop(guard);
///
/// // code here is no longer within the span
///
/// # }
/// ```
///
/// Guards need not be explicitly dropped:
///
/// ```
/// #[macro_use] extern crate tokio_trace;
/// # fn main() {
/// fn my_function() -> String {
/// // enter a span for the duration of this function.
/// let span = trace_span!("my_function");
/// let _enter = span.enter();
///
/// // anything happening in functions we call is still inside the span...
/// my_other_function();
///
/// // returning from the function drops the guard, exiting the span.
/// return "Hello world".to_owned();
/// }
///
/// fn my_other_function() {
/// // ...
/// }
/// # }
/// ```
///
/// Sub-scopes may be created to limit the duration for which the span is
/// entered:
///
/// ```
/// #[macro_use] extern crate tokio_trace;
/// # fn main() {
/// let span = info_span!("my_great_span");
///
/// {
/// let _enter = span.enter();
///
/// // this event occurs inside the span.
/// info!("i'm in the span!");
///
/// // exiting the scope drops the guard, exiting the span.
/// }
///
/// // this event is not inside the span.
/// info!("i'm outside the span!")
/// # }
/// ```
///
/// [`Subscriber::enter`]: ../subscriber/trait.Subscriber.html#method.enter
/// [`Subscriber::exit`]: ../subscriber/trait.Subscriber.html#method.exit
/// [`Id`]: ../struct.Id.html
pub fn enter<'a>(&'a self) -> Entered<'a> {
if let Some(ref inner) = self.inner.as_ref() {
inner.subscriber.enter(&inner.id);
}
self.log(format_args!("-> {}", self.meta.name));
Entered { span: self }
}
/// Executes the given function in the context of this span.
///
/// If this span is enabled, then this function enters the span, invokes `f`
/// and then exits the span. If the span is disabled, `f` will still be
/// invoked, but in the context of the currently-executing span (if there is
/// one).
///
/// Returns the result of evaluating `f`.
///
/// # Examples
///
/// ```
/// # #[macro_use] extern crate tokio_trace;
/// # use tokio_trace::Level;
/// # fn main() {
/// let my_span = span!(Level::TRACE, "my_span");
///
/// my_span.in_scope(|| {
/// // this event occurs within the span.
/// trace!("i'm in the span!");
/// });
///
/// // this event occurs outside the span.
/// trace!("i'm not in the span!");
/// # }
/// ```
///
/// Calling a function and returning the result:
/// ```
/// # #[macro_use] extern crate tokio_trace;
/// # use tokio_trace::Level;
/// fn hello_world() -> String {
/// "Hello world!".to_owned()
/// }
///
/// # fn main() {
/// let span = info_span!("hello_world");
/// // the span will be entered for the duration of the call to
/// // `hello_world`.
/// let a_string = span.in_scope(hello_world);
/// # }
///
pub fn in_scope<F: FnOnce() -> T, T>(&self, f: F) -> T {
let _enter = self.enter();
f()
}
/// Returns a [`Field`](../field/struct.Field.html) for the field with the
/// given `name`, if one exists,
pub fn field<Q: ?Sized>(&self, field: &Q) -> Option<field::Field>
where
Q: field::AsField,
{
self.metadata().and_then(|meta| field.as_field(meta))
}
/// Returns true if this `Span` has a field for the given
/// [`Field`](../field/struct.Field.html) or field name.
#[inline]
pub fn has_field<Q: ?Sized>(&self, field: &Q) -> bool
where
Q: field::AsField,
{
self.field(field).is_some()
}
/// Visits that the field described by `field` has the value `value`.
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
}
/// Visit all the fields in the span
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
}
/// Returns `true` if this span was disabled by the subscriber and does not
/// exist.
#[inline]
pub fn is_disabled(&self) -> bool {
self.inner.is_none()
}
/// Indicates that the span with the given ID has an indirect causal
/// relationship with this span.
///
/// This relationship differs somewhat from the parent-child relationship: a
/// span may have any number of prior spans, rather than a single one; and
/// spans are not considered to be executing _inside_ of the spans they
/// follow from. This means that a span may close even if subsequent spans
/// that follow from it are still open, and time spent inside of a
/// subsequent span should not be included in the time its precedents were
/// executing. This is used to model causal relationships such as when a
/// single future spawns several related background tasks, et cetera.
///
/// If this span is disabled, or the resulting follows-from relationship
/// would be invalid, this function will do nothing.
pub fn follows_from(&self, from: impl for<'a> Into<Option<&'a Id>>) -> &Self {
if let Some(ref inner) = self.inner {
if let Some(from) = from.into() {
inner.follows_from(from);
}
}
self
}
/// Returns this span's `Id`, if it is enabled.
pub fn id(&self) -> Option<Id> {
self.inner.as_ref().map(Inner::id)
}
/// Returns this span's `Metadata`, if it is enabled.
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<'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 Inner =====
impl Inner {
/// Indicates that the span with the given ID has an indirect causal
/// relationship with this span.
///
/// This relationship differs somewhat from the parent-child relationship: a
/// span may have any number of prior spans, rather than a single one; and
/// spans are not considered to be executing _inside_ of the spans they
/// follow from. This means that a span may close even if subsequent spans
/// that follow from it are still open, and time spent inside of a
/// subsequent span should not be included in the time its precedents were
/// executing. This is used to model causal relationships such as when a
/// single future spawns several related background tasks, et cetera.
///
/// If this span is disabled, this function will do nothing. Otherwise, it
/// returns `Ok(())` if the other span was added as a precedent of this
/// span, or an error if this was not possible.
fn follows_from(&self, from: &Id) {
self.subscriber.record_follows_from(&self.id, &from)
}
/// Returns the span's ID.
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 Entered =====
impl<'a> Drop for Entered<'a> {
#[inline]
fn drop(&mut self) {
// Dropping the guard exits the span.
//
// Running this behaviour on drop rather than with an explicit function
// call means that spans may still be exited when unwinding.
if let Some(inner) = self.span.inner.as_ref() {
inner.subscriber.exit(&inner.id);
}
self.span.log(format_args!("<- {}", self.span.meta.name));
}
}
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
}
}
#[cfg(test)]
mod test {
use super::*;
trait AssertSend: Send {}
impl AssertSend for Span {}
trait AssertSync: Sync {}
impl AssertSync for Span {}
}
-40
View File
@@ -1,40 +0,0 @@
//! Collects and records trace data.
pub use tokio_trace_core::subscriber::*;
/// Sets this subscriber as the default for the duration of a closure.
///
/// The default subscriber is used when creating a new [`Span`] or
/// [`Event`], _if no span is currently executing_. If a span is currently
/// executing, new spans or events are dispatched to the subscriber that
/// tagged that span, instead.
///
/// [`Span`]: ../span/struct.Span.html
/// [`Subscriber`]: ../subscriber/trait.Subscriber.html
/// [`Event`]: :../event/struct.Event.html
pub fn with_default<T, S>(subscriber: S, f: impl FnOnce() -> T) -> T
where
S: Subscriber + Send + Sync + 'static,
{
::dispatcher::with_default(&::Dispatch::new(subscriber), f)
}
/// Sets this subscriber as the global default for the duration of the entire program.
/// Will be used as a fallback if no thread-local subscriber has been set in a thread (using `with_default`.)
///
/// Can only be set once; subsequent attempts to set the global default will fail.
/// Returns whether the initialization was successful.
///
/// Note: Libraries should *NOT* call `set_global_default()`! That will cause conflicts when
/// executables try to set them later.
///
/// [span]: ../span/index.html
/// [`Subscriber`]: ../subscriber/trait.Subscriber.html
/// [`Event`]: ../event/struct.Event.html
pub fn set_global_default<S>(subscriber: S) -> Result<(), SetGlobalDefaultError>
where
S: Subscriber + Send + Sync + 'static,
{
::dispatcher::set_global_default(::Dispatch::new(subscriber))
}
pub use tokio_trace_core::dispatcher::SetGlobalDefaultError;
-10
View File
@@ -1,10 +0,0 @@
[workspace]
[package]
name = "test_log_support"
version = "0.1.0"
publish = false
[dependencies]
tokio-trace = { path = "..", features = ["log"] }
log = { version = "0.4", features = ["std"] }
@@ -1,74 +0,0 @@
extern crate log;
#[macro_use]
extern crate tokio_trace;
use log::{LevelFilter, Log, Metadata, Record};
use std::sync::{Arc, Mutex};
use tokio_trace::Level;
struct State {
last_log: Mutex<Option<String>>,
}
struct Logger(Arc<State>);
impl Log for Logger {
fn enabled(&self, _: &Metadata) -> bool {
true
}
fn log(&self, record: &Record) {
let line = format!("{}", record.args());
println!("{:<5} {} {}", record.level(), record.target(), line);
*self.0.last_log.lock().unwrap() = Some(line);
}
fn flush(&self) {}
}
#[test]
fn test_always_log() {
let me = Arc::new(State {
last_log: Mutex::new(None),
});
let a = me.clone();
log::set_boxed_logger(Box::new(Logger(me))).unwrap();
log::set_max_level(LevelFilter::Trace);
error!(foo = 5);
last(&a, "foo=5");
warn!("hello {};", "world");
last(&a, "hello world;");
info!(message = "hello world;", thingy = 42, other_thingy = 666);
last(&a, "hello world; thingy=42 other_thingy=666");
let foo = span!(Level::TRACE, "foo");
last(&a, "foo;");
foo.in_scope(|| {
last(&a, "-> foo");
trace!({foo = 3, bar = 4}, "hello {};", "san francisco");
last(&a, "hello san francisco; foo=3 bar=4");
});
last(&a, "<- foo");
span!(Level::TRACE, "foo", bar = 3, baz = false);
last(&a, "foo; bar=3 baz=false");
// TODO(#1138): determine a new syntax for uninitialized span fields, and
// re-enable these.
// let span = span!(Level::TRACE, "foo", bar = _, baz = _);
// span.record("bar", &3);
// last(&a, "foo; bar=3");
// span.record("baz", &"a string");
// last(&a, "foo; baz=\"a string\"");
}
fn last(state: &State, expected: &str) {
let mut lock = state.last_log.lock().unwrap();
{
let last = lock.as_ref().map(|s| s.as_str().trim());
assert_eq!(last, Some(expected));
}
*lock = None;
}
@@ -1,10 +0,0 @@
[workspace]
[package]
name = "test_cargo_max_level_features"
version = "0.1.0"
publish = false
[dependencies.tokio-trace]
path = ".."
features = ["max_level_debug", "release_max_level_info"]
@@ -1,71 +0,0 @@
#[macro_use]
extern crate tokio_trace;
use std::sync::{Arc, Mutex};
use tokio_trace::span::{Attributes, Record};
use tokio_trace::{span, Event, Id, Level, Metadata, Subscriber};
struct State {
last_level: Mutex<Option<Level>>,
}
struct TestSubscriber(Arc<State>);
impl Subscriber for TestSubscriber {
fn enabled(&self, _: &Metadata) -> bool {
true
}
fn new_span(&self, _span: &Attributes) -> Id {
span::Id::from_u64(42)
}
fn record(&self, _span: &Id, _values: &Record) {}
fn record_follows_from(&self, _span: &Id, _follows: &Id) {}
fn event(&self, event: &Event) {
*self.0.last_level.lock().unwrap() = Some(event.metadata().level().clone());
}
fn enter(&self, _span: &Id) {}
fn exit(&self, _span: &Id) {}
}
#[cfg(test)]
fn test_static_max_level_features() {
let me = Arc::new(State {
last_level: Mutex::new(None),
});
let a = me.clone();
tokio_trace::subscriber::with_default(TestSubscriber(me), || {
error!("");
last(&a, Some(Level::ERROR));
warn!("");
last(&a, Some(Level::WARN));
info!("");
last(&a, Some(Level::INFO));
debug!("");
last(&a, Some(Level::DEBUG));
trace!("");
last(&a, None);
span!(Level::ERROR, "");
last(&a, None);
span!(Level::WARN, "");
last(&a, None);
span!(Level::INFO, "");
last(&a, None);
span!(Level::DEBUG, "");
last(&a, None);
span!(Level::TRACE, "");
last(&a, None);
});
}
fn last(state: &State, expected: Option<Level>) {
let mut lvl = state.last_level.lock().unwrap();
assert_eq!(*lvl, expected);
*lvl = None;
}
-251
View File
@@ -1,251 +0,0 @@
#[macro_use]
extern crate tokio_trace;
mod support;
use self::support::*;
use tokio_trace::{
field::{debug, display},
subscriber::with_default,
Level,
};
#[test]
fn event_without_message() {
let (subscriber, handle) = subscriber::mock()
.event(
event::mock().with_fields(
field::mock("answer")
.with_value(&42)
.and(
field::mock("to_question")
.with_value(&"life, the universe, and everything"),
)
.only(),
),
)
.done()
.run_with_handle();
with_default(subscriber, || {
info!(
answer = 42,
to_question = "life, the universe, and everything"
);
});
handle.assert_finished();
}
#[test]
fn event_with_message() {
let (subscriber, handle) = subscriber::mock()
.event(event::mock().with_fields(field::mock("message").with_value(
&tokio_trace::field::debug(format_args!(
"hello from my event! yak shaved = {:?}",
true
)),
)))
.done()
.run_with_handle();
with_default(subscriber, || {
debug!("hello from my event! yak shaved = {:?}", true);
});
handle.assert_finished();
}
#[test]
fn one_with_everything() {
let (subscriber, handle) = subscriber::mock()
.event(
event::mock()
.with_fields(
field::mock("message")
.with_value(&tokio_trace::field::debug(format_args!(
"{:#x} make me one with{what:.>20}",
4277009102u64,
what = "everything"
)))
.and(field::mock("foo").with_value(&666))
.and(field::mock("bar").with_value(&false))
.only(),
)
.at_level(Level::ERROR)
.with_target("whatever"),
)
.done()
.run_with_handle();
with_default(subscriber, || {
event!(
target: "whatever",
Level::ERROR,
{ foo = 666, bar = false },
"{:#x} make me one with{what:.>20}", 4277009102u64, what = "everything"
);
});
handle.assert_finished();
}
#[test]
fn moved_field() {
let (subscriber, handle) = subscriber::mock()
.event(
event::mock().with_fields(
field::mock("foo")
.with_value(&display("hello from my event"))
.only(),
),
)
.done()
.run_with_handle();
with_default(subscriber, || {
let from = "my event";
event!(Level::INFO, foo = display(format!("hello from {}", from)))
});
handle.assert_finished();
}
#[test]
fn dotted_field_name() {
let (subscriber, handle) = subscriber::mock()
.event(
event::mock().with_fields(
field::mock("foo.bar")
.with_value(&true)
.and(field::mock("foo.baz").with_value(&false))
.only(),
),
)
.done()
.run_with_handle();
with_default(subscriber, || {
event!(Level::INFO, foo.bar = true, foo.baz = false);
});
handle.assert_finished();
}
#[test]
fn borrowed_field() {
let (subscriber, handle) = subscriber::mock()
.event(
event::mock().with_fields(
field::mock("foo")
.with_value(&display("hello from my event"))
.only(),
),
)
.done()
.run_with_handle();
with_default(subscriber, || {
let from = "my event";
let mut message = format!("hello from {}", from);
event!(Level::INFO, foo = display(&message));
message.push_str(", which happened!");
});
handle.assert_finished();
}
#[test]
// If emitting log instrumentation, this gets moved anyway, breaking the test.
#[cfg(not(feature = "log"))]
fn move_field_out_of_struct() {
use tokio_trace::field::debug;
#[derive(Debug)]
struct Position {
x: f32,
y: f32,
}
let pos = Position {
x: 3.234,
y: -1.223,
};
let (subscriber, handle) = subscriber::mock()
.event(
event::mock().with_fields(
field::mock("x")
.with_value(&debug(3.234))
.and(field::mock("y").with_value(&debug(-1.223)))
.only(),
),
)
.event(event::mock().with_fields(field::mock("position").with_value(&debug(&pos))))
.done()
.run_with_handle();
with_default(subscriber, || {
let pos = Position {
x: 3.234,
y: -1.223,
};
debug!(x = debug(pos.x), y = debug(pos.y));
debug!(target: "app_events", { position = debug(pos) }, "New position");
});
handle.assert_finished();
}
#[test]
fn display_shorthand() {
let (subscriber, handle) = subscriber::mock()
.event(
event::mock().with_fields(
field::mock("my_field")
.with_value(&display("hello world"))
.only(),
),
)
.done()
.run_with_handle();
with_default(subscriber, || {
event!(Level::TRACE, my_field = %"hello world");
});
handle.assert_finished();
}
#[test]
fn debug_shorthand() {
let (subscriber, handle) = subscriber::mock()
.event(
event::mock().with_fields(
field::mock("my_field")
.with_value(&debug("hello world"))
.only(),
),
)
.done()
.run_with_handle();
with_default(subscriber, || {
event!(Level::TRACE, my_field = ?"hello world");
});
handle.assert_finished();
}
#[test]
fn both_shorthands() {
let (subscriber, handle) = subscriber::mock()
.event(
event::mock().with_fields(
field::mock("display_field")
.with_value(&display("hello world"))
.and(field::mock("debug_field").with_value(&debug("hello world")))
.only(),
),
)
.done()
.run_with_handle();
with_default(subscriber, || {
event!(Level::TRACE, display_field = %"hello world", debug_field = ?"hello world");
});
handle.assert_finished();
}
@@ -1,64 +0,0 @@
// Tests that depend on a count of the number of times their filter is evaluated
// can't exist in the same file with other tests that add subscribers to the
// registry. The registry was changed so that each time a new dispatcher is
// added all filters are re-evaluated. The tests being run only in separate
// threads with shared global state lets them interfere with eachother
#[macro_use]
extern crate tokio_trace;
mod support;
use self::support::*;
use tokio_trace::subscriber::with_default;
use tokio_trace::Level;
use std::sync::{
atomic::{AtomicUsize, Ordering},
Arc,
};
#[test]
fn filter_caching_is_lexically_scoped() {
pub fn my_great_function() -> bool {
span!(Level::TRACE, "emily").in_scope(|| true)
}
pub fn my_other_function() -> bool {
span!(Level::TRACE, "frank").in_scope(|| true)
}
let count = Arc::new(AtomicUsize::new(0));
let count2 = count.clone();
let subscriber = subscriber::mock()
.with_filter(move |meta| match meta.name {
"emily" | "frank" => {
count2.fetch_add(1, Ordering::Relaxed);
true
}
_ => false,
})
.run();
with_default(subscriber, || {
// Call the function once. The filter should be re-evaluated.
assert!(my_great_function());
assert_eq!(count.load(Ordering::Relaxed), 1);
// Call the function again. The cached result should be used.
assert!(my_great_function());
assert_eq!(count.load(Ordering::Relaxed), 1);
assert!(my_other_function());
assert_eq!(count.load(Ordering::Relaxed), 2);
assert!(my_great_function());
assert_eq!(count.load(Ordering::Relaxed), 2);
assert!(my_other_function());
assert_eq!(count.load(Ordering::Relaxed), 2);
assert!(my_great_function());
assert_eq!(count.load(Ordering::Relaxed), 2);
});
}
@@ -1,69 +0,0 @@
// Tests that depend on a count of the number of times their filter is evaluated
// cant exist in the same file with other tests that add subscribers to the
// registry. The registry was changed so that each time a new dispatcher is
// added all filters are re-evaluated. The tests being run only in separate
// threads with shared global state lets them interfere with eachother
#[macro_use]
extern crate tokio_trace;
mod support;
use self::support::*;
use tokio_trace::subscriber::with_default;
use tokio_trace::Level;
use std::sync::{
atomic::{AtomicUsize, Ordering},
Arc,
};
#[test]
fn filters_are_not_reevaluated_for_the_same_span() {
// Asserts that the `span!` macro caches the result of calling
// `Subscriber::enabled` for each span.
let alice_count = Arc::new(AtomicUsize::new(0));
let bob_count = Arc::new(AtomicUsize::new(0));
let alice_count2 = alice_count.clone();
let bob_count2 = bob_count.clone();
let (subscriber, handle) = subscriber::mock()
.with_filter(move |meta| match meta.name {
"alice" => {
alice_count2.fetch_add(1, Ordering::Relaxed);
false
}
"bob" => {
bob_count2.fetch_add(1, Ordering::Relaxed);
true
}
_ => false,
})
.run_with_handle();
with_default(subscriber, move || {
// Enter "alice" and then "bob". The dispatcher expects to see "bob" but
// not "alice."
let alice = span!(Level::TRACE, "alice");
let bob = alice.in_scope(|| {
let bob = span!(Level::TRACE, "bob");
bob.in_scope(|| ());
bob
});
// The filter should have seen each span a single time.
assert_eq!(alice_count.load(Ordering::Relaxed), 1);
assert_eq!(bob_count.load(Ordering::Relaxed), 1);
alice.in_scope(|| bob.in_scope(|| {}));
// The subscriber should see "bob" again, but the filter should not have
// been called.
assert_eq!(alice_count.load(Ordering::Relaxed), 1);
assert_eq!(bob_count.load(Ordering::Relaxed), 1);
bob.in_scope(|| {});
assert_eq!(alice_count.load(Ordering::Relaxed), 1);
assert_eq!(bob_count.load(Ordering::Relaxed), 1);
});
handle.assert_finished();
}
@@ -1,80 +0,0 @@
// Tests that depend on a count of the number of times their filter is evaluated
// cant exist in the same file with other tests that add subscribers to the
// registry. The registry was changed so that each time a new dispatcher is
// added all filters are re-evaluated. The tests being run only in separate
// threads with shared global state lets them interfere with eachother
#[macro_use]
extern crate tokio_trace;
mod support;
use self::support::*;
use tokio_trace::subscriber::with_default;
use tokio_trace::Level;
use std::sync::{
atomic::{AtomicUsize, Ordering},
Arc,
};
#[test]
fn filters_are_reevaluated_for_different_call_sites() {
// Asserts that the `span!` macro caches the result of calling
// `Subscriber::enabled` for each span.
let charlie_count = Arc::new(AtomicUsize::new(0));
let dave_count = Arc::new(AtomicUsize::new(0));
let charlie_count2 = charlie_count.clone();
let dave_count2 = dave_count.clone();
let subscriber = subscriber::mock()
.with_filter(move |meta| {
println!("Filter: {:?}", meta.name);
match meta.name {
"charlie" => {
charlie_count2.fetch_add(1, Ordering::Relaxed);
false
}
"dave" => {
dave_count2.fetch_add(1, Ordering::Relaxed);
true
}
_ => false,
}
})
.run();
with_default(subscriber, move || {
// Enter "charlie" and then "dave". The dispatcher expects to see "dave" but
// not "charlie."
let charlie = span!(Level::TRACE, "charlie");
let dave = charlie.in_scope(|| {
let dave = span!(Level::TRACE, "dave");
dave.in_scope(|| {});
dave
});
// The filter should have seen each span a single time.
assert_eq!(charlie_count.load(Ordering::Relaxed), 1);
assert_eq!(dave_count.load(Ordering::Relaxed), 1);
charlie.in_scope(|| dave.in_scope(|| {}));
// The subscriber should see "dave" again, but the filter should not have
// been called.
assert_eq!(charlie_count.load(Ordering::Relaxed), 1);
assert_eq!(dave_count.load(Ordering::Relaxed), 1);
// A different span with the same name has a different call site, so it
// should cause the filter to be reapplied.
let charlie2 = span!(Level::TRACE, "charlie");
charlie.in_scope(|| {});
assert_eq!(charlie_count.load(Ordering::Relaxed), 2);
assert_eq!(dave_count.load(Ordering::Relaxed), 1);
// But, the filter should not be re-evaluated for the new "charlie" span
// when it is re-entered.
charlie2.in_scope(|| span!(Level::TRACE, "dave").in_scope(|| {}));
assert_eq!(charlie_count.load(Ordering::Relaxed), 2);
assert_eq!(dave_count.load(Ordering::Relaxed), 2);
});
}
-713
View File
@@ -1,713 +0,0 @@
use tokio_trace::Level;
#[macro_use]
extern crate tokio_trace;
// Tests that macros work across various invocation syntax.
//
// These are quite repetitive, and _could_ be generated by a macro. However,
// they're compile-time tests, so I want to get line numbers etc out of
// failures, and producing them with a macro would muddy the waters a bit.
#[test]
fn span() {
span!(Level::DEBUG, target: "foo_events", "foo", bar.baz = ?2, quux = %3, quuux = 4);
span!(Level::DEBUG, target: "foo_events", "foo", bar.baz = 2, quux = 3);
span!(Level::DEBUG, target: "foo_events", "foo", bar.baz = 2, quux = 4,);
span!(Level::DEBUG, target: "foo_events", "foo");
span!(Level::DEBUG, target: "foo_events", "bar",);
span!(Level::DEBUG, "foo", bar.baz = 2, quux = 3);
span!(Level::DEBUG, "foo", bar.baz = 2, quux = 4,);
span!(Level::TRACE, "foo", bar.baz = 2, quux = 3);
span!(Level::TRACE, "foo", bar.baz = 2, quux = 4,);
span!(Level::TRACE, "foo", bar.baz = ?2);
span!(Level::TRACE, "foo", bar.baz = %2);
span!(Level::TRACE, "foo");
span!(Level::TRACE, "bar",);
}
#[test]
fn trace_span() {
trace_span!(target: "foo_events", "foo", bar.baz = ?2, quux = %3, quuux = 4);
trace_span!(target: "foo_events", "foo", bar.baz = 2, quux = 3);
trace_span!(target: "foo_events", "foo", bar.baz = 2, quux = 4,);
trace_span!(target: "foo_events", "foo");
trace_span!(target: "foo_events", "bar",);
trace_span!("foo", bar.baz = 2, quux = 3);
trace_span!("foo", bar.baz = 2, quux = 4,);
trace_span!("foo", bar.baz = ?2);
trace_span!("foo", bar.baz = %2);
trace_span!("bar");
trace_span!("bar",);
}
#[test]
fn debug_span() {
debug_span!(target: "foo_events", "foo", bar.baz = ?2, quux = %3, quuux = 4);
debug_span!(target: "foo_events", "foo", bar.baz = 2, quux = 3);
debug_span!(target: "foo_events", "foo", bar.baz = 2, quux = 4,);
debug_span!(target: "foo_events", "foo");
debug_span!(target: "foo_events", "bar",);
debug_span!("foo", bar.baz = 2, quux = 3);
debug_span!("foo", bar.baz = 2, quux = 4,);
debug_span!("foo", bar.baz = ?2);
debug_span!("foo", bar.baz = %2);
debug_span!("bar");
debug_span!("bar",);
}
#[test]
fn info_span() {
info_span!(target: "foo_events", "foo", bar.baz = ?2, quux = %3, quuux = 4);
info_span!(target: "foo_events", "foo", bar.baz = 2, quux = 3);
info_span!(target: "foo_events", "foo", bar.baz = 2, quux = 4,);
info_span!(target: "foo_events", "foo");
info_span!(target: "foo_events", "bar",);
info_span!("foo", bar.baz = 2, quux = 3);
info_span!("foo", bar.baz = 2, quux = 4,);
info_span!("foo", bar.baz = ?2);
info_span!("foo", bar.baz = %2);
info_span!("bar");
info_span!("bar",);
}
#[test]
fn warn_span() {
warn_span!(target: "foo_events", "foo", bar.baz = ?2, quux = %3, quuux = 4);
warn_span!(target: "foo_events", "foo", bar.baz = 2, quux = 3);
warn_span!(target: "foo_events", "foo", bar.baz = 2, quux = 4,);
warn_span!(target: "foo_events", "foo");
warn_span!(target: "foo_events", "bar",);
warn_span!("foo", bar.baz = 2, quux = 3);
warn_span!("foo", bar.baz = 2, quux = 4,);
warn_span!("foo", bar.baz = ?2);
warn_span!("foo", bar.baz = %2);
warn_span!("bar");
warn_span!("bar",);
}
#[test]
fn error_span() {
error_span!(target: "foo_events", "foo", bar.baz = ?2, quux = %3, quuux = 4);
error_span!(target: "foo_events", "foo", bar.baz = 2, quux = 3);
error_span!(target: "foo_events", "foo", bar.baz = 2, quux = 4,);
error_span!(target: "foo_events", "foo");
error_span!(target: "foo_events", "bar",);
error_span!("foo", bar.baz = 2, quux = 3);
error_span!("foo", bar.baz = 2, quux = 4,);
error_span!("foo", bar.baz = ?2);
error_span!("foo", bar.baz = %2);
error_span!("bar");
error_span!("bar",);
}
#[test]
fn span_root() {
span!(Level::DEBUG, target: "foo_events", parent: None, "foo", bar.baz = 2, quux = 3);
span!(Level::DEBUG, target: "foo_events", parent: None, "foo", bar.baz = 2, quux = 3);
span!(Level::DEBUG, target: "foo_events", parent: None, "foo", bar.baz = 2, quux = 4,);
span!(Level::DEBUG, target: "foo_events", parent: None, "foo");
span!(Level::DEBUG, target: "foo_events", parent: None, "bar",);
span!(Level::TRACE, parent: None, "foo", bar.baz = 2, quux = 3);
span!(Level::TRACE, parent: None, "foo", bar.baz = 2, quux = 4,);
span!(Level::TRACE, parent: None, "foo");
span!(Level::TRACE, parent: None, "bar",);
}
#[test]
fn trace_span_root() {
trace_span!(target: "foo_events", parent: None, "foo", bar.baz = 2, quux = 3);
trace_span!(target: "foo_events", parent: None, "foo", bar.baz = 2, quux = 4,);
trace_span!(target: "foo_events", parent: None, "foo");
trace_span!(target: "foo_events", parent: None, "bar",);
trace_span!(parent: None, "foo", bar.baz = 2, quux = 3);
trace_span!(parent: None, "foo", bar.baz = 2, quux = 4,);
trace_span!(parent: None, "foo");
trace_span!(parent: None, "bar",);
}
#[test]
fn debug_span_root() {
debug_span!(target: "foo_events", parent: None, "foo", bar.baz = 2, quux = 3);
debug_span!(target: "foo_events", parent: None, "foo", bar.baz = 2, quux = 4,);
debug_span!(target: "foo_events", parent: None, "foo");
debug_span!(target: "foo_events", parent: None, "bar",);
debug_span!(parent: None, "foo", bar.baz = 2, quux = 3);
debug_span!(parent: None, "foo", bar.baz = 2, quux = 4,);
debug_span!(parent: None, "foo");
debug_span!(parent: None, "bar",);
}
#[test]
fn info_span_root() {
info_span!(target: "foo_events", parent: None, "foo", bar.baz = 2, quux = 3);
info_span!(target: "foo_events", parent: None, "foo", bar.baz = 2, quux = 4,);
info_span!(target: "foo_events", parent: None, "foo");
info_span!(target: "foo_events", parent: None, "bar",);
info_span!(parent: None, "foo", bar.baz = 2, quux = 3);
info_span!(parent: None, "foo", bar.baz = 2, quux = 4,);
info_span!(parent: None, "foo");
info_span!(parent: None, "bar",);
}
#[test]
fn warn_span_root() {
warn_span!(target: "foo_events", parent: None, "foo", bar.baz = 2, quux = 3);
warn_span!(target: "foo_events", parent: None, "foo", bar.baz = 2, quux = 4,);
warn_span!(target: "foo_events", parent: None, "foo");
warn_span!(target: "foo_events", parent: None, "bar",);
warn_span!(parent: None, "foo", bar.baz = 2, quux = 3);
warn_span!(parent: None, "foo", bar.baz = 2, quux = 4,);
warn_span!(parent: None, "foo");
warn_span!(parent: None, "bar",);
}
#[test]
fn error_span_root() {
error_span!(target: "foo_events", parent: None, "foo", bar.baz = 2, quux = 3);
error_span!(target: "foo_events", parent: None, "foo", bar.baz = 2, quux = 4,);
error_span!(target: "foo_events", parent: None, "foo");
error_span!(target: "foo_events", parent: None, "bar",);
error_span!(parent: None, "foo", bar.baz = 2, quux = 3);
error_span!(parent: None, "foo", bar.baz = 2, quux = 4,);
error_span!(parent: None, "foo");
error_span!(parent: None, "bar",);
}
#[test]
fn span_with_parent() {
let p = span!(Level::TRACE, "im_a_parent!");
span!(Level::DEBUG, target: "foo_events", parent: &p, "foo", bar.baz = 2, quux = 3);
span!(Level::DEBUG, target: "foo_events", parent: &p, "foo", bar.baz = 2, quux = 4,);
span!(Level::DEBUG, target: "foo_events", parent: &p, "foo");
span!(Level::DEBUG, target: "foo_events", parent: &p, "bar",);
span!(Level::DEBUG, parent: &p, "foo", bar.baz = 2, quux = 3);
span!(Level::DEBUG, parent: &p, "foo", bar.baz = 2, quux = 4,);
span!(Level::DEBUG, parent: &p, "foo");
span!(Level::DEBUG, parent: &p, "bar",);
}
#[test]
fn trace_span_with_parent() {
let p = span!(Level::TRACE, "im_a_parent!");
trace_span!(target: "foo_events", parent: &p, "foo", bar.baz = 2, quux = 3);
trace_span!(target: "foo_events", parent: &p, "foo", bar.baz = 2, quux = 4,);
trace_span!(target: "foo_events", parent: &p, "foo");
trace_span!(target: "foo_events", parent: &p, "bar",);
trace_span!(parent: &p, "foo", bar.baz = 2, quux = 3);
trace_span!(parent: &p, "foo", bar.baz = 2, quux = 4,);
trace_span!(parent: &p, "foo");
trace_span!(parent: &p, "bar",);
}
#[test]
fn debug_span_with_parent() {
let p = span!(Level::TRACE, "im_a_parent!");
debug_span!(target: "foo_events", parent: &p, "foo", bar.baz = 2, quux = 3);
debug_span!(target: "foo_events", parent: &p, "foo", bar.baz = 2, quux = 4,);
debug_span!(target: "foo_events", parent: &p, "foo");
debug_span!(target: "foo_events", parent: &p, "bar",);
debug_span!(parent: &p, "foo", bar.baz = 2, quux = 3);
debug_span!(parent: &p, "foo", bar.baz = 2, quux = 4,);
debug_span!(parent: &p, "foo");
debug_span!(parent: &p, "bar",);
}
#[test]
fn info_span_with_parent() {
let p = span!(Level::TRACE, "im_a_parent!");
info_span!(target: "foo_events", parent: &p, "foo", bar.baz = 2, quux = 3);
info_span!(target: "foo_events", parent: &p, "foo", bar.baz = 2, quux = 4,);
info_span!(target: "foo_events", parent: &p, "foo");
info_span!(target: "foo_events", parent: &p, "bar",);
info_span!(parent: &p, "foo", bar.baz = 2, quux = 3);
info_span!(parent: &p, "foo", bar.baz = 2, quux = 4,);
info_span!(parent: &p, "foo");
info_span!(parent: &p, "bar",);
}
#[test]
fn warn_span_with_parent() {
let p = span!(Level::TRACE, "im_a_parent!");
warn_span!(target: "foo_events", parent: &p, "foo", bar.baz = 2, quux = 3);
warn_span!(target: "foo_events", parent: &p, "foo", bar.baz = 2, quux = 4,);
warn_span!(target: "foo_events", parent: &p, "foo");
warn_span!(target: "foo_events", parent: &p, "bar",);
warn_span!(parent: &p, "foo", bar.baz = 2, quux = 3);
warn_span!(parent: &p, "foo", bar.baz = 2, quux = 4,);
warn_span!(parent: &p, "foo");
warn_span!(parent: &p, "bar",);
}
#[test]
fn error_span_with_parent() {
let p = span!(Level::TRACE, "im_a_parent!");
error_span!(target: "foo_events", parent: &p, "foo", bar.baz = 2, quux = 3);
error_span!(target: "foo_events", parent: &p, "foo", bar.baz = 2, quux = 4,);
error_span!(target: "foo_events", parent: &p, "foo");
error_span!(target: "foo_events", parent: &p, "bar",);
error_span!(parent: &p, "foo", bar.baz = 2, quux = 3);
error_span!(parent: &p, "foo", bar.baz = 2, quux = 4,);
error_span!(parent: &p, "foo");
error_span!(parent: &p, "bar",);
}
#[test]
fn event() {
event!(Level::DEBUG, foo = ?3, bar.baz = %2, quux = false);
event!(Level::DEBUG, foo = 3, bar.baz = 2, quux = false);
event!(Level::DEBUG, foo = 3, bar.baz = 3,);
event!(Level::DEBUG, "foo");
event!(Level::DEBUG, "foo: {}", 3);
event!(Level::DEBUG, { foo = 3, bar.baz = 80 }, "quux");
event!(Level::DEBUG, { foo = 2, bar.baz = 79 }, "quux {:?}", true);
event!(Level::DEBUG, { foo = 2, bar.baz = 79 }, "quux {:?}, {quux}", true, quux = false);
event!(Level::DEBUG, { foo = ?2, bar.baz = %78 }, "quux");
event!(target: "foo_events", Level::DEBUG, foo = 3, bar.baz = 2, quux = false);
event!(target: "foo_events", Level::DEBUG, foo = 3, bar.baz = 3,);
event!(target: "foo_events", Level::DEBUG, "foo");
event!(target: "foo_events", Level::DEBUG, "foo: {}", 3);
event!(target: "foo_events", Level::DEBUG, { foo = 3, bar.baz = 80 }, "quux");
event!(target: "foo_events", Level::DEBUG, { foo = 2, bar.baz = 79 }, "quux {:?}", true);
event!(target: "foo_events", Level::DEBUG, { foo = 2, bar.baz = 79 }, "quux {:?}, {quux}", true, quux = false);
event!(target: "foo_events", Level::DEBUG, { foo = 2, bar.baz = 78, }, "quux");
}
#[test]
fn trace() {
trace!(foo = ?3, bar.baz = %2, quux = false);
trace!(foo = 3, bar.baz = 2, quux = false);
trace!(foo = 3, bar.baz = 3,);
trace!("foo");
trace!("foo: {}", 3);
trace!({ foo = 3, bar.baz = 80 }, "quux");
trace!({ foo = 2, bar.baz = 79 }, "quux {:?}", true);
trace!({ foo = 2, bar.baz = 79 }, "quux {:?}, {quux}", true, quux = false);
trace!({ foo = 2, bar.baz = 78 }, "quux");
trace!({ foo = ?2, bar.baz = %78 }, "quux");
trace!(target: "foo_events", foo = 3, bar.baz = 2, quux = false);
trace!(target: "foo_events", foo = 3, bar.baz = 3,);
trace!(target: "foo_events", "foo");
trace!(target: "foo_events", "foo: {}", 3);
trace!(target: "foo_events", { foo = 3, bar.baz = 80 }, "quux");
trace!(target: "foo_events", { foo = 2, bar.baz = 79 }, "quux {:?}", true);
trace!(target: "foo_events", { foo = 2, bar.baz = 79 }, "quux {:?}, {quux}", true, quux = false);
trace!(target: "foo_events", { foo = 2, bar.baz = 78, }, "quux");
}
#[test]
fn debug() {
debug!(foo = ?3, bar.baz = %2, quux = false);
debug!(foo = 3, bar.baz = 2, quux = false);
debug!(foo = 3, bar.baz = 3,);
debug!("foo");
debug!("foo: {}", 3);
debug!({ foo = 3, bar.baz = 80 }, "quux");
debug!({ foo = 2, bar.baz = 79 }, "quux {:?}", true);
debug!({ foo = 2, bar.baz = 79 }, "quux {:?}, {quux}", true, quux = false);
debug!({ foo = 2, bar.baz = 78 }, "quux");
debug!({ foo = ?2, bar.baz = %78 }, "quux");
debug!(target: "foo_events", foo = 3, bar.baz = 2, quux = false);
debug!(target: "foo_events", foo = 3, bar.baz = 3,);
debug!(target: "foo_events", "foo");
debug!(target: "foo_events", "foo: {}", 3);
debug!(target: "foo_events", { foo = 3, bar.baz = 80 }, "quux");
debug!(target: "foo_events", { foo = 2, bar.baz = 79 }, "quux {:?}", true);
debug!(target: "foo_events", { foo = 2, bar.baz = 79 }, "quux {:?}, {quux}", true, quux = false);
debug!(target: "foo_events", { foo = 2, bar.baz = 78, }, "quux");
}
#[test]
fn info() {
info!(foo = ?3, bar.baz = %2, quux = false);
info!(foo = 3, bar.baz = 2, quux = false);
info!(foo = 3, bar.baz = 3,);
info!("foo");
info!("foo: {}", 3);
info!({ foo = 3, bar.baz = 80 }, "quux");
info!({ foo = 2, bar.baz = 79 }, "quux {:?}", true);
info!({ foo = 2, bar.baz = 79 }, "quux {:?}, {quux}", true, quux = false);
info!({ foo = 2, bar.baz = 78 }, "quux");
info!({ foo = ?2, bar.baz = %78 }, "quux");
info!(target: "foo_events", foo = 3, bar.baz = 2, quux = false);
info!(target: "foo_events", foo = 3, bar.baz = 3,);
info!(target: "foo_events", "foo");
info!(target: "foo_events", "foo: {}", 3);
info!(target: "foo_events", { foo = 3, bar.baz = 80 }, "quux");
info!(target: "foo_events", { foo = 2, bar.baz = 79 }, "quux {:?}", true);
info!(target: "foo_events", { foo = 2, bar.baz = 79 }, "quux {:?}, {quux}", true, quux = false);
info!(target: "foo_events", { foo = 2, bar.baz = 78, }, "quux");
}
#[test]
fn warn() {
warn!(foo = ?3, bar.baz = %2, quux = false);
warn!(foo = 3, bar.baz = 2, quux = false);
warn!(foo = 3, bar.baz = 3,);
warn!("foo");
warn!("foo: {}", 3);
warn!({ foo = 3, bar.baz = 80 }, "quux");
warn!({ foo = 2, bar.baz = 79 }, "quux {:?}", true);
warn!({ foo = 2, bar.baz = 79 }, "quux {:?}, {quux}", true, quux = false);
warn!({ foo = 2, bar.baz = 78 }, "quux");
warn!({ foo = ?2, bar.baz = %78 }, "quux");
warn!(target: "foo_events", foo = 3, bar.baz = 2, quux = false);
warn!(target: "foo_events", foo = 3, bar.baz = 3,);
warn!(target: "foo_events", "foo");
warn!(target: "foo_events", "foo: {}", 3);
warn!(target: "foo_events", { foo = 3, bar.baz = 80 }, "quux");
warn!(target: "foo_events", { foo = 2, bar.baz = 79 }, "quux {:?}", true);
warn!(target: "foo_events", { foo = 2, bar.baz = 79 }, "quux {:?}, {quux}", true, quux = false);
warn!(target: "foo_events", { foo = 2, bar.baz = 78, }, "quux");
}
#[test]
fn error() {
error!(foo = ?3, bar.baz = %2, quux = false);
error!(foo = 3, bar.baz = 2, quux = false);
error!(foo = 3, bar.baz = 3,);
error!("foo");
error!("foo: {}", 3);
error!({ foo = 3, bar.baz = 80 }, "quux");
error!({ foo = 2, bar.baz = 79 }, "quux {:?}", true);
error!({ foo = 2, bar.baz = 79 }, "quux {:?}, {quux}", true, quux = false);
error!({ foo = 2, bar.baz = 78, }, "quux");
error!({ foo = ?2, bar.baz = %78 }, "quux");
error!(target: "foo_events", foo = 3, bar.baz = 2, quux = false);
error!(target: "foo_events", foo = 3, bar.baz = 3,);
error!(target: "foo_events", "foo");
error!(target: "foo_events", "foo: {}", 3);
error!(target: "foo_events", { foo = 3, bar.baz = 80 }, "quux");
error!(target: "foo_events", { foo = 2, bar.baz = 79 }, "quux {:?}", true);
error!(target: "foo_events", { foo = 2, bar.baz = 79 }, "quux {:?}, {quux}", true, quux = false);
error!(target: "foo_events", { foo = 2, bar.baz = 78, }, "quux");
}
#[test]
fn event_root() {
event!(Level::DEBUG, parent: None, foo = ?3, bar.baz = %2, quux = false);
event!(
Level::DEBUG,
parent: None,
foo = 3,
bar.baz = 2,
quux = false
);
event!(Level::DEBUG, parent: None, foo = 3, bar.baz = 3,);
event!(Level::DEBUG, parent: None, "foo");
event!(Level::DEBUG, parent: None, "foo: {}", 3);
event!(Level::DEBUG, parent: None, { foo = 3, bar.baz = 80 }, "quux");
event!(Level::DEBUG, parent: None, { foo = 2, bar.baz = 79 }, "quux {:?}", true);
event!(Level::DEBUG, parent: None, { foo = 2, bar.baz = 79 }, "quux {:?}, {quux}", true, quux = false);
event!(Level::DEBUG, parent: None, { foo = ?2, bar.baz = %78 }, "quux");
event!(target: "foo_events", Level::DEBUG, parent: None, foo = 3, bar.baz = 2, quux = false);
event!(target: "foo_events", Level::DEBUG, parent: None, foo = 3, bar.baz = 3,);
event!(target: "foo_events", Level::DEBUG, parent: None, "foo");
event!(target: "foo_events", Level::DEBUG, parent: None, "foo: {}", 3);
event!(target: "foo_events", Level::DEBUG, parent: None, { foo = 3, bar.baz = 80 }, "quux");
event!(target: "foo_events", Level::DEBUG, parent: None, { foo = 2, bar.baz = 79 }, "quux {:?}", true);
event!(target: "foo_events", Level::DEBUG, parent: None, { foo = 2, bar.baz = 79 }, "quux {:?}, {quux}", true, quux = false);
event!(target: "foo_events", Level::DEBUG, parent: None, { foo = 2, bar.baz = 78, }, "quux");
}
#[test]
fn trace_root() {
trace!(parent: None, foo = ?3, bar.baz = %2, quux = false);
trace!(parent: None, foo = 3, bar.baz = 2, quux = false);
trace!(parent: None, foo = 3, bar.baz = 3,);
trace!(parent: None, "foo");
trace!(parent: None, "foo: {}", 3);
trace!(parent: None, { foo = 3, bar.baz = 80 }, "quux");
trace!(parent: None, { foo = 2, bar.baz = 79 }, "quux {:?}", true);
trace!(parent: None, { foo = 2, bar.baz = 79 }, "quux {:?}, {quux}", true, quux = false);
trace!(parent: None, { foo = 2, bar.baz = 78 }, "quux");
trace!(parent:None, { foo = ?2, bar.baz = %78 }, "quux");
trace!(target: "foo_events", parent: None, foo = 3, bar.baz = 2, quux = false);
trace!(target: "foo_events", parent: None, foo = 3, bar.baz = 3,);
trace!(target: "foo_events", parent: None, "foo");
trace!(target: "foo_events", parent: None, "foo: {}", 3);
trace!(target: "foo_events", parent: None, { foo = 3, bar.baz = 80 }, "quux");
trace!(target: "foo_events", parent: None, { foo = 2, bar.baz = 79 }, "quux {:?}", true);
trace!(target: "foo_events", parent: None, { foo = 2, bar.baz = 79 }, "quux {:?}, {quux}", true, quux = false);
trace!(target: "foo_events", parent: None, { foo = 2, bar.baz = 78, }, "quux");
}
#[test]
fn debug_root() {
debug!(parent: None, foo = ?3, bar.baz = %2, quux = false);
debug!(parent: None, foo = 3, bar.baz = 2, quux = false);
debug!(parent: None, foo = 3, bar.baz = 3,);
debug!(parent: None, "foo");
debug!(parent: None, "foo: {}", 3);
debug!(parent: None, { foo = 3, bar.baz = 80 }, "quux");
debug!(parent: None, { foo = 2, bar.baz = 79 }, "quux {:?}", true);
debug!(parent: None, { foo = 2, bar.baz = 79 }, "quux {:?}, {quux}", true, quux = false);
debug!(parent: None, { foo = 2, bar.baz = 78 }, "quux");
debug!(parent: None, { foo = ?2, bar.baz = %78 }, "quux");
debug!(target: "foo_events", parent: None, foo = 3, bar.baz = 2, quux = false);
debug!(target: "foo_events", parent: None, foo = 3, bar.baz = 3,);
debug!(target: "foo_events", parent: None, "foo");
debug!(target: "foo_events", parent: None, "foo: {}", 3);
debug!(target: "foo_events", parent: None, { foo = 3, bar.baz = 80 }, "quux");
debug!(target: "foo_events", parent: None, { foo = 2, bar.baz = 79 }, "quux {:?}", true);
debug!(target: "foo_events", parent: None, { foo = 2, bar.baz = 79 }, "quux {:?}, {quux}", true, quux = false);
debug!(target: "foo_events", parent: None, { foo = 2, bar.baz = 78, }, "quux");
}
#[test]
fn info_root() {
info!(parent: None, foo = ?3, bar.baz = %2, quux = false);
info!(parent: None, foo = 3, bar.baz = 2, quux = false);
info!(parent: None, foo = 3, bar.baz = 3,);
info!(parent: None, "foo");
info!(parent: None, "foo: {}", 3);
info!(parent: None, { foo = 3, bar.baz = 80 }, "quux");
info!(parent: None, { foo = 2, bar.baz = 79 }, "quux {:?}", true);
info!(parent: None, { foo = 2, bar.baz = 79 }, "quux {:?}, {quux}", true, quux = false);
info!(parent: None, { foo = 2, bar.baz = 78 }, "quux");
info!(parent: None, { foo = ?2, bar.baz = %78 }, "quux");
info!(target: "foo_events", parent: None, foo = 3, bar.baz = 2, quux = false);
info!(target: "foo_events", parent: None, foo = 3, bar.baz = 3,);
info!(target: "foo_events", parent: None, "foo");
info!(target: "foo_events", parent: None, "foo: {}", 3);
info!(target: "foo_events", parent: None, { foo = 3, bar.baz = 80 }, "quux");
info!(target: "foo_events", parent: None, { foo = 2, bar.baz = 79 }, "quux {:?}", true);
info!(target: "foo_events", parent: None, { foo = 2, bar.baz = 79 }, "quux {:?}, {quux}", true, quux = false);
info!(target: "foo_events", parent: None, { foo = 2, bar.baz = 78, }, "quux");
}
#[test]
fn warn_root() {
warn!(parent: None, foo = ?3, bar.baz = %2, quux = false);
warn!(parent: None, foo = 3, bar.baz = 2, quux = false);
warn!(parent: None, foo = 3, bar.baz = 3,);
warn!(parent: None, "foo");
warn!(parent: None, "foo: {}", 3);
warn!(parent: None, { foo = 3, bar.baz = 80 }, "quux");
warn!(parent: None, { foo = 2, bar.baz = 79 }, "quux {:?}", true);
warn!(parent: None, { foo = 2, bar.baz = 79 }, "quux {:?}, {quux}", true, quux = false);
warn!(parent: None, { foo = 2, bar.baz = 78 }, "quux");
warn!(parent: None, { foo = ?2, bar.baz = %78 }, "quux");
warn!(target: "foo_events", parent: None, foo = 3, bar.baz = 2, quux = false);
warn!(target: "foo_events", parent: None, foo = 3, bar.baz = 3,);
warn!(target: "foo_events", parent: None, "foo");
warn!(target: "foo_events", parent: None, "foo: {}", 3);
warn!(target: "foo_events", parent: None, { foo = 3, bar.baz = 80 }, "quux");
warn!(target: "foo_events", parent: None, { foo = 2, bar.baz = 79 }, "quux {:?}", true);
warn!(target: "foo_events", parent: None, { foo = 2, bar.baz = 79 }, "quux {:?}, {quux}", true, quux = false);
warn!(target: "foo_events", parent: None, { foo = 2, bar.baz = 78, }, "quux");
}
#[test]
fn error_root() {
error!(parent: None, foo = ?3, bar.baz = %2, quux = false);
error!(parent: None, foo = 3, bar.baz = 2, quux = false);
error!(parent: None, foo = 3, bar.baz = 3,);
error!(parent: None, "foo");
error!(parent: None, "foo: {}", 3);
error!(parent: None, { foo = 3, bar.baz = 80 }, "quux");
error!(parent: None, { foo = 2, bar.baz = 79 }, "quux {:?}", true);
error!(parent: None, { foo = 2, bar.baz = 79 }, "quux {:?}, {quux}", true, quux = false);
error!(parent: None, { foo = 2, bar.baz = 78 }, "quux");
error!(parent: None, { foo = ?2, bar.baz = %78 }, "quux");
error!(target: "foo_events", parent: None, foo = 3, bar.baz = 2, quux = false);
error!(target: "foo_events", parent: None, foo = 3, bar.baz = 3,);
error!(target: "foo_events", parent: None, "foo");
error!(target: "foo_events", parent: None, "foo: {}", 3);
error!(target: "foo_events", parent: None, { foo = 3, bar.baz = 80 }, "quux");
error!(target: "foo_events", parent: None, { foo = 2, bar.baz = 79 }, "quux {:?}", true);
error!(target: "foo_events", parent: None, { foo = 2, bar.baz = 79 }, "quux {:?}, {quux}", true, quux = false);
error!(target: "foo_events", parent: None, { foo = 2, bar.baz = 78, }, "quux");
}
#[test]
fn event_with_parent() {
let p = span!(Level::TRACE, "im_a_parent!");
event!(Level::DEBUG, parent: &p, foo = ?3, bar.baz = %2, quux = false);
event!(Level::DEBUG, parent: &p, foo = 3, bar.baz = 2, quux = false);
event!(Level::DEBUG, parent: &p, foo = 3, bar.baz = 3,);
event!(Level::DEBUG, parent: &p, "foo");
event!(Level::DEBUG, parent: &p, "foo: {}", 3);
event!(Level::DEBUG, parent: &p, { foo = 3, bar.baz = 80 }, "quux");
event!(Level::DEBUG, parent: &p, { foo = 2, bar.baz = 79 }, "quux {:?}", true);
event!(Level::DEBUG, parent: &p, { foo = 2, bar.baz = 79 }, "quux {:?}, {quux}", true, quux = false);
event!(Level::DEBUG, parent: &p, { foo = ?2, bar.baz = %78 }, "quux");
event!(target: "foo_events", Level::DEBUG, parent: &p, foo = 3, bar.baz = 2, quux = false);
event!(target: "foo_events", Level::DEBUG, parent: &p, foo = 3, bar.baz = 3,);
event!(target: "foo_events", Level::DEBUG, parent: &p, "foo");
event!(target: "foo_events", Level::DEBUG, parent: &p, "foo: {}", 3);
event!(target: "foo_events", Level::DEBUG, parent: &p, { foo = 3, bar.baz = 80 }, "quux");
event!(target: "foo_events", Level::DEBUG, parent: &p, { foo = 2, bar.baz = 79 }, "quux {:?}", true);
event!(target: "foo_events", Level::DEBUG, parent: &p, { foo = 2, bar.baz = 79 }, "quux {:?}, {quux}", true, quux = false);
event!(target: "foo_events", Level::DEBUG, parent: &p, { foo = 2, bar.baz = 78, }, "quux");
}
#[test]
fn trace_with_parent() {
let p = span!(Level::TRACE, "im_a_parent!");
trace!(parent: &p, foo = ?3, bar.baz = %2, quux = false);
trace!(parent: &p, foo = 3, bar.baz = 2, quux = false);
trace!(parent: &p, foo = 3, bar.baz = 3,);
trace!(parent: &p, "foo");
trace!(parent: &p, "foo: {}", 3);
trace!(parent: &p, { foo = 3, bar.baz = 80 }, "quux");
trace!(parent: &p, { foo = 2, bar.baz = 79 }, "quux {:?}", true);
trace!(parent: &p, { foo = 2, bar.baz = 79 }, "quux {:?}, {quux}", true, quux = false);
trace!(parent: &p, { foo = 2, bar.baz = 78 }, "quux");
trace!(parent: &p, { foo = ?2, bar.baz = %78 }, "quux");
trace!(target: "foo_events", parent: &p, foo = 3, bar.baz = 2, quux = false);
trace!(target: "foo_events", parent: &p, foo = 3, bar.baz = 3,);
trace!(target: "foo_events", parent: &p, "foo");
trace!(target: "foo_events", parent: &p, "foo: {}", 3);
trace!(target: "foo_events", parent: &p, { foo = 3, bar.baz = 80 }, "quux");
trace!(target: "foo_events", parent: &p, { foo = 2, bar.baz = 79 }, "quux {:?}", true);
trace!(target: "foo_events", parent: &p, { foo = 2, bar.baz = 79 }, "quux {:?}, {quux}", true, quux = false);
trace!(target: "foo_events", parent: &p, { foo = 2, bar.baz = 78, }, "quux");
}
#[test]
fn debug_with_parent() {
let p = span!(Level::TRACE, "im_a_parent!");
debug!(parent: &p, foo = ?3, bar.baz = %2, quux = false);
debug!(parent: &p, foo = 3, bar.baz = 2, quux = false);
debug!(parent: &p, foo = 3, bar.baz = 3,);
debug!(parent: &p, "foo");
debug!(parent: &p, "foo: {}", 3);
debug!(parent: &p, { foo = 3, bar.baz = 80 }, "quux");
debug!(parent: &p, { foo = 2, bar.baz = 79 }, "quux {:?}", true);
debug!(parent: &p, { foo = 2, bar.baz = 79 }, "quux {:?}, {quux}", true, quux = false);
debug!(parent: &p, { foo = 2, bar.baz = 78 }, "quux");
debug!(parent: &p, { foo = ?2, bar.baz = %78 }, "quux");
debug!(target: "foo_events", parent: &p, foo = 3, bar.baz = 2, quux = false);
debug!(target: "foo_events", parent: &p, foo = 3, bar.baz = 3,);
debug!(target: "foo_events", parent: &p, "foo");
debug!(target: "foo_events", parent: &p, "foo: {}", 3);
debug!(target: "foo_events", parent: &p, { foo = 3, bar.baz = 80 }, "quux");
debug!(target: "foo_events", parent: &p, { foo = 2, bar.baz = 79 }, "quux {:?}", true);
debug!(target: "foo_events", parent: &p, { foo = 2, bar.baz = 79 }, "quux {:?}, {quux}", true, quux = false);
debug!(target: "foo_events", parent: &p, { foo = 2, bar.baz = 78, }, "quux");
}
#[test]
fn info_with_parent() {
let p = span!(Level::TRACE, "im_a_parent!");
info!(parent: &p, foo = ?3, bar.baz = %2, quux = false);
info!(parent: &p, foo = 3, bar.baz = 2, quux = false);
info!(parent: &p, foo = 3, bar.baz = 3,);
info!(parent: &p, "foo");
info!(parent: &p, "foo: {}", 3);
info!(parent: &p, { foo = 3, bar.baz = 80 }, "quux");
info!(parent: &p, { foo = 2, bar.baz = 79 }, "quux {:?}", true);
info!(parent: &p, { foo = 2, bar.baz = 79 }, "quux {:?}, {quux}", true, quux = false);
info!(parent: &p, { foo = 2, bar.baz = 78 }, "quux");
info!(parent: &p, { foo = ?2, bar.baz = %78 }, "quux");
info!(target: "foo_events", parent: &p, foo = 3, bar.baz = 2, quux = false);
info!(target: "foo_events", parent: &p, foo = 3, bar.baz = 3,);
info!(target: "foo_events", parent: &p, "foo");
info!(target: "foo_events", parent: &p, "foo: {}", 3);
info!(target: "foo_events", parent: &p, { foo = 3, bar.baz = 80 }, "quux");
info!(target: "foo_events", parent: &p, { foo = 2, bar.baz = 79 }, "quux {:?}", true);
info!(target: "foo_events", parent: &p, { foo = 2, bar.baz = 79 }, "quux {:?}, {quux}", true, quux = false);
info!(target: "foo_events", parent: &p, { foo = 2, bar.baz = 78, }, "quux");
}
#[test]
fn warn_with_parent() {
let p = span!(Level::TRACE, "im_a_parent!");
warn!(parent: &p, foo = ?3, bar.baz = %2, quux = false);
warn!(parent: &p, foo = 3, bar.baz = 2, quux = false);
warn!(parent: &p, foo = 3, bar.baz = 3,);
warn!(parent: &p, "foo");
warn!(parent: &p, "foo: {}", 3);
warn!(parent: &p, { foo = 3, bar.baz = 80 }, "quux");
warn!(parent: &p, { foo = 2, bar.baz = 79 }, "quux {:?}", true);
warn!(parent: &p, { foo = 2, bar.baz = 79 }, "quux {:?}, {quux}", true, quux = false);
warn!(parent: &p, { foo = 2, bar.baz = 78 }, "quux");
warn!(parent: &p, { foo = ?2, bar.baz = %78 }, "quux");
warn!(target: "foo_events", parent: &p, foo = 3, bar.baz = 2, quux = false);
warn!(target: "foo_events", parent: &p, foo = 3, bar.baz = 3,);
warn!(target: "foo_events", parent: &p, "foo");
warn!(target: "foo_events", parent: &p, "foo: {}", 3);
warn!(target: "foo_events", parent: &p, { foo = 3, bar.baz = 80 }, "quux");
warn!(target: "foo_events", parent: &p, { foo = 2, bar.baz = 79 }, "quux {:?}", true);
warn!(target: "foo_events", parent: &p, { foo = 2, bar.baz = 79 }, "quux {:?}, {quux}", true, quux = false);
warn!(target: "foo_events", parent: &p, { foo = 2, bar.baz = 78, }, "quux");
}
#[test]
fn error_with_parent() {
let p = span!(Level::TRACE, "im_a_parent!");
error!(parent: &p, foo = ?3, bar.baz = %2, quux = false);
error!(parent: &p, foo = 3, bar.baz = 2, quux = false);
error!(parent: &p, foo = 3, bar.baz = 3,);
error!(parent: &p, "foo");
error!(parent: &p, "foo: {}", 3);
error!(parent: &p, { foo = 3, bar.baz = 80 }, "quux");
error!(parent: &p, { foo = 2, bar.baz = 79 }, "quux {:?}", true);
error!(parent: &p, { foo = 2, bar.baz = 79 }, "quux {:?}, {quux}", true, quux = false);
error!(parent: &p, { foo = 2, bar.baz = 78 }, "quux");
error!(parent: &p, { foo = ?2, bar.baz = %78 }, "quux");
error!(target: "foo_events", parent: &p, foo = 3, bar.baz = 2, quux = false);
error!(target: "foo_events", parent: &p, foo = 3, bar.baz = 3,);
error!(target: "foo_events", parent: &p, "foo");
error!(target: "foo_events", parent: &p, "foo: {}", 3);
error!(target: "foo_events", parent: &p, { foo = 3, bar.baz = 80 }, "quux");
error!(target: "foo_events", parent: &p, { foo = 2, bar.baz = 79 }, "quux {:?}", true);
error!(target: "foo_events", parent: &p, { foo = 2, bar.baz = 79 }, "quux {:?}, {quux}", true, quux = false);
error!(target: "foo_events", parent: &p, { foo = 2, bar.baz = 78, }, "quux");
}
#[test]
fn field_shorthand_only() {
#[derive(Debug)]
struct Position {
x: f32,
y: f32,
}
let pos = Position {
x: 3.234,
y: -1.223,
};
trace!(?pos.x, ?pos.y);
debug!(?pos.x, ?pos.y);
info!(?pos.x, ?pos.y);
warn!(?pos.x, ?pos.y);
error!(?pos.x, ?pos.y);
event!(Level::TRACE, ?pos.x, ?pos.y);
}
#[test]
fn callsite_macro_api() {
// This test should catch any inadvertant breaking changes
// caused bu changes to the macro.
let _callsite = callsite! {
name: "test callsite",
kind: tokio_trace::metadata::Kind::EVENT,
target: "test target",
level: tokio_trace::Level::TRACE,
fields: foo, bar,
};
let _callsite = callsite! {
name: "test callsite",
kind: tokio_trace::metadata::Kind::SPAN,
level: tokio_trace::Level::TRACE,
fields: foo,
};
let _callsite = callsite! {
name: "test callsite",
kind: tokio_trace::metadata::Kind::SPAN,
fields: foo,
};
}
-723
View File
@@ -1,723 +0,0 @@
#[macro_use]
extern crate tokio_trace;
mod support;
use self::support::*;
use std::thread;
use tokio_trace::{
field::{debug, display},
subscriber::with_default,
Level, Span,
};
#[test]
fn handles_to_the_same_span_are_equal() {
// Create a mock subscriber that will return `true` on calls to
// `Subscriber::enabled`, so that the spans will be constructed. We
// won't enter any spans in this test, so the subscriber won't actually
// expect to see any spans.
with_default(subscriber::mock().run(), || {
let foo1 = span!(Level::TRACE, "foo");
let foo2 = foo1.clone();
// Two handles that point to the same span are equal.
assert_eq!(foo1, foo2);
});
}
#[test]
fn handles_to_different_spans_are_not_equal() {
with_default(subscriber::mock().run(), || {
// Even though these spans have the same name and fields, they will have
// differing metadata, since they were created on different lines.
let foo1 = span!(Level::TRACE, "foo", bar = 1u64, baz = false);
let foo2 = span!(Level::TRACE, "foo", bar = 1u64, baz = false);
assert_ne!(foo1, foo2);
});
}
#[test]
fn handles_to_different_spans_with_the_same_metadata_are_not_equal() {
// Every time time this function is called, it will return a _new
// instance_ of a span with the same metadata, name, and fields.
fn make_span() -> Span {
span!(Level::TRACE, "foo", bar = 1u64, baz = false)
}
with_default(subscriber::mock().run(), || {
let foo1 = make_span();
let foo2 = make_span();
assert_ne!(foo1, foo2);
// assert_ne!(foo1.data(), foo2.data());
});
}
#[test]
fn spans_always_go_to_the_subscriber_that_tagged_them() {
let subscriber1 = subscriber::mock()
.enter(span::mock().named("foo"))
.exit(span::mock().named("foo"))
.enter(span::mock().named("foo"))
.exit(span::mock().named("foo"))
.drop_span(span::mock().named("foo"))
.done()
.run();
let subscriber2 = subscriber::mock().run();
let foo = with_default(subscriber1, || {
let foo = span!(Level::TRACE, "foo");
foo.in_scope(|| {});
foo
});
// Even though we enter subscriber 2's context, the subscriber that
// tagged the span should see the enter/exit.
with_default(subscriber2, move || foo.in_scope(|| {}));
}
#[test]
fn spans_always_go_to_the_subscriber_that_tagged_them_even_across_threads() {
let subscriber1 = subscriber::mock()
.enter(span::mock().named("foo"))
.exit(span::mock().named("foo"))
.enter(span::mock().named("foo"))
.exit(span::mock().named("foo"))
.drop_span(span::mock().named("foo"))
.done()
.run();
let foo = with_default(subscriber1, || {
let foo = span!(Level::TRACE, "foo");
foo.in_scope(|| {});
foo
});
// Even though we enter subscriber 2's context, the subscriber that
// tagged the span should see the enter/exit.
thread::spawn(move || {
with_default(subscriber::mock().run(), || {
foo.in_scope(|| {});
})
})
.join()
.unwrap();
}
#[test]
fn dropping_a_span_calls_drop_span() {
let (subscriber, handle) = subscriber::mock()
.enter(span::mock().named("foo"))
.exit(span::mock().named("foo"))
.drop_span(span::mock().named("foo"))
.done()
.run_with_handle();
with_default(subscriber, || {
let span = span!(Level::TRACE, "foo");
span.in_scope(|| {});
drop(span);
});
handle.assert_finished();
}
#[test]
fn span_closes_after_event() {
let (subscriber, handle) = subscriber::mock()
.enter(span::mock().named("foo"))
.event(event::mock())
.exit(span::mock().named("foo"))
.drop_span(span::mock().named("foo"))
.done()
.run_with_handle();
with_default(subscriber, || {
span!(Level::TRACE, "foo").in_scope(|| {
event!(Level::DEBUG, {}, "my event!");
});
});
handle.assert_finished();
}
#[test]
fn new_span_after_event() {
let (subscriber, handle) = subscriber::mock()
.enter(span::mock().named("foo"))
.event(event::mock())
.exit(span::mock().named("foo"))
.drop_span(span::mock().named("foo"))
.enter(span::mock().named("bar"))
.exit(span::mock().named("bar"))
.drop_span(span::mock().named("bar"))
.done()
.run_with_handle();
with_default(subscriber, || {
span!(Level::TRACE, "foo").in_scope(|| {
event!(Level::DEBUG, {}, "my event!");
});
span!(Level::TRACE, "bar").in_scope(|| {});
});
handle.assert_finished();
}
#[test]
fn event_outside_of_span() {
let (subscriber, handle) = subscriber::mock()
.event(event::mock())
.enter(span::mock().named("foo"))
.exit(span::mock().named("foo"))
.drop_span(span::mock().named("foo"))
.done()
.run_with_handle();
with_default(subscriber, || {
debug!("my event!");
span!(Level::TRACE, "foo").in_scope(|| {});
});
handle.assert_finished();
}
#[test]
fn cloning_a_span_calls_clone_span() {
let (subscriber, handle) = subscriber::mock()
.clone_span(span::mock().named("foo"))
.run_with_handle();
with_default(subscriber, || {
let span = span!(Level::TRACE, "foo");
let _span2 = span.clone();
});
handle.assert_finished();
}
#[test]
fn drop_span_when_exiting_dispatchers_context() {
let (subscriber, handle) = subscriber::mock()
.clone_span(span::mock().named("foo"))
.drop_span(span::mock().named("foo"))
.drop_span(span::mock().named("foo"))
.run_with_handle();
with_default(subscriber, || {
let span = span!(Level::TRACE, "foo");
let _span2 = span.clone();
drop(span);
});
handle.assert_finished();
}
#[test]
fn clone_and_drop_span_always_go_to_the_subscriber_that_tagged_the_span() {
let (subscriber1, handle1) = subscriber::mock()
.enter(span::mock().named("foo"))
.exit(span::mock().named("foo"))
.clone_span(span::mock().named("foo"))
.enter(span::mock().named("foo"))
.exit(span::mock().named("foo"))
.drop_span(span::mock().named("foo"))
.drop_span(span::mock().named("foo"))
.run_with_handle();
let subscriber2 = subscriber::mock().done().run();
let foo = with_default(subscriber1, || {
let foo = span!(Level::TRACE, "foo");
foo.in_scope(|| {});
foo
});
// Even though we enter subscriber 2's context, the subscriber that
// tagged the span should see the enter/exit.
with_default(subscriber2, move || {
let foo2 = foo.clone();
foo.in_scope(|| {});
drop(foo);
drop(foo2);
});
handle1.assert_finished();
}
#[test]
fn span_closes_when_exited() {
let (subscriber, handle) = subscriber::mock()
.enter(span::mock().named("foo"))
.exit(span::mock().named("foo"))
.drop_span(span::mock().named("foo"))
.done()
.run_with_handle();
with_default(subscriber, || {
let foo = span!(Level::TRACE, "foo");
foo.in_scope(|| {});
drop(foo);
});
handle.assert_finished();
}
#[test]
fn enter() {
let (subscriber, handle) = subscriber::mock()
.enter(span::mock().named("foo"))
.event(event::mock())
.exit(span::mock().named("foo"))
.drop_span(span::mock().named("foo"))
.done()
.run_with_handle();
with_default(subscriber, || {
let foo = span!(Level::TRACE, "foo");
let _enter = foo.enter();
debug!("dropping guard...");
});
handle.assert_finished();
}
#[test]
fn moved_field() {
let (subscriber, handle) = subscriber::mock()
.new_span(
span::mock().named("foo").with_field(
field::mock("bar")
.with_value(&display("hello from my span"))
.only(),
),
)
.enter(span::mock().named("foo"))
.exit(span::mock().named("foo"))
.drop_span(span::mock().named("foo"))
.done()
.run_with_handle();
with_default(subscriber, || {
let from = "my span";
let span = span!(
Level::TRACE,
"foo",
bar = display(format!("hello from {}", from))
);
span.in_scope(|| {});
});
handle.assert_finished();
}
#[test]
fn dotted_field_name() {
let (subscriber, handle) = subscriber::mock()
.new_span(
span::mock()
.named("foo")
.with_field(field::mock("fields.bar").with_value(&true).only()),
)
.done()
.run_with_handle();
with_default(subscriber, || {
span!(Level::TRACE, "foo", fields.bar = true);
});
handle.assert_finished();
}
#[test]
fn borrowed_field() {
let (subscriber, handle) = subscriber::mock()
.new_span(
span::mock().named("foo").with_field(
field::mock("bar")
.with_value(&display("hello from my span"))
.only(),
),
)
.enter(span::mock().named("foo"))
.exit(span::mock().named("foo"))
.drop_span(span::mock().named("foo"))
.done()
.run_with_handle();
with_default(subscriber, || {
let from = "my span";
let mut message = format!("hello from {}", from);
let span = span!(Level::TRACE, "foo", bar = display(&message));
span.in_scope(|| {
message.insert_str(10, " inside");
});
});
handle.assert_finished();
}
#[test]
// If emitting log instrumentation, this gets moved anyway, breaking the test.
#[cfg(not(feature = "log"))]
fn move_field_out_of_struct() {
use tokio_trace::field::debug;
#[derive(Debug)]
struct Position {
x: f32,
y: f32,
}
let pos = Position {
x: 3.234,
y: -1.223,
};
let (subscriber, handle) = subscriber::mock()
.new_span(
span::mock().named("foo").with_field(
field::mock("x")
.with_value(&debug(3.234))
.and(field::mock("y").with_value(&debug(-1.223)))
.only(),
),
)
.new_span(
span::mock()
.named("bar")
.with_field(field::mock("position").with_value(&debug(&pos)).only()),
)
.run_with_handle();
with_default(subscriber, || {
let pos = Position {
x: 3.234,
y: -1.223,
};
let foo = span!(Level::TRACE, "foo", x = debug(pos.x), y = debug(pos.y));
let bar = span!(Level::TRACE, "bar", position = debug(pos));
foo.in_scope(|| {});
bar.in_scope(|| {});
});
handle.assert_finished();
}
// TODO(#1138): determine a new syntax for uninitialized span fields, and
// re-enable these.
/*
#[test]
fn add_field_after_new_span() {
let (subscriber, handle) = subscriber::mock()
.new_span(
span::mock()
.named("foo")
.with_field(field::mock("bar").with_value(&5)
.and(field::mock("baz").with_value).only()),
)
.record(
span::mock().named("foo"),
field::mock("baz").with_value(&true).only(),
)
.enter(span::mock().named("foo"))
.exit(span::mock().named("foo"))
.drop_span(span::mock().named("foo"))
.done()
.run_with_handle();
with_default(subscriber, || {
let span = span!(Level::TRACE, "foo", bar = 5, baz = false);
span.record("baz", &true);
span.in_scope(|| {})
});
handle.assert_finished();
}
#[test]
fn add_fields_only_after_new_span() {
let (subscriber, handle) = subscriber::mock()
.new_span(span::mock().named("foo"))
.record(
span::mock().named("foo"),
field::mock("bar").with_value(&5).only(),
)
.record(
span::mock().named("foo"),
field::mock("baz").with_value(&true).only(),
)
.enter(span::mock().named("foo"))
.exit(span::mock().named("foo"))
.drop_span(span::mock().named("foo"))
.done()
.run_with_handle();
with_default(subscriber, || {
let span = span!(Level::TRACE, "foo", bar = _, baz = _);
span.record("bar", &5);
span.record("baz", &true);
span.in_scope(|| {})
});
handle.assert_finished();
}
*/
#[test]
fn record_new_value_for_field() {
let (subscriber, handle) = subscriber::mock()
.new_span(
span::mock().named("foo").with_field(
field::mock("bar")
.with_value(&5)
.and(field::mock("baz").with_value(&false))
.only(),
),
)
.record(
span::mock().named("foo"),
field::mock("baz").with_value(&true).only(),
)
.enter(span::mock().named("foo"))
.exit(span::mock().named("foo"))
.drop_span(span::mock().named("foo"))
.done()
.run_with_handle();
with_default(subscriber, || {
let span = span!(Level::TRACE, "foo", bar = 5, baz = false);
span.record("baz", &true);
span.in_scope(|| {})
});
handle.assert_finished();
}
#[test]
fn record_new_values_for_fields() {
let (subscriber, handle) = subscriber::mock()
.new_span(
span::mock().named("foo").with_field(
field::mock("bar")
.with_value(&4)
.and(field::mock("baz").with_value(&false))
.only(),
),
)
.record(
span::mock().named("foo"),
field::mock("bar").with_value(&5).only(),
)
.record(
span::mock().named("foo"),
field::mock("baz").with_value(&true).only(),
)
.enter(span::mock().named("foo"))
.exit(span::mock().named("foo"))
.drop_span(span::mock().named("foo"))
.done()
.run_with_handle();
with_default(subscriber, || {
let span = span!(Level::TRACE, "foo", bar = 4, baz = false);
span.record("bar", &5);
span.record("baz", &true);
span.in_scope(|| {})
});
handle.assert_finished();
}
#[test]
fn new_span_with_target_and_log_level() {
let (subscriber, handle) = subscriber::mock()
.new_span(
span::mock()
.named("foo")
.with_target("app_span")
.at_level(Level::DEBUG),
)
.done()
.run_with_handle();
with_default(subscriber, || {
span!(Level::DEBUG, target: "app_span", "foo");
});
handle.assert_finished();
}
#[test]
fn explicit_root_span_is_root() {
let (subscriber, handle) = subscriber::mock()
.new_span(span::mock().named("foo").with_explicit_parent(None))
.done()
.run_with_handle();
with_default(subscriber, || {
span!(Level::TRACE, parent: None, "foo");
});
handle.assert_finished();
}
#[test]
fn explicit_root_span_is_root_regardless_of_ctx() {
let (subscriber, handle) = subscriber::mock()
.new_span(span::mock().named("foo"))
.enter(span::mock().named("foo"))
.new_span(span::mock().named("bar").with_explicit_parent(None))
.exit(span::mock().named("foo"))
.done()
.run_with_handle();
with_default(subscriber, || {
span!(Level::TRACE, "foo").in_scope(|| {
span!(Level::TRACE, parent: None, "bar");
})
});
handle.assert_finished();
}
#[test]
fn explicit_child() {
let (subscriber, handle) = subscriber::mock()
.new_span(span::mock().named("foo"))
.new_span(span::mock().named("bar").with_explicit_parent(Some("foo")))
.done()
.run_with_handle();
with_default(subscriber, || {
let foo = span!(Level::TRACE, "foo");
span!(Level::TRACE, parent: foo.id(), "bar");
});
handle.assert_finished();
}
#[test]
fn explicit_child_at_levels() {
let (subscriber, handle) = subscriber::mock()
.new_span(span::mock().named("foo"))
.new_span(span::mock().named("a").with_explicit_parent(Some("foo")))
.new_span(span::mock().named("b").with_explicit_parent(Some("foo")))
.new_span(span::mock().named("c").with_explicit_parent(Some("foo")))
.new_span(span::mock().named("d").with_explicit_parent(Some("foo")))
.new_span(span::mock().named("e").with_explicit_parent(Some("foo")))
.done()
.run_with_handle();
with_default(subscriber, || {
let foo = span!(Level::TRACE, "foo");
trace_span!(parent: foo.id(), "a");
debug_span!(parent: foo.id(), "b");
info_span!(parent: foo.id(), "c");
warn_span!(parent: foo.id(), "d");
error_span!(parent: foo.id(), "e");
});
handle.assert_finished();
}
#[test]
fn explicit_child_regardless_of_ctx() {
let (subscriber, handle) = subscriber::mock()
.new_span(span::mock().named("foo"))
.new_span(span::mock().named("bar"))
.enter(span::mock().named("bar"))
.new_span(span::mock().named("baz").with_explicit_parent(Some("foo")))
.exit(span::mock().named("bar"))
.done()
.run_with_handle();
with_default(subscriber, || {
let foo = span!(Level::TRACE, "foo");
span!(Level::TRACE, "bar").in_scope(|| span!(Level::TRACE, parent: foo.id(), "baz"))
});
handle.assert_finished();
}
#[test]
fn contextual_root() {
let (subscriber, handle) = subscriber::mock()
.new_span(span::mock().named("foo").with_contextual_parent(None))
.done()
.run_with_handle();
with_default(subscriber, || {
span!(Level::TRACE, "foo");
});
handle.assert_finished();
}
#[test]
fn contextual_child() {
let (subscriber, handle) = subscriber::mock()
.new_span(span::mock().named("foo"))
.enter(span::mock().named("foo"))
.new_span(
span::mock()
.named("bar")
.with_contextual_parent(Some("foo")),
)
.exit(span::mock().named("foo"))
.done()
.run_with_handle();
with_default(subscriber, || {
span!(Level::TRACE, "foo").in_scope(|| {
span!(Level::TRACE, "bar");
})
});
handle.assert_finished();
}
#[test]
fn display_shorthand() {
let (subscriber, handle) = subscriber::mock()
.new_span(
span::mock().named("my_span").with_field(
field::mock("my_field")
.with_value(&display("hello world"))
.only(),
),
)
.done()
.run_with_handle();
with_default(subscriber, || {
span!(Level::TRACE, "my_span", my_field = %"hello world");
});
handle.assert_finished();
}
#[test]
fn debug_shorthand() {
let (subscriber, handle) = subscriber::mock()
.new_span(
span::mock().named("my_span").with_field(
field::mock("my_field")
.with_value(&debug("hello world"))
.only(),
),
)
.done()
.run_with_handle();
with_default(subscriber, || {
span!(Level::TRACE, "my_span", my_field = ?"hello world");
});
handle.assert_finished();
}
#[test]
fn both_shorthands() {
let (subscriber, handle) = subscriber::mock()
.new_span(
span::mock().named("my_span").with_field(
field::mock("display_field")
.with_value(&display("hello world"))
.and(field::mock("debug_field").with_value(&debug("hello world")))
.only(),
),
)
.done()
.run_with_handle();
with_default(subscriber, || {
span!(Level::TRACE, "my_span", display_field = %"hello world", debug_field = ?"hello world");
});
handle.assert_finished();
}
-48
View File
@@ -1,48 +0,0 @@
#[macro_use]
extern crate tokio_trace;
use tokio_trace::{
span,
subscriber::{with_default, Interest, Subscriber},
Event, Level, Metadata,
};
#[test]
fn event_macros_dont_infinite_loop() {
// This test ensures that an event macro within a subscriber
// won't cause an infinite loop of events.
struct TestSubscriber;
impl Subscriber for TestSubscriber {
fn register_callsite(&self, _: &Metadata) -> Interest {
// Always return sometimes so that `enabled` will be called
// (which can loop).
Interest::sometimes()
}
fn enabled(&self, meta: &Metadata) -> bool {
assert!(meta.fields().iter().any(|f| f.name() == "foo"));
event!(Level::TRACE, bar = false);
true
}
fn new_span(&self, _: &span::Attributes) -> span::Id {
span::Id::from_u64(0xAAAA)
}
fn record(&self, _: &span::Id, _: &span::Record) {}
fn record_follows_from(&self, _: &span::Id, _: &span::Id) {}
fn event(&self, event: &Event) {
assert!(event.metadata().fields().iter().any(|f| f.name() == "foo"));
event!(Level::TRACE, baz = false);
}
fn enter(&self, _: &span::Id) {}
fn exit(&self, _: &span::Id) {}
}
with_default(TestSubscriber, || {
event!(Level::TRACE, foo = false);
})
}
-93
View File
@@ -1,93 +0,0 @@
#![allow(missing_docs)]
use super::{field, metadata};
use std::fmt;
/// A mock event.
///
/// This is intended for use with the mock subscriber API in the
/// `subscriber` module.
#[derive(Debug, Default, Eq, PartialEq)]
pub struct MockEvent {
pub fields: Option<field::Expect>,
metadata: metadata::Expect,
}
pub fn mock() -> MockEvent {
MockEvent {
..Default::default()
}
}
impl MockEvent {
pub fn named<I>(self, name: I) -> Self
where
I: Into<String>,
{
Self {
metadata: metadata::Expect {
name: Some(name.into()),
..self.metadata
},
..self
}
}
pub fn with_fields<I>(self, fields: I) -> Self
where
I: Into<field::Expect>,
{
Self {
fields: Some(fields.into()),
..self
}
}
pub fn at_level(self, level: tokio_trace::Level) -> Self {
Self {
metadata: metadata::Expect {
level: Some(level),
..self.metadata
},
..self
}
}
pub fn with_target<I>(self, target: I) -> Self
where
I: Into<String>,
{
Self {
metadata: metadata::Expect {
target: Some(target.into()),
..self.metadata
},
..self
}
}
pub(in support) fn check(self, event: &tokio_trace::Event) {
let meta = event.metadata();
let name = meta.name();
self.metadata.check(meta, format_args!("event {}", name));
assert!(meta.is_event(), "expected an event but got {:?}", event);
if let Some(mut expected_fields) = self.fields {
let mut checker = expected_fields.checker(format!("{}", name));
event.record(&mut checker);
checker.finish();
}
}
}
impl fmt::Display for MockEvent {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "an event")?;
if let Some(ref name) = self.metadata.name {
write!(f, " named {:?}", name)?;
}
if let Some(ref fields) = self.fields {
write!(f, " with {}", fields)?
}
Ok(())
}
}
-225
View File
@@ -1,225 +0,0 @@
use tokio_trace::{
callsite::Callsite,
field::{self, Field, Value, Visit},
metadata::Kind,
};
use std::{collections::HashMap, fmt};
#[derive(Default, Debug, Eq, PartialEq)]
pub struct Expect {
fields: HashMap<String, MockValue>,
only: bool,
}
#[derive(Debug)]
pub struct MockField {
name: String,
value: MockValue,
}
#[derive(Debug, Eq, PartialEq)]
pub enum MockValue {
I64(i64),
U64(u64),
Bool(bool),
Str(String),
Debug(String),
Any,
}
pub fn mock<K>(name: K) -> MockField
where
String: From<K>,
{
MockField {
name: name.into(),
value: MockValue::Any,
}
}
impl MockField {
/// Expect a field with the given name and value.
pub fn with_value(self, value: &Value) -> Self {
Self {
value: MockValue::from(value),
..self
}
}
pub fn and(self, other: MockField) -> Expect {
Expect {
fields: HashMap::new(),
only: false,
}
.and(self)
.and(other)
}
pub fn only(self) -> Expect {
Expect {
fields: HashMap::new(),
only: true,
}
.and(self)
}
}
impl Into<Expect> for MockField {
fn into(self) -> Expect {
Expect {
fields: HashMap::new(),
only: false,
}
.and(self)
}
}
impl Expect {
pub fn and(mut self, field: MockField) -> Self {
self.fields.insert(field.name, field.value);
self
}
/// Indicates that no fields other than those specified should be expected.
pub fn only(self) -> Self {
Self { only: true, ..self }
}
fn compare_or_panic(&mut self, name: &str, value: &Value, ctx: &str) {
let value = value.into();
match self.fields.remove(name) {
Some(MockValue::Any) => {}
Some(expected) => assert!(
expected == value,
"\nexpected `{}` to contain:\n\t`{}{}`\nbut got:\n\t`{}{}`",
ctx,
name,
expected,
name,
value
),
None if self.only => panic!(
"\nexpected `{}` to contain only:\n\t`{}`\nbut got:\n\t`{}{}`",
ctx, self, name, value
),
_ => {}
}
}
pub fn checker<'a>(&'a mut self, ctx: String) -> CheckVisitor<'a> {
CheckVisitor { expect: self, ctx }
}
pub fn is_empty(&self) -> bool {
self.fields.is_empty()
}
}
impl fmt::Display for MockValue {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
MockValue::I64(v) => write!(f, ": i64 = {:?}", v),
MockValue::U64(v) => write!(f, ": u64 = {:?}", v),
MockValue::Bool(v) => write!(f, ": bool = {:?}", v),
MockValue::Str(v) => write!(f, ": &str = {:?}", v),
MockValue::Debug(v) => write!(f, ": &fmt::Debug = {:?}", v),
MockValue::Any => write!(f, ": _ = _"),
}
}
}
pub struct CheckVisitor<'a> {
expect: &'a mut Expect,
ctx: String,
}
impl<'a> Visit for CheckVisitor<'a> {
fn record_i64(&mut self, field: &Field, value: i64) {
self.expect
.compare_or_panic(field.name(), &value, &self.ctx[..])
}
fn record_u64(&mut self, field: &Field, value: u64) {
self.expect
.compare_or_panic(field.name(), &value, &self.ctx[..])
}
fn record_bool(&mut self, field: &Field, value: bool) {
self.expect
.compare_or_panic(field.name(), &value, &self.ctx[..])
}
fn record_str(&mut self, field: &Field, value: &str) {
self.expect
.compare_or_panic(field.name(), &value, &self.ctx[..])
}
fn record_debug(&mut self, field: &Field, value: &fmt::Debug) {
self.expect
.compare_or_panic(field.name(), &field::debug(value), &self.ctx)
}
}
impl<'a> CheckVisitor<'a> {
pub fn finish(self) {
assert!(
self.expect.fields.is_empty(),
"{}missing {}",
self.expect,
self.ctx
);
}
}
impl<'a> From<&'a Value> for MockValue {
fn from(value: &'a Value) -> Self {
struct MockValueBuilder {
value: Option<MockValue>,
}
impl Visit for MockValueBuilder {
fn record_i64(&mut self, _: &Field, value: i64) {
self.value = Some(MockValue::I64(value));
}
fn record_u64(&mut self, _: &Field, value: u64) {
self.value = Some(MockValue::U64(value));
}
fn record_bool(&mut self, _: &Field, value: bool) {
self.value = Some(MockValue::Bool(value));
}
fn record_str(&mut self, _: &Field, value: &str) {
self.value = Some(MockValue::Str(value.to_owned()));
}
fn record_debug(&mut self, _: &Field, value: &fmt::Debug) {
self.value = Some(MockValue::Debug(format!("{:?}", value)));
}
}
let fake_field = callsite!(name: "fake", kind: Kind::EVENT, fields: fake_field)
.metadata()
.fields()
.field("fake_field")
.unwrap();
let mut builder = MockValueBuilder { value: None };
value.record(&fake_field, &mut builder);
builder
.value
.expect("finish called before a value was recorded")
}
}
impl fmt::Display for Expect {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "fields ")?;
let entries = self
.fields
.iter()
.map(|(k, v)| (field::display(k), field::display(v)));
f.debug_map().entries(entries).finish()
}
}
-64
View File
@@ -1,64 +0,0 @@
use std::fmt;
use tokio_trace::Metadata;
#[derive(Debug, Eq, PartialEq, Default)]
pub struct Expect {
pub name: Option<String>,
pub level: Option<tokio_trace::Level>,
pub target: Option<String>,
}
impl Expect {
pub(in support) fn check(&self, actual: &Metadata, ctx: fmt::Arguments) {
if let Some(ref expected_name) = self.name {
let name = actual.name();
assert!(
expected_name == name,
"expected {} to be named `{}`, but got one named `{}`",
ctx,
expected_name,
name
)
}
if let Some(ref expected_level) = self.level {
let level = actual.level();
assert!(
expected_level == level,
"expected {} to be at level `{:?}`, but it was at level `{:?}` instead",
ctx,
expected_level,
level,
)
}
if let Some(ref expected_target) = self.target {
let target = actual.target();
assert!(
expected_target == &target,
"expected {} to have target `{}`, but it had target `{}` instead",
ctx,
expected_target,
target,
)
}
}
}
impl fmt::Display for Expect {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
if let Some(ref name) = self.name {
write!(f, "named `{}`", name)?;
}
if let Some(ref level) = self.level {
write!(f, " at the `{:?}` level", level)?;
}
if let Some(ref target) = self.target {
write!(f, " with target `{}`", target)?;
}
Ok(())
}
}
-8
View File
@@ -1,8 +0,0 @@
#![allow(dead_code)]
pub mod event;
pub mod field;
mod metadata;
pub mod span;
pub mod subscriber;
extern crate tokio_trace_core;
-178
View File
@@ -1,178 +0,0 @@
#![allow(missing_docs)]
use super::{field, metadata};
use std::fmt;
/// A mock span.
///
/// This is intended for use with the mock subscriber API in the
/// `subscriber` module.
#[derive(Debug, Default, Eq, PartialEq)]
pub struct MockSpan {
pub(in support) metadata: metadata::Expect,
}
#[derive(Debug, Eq, PartialEq)]
pub(in support) enum Parent {
ContextualRoot,
Contextual(String),
ExplicitRoot,
Explicit(String),
}
#[derive(Debug, Default, Eq, PartialEq)]
pub struct NewSpan {
pub(in support) span: MockSpan,
pub(in support) fields: field::Expect,
pub(in support) parent: Option<Parent>,
}
pub fn mock() -> MockSpan {
MockSpan {
..Default::default()
}
}
impl MockSpan {
pub fn named<I>(self, name: I) -> Self
where
I: Into<String>,
{
Self {
metadata: metadata::Expect {
name: Some(name.into()),
..self.metadata
},
..self
}
}
pub fn at_level(self, level: tokio_trace::Level) -> Self {
Self {
metadata: metadata::Expect {
level: Some(level),
..self.metadata
},
..self
}
}
pub fn with_target<I>(self, target: I) -> Self
where
I: Into<String>,
{
Self {
metadata: metadata::Expect {
target: Some(target.into()),
..self.metadata
},
..self
}
}
pub fn with_explicit_parent(self, parent: Option<&str>) -> NewSpan {
let parent = match parent {
Some(name) => Parent::Explicit(name.into()),
None => Parent::ExplicitRoot,
};
NewSpan {
parent: Some(parent),
span: self,
..Default::default()
}
}
pub fn with_contextual_parent(self, parent: Option<&str>) -> NewSpan {
let parent = match parent {
Some(name) => Parent::Contextual(name.into()),
None => Parent::ContextualRoot,
};
NewSpan {
parent: Some(parent),
span: self,
..Default::default()
}
}
pub fn name(&self) -> Option<&str> {
self.metadata.name.as_ref().map(String::as_ref)
}
pub fn with_field<I>(self, fields: I) -> NewSpan
where
I: Into<field::Expect>,
{
NewSpan {
span: self,
fields: fields.into(),
..Default::default()
}
}
pub(in support) fn check_metadata(&self, actual: &tokio_trace::Metadata) {
self.metadata.check(actual, format_args!("span {}", self));
assert!(actual.is_span(), "expected a span but got {:?}", actual);
}
}
impl fmt::Display for MockSpan {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
if self.metadata.name.is_some() {
write!(f, "a span{}", self.metadata)
} else {
write!(f, "any span{}", self.metadata)
}
}
}
impl Into<NewSpan> for MockSpan {
fn into(self) -> NewSpan {
NewSpan {
span: self,
..Default::default()
}
}
}
impl NewSpan {
pub fn with_explicit_parent(self, parent: Option<&str>) -> NewSpan {
let parent = match parent {
Some(name) => Parent::Explicit(name.into()),
None => Parent::ExplicitRoot,
};
NewSpan {
parent: Some(parent),
..self
}
}
pub fn with_contextual_parent(self, parent: Option<&str>) -> NewSpan {
let parent = match parent {
Some(name) => Parent::Contextual(name.into()),
None => Parent::ContextualRoot,
};
NewSpan {
parent: Some(parent),
..self
}
}
pub fn with_field<I>(self, fields: I) -> NewSpan
where
I: Into<field::Expect>,
{
NewSpan {
fields: fields.into(),
..self
}
}
}
impl fmt::Display for NewSpan {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "a new span{}", self.span.metadata)?;
if !self.fields.is_empty() {
write!(f, " with {}", self.fields)?;
}
Ok(())
}
}
-409
View File
@@ -1,409 +0,0 @@
#![allow(missing_docs)]
use super::{
event::MockEvent,
field as mock_field,
span::{MockSpan, NewSpan},
};
use std::{
collections::{HashMap, VecDeque},
fmt,
sync::{
atomic::{AtomicUsize, Ordering},
Arc, Mutex,
},
};
use tokio_trace::{
span::{self, Attributes, Id},
Event, Metadata, Subscriber,
};
#[derive(Debug, Eq, PartialEq)]
enum Expect {
Event(MockEvent),
Enter(MockSpan),
Exit(MockSpan),
CloneSpan(MockSpan),
DropSpan(MockSpan),
Visit(MockSpan, mock_field::Expect),
NewSpan(NewSpan),
Nothing,
}
struct SpanState {
name: &'static str,
refs: usize,
}
struct Running<F: Fn(&Metadata) -> bool> {
spans: Mutex<HashMap<Id, SpanState>>,
expected: Arc<Mutex<VecDeque<Expect>>>,
current: Mutex<Vec<Id>>,
ids: AtomicUsize,
filter: F,
}
pub struct MockSubscriber<F: Fn(&Metadata) -> bool> {
expected: VecDeque<Expect>,
filter: F,
}
pub struct MockHandle(Arc<Mutex<VecDeque<Expect>>>);
pub fn mock() -> MockSubscriber<fn(&Metadata) -> bool> {
MockSubscriber {
expected: VecDeque::new(),
filter: (|_: &Metadata| true) as for<'r, 's> fn(&'r Metadata<'s>) -> _,
}
}
impl<F> MockSubscriber<F>
where
F: Fn(&Metadata) -> bool + 'static,
{
pub fn enter(mut self, span: MockSpan) -> Self {
self.expected.push_back(Expect::Enter(span));
self
}
pub fn event(mut self, event: MockEvent) -> Self {
self.expected.push_back(Expect::Event(event));
self
}
pub fn exit(mut self, span: MockSpan) -> Self {
self.expected.push_back(Expect::Exit(span));
self
}
pub fn clone_span(mut self, span: MockSpan) -> Self {
self.expected.push_back(Expect::CloneSpan(span));
self
}
pub fn drop_span(mut self, span: MockSpan) -> Self {
self.expected.push_back(Expect::DropSpan(span));
self
}
pub fn done(mut self) -> Self {
self.expected.push_back(Expect::Nothing);
self
}
pub fn record<I>(mut self, span: MockSpan, fields: I) -> Self
where
I: Into<mock_field::Expect>,
{
self.expected.push_back(Expect::Visit(span, fields.into()));
self
}
pub fn new_span<I>(mut self, new_span: I) -> Self
where
I: Into<NewSpan>,
{
self.expected.push_back(Expect::NewSpan(new_span.into()));
self
}
pub fn with_filter<G>(self, filter: G) -> MockSubscriber<G>
where
G: Fn(&Metadata) -> bool + 'static,
{
MockSubscriber {
filter,
expected: self.expected,
}
}
pub fn run(self) -> impl Subscriber {
let (subscriber, _) = self.run_with_handle();
subscriber
}
pub fn run_with_handle(self) -> (impl Subscriber, MockHandle) {
let expected = Arc::new(Mutex::new(self.expected));
let handle = MockHandle(expected.clone());
let subscriber = Running {
spans: Mutex::new(HashMap::new()),
expected,
current: Mutex::new(Vec::new()),
ids: AtomicUsize::new(1),
filter: self.filter,
};
(subscriber, handle)
}
}
impl<F> Subscriber for Running<F>
where
F: Fn(&Metadata) -> bool + 'static,
{
fn enabled(&self, meta: &Metadata) -> bool {
(self.filter)(meta)
}
fn record(&self, id: &Id, values: &span::Record) {
let spans = self.spans.lock().unwrap();
let mut expected = self.expected.lock().unwrap();
let span = spans
.get(id)
.unwrap_or_else(|| panic!("no span for ID {:?}", id));
println!("record: {}; id={:?}; values={:?};", span.name, id, values);
let was_expected = if let Some(Expect::Visit(_, _)) = expected.front() {
true
} else {
false
};
if was_expected {
if let Expect::Visit(expected_span, mut expected_values) = expected.pop_front().unwrap()
{
if let Some(name) = expected_span.name() {
assert_eq!(name, span.name);
}
let mut checker = expected_values.checker(format!("span {}: ", span.name));
values.record(&mut checker);
checker.finish();
}
}
}
fn event(&self, event: &Event) {
let name = event.metadata().name();
println!("event: {};", name);
match self.expected.lock().unwrap().pop_front() {
None => {}
Some(Expect::Event(expected)) => expected.check(event),
Some(ex) => ex.bad(format_args!("observed event {:?}", event)),
}
}
fn record_follows_from(&self, _span: &Id, _follows: &Id) {
// TODO: it should be possible to expect spans to follow from other spans
}
fn new_span(&self, span: &Attributes) -> Id {
use span::Parent;
let meta = span.metadata();
let id = self.ids.fetch_add(1, Ordering::SeqCst);
let id = Id::from_u64(id as u64);
println!(
"new_span: name={:?}; target={:?}; id={:?};",
meta.name(),
meta.target(),
id
);
let mut expected = self.expected.lock().unwrap();
let was_expected = match expected.front() {
Some(Expect::NewSpan(_)) => true,
_ => false,
};
let mut spans = self.spans.lock().unwrap();
if was_expected {
if let Expect::NewSpan(mut expected) = expected.pop_front().unwrap() {
let name = meta.name();
expected
.span
.metadata
.check(meta, format_args!("span `{}`", name));
let mut checker = expected.fields.checker(format!("{}", name));
span.record(&mut checker);
checker.finish();
match expected.parent {
Some(Parent::ExplicitRoot) => {
assert!(
span.is_root(),
"expected {:?} to be an explicit root span",
name
);
}
Some(Parent::Explicit(expected_parent)) => {
let actual_parent =
span.parent().and_then(|id| spans.get(id)).map(|s| s.name);
assert_eq!(
Some(expected_parent.as_ref()),
actual_parent,
"expected {:?} to have explicit parent {:?}",
name,
expected_parent,
);
}
Some(Parent::ContextualRoot) => {
assert!(
span.is_contextual(),
"expected {:?} to have a contextual parent",
name
);
assert!(
self.current.lock().unwrap().last().is_none(),
"expected {:?} to be a root, but we were inside a span",
name
);
}
Some(Parent::Contextual(expected_parent)) => {
assert!(
span.is_contextual(),
"expected {:?} to have a contextual parent",
name
);
let stack = self.current.lock().unwrap();
let actual_parent =
stack.last().and_then(|id| spans.get(id)).map(|s| s.name);
assert_eq!(
Some(expected_parent.as_ref()),
actual_parent,
"expected {:?} to have contextual parent {:?}",
name,
expected_parent,
);
}
None => {}
}
}
}
spans.insert(
id.clone(),
SpanState {
name: meta.name(),
refs: 1,
},
);
id
}
fn enter(&self, id: &Id) {
let spans = self.spans.lock().unwrap();
if let Some(span) = spans.get(id) {
println!("enter: {}; id={:?};", span.name, id);
match self.expected.lock().unwrap().pop_front() {
None => {}
Some(Expect::Enter(ref expected_span)) => {
if let Some(name) = expected_span.name() {
assert_eq!(name, span.name);
}
}
Some(ex) => ex.bad(format_args!("entered span {:?}", span.name)),
}
};
self.current.lock().unwrap().push(id.clone());
}
fn exit(&self, id: &Id) {
let spans = self.spans.lock().unwrap();
let span = spans
.get(id)
.unwrap_or_else(|| panic!("no span for ID {:?}", id));
println!("exit: {}; id={:?};", span.name, id);
match self.expected.lock().unwrap().pop_front() {
None => {}
Some(Expect::Exit(ref expected_span)) => {
if let Some(name) = expected_span.name() {
assert_eq!(name, span.name);
}
let curr = self.current.lock().unwrap().pop();
assert_eq!(
Some(id),
curr.as_ref(),
"exited span {:?}, but the current span was {:?}",
span.name,
curr.as_ref().and_then(|id| spans.get(id)).map(|s| s.name)
);
}
Some(ex) => ex.bad(format_args!("exited span {:?}", span.name)),
};
}
fn clone_span(&self, id: &Id) -> Id {
let name = self.spans.lock().unwrap().get_mut(id).map(|span| {
let name = span.name;
println!("clone_span: {}; id={:?}; refs={:?};", name, id, span.refs);
span.refs += 1;
name
});
if name.is_none() {
println!("clone_span: id={:?};", id);
}
let mut expected = self.expected.lock().unwrap();
let was_expected = if let Some(Expect::CloneSpan(ref span)) = expected.front() {
assert_eq!(name, span.name());
true
} else {
false
};
if was_expected {
expected.pop_front();
}
id.clone()
}
fn drop_span(&self, id: Id) {
let mut is_event = false;
let name = if let Ok(mut spans) = self.spans.try_lock() {
spans.get_mut(&id).map(|span| {
let name = span.name;
if name.contains("event") {
is_event = true;
}
println!("drop_span: {}; id={:?}; refs={:?};", name, id, span.refs);
span.refs -= 1;
name
})
} else {
None
};
if name.is_none() {
println!("drop_span: id={:?}", id);
}
if let Ok(mut expected) = self.expected.try_lock() {
let was_expected = match expected.front() {
Some(Expect::DropSpan(ref span)) => {
// Don't assert if this function was called while panicking,
// as failing the assertion can cause a double panic.
if !::std::thread::panicking() {
assert_eq!(name, span.name());
}
true
}
Some(Expect::Event(_)) => {
if !::std::thread::panicking() {
assert!(is_event);
}
true
}
_ => false,
};
if was_expected {
expected.pop_front();
}
}
}
}
impl MockHandle {
pub fn assert_finished(&self) {
if let Ok(ref expected) = self.0.lock() {
assert!(
!expected.iter().any(|thing| thing != &Expect::Nothing),
"more notifications expected: {:?}",
**expected
);
}
}
}
impl Expect {
fn bad<'a>(&self, what: fmt::Arguments<'a>) {
match self {
Expect::Event(e) => panic!("expected event {}, but {} instead", e, what,),
Expect::Enter(e) => panic!("expected to enter {} but {} instead", e, what,),
Expect::Exit(e) => panic!("expected to exit {} but {} instead", e, what,),
Expect::CloneSpan(e) => panic!("expected to clone {} but {} instead", e, what,),
Expect::DropSpan(e) => panic!("expected to drop {} but {} instead", e, what,),
Expect::Visit(e, fields) => {
panic!("expected {} to record {} but {} instead", e, fields, what,)
}
Expect::NewSpan(e) => panic!("expected {} but {} instead", e, what),
Expect::Nothing => panic!("expected nothing else to happen, but {} instead", what,),
}
}
}
-22
View File
@@ -1,22 +0,0 @@
# 0.2.0 (April 21, 2019)
### Breaking Changes
- Remove `Callsite::clear_interest` and `Callsite::add_interest` (#1039)
- `metadata!` macro now requires a `Kind` field (#1046)
### Added
- Add a function to rebuild cached interest (#1039)
- Add overrideable downcasting to `Subscriber`s (#974)
- Add slightly more useful debug impls (#1014)
- Introduce callsite classification in metadata (#1046)
### Fixed
- `fmt::Debug` impls for `field::Display` and `field::Debug` not passing through
to the inner value (#992)
- Entering a `Dispatch` function unsets the default dispatcher for the duration
of the function (so that events inside the subscriber cannot cause infinite
loops) (#1033)
# 0.1.0 (March 13, 2019)
- Initial release
-24
View File
@@ -1,24 +0,0 @@
[package]
name = "tokio-trace-core"
# When releasing to crates.io:
# - Remove path dependencies
# - Update html_root_url.
# - Update doc url
# - Cargo.toml
# - README.md
# - Update CHANGELOG.md.
# - Create "v0.2.x" git tag.
version = "0.2.0"
authors = ["Tokio Contributors <[email protected]>"]
license = "MIT"
repository = "https://github.com/tokio-rs/tokio"
homepage = "https://tokio.rs"
documentation = "https://docs.rs/tokio-trace-core/0.2.0/tokio_trace_core"
description = """
Core primitives for tokio-trace.
"""
categories = ["development-tools::debugging"]
keywords = ["logging", "tracing"]
[dependencies]
lazy_static = "1.0.0"
-25
View File
@@ -1,25 +0,0 @@
Copyright (c) 2019 Tokio Contributors
Permission is hereby granted, free of charge, to any
person obtaining a copy of this software and associated
documentation files (the "Software"), to deal in the
Software without restriction, including without
limitation the rights to use, copy, modify, merge,
publish, distribute, sublicense, and/or sell copies of
the Software, and to permit persons to whom the Software
is furnished to do so, subject to the following
conditions:
The above copyright notice and this permission notice
shall be included in all copies or substantial portions
of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.
-56
View File
@@ -1,56 +0,0 @@
# tokio-trace-core
Core primitives for `tokio-trace`.
[Documentation](https://docs.rs/tokio-trace-core/0.2.0/tokio_trace_core/index.html)
## Overview
`tokio-trace` is a framework for instrumenting Rust programs to collect
structured, event-based diagnostic information. This crate defines the core
primitives of `tokio-trace`.
The crate provides:
* [`Span`] identifies a span within the execution of a program.
* [`Event`] represents a single event within a trace.
* [`Subscriber`], the trait implemented to collect trace data.
* [`Metadata`] and [`Callsite`] provide information describing `Span`s.
* [`Field`], [`FieldSet`], [`Value`], and [`ValueSet`] represent the
structured data attached to a `Span`.
* [`Dispatch`] allows span events to be dispatched to `Subscriber`s.
In addition, it defines the global callsite registry and per-thread current
dispatcher which other components of the tracing system rely on.
Application authors will typically not use this crate directly. Instead, they
will use the [`tokio-trace`] crate, which provides a much more fully-featured
API. However, this crate's API will change very infrequently, so it may be used
when dependencies must be very stable.
[`tokio-trace`]: ../
[`Span`]: https://docs.rs/tokio-trace-core/0.2.0/tokio_trace_core/span/struct.Span.html
[`Event`]: https://docs.rs/tokio-trace-core/0.2.0/tokio_trace_core/event/struct.Event.html
[`Subscriber`]: https://docs.rs/tokio-trace-core/0.2.0/tokio_trace_core/subscriber/trait.Subscriber.html
[`Metadata`]: https://docs.rs/tokio-trace-core/0.2.0/tokio_trace_core/metadata/struct.Metadata.html
[`Callsite`]: https://docs.rs/tokio-trace-core/0.2.0/tokio_trace_core/callsite/trait.Callsite.html
[`Field`]: https://docs.rs/tokio-trace-core/0.2.0/tokio_trace_core/field/struct.Field.html
[`FieldSet`]: https://docs.rs/tokio-trace-core/0.2.0/tokio_trace_core/field/struct.FieldSet.html
[`Value`]: https://docs.rs/tokio-trace-core/0.2.0/tokio_trace_core/field/trait.Value.html
[`ValueSet`]: https://docs.rs/tokio-trace-core/0.2.0/tokio_trace_core/field/struct.ValueSet.html
[`Dispatch`]: https://docs.rs/tokio-trace-core/0.2.0/tokio_trace_core/dispatcher/struct.Dispatch.html
## License
This project is licensed under the [MIT license](LICENSE).
### Contribution
Unless you explicitly state otherwise, any contribution intentionally submitted
for inclusion in Tokio by you, shall be licensed as MIT, without any additional
terms or conditions.
@@ -1,142 +0,0 @@
//! Callsites represent the source locations from which spans or events
//! originate.
use std::{
fmt,
hash::{Hash, Hasher},
ptr,
sync::Mutex,
};
use {
dispatcher::{self, Dispatch, Registrar},
subscriber::Interest,
Metadata,
};
lazy_static! {
static ref REGISTRY: Mutex<Registry> = Mutex::new(Registry {
callsites: Vec::new(),
dispatchers: Vec::new(),
});
}
struct Registry {
callsites: Vec<&'static Callsite>,
dispatchers: Vec<dispatcher::Registrar>,
}
impl Registry {
fn rebuild_callsite_interest(&self, callsite: &'static Callsite) {
let meta = callsite.metadata();
let mut interest = Interest::never();
for registrar in &self.dispatchers {
if let Some(sub_interest) = registrar.try_register(meta) {
interest = interest.and(sub_interest);
}
}
callsite.set_interest(interest)
}
fn rebuild_interest(&mut self) {
self.dispatchers.retain(Registrar::is_alive);
self.callsites.iter().for_each(|&callsite| {
self.rebuild_callsite_interest(callsite);
});
}
}
/// Trait implemented by callsites.
///
/// These functions are only intended to be called by the [`Registry`] which
/// correctly handles determining the common interest between all subscribers.
pub trait Callsite: Sync {
/// Sets the [`Interest`] for this callsite.
///
/// [`Interest`]: ../subscriber/struct.Interest.html
fn set_interest(&self, interest: Interest);
/// Returns the [metadata] associated with the callsite.
///
/// [metadata]: ../metadata/struct.Metadata.html
fn metadata(&self) -> &Metadata;
}
/// Uniquely identifies a [`Callsite`]
///
/// Two `Identifier`s are equal if they both refer to the same callsite.
///
/// [`Callsite`]: ../callsite/trait.Callsite.html
#[derive(Clone)]
pub struct Identifier(
/// **Warning**: The fields on this type are currently `pub` because it must
/// be able to be constructed statically by macros. However, when `const
/// fn`s are available on stable Rust, this will no longer be necessary.
/// Thus, these fields are *not* considered stable public API, and they may
/// change warning. Do not rely on any fields on `Identifier`. When
/// constructing new `Identifier`s, use the `identify_callsite!` macro or
/// the `Callsite::id` function instead.
// TODO: When `Callsite::id` is a const fn, this need no longer be `pub`.
#[doc(hidden)]
pub &'static Callsite,
);
/// Clear and reregister interest on every [`Callsite`]
///
/// This function is intended for runtime reconfiguration of filters on traces
/// when the filter recalculation is much less frequent than trace events are.
/// The alternative is to have the [`Subscriber`] that supports runtime
/// reconfiguration of filters always return [`Interest::sometimes()`] so that
/// [`enabled`] is evaluated for every event.
///
/// [`Callsite`]: ../callsite/trait.Callsite.html
/// [`enabled`]: ../subscriber/trait.Subscriber.html#tymethod.enabled
/// [`Interest::sometimes()`]: ../subscriber/struct.Interest.html#method.sometimes
/// [`Subscriber`]: ../subscriber/trait.Subscriber.html
pub fn rebuild_interest_cache() {
let mut registry = REGISTRY.lock().unwrap();
registry.rebuild_interest();
}
/// Register a new `Callsite` with the global registry.
///
/// This should be called once per callsite after the callsite has been
/// constructed.
pub fn register(callsite: &'static Callsite) {
let mut registry = REGISTRY.lock().unwrap();
registry.rebuild_callsite_interest(callsite);
registry.callsites.push(callsite);
}
pub(crate) fn register_dispatch(dispatch: &Dispatch) {
let mut registry = REGISTRY.lock().unwrap();
registry.dispatchers.push(dispatch.registrar());
registry.rebuild_interest();
}
// ===== impl Identifier =====
impl PartialEq for Identifier {
fn eq(&self, other: &Identifier) -> bool {
ptr::eq(self.0, other.0)
}
}
impl Eq for Identifier {}
impl fmt::Debug for Identifier {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "Identifier({:p})", self.0)
}
}
impl Hash for Identifier {
fn hash<H>(&self, state: &mut H)
where
H: Hasher,
{
(self.0 as *const Callsite).hash(state)
}
}
@@ -1,612 +0,0 @@
//! Dispatches trace events to `Subscriber`s.c
use {
callsite, span,
subscriber::{self, Subscriber},
Event, Metadata,
};
use std::{
any::Any,
cell::{Cell, RefCell},
error, fmt,
sync::{
atomic::{AtomicUsize, Ordering},
Arc, Weak,
},
};
/// `Dispatch` trace data to a [`Subscriber`].
///
/// [`Subscriber`]: ../subscriber/trait.Subscriber.html
#[derive(Clone)]
pub struct Dispatch {
subscriber: Arc<Subscriber + Send + Sync>,
}
thread_local! {
static CURRENT_STATE: State = State {
default: RefCell::new(Dispatch::none()),
can_enter: Cell::new(true),
};
}
static GLOBAL_INIT: AtomicUsize = AtomicUsize::new(UNINITIALIZED);
const UNINITIALIZED: usize = 0;
const INITIALIZING: usize = 1;
const INITIALIZED: usize = 2;
static mut GLOBAL_DISPATCH: Option<Dispatch> = None;
/// The dispatch state of a thread.
struct State {
/// This thread's current default dispatcher.
default: RefCell<Dispatch>,
/// Whether or not we can currently begin dispatching a trace event.
///
/// This is set to `false` when functions such as `enter`, `exit`, `event`,
/// and `new_span` are called on this thread's default dispatcher, to
/// prevent further trace events triggered inside those functions from
/// creating an infinite recursion. When we finish handling a dispatch, this
/// is set back to `true`.
can_enter: Cell<bool>,
}
/// A guard that resets the current default dispatcher to the prior
/// default dispatcher when dropped.
struct ResetGuard(Option<Dispatch>);
/// Sets this dispatch as the default for the duration of a closure.
///
/// The default dispatcher is used when creating a new [span] or
/// [`Event`], _if no span is currently executing_. If a span is currently
/// executing, new spans or events are dispatched to the subscriber that
/// tagged that span, instead.
///
/// [span]: ../span/index.html
/// [`Subscriber`]: ../subscriber/trait.Subscriber.html
/// [`Event`]: ../event/struct.Event.html
pub fn with_default<T>(dispatcher: &Dispatch, f: impl FnOnce() -> T) -> T {
// When this guard is dropped, the default dispatcher will be reset to the
// prior default. Using this (rather than simply resetting after calling
// `f`) ensures that we always reset to the prior dispatcher even if `f`
// panics.
let _guard = State::set_default(dispatcher.clone());
f()
}
/// Sets this dispatch as the global default for the duration of the entire program.
/// Will be used as a fallback if no thread-local dispatch has been set in a thread
/// (using `with_default`.)
///
/// Can only be set once; subsequent attempts to set the global default will fail.
/// Returns `Err` if the global default has already been set.
///
/// Note: Libraries should *NOT* call `set_global_default()`! That will cause conflicts when
/// executables try to set them later.
///
/// [span]: ../span/index.html
/// [`Subscriber`]: ../subscriber/trait.Subscriber.html
/// [`Event`]: ../event/struct.Event.html
pub fn set_global_default(dispatcher: Dispatch) -> Result<(), SetGlobalDefaultError> {
if GLOBAL_INIT.compare_and_swap(UNINITIALIZED, INITIALIZING, Ordering::SeqCst) == UNINITIALIZED
{
unsafe {
GLOBAL_DISPATCH = Some(dispatcher.clone());
}
GLOBAL_INIT.store(INITIALIZED, Ordering::SeqCst);
Ok(())
} else {
Err(SetGlobalDefaultError { _no_construct: () })
}
}
/// Returned if setting the global dispatcher fails.
#[derive(Debug)]
pub struct SetGlobalDefaultError {
_no_construct: (),
}
impl fmt::Display for SetGlobalDefaultError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.pad("a global default trace dispatcher has already been set")
}
}
impl error::Error for SetGlobalDefaultError {}
/// Executes a closure with a reference to this thread's current [dispatcher].
///
/// Note that calls to `get_default` should not be nested; if this function is
/// called while inside of another `get_default`, that closure will be provided
/// with `Dispatch::none` rather than the previously set dispatcher.
///
/// [dispatcher]: ../dispatcher/struct.Dispatch.html
pub fn get_default<T, F>(mut f: F) -> T
where
F: FnMut(&Dispatch) -> T,
{
// While this guard is active, additional calls to subscriber functions on
// the default dispatcher will not be able to access the dispatch context.
// Dropping the guard will allow the dispatch context to be re-entered.
struct Entered<'a>(&'a Cell<bool>);
impl<'a> Drop for Entered<'a> {
#[inline]
fn drop(&mut self) {
self.0.set(true);
}
}
CURRENT_STATE
.try_with(|state| {
if state.can_enter.replace(false) {
let _guard = Entered(&state.can_enter);
let mut default = state.default.borrow_mut();
if default.is::<NoSubscriber>() && GLOBAL_INIT.load(Ordering::SeqCst) == INITIALIZED
{
// don't redo this call on the next check
unsafe {
*default = GLOBAL_DISPATCH
.as_ref()
.expect("invariant violated: GLOBAL_DISPATCH must be initialized before GLOBAL_INIT is set")
.clone()
}
}
f(&*default)
} else {
f(&Dispatch::none())
}
})
.unwrap_or_else(|_| f(&Dispatch::none()))
}
pub(crate) struct Registrar(Weak<Subscriber + Send + Sync>);
impl Dispatch {
/// Returns a new `Dispatch` that discards events and spans.
#[inline]
pub fn none() -> Self {
Dispatch {
subscriber: Arc::new(NoSubscriber),
}
}
/// Returns a `Dispatch` that forwards to the given [`Subscriber`].
///
/// [`Subscriber`]: ../subscriber/trait.Subscriber.html
pub fn new<S>(subscriber: S) -> Self
where
S: Subscriber + Send + Sync + 'static,
{
let me = Dispatch {
subscriber: Arc::new(subscriber),
};
callsite::register_dispatch(&me);
me
}
pub(crate) fn registrar(&self) -> Registrar {
Registrar(Arc::downgrade(&self.subscriber))
}
/// Registers a new callsite with this subscriber, returning whether or not
/// the subscriber is interested in being notified about the callsite.
///
/// This calls the [`register_callsite`] function on the [`Subscriber`]
/// that this `Dispatch` forwards to.
///
/// [`Subscriber`]: ../subscriber/trait.Subscriber.html
/// [`register_callsite`]: ../subscriber/trait.Subscriber.html#method.register_callsite
#[inline]
pub fn register_callsite(&self, metadata: &Metadata) -> subscriber::Interest {
self.subscriber.register_callsite(metadata)
}
/// Record the construction of a new span, returning a new [ID] for the
/// span being constructed.
///
/// This calls the [`new_span`] function on the [`Subscriber`] that this
/// `Dispatch` forwards to.
///
/// [ID]: ../span/struct.Id.html
/// [`Subscriber`]: ../subscriber/trait.Subscriber.html
/// [`new_span`]: ../subscriber/trait.Subscriber.html#method.new_span
#[inline]
pub fn new_span(&self, span: &span::Attributes) -> span::Id {
self.subscriber.new_span(span)
}
/// Record a set of values on a span.
///
/// This calls the [`record`] function on the [`Subscriber`] that this
/// `Dispatch` forwards to.
///
/// [`Subscriber`]: ../subscriber/trait.Subscriber.html
/// [`record`]: ../subscriber/trait.Subscriber.html#method.record
#[inline]
pub fn record(&self, span: &span::Id, values: &span::Record) {
self.subscriber.record(span, values)
}
/// Adds an indication that `span` follows from the span with the id
/// `follows`.
///
/// This calls the [`record_follows_from`] function on the [`Subscriber`]
/// that this `Dispatch` forwards to.
///
/// [`Subscriber`]: ../subscriber/trait.Subscriber.html
/// [`record_follows_from`]: ../subscriber/trait.Subscriber.html#method.record_follows_from
#[inline]
pub fn record_follows_from(&self, span: &span::Id, follows: &span::Id) {
self.subscriber.record_follows_from(span, follows)
}
/// Returns true if a span with the specified [metadata] would be
/// recorded.
///
/// This calls the [`enabled`] function on the [`Subscriber`] that this
/// `Dispatch` forwards to.
///
/// [metadata]: ../metadata/struct.Metadata.html
/// [`Subscriber`]: ../subscriber/trait.Subscriber.html
/// [`enabled`]: ../subscriber/trait.Subscriber.html#method.enabled
#[inline]
pub fn enabled(&self, metadata: &Metadata) -> bool {
self.subscriber.enabled(metadata)
}
/// Records that an [`Event`] has occurred.
///
/// This calls the [`event`] function on the [`Subscriber`] that this
/// `Dispatch` forwards to.
///
/// [`Event`]: ../event/struct.Event.html
/// [`Subscriber`]: ../subscriber/trait.Subscriber.html
/// [`event`]: ../subscriber/trait.Subscriber.html#method.event
#[inline]
pub fn event(&self, event: &Event) {
self.subscriber.event(event)
}
/// Records that a span has been can_enter.
///
/// This calls the [`enter`] function on the [`Subscriber`] that this
/// `Dispatch` forwards to.
///
/// [`Subscriber`]: ../subscriber/trait.Subscriber.html
/// [`event`]: ../subscriber/trait.Subscriber.html#method.event
#[inline]
pub fn enter(&self, span: &span::Id) {
self.subscriber.enter(span);
}
/// Records that a span has been exited.
///
/// This calls the [`exit`](::Subscriber::exit) function on the `Subscriber`
/// that this `Dispatch` forwards to.
#[inline]
pub fn exit(&self, span: &span::Id) {
self.subscriber.exit(span);
}
/// Notifies the subscriber that a [span ID] has been cloned.
///
/// This function is guaranteed to only be called with span IDs that were
/// returned by this `Dispatch`'s [`new_span`] function.
///
/// This calls the [`clone_span`] function on the `Subscriber` that this
/// `Dispatch` forwards to.
///
/// [span ID]: ../span/struct.Id.html
/// [`Subscriber`]: ../subscriber/trait.Subscriber.html
/// [`clone_span`]: ../subscriber/trait.Subscriber.html#method.clone_span
/// [`new_span`]: ../subscriber/trait.Subscriber.html#method.new_span
#[inline]
pub fn clone_span(&self, id: &span::Id) -> span::Id {
self.subscriber.clone_span(&id)
}
/// Notifies the subscriber that a [span ID] has been dropped.
///
/// This function is guaranteed to only be called with span IDs that were
/// returned by this `Dispatch`'s [`new_span`] function.
///
/// This calls the [`drop_span`] function on the [`Subscriber`] that this
/// `Dispatch` forwards to.
///
/// [span ID]: ../span/struct.Id.html
/// [`Subscriber`]: ../subscriber/trait.Subscriber.html
/// [`clone_span`]: ../subscriber/trait.Subscriber.html#method.clone_span
/// [`new_span`]: ../subscriber/trait.Subscriber.html#method.new_span
#[inline]
pub fn drop_span(&self, id: span::Id) {
self.subscriber.drop_span(id)
}
/// Returns `true` if this `Dispatch` forwards to a `Subscriber` of type
/// `T`.
#[inline]
pub fn is<T: Any>(&self) -> bool {
Subscriber::is::<T>(&*self.subscriber)
}
/// Returns some reference to the `Subscriber` this `Dispatch` forwards to
/// if it is of type `T`, or `None` if it isn't.
#[inline]
pub fn downcast_ref<T: Any>(&self) -> Option<&T> {
Subscriber::downcast_ref(&*self.subscriber)
}
}
impl fmt::Debug for Dispatch {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.pad("Dispatch(...)")
}
}
impl<S> From<S> for Dispatch
where
S: Subscriber + Send + Sync + 'static,
{
#[inline]
fn from(subscriber: S) -> Self {
Dispatch::new(subscriber)
}
}
struct NoSubscriber;
impl Subscriber for NoSubscriber {
#[inline]
fn register_callsite(&self, _: &Metadata) -> subscriber::Interest {
subscriber::Interest::never()
}
fn new_span(&self, _: &span::Attributes) -> span::Id {
span::Id::from_u64(0xDEAD)
}
fn event(&self, _event: &Event) {}
fn record(&self, _span: &span::Id, _values: &span::Record) {}
fn record_follows_from(&self, _span: &span::Id, _follows: &span::Id) {}
#[inline]
fn enabled(&self, _metadata: &Metadata) -> bool {
false
}
fn enter(&self, _span: &span::Id) {}
fn exit(&self, _span: &span::Id) {}
}
impl Registrar {
pub(crate) fn try_register(&self, metadata: &Metadata) -> Option<subscriber::Interest> {
self.0.upgrade().map(|s| s.register_callsite(metadata))
}
pub(crate) fn is_alive(&self) -> bool {
self.0.upgrade().is_some()
}
}
// ===== impl State =====
impl State {
/// Replaces the current default dispatcher on this thread with the provided
/// dispatcher.Any
///
/// Dropping the returned `ResetGuard` will reset the default dispatcher to
/// the previous value.
#[inline]
fn set_default(new_dispatch: Dispatch) -> ResetGuard {
let prior = CURRENT_STATE
.try_with(|state| {
state.can_enter.set(true);
state.default.replace(new_dispatch)
})
.ok();
ResetGuard(prior)
}
}
// ===== impl ResetGuard =====
impl Drop for ResetGuard {
#[inline]
fn drop(&mut self) {
if let Some(dispatch) = self.0.take() {
let _ = CURRENT_STATE.try_with(|state| {
*state.default.borrow_mut() = dispatch;
});
}
}
}
#[cfg(test)]
mod test {
use super::*;
use std::sync::atomic::{AtomicUsize, Ordering};
use {
callsite::Callsite,
metadata::{Kind, Level, Metadata},
span,
subscriber::{Interest, Subscriber},
Event,
};
#[test]
fn dispatch_is() {
let dispatcher = Dispatch::new(NoSubscriber);
assert!(dispatcher.is::<NoSubscriber>());
}
#[test]
fn dispatch_downcasts() {
let dispatcher = Dispatch::new(NoSubscriber);
assert!(dispatcher.downcast_ref::<NoSubscriber>().is_some());
}
struct TestCallsite;
static TEST_CALLSITE: TestCallsite = TestCallsite;
static TEST_META: Metadata<'static> = metadata! {
name: "test",
target: module_path!(),
level: Level::DEBUG,
fields: &[],
callsite: &TEST_CALLSITE,
kind: Kind::EVENT
};
impl Callsite for TestCallsite {
fn set_interest(&self, _: Interest) {}
fn metadata(&self) -> &Metadata {
&TEST_META
}
}
#[test]
fn events_dont_infinite_loop() {
// This test ensures that an event triggered within a subscriber
// won't cause an infinite loop of events.
struct TestSubscriber;
impl Subscriber for TestSubscriber {
fn enabled(&self, _: &Metadata) -> bool {
true
}
fn new_span(&self, _: &span::Attributes) -> span::Id {
span::Id::from_u64(0xAAAA)
}
fn record(&self, _: &span::Id, _: &span::Record) {}
fn record_follows_from(&self, _: &span::Id, _: &span::Id) {}
fn event(&self, _: &Event) {
static EVENTS: AtomicUsize = AtomicUsize::new(0);
assert_eq!(
EVENTS.fetch_add(1, Ordering::Relaxed),
0,
"event method called twice!"
);
Event::dispatch(&TEST_META, &TEST_META.fields().value_set(&[]))
}
fn enter(&self, _: &span::Id) {}
fn exit(&self, _: &span::Id) {}
}
with_default(&Dispatch::new(TestSubscriber), || {
Event::dispatch(&TEST_META, &TEST_META.fields().value_set(&[]))
})
}
#[test]
fn spans_dont_infinite_loop() {
// This test ensures that a span created within a subscriber
// won't cause an infinite loop of new spans.
fn mk_span() {
get_default(|current| {
current.new_span(&span::Attributes::new(
&TEST_META,
&TEST_META.fields().value_set(&[]),
))
});
}
struct TestSubscriber;
impl Subscriber for TestSubscriber {
fn enabled(&self, _: &Metadata) -> bool {
true
}
fn new_span(&self, _: &span::Attributes) -> span::Id {
static NEW_SPANS: AtomicUsize = AtomicUsize::new(0);
assert_eq!(
NEW_SPANS.fetch_add(1, Ordering::Relaxed),
0,
"new_span method called twice!"
);
mk_span();
span::Id::from_u64(0xAAAA)
}
fn record(&self, _: &span::Id, _: &span::Record) {}
fn record_follows_from(&self, _: &span::Id, _: &span::Id) {}
fn event(&self, _: &Event) {}
fn enter(&self, _: &span::Id) {}
fn exit(&self, _: &span::Id) {}
}
with_default(&Dispatch::new(TestSubscriber), || mk_span())
}
#[test]
fn global_dispatch() {
struct TestSubscriberA;
impl Subscriber for TestSubscriberA {
fn enabled(&self, _: &Metadata) -> bool {
true
}
fn new_span(&self, _: &span::Attributes) -> span::Id {
span::Id::from_u64(1)
}
fn record(&self, _: &span::Id, _: &span::Record) {}
fn record_follows_from(&self, _: &span::Id, _: &span::Id) {}
fn event(&self, _: &Event) {}
fn enter(&self, _: &span::Id) {}
fn exit(&self, _: &span::Id) {}
}
struct TestSubscriberB;
impl Subscriber for TestSubscriberB {
fn enabled(&self, _: &Metadata) -> bool {
true
}
fn new_span(&self, _: &span::Attributes) -> span::Id {
span::Id::from_u64(1)
}
fn record(&self, _: &span::Id, _: &span::Record) {}
fn record_follows_from(&self, _: &span::Id, _: &span::Id) {}
fn event(&self, _: &Event) {}
fn enter(&self, _: &span::Id) {}
fn exit(&self, _: &span::Id) {}
}
// NOTE: if you want to have other tests that set the default dispatch you'll need to
// write them as integration tests in ../tests/
set_global_default(Dispatch::new(TestSubscriberA)).expect("global dispatch set failed");
get_default(|current| {
assert!(
current.is::<TestSubscriberA>(),
"global dispatch get failed"
)
});
with_default(&Dispatch::new(TestSubscriberB), || {
get_default(|current| {
assert!(
current.is::<TestSubscriberB>(),
"thread-local override of global dispatch failed"
)
});
});
get_default(|current| {
assert!(
current.is::<TestSubscriberA>(),
"reset to global override failed"
)
});
set_global_default(Dispatch::new(TestSubscriberA))
.expect_err("double global dispatch set succeeded");
}
}
-119
View File
@@ -1,119 +0,0 @@
//! Events represent single points in time during the execution of a program.
use parent::Parent;
use span::Id;
use {field, Metadata};
/// `Event`s represent single points in time where something occurred during the
/// execution of a program.
///
/// An `Event` can be compared to a log record in unstructured logging, but with
/// two key differences:
/// - `Event`s exist _within the context of a [span]_. Unlike log lines, they
/// may be located within the trace tree, allowing visibility into the
/// _temporal_ context in which the event occurred, as well as the source
/// code location.
/// - Like spans, `Event`s have structured key-value data known as _[fields]_,
/// which may include textual message. In general, a majority of the data
/// associated with an event should be in the event's fields rather than in
/// the textual message, as the fields are more structed.
///
/// [span]: ../span
/// [fields]: ../field
#[derive(Debug)]
pub struct Event<'a> {
fields: &'a field::ValueSet<'a>,
metadata: &'a Metadata<'a>,
parent: Parent,
}
impl<'a> Event<'a> {
/// Constructs a new `Event` with the specified metadata and set of values,
/// and observes it with the current subscriber.
#[inline]
pub fn dispatch(metadata: &'a Metadata<'a>, fields: &'a field::ValueSet) {
let event = Event {
metadata,
fields,
parent: Parent::Current,
};
::dispatcher::get_default(|current| {
current.event(&event);
});
}
/// Constructs a new `Event` with the specified metadata and set of values,
/// and observes it with the current subscriber and an explicit parent.
#[inline]
pub fn child_of(
parent: impl Into<Option<Id>>,
metadata: &'a Metadata<'a>,
fields: &'a field::ValueSet,
) {
let parent = match parent.into() {
Some(p) => Parent::Explicit(p),
None => Parent::Root,
};
let event = Event {
metadata,
fields,
parent,
};
::dispatcher::get_default(|current| {
current.event(&event);
});
}
/// Visits all the fields on this `Event` with the specified [visitor].
///
/// [visitor]: ../field/trait.Visit.html
#[inline]
pub fn record(&self, visitor: &mut field::Visit) {
self.fields.record(visitor);
}
/// Returns an iterator over the set of values on this `Event`.
pub fn fields(&self) -> field::Iter {
self.fields.field_set().iter()
}
/// Returns [metadata] describing this `Event`.
///
/// [metadata]: ../metadata/struct.Metadata.html
pub fn metadata(&self) -> &Metadata {
self.metadata
}
/// Returns true if the new event shoold be a root.
pub fn is_root(&self) -> bool {
match self.parent {
Parent::Root => true,
_ => false,
}
}
/// Returns true if the new event's parent should be determined based on the
/// current context.
///
/// If this is true and the current thread is currently inside a span, then
/// that span should be the new events's parent. Otherwise, if the current
/// thread is _not_ inside a span, then the new event will be the root of its
/// own trace tree.
pub fn is_contextual(&self) -> bool {
match self.parent {
Parent::Current => true,
_ => false,
}
}
/// Returns the new event's explicitly-specified parent, if there is one.
///
/// Otherwise (if the new event is a root or is a child of the current span),
/// returns false.
pub fn parent(&self) -> Option<&Id> {
match self.parent {
Parent::Explicit(ref p) => Some(p),
_ => None,
}
}
}
-801
View File
@@ -1,801 +0,0 @@
//! Span and `Event` key-value data.
//!
//! Spans and events may be annotated with key-value data, referred to as known
//! as _fields_. These fields consist of a mapping from a key (corresponding to
//! a `&str` but represented internally as an array index) to a [`Value`].
//!
//! # `Value`s and `Subscriber`s
//!
//! `Subscriber`s consume `Value`s as fields attached to [span]s or [`Event`]s.
//! The set of field keys on a given span or is defined on its [`Metadata`].
//! When a span is created, it provides [`Attributes`] to the `Subscriber`'s
//! [`new_span`] method, containing any fields whose values were provided when
//! the span was created; and may call the `Subscriber`'s [`record`] method
//! with additional [`Record`]s if values are added for more of its fields.
//! Similarly, the [`Event`] type passed to the subscriber's [`event`] method
//! will contain any fields attached to each event.
//!
//! `tokio_trace` represents values as either one of a set of Rust primitives
//! (`i64`, `u64`, `bool`, and `&str`) or using a `fmt::Display` or `fmt::Debug`
//! implementation. The [`record`] trait method on the `Subscriber` trait
//! allow `Subscriber` implementations to provide type-specific behaviour for
//! consuming values of each type.
//!
//! Instances of the [`Visit`] trait are provided by `Subscriber`s to record the
//! values attached to spans and `Event`. This trait represents the behavior
//! used to record values of various types. For example, we might record
//! integers by incrementing counters for their field names, rather than printing
//! them.
//!
//! [`Value`]: trait.Value.html
//! [span]: ../span/
//! [`Event`]: ../event/struct.Event.html
//! [`Metadata`]: ../metadata/struct.Metadata.html
//! [`Attributes`]: ../span/struct.Attributes.html
//! [`Record`]: ../span/struct.Record.html
//! [`new_span`]: ../subscriber/trait.Subscriber.html#method.new_span
//! [`record`]: ../subscriber/trait.Subscriber.html#method.record
//! [`event`]: ../subscriber/trait.Subscriber.html#method.record
//! [`Visit`]: trait.Visit.html
use callsite;
use std::{
borrow::Borrow,
fmt,
hash::{Hash, Hasher},
ops::Range,
};
use self::private::ValidLen;
/// An opaque key allowing _O_(1) access to a field in a `Span`'s key-value
/// data.
///
/// As keys are defined by the _metadata_ of a span, rather than by an
/// individual instance of a span, a key may be used to access the same field
/// across all instances of a given span with the same metadata. Thus, when a
/// subscriber observes a new span, it need only access a field by name _once_,
/// and use the key for that name for all other accesses.
#[derive(Debug)]
pub struct Field {
i: usize,
fields: FieldSet,
}
/// Describes the fields present on a span.
// TODO: When `const fn` is stable, make this type's fields private.
pub struct FieldSet {
/// The names of each field on the described span.
///
/// **Warning**: The fields on this type are currently `pub` because it must be able
/// to be constructed statically by macros. However, when `const fn`s are
/// available on stable Rust, this will no longer be necessary. Thus, these
/// fields are *not* considered stable public API, and they may change
/// warning. Do not rely on any fields on `FieldSet`!
#[doc(hidden)]
pub names: &'static [&'static str],
/// The callsite where the described span originates.
///
/// **Warning**: The fields on this type are currently `pub` because it must be able
/// to be constructed statically by macros. However, when `const fn`s are
/// available on stable Rust, this will no longer be necessary. Thus, these
/// fields are *not* considered stable public API, and they may change
/// warning. Do not rely on any fields on `FieldSet`!
#[doc(hidden)]
pub callsite: callsite::Identifier,
}
/// A set of fields and values for a span.
pub struct ValueSet<'a> {
values: &'a [(&'a Field, Option<&'a (Value + 'a)>)],
fields: &'a FieldSet,
}
/// An iterator over a set of fields.
#[derive(Debug)]
pub struct Iter {
idxs: Range<usize>,
fields: FieldSet,
}
/// Visits typed values.
///
/// An instance of `Visit` ("a visitor") represents the logic necessary to
/// record field values of various types. When an implementor of [`Value`] is
/// [recorded], it calls the appropriate method on the provided visitor to
/// indicate the type that value should be recorded as.
///
/// When a [`Subscriber`] implementation [records an `Event`] or a
/// [set of `Value`s added to a `Span`], it can pass an `&mut Visit` to the
/// `record` method on the provided [`ValueSet`] or [`Event`]. This visitor
/// will then be used to record all the field-value pairs present on that
/// `Event` or `ValueSet`.
///
/// # Examples
///
/// A simple visitor that writes to a string might be implemented like so:
/// ```
/// # extern crate tokio_trace_core as tokio_trace;
/// use std::fmt::{self, Write};
/// use tokio_trace::field::{Value, Visit, Field};
/// # fn main() {
/// pub struct StringVisitor<'a> {
/// string: &'a mut String,
/// }
///
/// impl<'a> Visit for StringVisitor<'a> {
/// fn record_debug(&mut self, field: &Field, value: &fmt::Debug) {
/// write!(self.string, "{} = {:?}; ", field.name(), value).unwrap();
/// }
/// }
/// # }
/// ```
/// This visitor will format each recorded value using `fmt::Debug`, and
/// append the field name and formatted value to the provided string,
/// regardless of the type of the recorded value. When all the values have
/// been recorded, the `StringVisitor` may be dropped, allowing the string
/// to be printed or stored in some other data structure.
///
/// The `Visit` trait provides default implementations for `record_i64`,
/// `record_u64`, `record_bool`, and `record_str` which simply forward the
/// recorded value to `record_debug`. Thus, `record_debug` is the only method
/// which a `Visit` implementation *must* implement. However, visitors may
/// override the default implementations of these functions in order to
/// implement type-specific behavior.
///
/// Additionally, when a visitor recieves a value of a type it does not care
/// about, it is free to ignore those values completely. For example, a
/// visitor which only records numeric data might look like this:
///
/// ```
/// # extern crate tokio_trace_core as tokio_trace;
/// # use std::fmt::{self, Write};
/// # use tokio_trace::field::{Value, Visit, Field};
/// # fn main() {
/// pub struct SumVisitor {
/// sum: i64,
/// }
///
/// impl Visit for SumVisitor {
/// fn record_i64(&mut self, _field: &Field, value: i64) {
/// self.sum += value;
/// }
///
/// fn record_u64(&mut self, _field: &Field, value: u64) {
/// self.sum += value as i64;
/// }
///
/// fn record_debug(&mut self, _field: &Field, _value: &fmt::Debug) {
/// // Do nothing
/// }
/// }
/// # }
/// ```
///
/// This visitor (which is probably not particularly useful) keeps a running
/// sum of all the numeric values it records, and ignores all other values. A
/// more practical example of recording typed values is presented in
/// `examples/counters.rs`, which demonstrates a very simple metrics system
/// implemented using `tokio-trace`.
///
/// [`Value`]: trait.Value.html
/// [recorded]: trait.Value.html#method.record
/// [`Subscriber`]: ../subscriber/trait.Subscriber.html
/// [records an `Event`]: ../subscriber/trait.Subscriber.html#method.event
/// [set of `Value`s added to a `Span`]: ../subscriber/trait.Subscriber.html#method.record
/// [`Event`]: ../event/struct.Event.html
/// [`ValueSet`]: struct.ValueSet.html
pub trait Visit {
/// Visit a signed 64-bit integer value.
fn record_i64(&mut self, field: &Field, value: i64) {
self.record_debug(field, &value)
}
/// Visit an umsigned 64-bit integer value.
fn record_u64(&mut self, field: &Field, value: u64) {
self.record_debug(field, &value)
}
/// Visit a boolean value.
fn record_bool(&mut self, field: &Field, value: bool) {
self.record_debug(field, &value)
}
/// Visit a string value.
fn record_str(&mut self, field: &Field, value: &str) {
self.record_debug(field, &value)
}
/// Visit a value implementing `fmt::Debug`.
fn record_debug(&mut self, field: &Field, value: &fmt::Debug);
}
/// A field value of an erased type.
///
/// Implementors of `Value` may call the appropriate typed recording methods on
/// the [visitor] passed to their `record` method in order to indicate how
/// their data should be recorded.
///
/// [visitor]: trait.Visit.html
pub trait Value: ::sealed::Sealed {
/// Visits this value with the given `Visitor`.
fn record(&self, key: &Field, visitor: &mut Visit);
}
/// A `Value` which serializes as a string using `fmt::Display`.
#[derive(Clone)]
pub struct DisplayValue<T: fmt::Display>(T);
/// A `Value` which serializes as a string using `fmt::Debug`.
#[derive(Clone)]
pub struct DebugValue<T: fmt::Debug>(T);
/// Wraps a type implementing `fmt::Display` as a `Value` that can be
/// recorded using its `Display` implementation.
pub fn display<T>(t: T) -> DisplayValue<T>
where
T: fmt::Display,
{
DisplayValue(t)
}
/// Wraps a type implementing `fmt::Debug` as a `Value` that can be
/// recorded using its `Debug` implementation.
pub fn debug<T>(t: T) -> DebugValue<T>
where
T: fmt::Debug,
{
DebugValue(t)
}
// ===== impl Visit =====
impl<'a, 'b> Visit for fmt::DebugStruct<'a, 'b> {
fn record_debug(&mut self, field: &Field, value: &fmt::Debug) {
self.field(field.name(), value);
}
}
impl<'a, 'b> Visit for fmt::DebugMap<'a, 'b> {
fn record_debug(&mut self, field: &Field, value: &fmt::Debug) {
self.entry(&format_args!("{}", field), value);
}
}
impl<F> Visit for F
where
F: FnMut(&Field, &fmt::Debug),
{
fn record_debug(&mut self, field: &Field, value: &fmt::Debug) {
(self)(field, value)
}
}
// ===== impl Value =====
macro_rules! impl_values {
( $( $record:ident( $( $whatever:tt)+ ) ),+ ) => {
$(
impl_value!{ $record( $( $whatever )+ ) }
)+
}
}
macro_rules! impl_value {
( $record:ident( $( $value_ty:ty ),+ ) ) => {
$(
impl $crate::sealed::Sealed for $value_ty {}
impl $crate::field::Value for $value_ty {
fn record(
&self,
key: &$crate::field::Field,
visitor: &mut $crate::field::Visit,
) {
visitor.$record(key, *self)
}
}
)+
};
( $record:ident( $( $value_ty:ty ),+ as $as_ty:ty) ) => {
$(
impl $crate::sealed::Sealed for $value_ty {}
impl Value for $value_ty {
fn record(
&self,
key: &$crate::field::Field,
visitor: &mut $crate::field::Visit,
) {
visitor.$record(key, *self as $as_ty)
}
}
)+
};
}
// ===== impl Value =====
impl_values! {
record_u64(u64),
record_u64(usize, u32, u16 as u64),
record_i64(i64),
record_i64(isize, i32, i16, i8 as i64),
record_bool(bool)
}
impl ::sealed::Sealed for str {}
impl Value for str {
fn record(&self, key: &Field, visitor: &mut Visit) {
visitor.record_str(key, &self)
}
}
impl<'a, T: ?Sized> ::sealed::Sealed for &'a T where T: Value + ::sealed::Sealed + 'a {}
impl<'a, T: ?Sized> Value for &'a T
where
T: Value + 'a,
{
fn record(&self, key: &Field, visitor: &mut Visit) {
(*self).record(key, visitor)
}
}
impl<'a> ::sealed::Sealed for fmt::Arguments<'a> {}
impl<'a> Value for fmt::Arguments<'a> {
fn record(&self, key: &Field, visitor: &mut Visit) {
visitor.record_debug(key, self)
}
}
// ===== impl DisplayValue =====
impl<T: fmt::Display> ::sealed::Sealed for DisplayValue<T> {}
impl<T> Value for DisplayValue<T>
where
T: fmt::Display,
{
fn record(&self, key: &Field, visitor: &mut Visit) {
visitor.record_debug(key, &format_args!("{}", self.0))
}
}
impl<T: fmt::Display> fmt::Debug for DisplayValue<T> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", self.0)
}
}
// ===== impl DebugValue =====
impl<T: fmt::Debug> ::sealed::Sealed for DebugValue<T> {}
impl<T: fmt::Debug> Value for DebugValue<T>
where
T: fmt::Debug,
{
fn record(&self, key: &Field, visitor: &mut Visit) {
visitor.record_debug(key, &self.0)
}
}
impl<T: fmt::Debug> fmt::Debug for DebugValue<T> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{:?}", self.0)
}
}
// ===== impl Field =====
impl Field {
/// Returns an [`Identifier`] that uniquely identifies the [`Callsite`]
/// which defines this field.
///
/// [`Identifier`]: ../callsite/struct.Identifier.html
/// [`Callsite`]: ../callsite/trait.Callsite.html
#[inline]
pub fn callsite(&self) -> callsite::Identifier {
self.fields.callsite()
}
/// Returns a string representing the name of the field.
pub fn name(&self) -> &'static str {
self.fields.names[self.i]
}
}
impl fmt::Display for Field {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.pad(self.name())
}
}
impl AsRef<str> for Field {
fn as_ref(&self) -> &str {
self.name()
}
}
impl PartialEq for Field {
fn eq(&self, other: &Self) -> bool {
self.callsite() == other.callsite() && self.i == other.i
}
}
impl Eq for Field {}
impl Hash for Field {
fn hash<H>(&self, state: &mut H)
where
H: Hasher,
{
self.callsite().hash(state);
self.i.hash(state);
}
}
impl Clone for Field {
fn clone(&self) -> Self {
Field {
i: self.i,
fields: FieldSet {
names: self.fields.names,
callsite: self.fields.callsite(),
},
}
}
}
// ===== impl FieldSet =====
impl FieldSet {
/// Returns an [`Identifier`] that uniquely identifies the [`Callsite`]
/// which defines this set of fields..
///
/// [`Identifier`]: ../callsite/struct.Identifier.html
/// [`Callsite`]: ../callsite/trait.Callsite.html
pub(crate) fn callsite(&self) -> callsite::Identifier {
callsite::Identifier(self.callsite.0)
}
/// Returns the [`Field`] named `name`, or `None` if no such field exists.
///
/// [`Field`]: ../struct.Field.html
pub fn field<Q: ?Sized>(&self, name: &Q) -> Option<Field>
where
Q: Borrow<str>,
{
let name = &name.borrow();
self.names.iter().position(|f| f == name).map(|i| Field {
i,
fields: FieldSet {
names: self.names,
callsite: self.callsite(),
},
})
}
/// Returns `true` if `self` contains the given `field`.
///
/// **Note**: If `field` shares a name with a field in this `FieldSet`, but
/// was created by a `FieldSet` with a different callsite, this `FieldSet`
/// does _not_ contain it. This is so that if two separate span callsites
/// define a field named "foo", the `Field` corresponding to "foo" for each
/// of those callsites are not equivalent.
pub fn contains(&self, field: &Field) -> bool {
field.callsite() == self.callsite() && field.i <= self.len()
}
/// Returns an iterator over the `Field`s in this `FieldSet`.
pub fn iter(&self) -> Iter {
let idxs = 0..self.len();
Iter {
idxs,
fields: FieldSet {
names: self.names,
callsite: self.callsite(),
},
}
}
/// Returns a new `ValueSet` with entries for this `FieldSet`'s values.
///
/// Note that a `ValueSet` may not be constructed with arrays of over 32
/// elements.
#[doc(hidden)]
pub fn value_set<'v, V>(&'v self, values: &'v V) -> ValueSet<'v>
where
V: ValidLen<'v>,
{
ValueSet {
fields: self,
values: &values.borrow()[..],
}
}
/// Returns the number of fields in this `FieldSet`.
#[inline]
pub fn len(&self) -> usize {
self.names.len()
}
}
impl<'a> IntoIterator for &'a FieldSet {
type IntoIter = Iter;
type Item = Field;
#[inline]
fn into_iter(self) -> Self::IntoIter {
self.iter()
}
}
impl fmt::Debug for FieldSet {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.debug_struct("FieldSet")
.field("names", &self.names)
.field("callsite", &self.callsite)
.finish()
}
}
impl fmt::Display for FieldSet {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.debug_set()
.entries(self.names.iter().map(|n| display(n)))
.finish()
}
}
// ===== impl Iter =====
impl Iterator for Iter {
type Item = Field;
fn next(&mut self) -> Option<Field> {
let i = self.idxs.next()?;
Some(Field {
i,
fields: FieldSet {
names: self.fields.names,
callsite: self.fields.callsite(),
},
})
}
}
// ===== impl ValueSet =====
impl<'a> ValueSet<'a> {
/// Returns an [`Identifier`] that uniquely identifies the [`Callsite`]
/// defining the fields this `ValueSet` refers to.
///
/// [`Identifier`]: ../callsite/struct.Identifier.html
/// [`Callsite`]: ../callsite/trait.Callsite.html
#[inline]
pub fn callsite(&self) -> callsite::Identifier {
self.fields.callsite()
}
/// Visits all the fields in this `ValueSet` with the provided [visitor].
///
/// [visitor]: ../trait.Visit.html
pub(crate) fn record(&self, visitor: &mut Visit) {
let my_callsite = self.callsite();
for (field, value) in self.values {
if field.callsite() != my_callsite {
continue;
}
if let Some(value) = value {
value.record(field, visitor);
}
}
}
/// Returns `true` if this `ValueSet` contains a value for the given `Field`.
pub(crate) fn contains(&self, field: &Field) -> bool {
field.callsite() == self.callsite()
&& self
.values
.iter()
.any(|(key, val)| *key == field && val.is_some())
}
/// Returns true if this `ValueSet` contains _no_ values.
pub(crate) fn is_empty(&self) -> bool {
let my_callsite = self.callsite();
self.values
.iter()
.all(|(key, val)| val.is_none() || key.callsite() != my_callsite)
}
pub(crate) fn field_set(&self) -> &FieldSet {
self.fields
}
}
impl<'a> fmt::Debug for ValueSet<'a> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
self.values
.iter()
.fold(&mut f.debug_struct("ValueSet"), |dbg, (key, v)| {
if let Some(val) = v {
val.record(key, dbg);
}
dbg
})
.field("callsite", &self.callsite())
.finish()
}
}
impl<'a> fmt::Display for ValueSet<'a> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
self.values
.iter()
.fold(&mut f.debug_map(), |dbg, (key, v)| {
if let Some(val) = v {
val.record(key, dbg);
}
dbg
})
.finish()
}
}
// ===== impl ValidLen =====
mod private {
use super::*;
/// Marker trait implemented by arrays which are of valid length to
/// construct a `ValueSet`.
///
/// `ValueSet`s may only be constructed from arrays containing 32 or fewer
/// elements, to ensure the array is small enough to always be allocated on the
/// stack. This trait is only implemented by arrays of an appropriate length,
/// ensuring that the correct size arrays are used at compile-time.
pub trait ValidLen<'a>: Borrow<[(&'a Field, Option<&'a (Value + 'a)>)]> {}
}
macro_rules! impl_valid_len {
( $( $len:tt ),+ ) => {
$(
impl<'a> private::ValidLen<'a> for
[(&'a Field, Option<&'a (Value + 'a)>); $len] {}
)+
}
}
impl_valid_len! {
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20,
21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32
}
#[cfg(test)]
mod test {
use super::*;
use metadata::{Kind, Level, Metadata};
struct TestCallsite1;
static TEST_CALLSITE_1: TestCallsite1 = TestCallsite1;
static TEST_META_1: Metadata<'static> = metadata! {
name: "field_test1",
target: module_path!(),
level: Level::INFO,
fields: &["foo", "bar", "baz"],
callsite: &TEST_CALLSITE_1,
kind: Kind::SPAN,
};
impl ::callsite::Callsite for TestCallsite1 {
fn set_interest(&self, _: ::subscriber::Interest) {
unimplemented!()
}
fn metadata(&self) -> &Metadata {
&TEST_META_1
}
}
struct TestCallsite2;
static TEST_CALLSITE_2: TestCallsite2 = TestCallsite2;
static TEST_META_2: Metadata<'static> = metadata! {
name: "field_test2",
target: module_path!(),
level: Level::INFO,
fields: &["foo", "bar", "baz"],
callsite: &TEST_CALLSITE_2,
kind: Kind::SPAN,
};
impl ::callsite::Callsite for TestCallsite2 {
fn set_interest(&self, _: ::subscriber::Interest) {
unimplemented!()
}
fn metadata(&self) -> &Metadata {
&TEST_META_2
}
}
#[test]
fn value_set_with_no_values_is_empty() {
let fields = TEST_META_1.fields();
let values = &[
(&fields.field("foo").unwrap(), None),
(&fields.field("bar").unwrap(), None),
(&fields.field("baz").unwrap(), None),
];
let valueset = fields.value_set(values);
assert!(valueset.is_empty());
}
#[test]
fn empty_value_set_is_empty() {
let fields = TEST_META_1.fields();
let valueset = fields.value_set(&[]);
assert!(valueset.is_empty());
}
#[test]
fn value_sets_with_fields_from_other_callsites_are_empty() {
let fields = TEST_META_1.fields();
let values = &[
(&fields.field("foo").unwrap(), Some(&1 as &Value)),
(&fields.field("bar").unwrap(), Some(&2 as &Value)),
(&fields.field("baz").unwrap(), Some(&3 as &Value)),
];
let valueset = TEST_META_2.fields().value_set(values);
assert!(valueset.is_empty())
}
#[test]
fn sparse_value_sets_are_not_empty() {
let fields = TEST_META_1.fields();
let values = &[
(&fields.field("foo").unwrap(), None),
(&fields.field("bar").unwrap(), Some(&57 as &Value)),
(&fields.field("baz").unwrap(), None),
];
let valueset = fields.value_set(values);
assert!(!valueset.is_empty());
}
#[test]
fn fields_from_other_callsets_are_skipped() {
let fields = TEST_META_1.fields();
let values = &[
(&fields.field("foo").unwrap(), None),
(
&TEST_META_2.fields().field("bar").unwrap(),
Some(&57 as &Value),
),
(&fields.field("baz").unwrap(), None),
];
struct MyVisitor;
impl Visit for MyVisitor {
fn record_debug(&mut self, field: &Field, _: &::std::fmt::Debug) {
assert_eq!(field.callsite(), TEST_META_1.callsite())
}
}
let valueset = fields.value_set(values);
valueset.record(&mut MyVisitor);
}
#[test]
fn record_debug_fn() {
let fields = TEST_META_1.fields();
let values = &[
(&fields.field("foo").unwrap(), Some(&1 as &Value)),
(&fields.field("bar").unwrap(), Some(&2 as &Value)),
(&fields.field("baz").unwrap(), Some(&3 as &Value)),
];
let valueset = fields.value_set(values);
let mut result = String::new();
valueset.record(&mut |_: &Field, value: &fmt::Debug| {
use std::fmt::Write;
write!(&mut result, "{:?}", value).unwrap();
});
assert_eq!(result, "123".to_owned());
}
}
-213
View File
@@ -1,213 +0,0 @@
#![doc(html_root_url = "https://docs.rs/tokio-trace-core/0.2.0")]
#![deny(missing_debug_implementations, missing_docs, unreachable_pub)]
#![cfg_attr(test, deny(warnings))]
//! Core primitives for `tokio-trace`.
//!
//! `tokio-trace` is a framework for instrumenting Rust programs to collect
//! structured, event-based diagnostic information. This crate defines the core
//! primitives of `tokio-trace`.
//!
//! This crate provides:
//!
//! * [`Span`] identifies a span within the execution of a program.
//!
//! * [`Event`] represents a single event within a trace.
//!
//! * [`Subscriber`], the trait implemented to collect trace data.
//!
//! * [`Metadata`] and [`Callsite`] provide information describing `Span`s.
//!
//! * [`Field`], [`FieldSet`], [`Value`], and [`ValueSet`] represent the
//! structured data attached to a `Span`.
//!
//! * [`Dispatch`] allows span events to be dispatched to `Subscriber`s.
//!
//! In addition, it defines the global callsite registry and per-thread current
//! dispatcher which other components of the tracing system rely on.
//!
//! Application authors will typically not use this crate directly. Instead,
//! they will use the `tokio-trace` crate, which provides a much more
//! fully-featured API. However, this crate's API will change very infrequently,
//! so it may be used when dependencies must be very stable.
//!
//! The [`tokio-trace-nursery`] repository contains less stable crates designed to
//! be used with the `tokio-trace` ecosystem. It includes a collection of
//! `Subscriber` implementations, as well as utility and adapter crates.
//!
//! [`Span`]: span/struct.Span.html
//! [`Event`]: event/struct.Event.html
//! [`Subscriber`]: subscriber/trait.Subscriber.html
//! [`Metadata`]: metadata/struct.Metadata.html
//! [`Callsite`]: callsite/trait.Callsite.html
//! [`Field`]: field/struct.Field.html
//! [`FieldSet`]: field/struct.FieldSet.html
//! [`Value`]: field/trait.Value.html
//! [`ValueSet`]: field/struct.ValueSet.html
//! [`Dispatch`]: dispatcher/struct.Dispatch.html
//! [`tokio-trace-nursery`]: https://github.com/tokio-rs/tokio-trace-nursery
#[macro_use]
extern crate lazy_static;
/// Statically constructs an [`Identifier`] for the provided [`Callsite`].
///
/// This may be used in contexts, such as static initializers, where the
/// [`Callsite::id`] function is not currently usable.
///
/// For example:
/// ```rust
/// # #[macro_use]
/// # extern crate tokio_trace_core;
/// use tokio_trace_core::callsite;
/// # use tokio_trace_core::{Metadata, subscriber::Interest};
/// # fn main() {
/// pub struct MyCallsite {
/// // ...
/// }
/// impl callsite::Callsite for MyCallsite {
/// # fn set_interest(&self, _: Interest) { unimplemented!() }
/// # fn metadata(&self) -> &Metadata { unimplemented!() }
/// // ...
/// }
///
/// static CALLSITE: MyCallsite = MyCallsite {
/// // ...
/// };
///
/// static CALLSITE_ID: callsite::Identifier = identify_callsite!(&CALLSITE);
/// # }
/// ```
///
/// [`Identifier`]: callsite/struct.Identifier.html
/// [`Callsite`]: callsite/trait.Callsite.html
/// [`Callsite`]: callsite/trait.Callsite.html#method.id
#[macro_export]
macro_rules! identify_callsite {
($callsite:expr) => {
$crate::callsite::Identifier($callsite)
};
}
/// Statically constructs new span [metadata].
///
/// This may be used in contexts, such as static initializers, where the
/// [`Metadata::new`] function is not currently usable.
///
/// /// For example:
/// ```rust
/// # #[macro_use]
/// # extern crate tokio_trace_core;
/// # use tokio_trace_core::{callsite::Callsite, subscriber::Interest};
/// use tokio_trace_core::metadata::{Kind, Level, Metadata};
/// # fn main() {
/// # pub struct MyCallsite { }
/// # impl Callsite for MyCallsite {
/// # fn set_interest(&self, _: Interest) { unimplemented!() }
/// # fn metadata(&self) -> &Metadata { unimplemented!() }
/// # }
/// #
/// static FOO_CALLSITE: MyCallsite = MyCallsite {
/// // ...
/// };
///
/// static FOO_METADATA: Metadata = metadata!{
/// name: "foo",
/// target: module_path!(),
/// level: Level::DEBUG,
/// fields: &["bar", "baz"],
/// callsite: &FOO_CALLSITE,
/// kind: Kind::SPAN,
/// };
/// # }
/// ```
///
/// [metadata]: metadata/struct.Metadata.html
/// [`Metadata::new`]: metadata/struct.Metadata.html#method.new
#[macro_export(local_inner_macros)]
macro_rules! metadata {
(
name: $name:expr,
target: $target:expr,
level: $level:expr,
fields: $fields:expr,
callsite: $callsite:expr,
kind: $kind:expr
) => {
metadata! {
name: $name,
target: $target,
level: $level,
fields: $fields,
callsite: $callsite,
kind: $kind,
}
};
(
name: $name:expr,
target: $target:expr,
level: $level:expr,
fields: $fields:expr,
callsite: $callsite:expr,
kind: $kind:expr,
) => {
$crate::metadata::Metadata {
name: $name,
target: $target,
level: $level,
file: Some(__tokio_trace_core_file!()),
line: Some(__tokio_trace_core_line!()),
module_path: Some(__tokio_trace_core_module_path!()),
fields: $crate::field::FieldSet {
names: $fields,
callsite: identify_callsite!($callsite),
},
kind: $kind,
}
};
}
#[doc(hidden)]
#[macro_export]
macro_rules! __tokio_trace_core_module_path {
() => {
module_path!()
};
}
#[doc(hidden)]
#[macro_export]
macro_rules! __tokio_trace_core_file {
() => {
file!()
};
}
#[doc(hidden)]
#[macro_export]
macro_rules! __tokio_trace_core_line {
() => {
line!()
};
}
pub mod callsite;
pub mod dispatcher;
pub mod event;
pub mod field;
pub mod metadata;
mod parent;
pub mod span;
pub mod subscriber;
pub use self::{
callsite::Callsite,
dispatcher::Dispatch,
event::Event,
field::Field,
metadata::{Kind, Level, Metadata},
subscriber::{Interest, Subscriber},
};
mod sealed {
pub trait Sealed {}
}
@@ -1,370 +0,0 @@
//! Metadata describing trace data.
use super::{
callsite::{self, Callsite},
field,
};
use std::fmt;
/// Metadata describing a [span] or [event].
///
/// All spans and events have the following metadata:
/// - A [name], represented as a static string.
/// - A [target], a string that categorizes part of the system where the span
/// or event occurred. The `tokio_trace` macros default to using the module
/// path where the span or event originated as the target, but it may be
/// overridden.
/// - A [verbosity level].
/// - The names of the [fields] defined by the span or event.
/// - Whether the metadata corresponds to a span or event.
///
/// In addition, the following optional metadata describing the source code
/// location where the span or event originated _may_ be provided:
/// - The [file name]
/// - The [line number]
/// - The [module path]
///
/// Metadata is used by [`Subscriber`]s when filtering spans and events, and it
/// may also be used as part of their data payload.
///
/// When created by the `event!` or `span!` macro, the metadata describing a
/// particular event or span is constructed statically and exists as a single
/// static instance. Thus, the overhead of creating the metadata is
/// _significantly_ lower than that of creating the actual span. Therefore,
/// filtering is based on metadata, rather than on the constructed span.
///
/// **Note**: Although instances of `Metadata` cannot be compared directly, they
/// provide a method [`id`] which returns an an opaque [callsite identifier]
/// which uniquely identifies the callsite where the metadata originated.
/// This can be used for determining if two Metadata correspond to
/// the same callsite.
///
/// [span]: ../span/index.html
/// [event]: ../event/index.html
/// [name]: #method.name
/// [target]: #method.target
/// [fields]: #method.fields
/// [verbosity level]: #method.level
/// [file name]: #method.file
/// [line number]: #method.line
/// [module path]: #method.module
/// [`Subscriber`]: ../subscriber/trait.Subscriber.html
/// [`id`]: struct.Metadata.html#method.id
/// [callsite identifier]: ../callsite/struct.Identifier.html
// TODO: When `const fn` is stable, make this type's fields private.
pub struct Metadata<'a> {
/// The name of the span described by this metadata.
///
/// **Warning**: The fields on this type are currently `pub` because it must
/// be able to be constructed statically by macros. However, when `const
/// fn`s are available on stable Rust, this will no longer be necessary.
/// Thus, these fields are *not* considered stable public API, and they may
/// change warning. Do not rely on any fields on `Metadata`. When
/// constructing new `Metadata`, use the `metadata!` macro or the
/// `Metadata::new` constructor instead!
#[doc(hidden)]
pub name: &'static str,
/// The part of the system that the span that this metadata describes
/// occurred in.
///
/// Typically, this is the module path, but alternate targets may be set
/// when spans or events are constructed.
///
/// **Warning**: The fields on this type are currently `pub` because it must
/// be able to be constructed statically by macros. However, when `const
/// fn`s are available on stable Rust, this will no longer be necessary.
/// Thus, these fields are *not* considered stable public API, and they may
/// change warning. Do not rely on any fields on `Metadata`. When
/// constructing new `Metadata`, use the `metadata!` macro or the
/// `Metadata::new` constructor instead!
#[doc(hidden)]
pub target: &'a str,
/// The level of verbosity of the described span.
///
/// **Warning**: The fields on this type are currently `pub` because it must
/// be able to be constructed statically by macros. However, when `const
/// fn`s are available on stable Rust, this will no longer be necessary.
/// Thus, these fields are *not* considered stable public API, and they may
/// change warning. Do not rely on any fields on `Metadata`. When
/// constructing new `Metadata`, use the `metadata!` macro or the
/// `Metadata::new` constructor instead!
#[doc(hidden)]
pub level: Level,
/// The name of the Rust module where the span occurred, or `None` if this
/// could not be determined.
///
/// **Warning**: The fields on this type are currently `pub` because it must
/// be able to be constructed statically by macros. However, when `const
/// fn`s are available on stable Rust, this will no longer be necessary.
/// Thus, these fields are *not* considered stable public API, and they may
/// change warning. Do not rely on any fields on `Metadata`. When
/// constructing new `Metadata`, use the `metadata!` macro or the
/// `Metadata::new` constructor instead!
#[doc(hidden)]
pub module_path: Option<&'a str>,
/// The name of the source code file where the span occurred, or `None` if
/// this could not be determined.
///
/// **Warning**: The fields on this type are currently `pub` because it must
/// be able to be constructed statically by macros. However, when `const
/// fn`s are available on stable Rust, this will no longer be necessary.
/// Thus, these fields are *not* considered stable public API, and they may
/// change warning. Do not rely on any fields on `Metadata`. When
/// constructing new `Metadata`, use the `metadata!` macro or the
/// `Metadata::new` constructor instead!
#[doc(hidden)]
pub file: Option<&'a str>,
/// The line number in the source code file where the span occurred, or
/// `None` if this could not be determined.
///
/// **Warning**: The fields on this type are currently `pub` because it must
/// be able to be constructed statically by macros. However, when `const
/// fn`s are available on stable Rust, this will no longer be necessary.
/// Thus, these fields are *not* considered stable public API, and they may
/// change warning. Do not rely on any fields on `Metadata`. When
/// constructing new `Metadata`, use the `metadata!` macro or the
/// `Metadata::new` constructor instead!
#[doc(hidden)]
pub line: Option<u32>,
/// The names of the key-value fields attached to the described span or
/// event.
///
/// **Warning**: The fields on this type are currently `pub` because it must
/// be able to be constructed statically by macros. However, when `const
/// fn`s are available on stable Rust, this will no longer be necessary.
/// Thus, these fields are *not* considered stable public API, and they may
/// change warning. Do not rely on any fields on `Metadata`. When
/// constructing new `Metadata`, use the `metadata!` macro or the
/// `Metadata::new` constructor instead!
#[doc(hidden)]
pub fields: field::FieldSet,
/// The kind of the callsite.
///
/// **Warning**: The fields on this type are currently `pub` because it must
/// be able to be constructed statically by macros. However, when `const
/// fn`s are available on stable Rust, this will no longer be necessary.
/// Thus, these fields are *not* considered stable public API, and they may
/// change warning. Do not rely on any fields on `Metadata`. When
/// constructing new `Metadata`, use the `metadata!` macro or the
/// `Metadata::new` constructor instead!
#[doc(hidden)]
pub kind: Kind,
}
/// Indicates whether the callsite is a span or event.
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct Kind(KindInner);
/// Describes the level of verbosity of a span or event.
#[derive(Clone, Debug, Eq, PartialEq, Ord, PartialOrd)]
pub struct Level(LevelInner);
// ===== impl Metadata =====
impl<'a> Metadata<'a> {
/// Construct new metadata for a span, with a name, target, level, field
/// names, and optional source code location.
pub fn new(
name: &'static str,
target: &'a str,
level: Level,
module_path: Option<&'a str>,
file: Option<&'a str>,
line: Option<u32>,
field_names: &'static [&'static str],
callsite: &'static Callsite,
kind: Kind,
) -> Self {
Metadata {
name,
target,
level,
module_path,
file,
line,
fields: field::FieldSet {
names: field_names,
callsite: callsite::Identifier(callsite),
},
kind,
}
}
/// Returns the names of the fields on the described span or event.
pub fn fields(&self) -> &field::FieldSet {
&self.fields
}
/// Returns the level of verbosity of the described span or event.
pub fn level(&self) -> &Level {
&self.level
}
/// Returns the name of the span.
pub fn name(&self) -> &'static str {
self.name
}
/// Returns a string describing the part of the system where the span or
/// event that this metadata describes occurred.
///
/// Typically, this is the module path, but alternate targets may be set
/// when spans or events are constructed.
pub fn target(&self) -> &'a str {
self.target
}
/// Returns the path to the Rust module where the span occurred, or
/// `None` if the module path is unknown.
pub fn module_path(&self) -> Option<&'a str> {
self.module_path
}
/// Returns the name of the source code file where the span
/// occurred, or `None` if the file is unknown
pub fn file(&self) -> Option<&'a str> {
self.file
}
/// Returns the line number in the source code file where the span
/// occurred, or `None` if the line number is unknown.
pub fn line(&self) -> Option<u32> {
self.line
}
/// Returns an opaque `Identifier` that uniquely identifies the callsite
/// this `Metadata` originated from.
#[inline]
pub fn callsite(&self) -> callsite::Identifier {
self.fields.callsite()
}
/// Returns true if the callsite kind is `Event`.
pub fn is_event(&self) -> bool {
self.kind.is_event()
}
/// Return true if the callsite kind is `Span`.
pub fn is_span(&self) -> bool {
self.kind.is_span()
}
}
impl<'a> fmt::Debug for Metadata<'a> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let mut meta = f.debug_struct("Metadata");
meta.field("name", &self.name)
.field("target", &self.target)
.field("level", &self.level);
if let Some(path) = self.module_path() {
meta.field("module_path", &path);
}
match (self.file(), self.line()) {
(Some(file), Some(line)) => {
meta.field("location", &format_args!("{}:{}", file, line));
}
(Some(file), None) => {
meta.field("file", &format_args!("{}", file));
}
// Note: a line num with no file is a kind of weird case that _probably_ never occurs...
(None, Some(line)) => {
meta.field("line", &line);
}
(None, None) => {}
};
meta.field("fields", &format_args!("{}", self.fields))
.field("callsite", &self.callsite())
.field("kind", &self.kind)
.finish()
}
}
#[derive(Clone, Debug, Eq, PartialEq)]
enum KindInner {
Event,
Span,
}
impl Kind {
/// `Event` callsite
pub const EVENT: Kind = Kind(KindInner::Event);
/// `Span` callsite
pub const SPAN: Kind = Kind(KindInner::Span);
/// Return true if the callsite kind is `Span`
pub fn is_span(&self) -> bool {
match self {
Kind(KindInner::Span) => true,
_ => false,
}
}
/// Return true if the callsite kind is `Event`
pub fn is_event(&self) -> bool {
match self {
Kind(KindInner::Event) => true,
_ => false,
}
}
}
// ===== impl Level =====
impl Level {
/// The "error" level.
///
/// Designates very serious errors.
pub const ERROR: Level = Level(LevelInner::Error);
/// The "warn" level.
///
/// Designates hazardous situations.
pub const WARN: Level = Level(LevelInner::Warn);
/// The "info" level.
///
/// Designates useful information.
pub const INFO: Level = Level(LevelInner::Info);
/// The "debug" level.
///
/// Designates lower priority information.
pub const DEBUG: Level = Level(LevelInner::Debug);
/// The "trace" level.
///
/// Designates very low priority, often extremely verbose, information.
pub const TRACE: Level = Level(LevelInner::Trace);
}
#[repr(usize)]
#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash, Ord, PartialOrd)]
enum LevelInner {
/// The "error" level.
///
/// Designates very serious errors.
Error = 1,
/// The "warn" level.
///
/// Designates hazardous situations.
Warn,
/// The "info" level.
///
/// Designates useful information.
Info,
/// The "debug" level.
///
/// Designates lower priority information.
Debug,
/// The "trace" level.
///
/// Designates very low priority, often extremely verbose, information.
Trace,
}
@@ -1,11 +0,0 @@
use span::Id;
#[derive(Debug)]
pub(crate) enum Parent {
/// The new span will be a root span.
Root,
/// The new span will be rooted in the current span.
Current,
/// The new span has an explicitly-specified parent.
Explicit(Id),
}
-185
View File
@@ -1,185 +0,0 @@
//! Spans represent periods of time in the execution of a program.
use parent::Parent;
use {field, Metadata};
/// Identifies a span within the context of a subscriber.
///
/// They are generated by [`Subscriber`]s for each span as it is created, by
/// the [`new_span`] trait method. See the documentation for that method for
/// more information on span ID generation.
///
/// [`Subscriber`]: ../subscriber/trait.Subscriber.html
/// [`new_span`]: ../subscriber/trait.Subscriber.html#method.new_span
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
// TODO(eliza): when Tokio's minimum Rust version is >= 1.28, change the
// internal representation to a `NonZeroU64`.
pub struct Id(u64);
/// Attributes provided to a `Subscriber` describing a new span when it is
/// created.
#[derive(Debug)]
pub struct Attributes<'a> {
metadata: &'a Metadata<'a>,
values: &'a field::ValueSet<'a>,
parent: Parent,
}
/// A set of fields recorded by a span.
#[derive(Debug)]
pub struct Record<'a> {
values: &'a field::ValueSet<'a>,
}
// ===== impl Span =====
impl Id {
/// Constructs a new span ID from the given `u64`.
///
/// **Note**: Span IDs must be greater than zero.
///
/// # Panics
/// - If the provided `u64` is 0
pub fn from_u64(u: u64) -> Self {
assert!(u > 0);
Id(u)
}
/// Returns the span's ID as a `u64`.
pub fn into_u64(&self) -> u64 {
self.0
}
}
impl<'a> Into<Option<Id>> for &'a Id {
fn into(self) -> Option<Id> {
Some(self.clone())
}
}
// ===== impl Attributes =====
impl<'a> Attributes<'a> {
/// Returns `Attributes` describing a new child span of the current span,
/// with the provided metadata and values.
pub fn new(metadata: &'a Metadata<'a>, values: &'a field::ValueSet<'a>) -> Self {
Attributes {
metadata,
values,
parent: Parent::Current,
}
}
/// Returns `Attributes` describing a new span at the root of its own trace
/// tree, with the provided metadata and values.
pub fn new_root(metadata: &'a Metadata<'a>, values: &'a field::ValueSet<'a>) -> Self {
Attributes {
metadata,
values,
parent: Parent::Root,
}
}
/// Returns `Attributes` describing a new child span of the specified
/// parent span, with the provided metadata and values.
pub fn child_of(
parent: Id,
metadata: &'a Metadata<'a>,
values: &'a field::ValueSet<'a>,
) -> Self {
Attributes {
metadata,
values,
parent: Parent::Explicit(parent),
}
}
/// Returns a reference to the new span's metadata.
pub fn metadata(&self) -> &Metadata<'a> {
self.metadata
}
/// Returns a reference to a `ValueSet` containing any values the new span
/// was created with.
pub fn values(&self) -> &field::ValueSet<'a> {
self.values
}
/// Returns true if the new span shoold be a root.
pub fn is_root(&self) -> bool {
match self.parent {
Parent::Root => true,
_ => false,
}
}
/// Returns true if the new span's parent should be determined based on the
/// current context.
///
/// If this is true and the current thread is currently inside a span, then
/// that span should be the new span's parent. Otherwise, if the current
/// thread is _not_ inside a span, then the new span will be the root of its
/// own trace tree.
pub fn is_contextual(&self) -> bool {
match self.parent {
Parent::Current => true,
_ => false,
}
}
/// Returns the new span's explicitly-specified parent, if there is one.
///
/// Otherwise (if the new span is a root or is a child of the current span),
/// returns false.
pub fn parent(&self) -> Option<&Id> {
match self.parent {
Parent::Explicit(ref p) => Some(p),
_ => None,
}
}
/// Records all the fields in this set of `Attributes` with the provided
/// [Visitor].
///
/// [visitor]: ../field/trait.Visit.html
pub fn record(&self, visitor: &mut field::Visit) {
self.values.record(visitor)
}
/// Returns `true` if this set of `Attributes` contains a value for the
/// given `Field`.
pub fn contains(&self, field: &field::Field) -> bool {
self.values.contains(field)
}
/// Returns true if this set of `Attributes` contains _no_ values.
pub fn is_empty(&self) -> bool {
self.values.is_empty()
}
}
// ===== impl Record =====
impl<'a> Record<'a> {
/// Constructs a new `Record` from a `ValueSet`.
pub fn new(values: &'a field::ValueSet<'a>) -> Self {
Self { values }
}
/// Records all the fields in this `Record` with the provided [Visitor].
///
/// [visitor]: ../field/trait.Visit.html
pub fn record(&self, visitor: &mut field::Visit) {
self.values.record(visitor)
}
/// Returns `true` if this `Record` contains a value for the given `Field`.
pub fn contains(&self, field: &field::Field) -> bool {
self.values.contains(field)
}
/// Returns true if this `Record` contains _no_ values.
pub fn is_empty(&self) -> bool {
self.values.is_empty()
}
}
@@ -1,477 +0,0 @@
//! Subscribers collect and record trace data.
use {span, Event, Metadata};
use std::{
any::{Any, TypeId},
ptr,
};
/// Trait representing the functions required to collect trace data.
///
/// Crates that provide implementations of methods for collecting or recording
/// trace data should implement the `Subscriber` interface. This trait is
/// intended to represent fundamental primitives for collecting trace events and
/// spans — other libraries may offer utility functions and types to make
/// subscriber implementations more modular or improve the ergonomics of writing
/// subscribers.
///
/// A subscriber is responsible for the following:
/// - Registering new spans as they are created, and providing them with span
/// IDs. Implicitly, this means the subscriber may determine the strategy for
/// determining span equality.
/// - Recording the attachment of field values and follows-from annotations to
/// spans.
/// - Filtering spans and events, and determining when those filters must be
/// invalidated.
/// - Observing spans as they are entered, exited, and closed, and events as
/// they occur.
///
/// When a span is entered or exited, the subscriber is provided only with the
/// [ID] with which it tagged that span when it was created. This means
/// that it is up to the subscriber to determine whether and how span _data_ —
/// the fields and metadata describing the span — should be stored. The
/// [`new_span`] function is called when a new span is created, and at that
/// point, the subscriber _may_ choose to store the associated data if it will
/// be referenced again. However, if the data has already been recorded and will
/// not be needed by the implementations of `enter` and `exit`, the subscriber
/// may freely discard that data without allocating space to store it.
///
/// [ID]: ../span/struct.Id.html
/// [`new_span`]: trait.Subscriber.html#method.new_span
pub trait Subscriber: 'static {
// === Span registry methods ==============================================
/// Registers a new callsite with this subscriber, returning whether or not
/// the subscriber is interested in being notified about the callsite.
///
/// By default, this function assumes that the subscriber's [filter]
/// represents an unchanging view of its interest in the callsite. However,
/// if this is not the case, subscribers may override this function to
/// indicate different interests, or to implement behaviour that should run
/// once for every callsite.
///
/// This function is guaranteed to be called at least once per callsite on
/// every active subscriber. The subscriber may store the keys to fields it
/// cares about in order to reduce the cost of accessing fields by name,
/// preallocate storage for that callsite, or perform any other actions it
/// wishes to perform once for each callsite.
///
/// The subscriber should then return an [`Interest`], indicating
/// whether it is interested in being notified about that callsite in the
/// future. This may be `Always` indicating that the subscriber always
/// wishes to be notified about the callsite, and its filter need not be
/// re-evaluated; `Sometimes`, indicating that the subscriber may sometimes
/// care about the callsite but not always (such as when sampling), or
/// `Never`, indicating that the subscriber never wishes to be notified about
/// that callsite. If all active subscribers return `Never`, a callsite will
/// never be enabled unless a new subscriber expresses interest in it.
///
/// `Subscriber`s which require their filters to be run every time an event
/// occurs or a span is entered/exited should return `Interest::sometimes`.
/// If a subscriber returns `Interest::sometimes`, then its' [`enabled`] method
/// will be called every time an event or span is created from that callsite.
///
/// For example, suppose a sampling subscriber is implemented by
/// incrementing a counter every time `enabled` is called and only returning
/// `true` when the counter is divisible by a specified sampling rate. If
/// that subscriber returns `Interest::always` from `register_callsite`, then
/// the filter will not be re-evaluated once it has been applied to a given
/// set of metadata. Thus, the counter will not be incremented, and the span
/// or event that correspands to the metadata will never be `enabled`.
///
/// `Subscriber`s that need to change their filters occasionally should call
/// [`rebuild_interest_cache`] to re-evaluate `register_callsite` for all
/// callsites.
///
/// Similarly, if a `Subscriber` has a filtering strategy that can be
/// changed dynamically at runtime, it would need to re-evaluate that filter
/// if the cached results have changed.
///
/// A subscriber which manages fanout to multiple other subscribers
/// should proxy this decision to all of its child subscribers,
/// returning `Interest::never` only if _all_ such children return
/// `Interest::never`. If the set of subscribers to which spans are
/// broadcast may change dynamically, the subscriber should also never
/// return `Interest::Never`, as a new subscriber may be added that _is_
/// interested.
///
/// # Notes
/// This function may be called again when a new subscriber is created or
/// when the registry is invalidated.
///
/// If a subscriber returns `Interest::never` for a particular callsite, it
/// _may_ still see spans and events originating from that callsite, if
/// another subscriber expressed interest in it.
///
/// [filter]: #method.enabled
/// [metadata]: ../metadata/struct.Metadata.html
/// [`Interest`]: struct.Interest.html
/// [`enabled`]: #method.enabled
/// [`rebuild_interest_cache`]: ../callsite/fn.rebuild_interest_cache.html
fn register_callsite(&self, metadata: &Metadata) -> Interest {
match self.enabled(metadata) {
true => Interest::always(),
false => Interest::never(),
}
}
/// Returns true if a span or event with the specified [metadata] would be
/// recorded.
///
/// By default, it is assumed that this filter needs only be evaluated once
/// for each callsite, so it is called by [`register_callsite`] when each
/// callsite is registered. The result is used to determine if the subscriber
/// is always [interested] or never interested in that callsite. This is intended
/// primarily as an optimization, so that expensive filters (such as those
/// involving string search, et cetera) need not be re-evaluated.
///
/// However, if the subscriber's interest in a particular span or event may
/// change, or depends on contexts only determined dynamically at runtime,
/// then the `register_callsite` method should be overridden to return
/// [`Interest::sometimes`]. In that case, this function will be called every
/// time that span or event occurs.
///
/// [metadata]: ../metadata/struct.Metadata.html
/// [interested]: struct.Interest.html
/// [`Interest::sometimes`]: struct.Interest.html#method.sometimes
/// [`register_callsite`]: #method.register_callsite
fn enabled(&self, metadata: &Metadata) -> bool;
/// Visit the construction of a new span, returning a new [span ID] for the
/// span being constructed.
///
/// The provided [`Attributes`] contains any field values that were provided
/// when the span was created. The subscriber may pass a [visitor] to the
/// `Attributes`' [`record` method] to record these values.
///
/// IDs are used to uniquely identify spans and events within the context of a
/// subscriber, so span equality will be based on the returned ID. Thus, if
/// the subscriber wishes for all spans with the same metadata to be
/// considered equal, it should return the same ID every time it is given a
/// particular set of metadata. Similarly, if it wishes for two separate
/// instances of a span with the same metadata to *not* be equal, it should
/// return a distinct ID every time this function is called, regardless of
/// the metadata.
///
/// Note that the subscriber is free to assign span IDs based on whatever
/// scheme it sees fit. Any guarantees about uniqueness, ordering, or ID
/// reuse are left up to the subscriber implementation to determine.
///
/// [span ID]: ../span/struct.Id.html
/// [`Attributes`]: ../span/struct.Attributes.html
/// [visitor]: ../field/trait.Visit.html
/// [`record` method]: ../span/struct.Attributes.html#method.record
fn new_span(&self, span: &span::Attributes) -> span::Id;
// === Notification methods ===============================================
/// Record a set of values on a span.
///
/// This method will be invoked when value is recorded on a span.
/// Recording multiple values for the same field is possible,
/// but the actual behaviour is defined by the subscriber implementation.
///
/// Keep in mind that a span might not provide a value
/// for each field it declares.
///
/// The subscriber is expected to provide a [visitor] to the `Record`'s
/// [`record` method] in order to record the added values.
///
/// # Example
/// "foo = 3" will be recorded when [`record`] is called on the
/// `Attributes` passed to `new_span`.
/// Since values are not provided for the `bar` and `baz` fields,
/// the span's `Metadata` will indicate that it _has_ those fields,
/// but values for them won't be recorded at this time.
///
/// ```rust,ignore
/// #[macro_use]
/// extern crate tokio_trace;
///
/// let mut span = span!("my_span", foo = 3, bar, baz);
///
/// // `Subscriber::record` will be called with a `Record`
/// // containing "bar = false"
/// span.record("bar", &false);
///
/// // `Subscriber::record` will be called with a `Record`
/// // containing "baz = "a string""
/// span.record("baz", &"a string");
/// ```
///
/// [visitor]: ../field/trait.Visit.html
/// [`record`]: ../span/struct.Attributes.html#method.record
/// [`record` method]: ../span/struct.Record.html#method.record
fn record(&self, span: &span::Id, values: &span::Record);
/// Adds an indication that `span` follows from the span with the id
/// `follows`.
///
/// This relationship differs somewhat from the parent-child relationship: a
/// span may have any number of prior spans, rather than a single one; and
/// spans are not considered to be executing _inside_ of the spans they
/// follow from. This means that a span may close even if subsequent spans
/// that follow from it are still open, and time spent inside of a
/// subsequent span should not be included in the time its precedents were
/// executing. This is used to model causal relationships such as when a
/// single future spawns several related background tasks, et cetera.
///
/// If the subscriber has spans corresponding to the given IDs, it should
/// record this relationship in whatever way it deems necessary. Otherwise,
/// if one or both of the given span IDs do not correspond to spans that the
/// subscriber knows about, or if a cyclical relationship would be created
/// (i.e., some span _a_ which proceeds some other span _b_ may not also
/// follow from _b_), it may silently do nothing.
fn record_follows_from(&self, span: &span::Id, follows: &span::Id);
/// Records that an [`Event`] has occurred.
///
/// This method will be invoked when an Event is constructed by
/// the `Event`'s [`dispatch` method]. For example, this happens internally
/// when an event macro from `tokio-trace` is called.
///
/// The key difference between this method and `record` is that `record` is
/// called when a value is recorded for a field defined by a span,
/// while `event` is called when a new event occurs.
///
/// The provided `Event` struct contains any field values attached to the
/// event. The subscriber may pass a [visitor] to the `Event`'s
/// [`record` method] to record these values.
///
/// [`Event`]: ../event/struct.Event.html
/// [visitor]: ../field/trait.Visit.html
/// [`record` method]: ../event/struct.Event.html#method.record
/// [`dispatch` method]: ../event/struct.Event.html#method.dispatch
fn event(&self, event: &Event);
/// Records that a span has been entered.
///
/// When entering a span, this method is called to notify the subscriber
/// that the span has been entered. The subscriber is provided with the
/// [span ID] of the entered span, and should update any internal state
/// tracking the current span accordingly.
///
/// [span ID]: ../span/struct.Id.html
fn enter(&self, span: &span::Id);
/// Records that a span has been exited.
///
/// When entering a span, this method is called to notify the subscriber
/// that the span has been exited. The subscriber is provided with the
/// [span ID] of the exited span, and should update any internal state
/// tracking the current span accordingly.
///
/// Exiting a span does not imply that the span will not be re-entered.
///
/// [span ID]: ../span/struct.Id.html
fn exit(&self, span: &span::Id);
/// Notifies the subscriber that a [span ID] has been cloned.
///
/// This function is guaranteed to only be called with span IDs that were
/// returned by this subscriber's `new_span` function.
///
/// Note that the default implementation of this function this is just the
/// identity function, passing through the identifier. However, it can be
/// used in conjunction with [`drop_span`] to track the number of handles
/// capable of `enter`ing a span. When all the handles have been dropped
/// (i.e., `drop_span` has been called one more time than `clone_span` for a
/// given ID), the subscriber may assume that the span will not be entered
/// again. It is then free to deallocate storage for data associated with
/// that span, write data from that span to IO, and so on.
///
/// For more unsafe situations, however, if `id` is itself a pointer of some
/// kind this can be used as a hook to "clone" the pointer, depending on
/// what that means for the specified pointer.
///
/// [span ID]: ../span/struct.Id.html
/// [`drop_span`]: trait.Subscriber.html#method.drop_span
fn clone_span(&self, id: &span::Id) -> span::Id {
id.clone()
}
/// Notifies the subscriber that a [span ID] has been dropped.
///
/// This function is guaranteed to only be called with span IDs that were
/// returned by this subscriber's `new_span` function.
///
/// It's guaranteed that if this function has been called once more than the
/// number of times `clone_span` was called with the same `id`, then no more
/// spans using that `id` exist. This means that it can be used in
/// conjunction with [`clone_span`] to track the number of handles
/// capable of `enter`ing a span. When all the handles have been dropped
/// (i.e., `drop_span` has been called one more time than `clone_span` for a
/// given ID), the subscriber may assume that the span will not be entered
/// again. It is then free to deallocate storage for data associated with
/// that span, write data from that span to IO, and so on.
///
/// **Note**: since this function is called when spans are dropped,
/// implementations should ensure that they are unwind-safe. Panicking from
/// inside of a `drop_span` function may cause a double panic, if the span
/// was dropped due to a thread unwinding.
///
/// [span ID]: ../span/struct.Id.html
/// [`clone_span`]: trait.Subscriber.html#method.clone_span
fn drop_span(&self, id: span::Id) {
let _ = id;
}
// === Downcasting methods ================================================
/// If `self` is the same type as the provided `TypeId`, returns an untyped
/// `*const` pointer to that type. Otherwise, returns `None`.
///
/// If you wish to downcast a `Subscriber`, it is strongly advised to use
/// the safe API provided by [`downcast_ref`] instead.
///
/// This API is required for `downcast_raw` to be a trait method; a method
/// signature like [`downcast_ref`] (with a generic type parameter) is not
/// object-safe, and thus cannot be a trait method for `Subscriber`. This
/// means that if we only exposed `downcast_ref`, `Subscriber`
/// implementations could not override the downcasting behavior
///
/// This method may be overridden by "fan out" or "chained" subscriber
/// implementations which consist of multiple composed types. Such
/// subscribers might allow `downcast_raw` by returning references to those
/// component if they contain components with the given `TypeId`.
///
/// # Safety
///
/// The [`downcast_ref`] method expects that the pointer returned by
/// `downcast_raw` is non-null and points to a valid instance of the type
/// with the provided `TypeId`. Failure to ensure this will result in
/// undefined behaviour, so implementing `downcast_raw` is unsafe.
///
/// [`downcast_ref`]: #method.downcast_ref
unsafe fn downcast_raw(&self, id: TypeId) -> Option<*const ()> {
if id == TypeId::of::<Self>() {
Some(self as *const Self as *const ())
} else {
None
}
}
}
impl Subscriber {
/// Returns `true` if this `Subscriber` is the same type as `T`.
pub fn is<T: Any>(&self) -> bool {
self.downcast_ref::<T>().is_some()
}
/// Returns some reference to this `Subscriber` value if it is of type `T`,
/// or `None` if it isn't.
pub fn downcast_ref<T: Any>(&self) -> Option<&T> {
unsafe {
let raw = self.downcast_raw(TypeId::of::<T>())?;
if raw == ptr::null() {
None
} else {
Some(&*(raw as *const _))
}
}
}
}
/// Indicates a [`Subscriber`]'s interest in a particular callsite.
///
/// `Subscriber`s return an `Interest` from their [`register_callsite`] methods
/// in order to determine whether that span should be enabled or disabled.
///
/// [`Subscriber`] trait.Subscriber.html
/// [clone_span]: trait.Subscriber.html#method.register_callsite
#[derive(Clone, Debug)]
pub struct Interest(InterestKind);
#[derive(Copy, Clone, Debug, Eq, PartialEq, Ord, PartialOrd)]
enum InterestKind {
Never = 0,
Sometimes = 1,
Always = 2,
}
impl Interest {
/// Returns an `Interest` indicating that the subscriber is never interested
/// in being notified about a callsite.
///
/// If all active subscribers are `never()` interested in a callsite, it will
/// be completely disabled unless a new subscriber becomes active.
#[inline]
pub fn never() -> Self {
Interest(InterestKind::Never)
}
/// Returns an `Interest` indicating the subscriber is sometimes interested
/// in being notified about a callsite.
///
/// If all active subscribers are `sometimes` or `never` interested in a
/// callsite, the currently active subscriber will be asked to filter that
/// callsite every time it creates a span. This will be the case until a new
/// subscriber expresses that it is `always` interested in the callsite.
#[inline]
pub fn sometimes() -> Self {
Interest(InterestKind::Sometimes)
}
/// Returns an `Interest` indicating the subscriber is always interested in
/// being notified about a callsite.
///
/// If any subscriber expresses that it is `always()` interested in a given
/// callsite, then the callsite will always be enabled.
#[inline]
pub fn always() -> Self {
Interest(InterestKind::Always)
}
/// Returns `true` if the subscriber is never interested in being notified
/// about this callsite.
#[inline]
pub fn is_never(&self) -> bool {
match self.0 {
InterestKind::Never => true,
_ => false,
}
}
/// Returns `true` if the subscriber is sometimes interested in being notified
/// about this callsite.
#[inline]
pub fn is_sometimes(&self) -> bool {
match self.0 {
InterestKind::Sometimes => true,
_ => false,
}
}
/// Returns `true` if the subscriber is always interested in being notified
/// about this callsite.
#[inline]
pub fn is_always(&self) -> bool {
match self.0 {
InterestKind::Always => true,
_ => false,
}
}
/// Returns the common interest between these two Interests.
///
/// The common interest is defined as the least restrictive, so if one
/// interest is `never` and the other is `always` the common interest is
/// `always`.
pub(crate) fn and(self, rhs: Interest) -> Self {
match rhs.0 {
// If the added interest is `never()`, don't change anything —
// either a different subscriber added a higher interest, which we
// want to preserve, or the interest is 0 anyway (as it's
// initialized to 0).
InterestKind::Never => self,
// If the interest is `sometimes()`, that overwrites a `never()`
// interest, but doesn't downgrade an `always()` interest.
InterestKind::Sometimes if self.0 == InterestKind::Never => rhs,
// If the interest is `always()`, we overwrite the current interest,
// as always() is the highest interest level and should take
// precedent.
InterestKind::Always => rhs,
_ => self,
}
}
}
@@ -1,49 +0,0 @@
#[macro_use]
extern crate tokio_trace_core;
use tokio_trace_core::{
callsite::Callsite,
metadata::{Kind, Level, Metadata},
subscriber::Interest,
};
#[test]
fn metadata_macro_api() {
// This test should catch any inadvertant breaking changes
// caused bu changes to the macro.
struct TestCallsite;
impl Callsite for TestCallsite {
fn set_interest(&self, _: Interest) {
unimplemented!("test")
}
fn metadata(&self) -> &Metadata {
unimplemented!("test")
}
}
static CALLSITE: TestCallsite = TestCallsite;
let _metadata = metadata! {
name: "test_metadata",
target: "test_target",
level: Level::DEBUG,
fields: &["foo", "bar", "baz"],
callsite: &CALLSITE,
kind: Kind::SPAN,
};
let _metadata = metadata! {
name: "test_metadata",
target: "test_target",
level: Level::TRACE,
fields: &[],
callsite: &CALLSITE,
kind: Kind::EVENT,
};
let _metadata = metadata! {
name: "test_metadata",
target: "test_target",
level: Level::INFO,
fields: &[],
callsite: &CALLSITE,
kind: Kind::EVENT
};
}
+2 -2
View File
@@ -50,7 +50,7 @@ rt-full = [
"tokio-executor",
"tokio-macros",
# "tokio-threadpool",
# "tokio-trace-core",
"tracing-core",
]
sync = ["tokio-sync"]
tcp = ["tokio-tcp"]
@@ -77,7 +77,7 @@ tokio-sync = { version = "0.2.0", optional = true, path = "../tokio-sync" }
tokio-tcp = { version = "0.2.0", optional = true, path = "../tokio-tcp" }
tokio-udp = { version = "0.2.0", optional = true, path = "../tokio-udp" }
#tokio-timer = { version = "0.3.0", optional = true, path = "../tokio-timer" }
#tokio-trace-core = { version = "0.2", optional = true }
tracing-core = { version = "0.1", optional = true }
# Needed for async/await preview support
#tokio-futures = { version = "0.2.0", optional = true, path = "../tokio-futures" }
+1 -1
View File
@@ -4,7 +4,7 @@ use tokio_reactor;
use tokio_threadpool::Builder as ThreadPoolBuilder;
use tokio_timer::clock::{self, Clock};
use tokio_timer::timer::{self, Timer};
use tokio_trace_core as trace;
use tracing_core as trace;
use std::io;
use std::sync::Mutex;
use std::time::Duration;