mirror of
https://github.com/tokio-rs/tokio.git
synced 2026-08-26 00:00:16 +02:00
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:
@@ -18,8 +18,6 @@ members = [
|
||||
"tokio-timer",
|
||||
"tokio-tcp",
|
||||
# "tokio-tls",
|
||||
# "tokio-trace",
|
||||
# "tokio-trace/tokio-trace-core",
|
||||
"tokio-udp",
|
||||
# "tokio-uds",
|
||||
]
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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" }
|
||||
|
||||
@@ -1,3 +0,0 @@
|
||||
# 0.1.0 (April 22, 2019)
|
||||
|
||||
- Initial release
|
||||
@@ -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
|
||||
@@ -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.
|
||||
@@ -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.
|
||||
@@ -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);
|
||||
@@ -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);
|
||||
@@ -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();
|
||||
}
|
||||
@@ -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.
|
||||
}
|
||||
}
|
||||
@@ -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 {}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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
@@ -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 {}
|
||||
}
|
||||
@@ -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;
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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);
|
||||
});
|
||||
}
|
||||
@@ -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,
|
||||
};
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
@@ -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);
|
||||
})
|
||||
}
|
||||
@@ -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(())
|
||||
}
|
||||
}
|
||||
@@ -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()
|
||||
}
|
||||
}
|
||||
@@ -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(())
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
@@ -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(())
|
||||
}
|
||||
}
|
||||
@@ -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,),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
@@ -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"
|
||||
@@ -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.
|
||||
@@ -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");
|
||||
}
|
||||
}
|
||||
@@ -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,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
@@ -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),
|
||||
}
|
||||
@@ -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
@@ -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" }
|
||||
|
||||
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user