mirror of
https://github.com/tokio-rs/tokio.git
synced 2026-09-09 00:00:08 +02:00
Compare commits
14
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
da186a7859 | ||
|
|
2117ce7bac | ||
|
|
39f369f686 | ||
|
|
83e8fff090 | ||
|
|
f545d1276b | ||
|
|
59fb5b9a7d | ||
|
|
57ba3a7fbc | ||
|
|
c3c3481d74 | ||
|
|
7b39388415 | ||
|
|
11a1ce2721 | ||
|
|
c9532e49d7 | ||
|
|
b4cb3226ab | ||
|
|
4446eb4db8 | ||
|
|
cad0c35623 |
@@ -32,8 +32,6 @@ task:
|
||||
test_script:
|
||||
- . $HOME/.cargo/env
|
||||
- cargo test --all
|
||||
- (cd tokio-trace/test-log-support && cargo test)
|
||||
- (cd tokio-trace/test_static_max_level_features && cargo test)
|
||||
- cargo doc --all
|
||||
i686_test_script:
|
||||
- . $HOME/.cargo/env
|
||||
|
||||
@@ -9,7 +9,6 @@ members = [
|
||||
"tokio-fs",
|
||||
"tokio-futures",
|
||||
"tokio-io",
|
||||
"tokio-macros",
|
||||
"tokio-reactor",
|
||||
"tokio-signal",
|
||||
"tokio-sync",
|
||||
@@ -18,8 +17,6 @@ members = [
|
||||
"tokio-timer",
|
||||
"tokio-tcp",
|
||||
"tokio-tls",
|
||||
"tokio-trace",
|
||||
"tokio-trace/tokio-trace-core",
|
||||
"tokio-udp",
|
||||
"tokio-uds",
|
||||
]
|
||||
|
||||
@@ -170,6 +170,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
|
||||
|
||||
+2
-5
@@ -46,10 +46,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:
|
||||
@@ -68,6 +64,7 @@ jobs:
|
||||
- udp
|
||||
- uds
|
||||
- sync
|
||||
- experimental-tracing
|
||||
tokio-buf:
|
||||
- util
|
||||
|
||||
@@ -94,7 +91,7 @@ jobs:
|
||||
- template: ci/azure-check-minrust.yml
|
||||
parameters:
|
||||
name: minrust
|
||||
rust_version: 1.26.0
|
||||
rust_version: 1.31.0
|
||||
|
||||
- template: ci/azure-tsan.yml
|
||||
parameters:
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
#![feature(test)]
|
||||
#![deny(warnings)]
|
||||
|
||||
extern crate test;
|
||||
#[macro_use]
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
// Measure cost of different operations
|
||||
// to get a sense of performance tradeoffs
|
||||
#![feature(test)]
|
||||
#![deny(warnings)]
|
||||
|
||||
extern crate mio;
|
||||
extern crate test;
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
#![feature(test)]
|
||||
#![deny(warnings)]
|
||||
|
||||
extern crate futures;
|
||||
extern crate tokio;
|
||||
|
||||
+1
-1
@@ -12,7 +12,7 @@ jobs:
|
||||
steps:
|
||||
- template: azure-install-rust.yml
|
||||
parameters:
|
||||
rust_version: nightly-2018-11-18
|
||||
rust_version: nightly-2019-07-17
|
||||
|
||||
- template: azure-patch-crates.yml
|
||||
- script: |
|
||||
|
||||
@@ -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" }
|
||||
|
||||
@@ -35,3 +35,10 @@ race:WorkerEntry::set_next_sleeper
|
||||
# This ignores a false positive caused by `thread::park()`/`thread::unpark()`.
|
||||
# See: https://github.com/rust-lang/rust/pull/54806#issuecomment-436193353
|
||||
race:pthread_cond_destroy
|
||||
|
||||
# Recent rand dependency updates and seeding changes have introduced
|
||||
# lazy_static's and other racy code. See:
|
||||
# https://github.com/tokio-rs/tokio/pull/1358#issuecomment-516172383
|
||||
race:RandomState*::build_hasher
|
||||
race:lazy_static::
|
||||
race:c2_chacha::guts
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
#![doc(html_root_url = "https://docs.rs/tokio-buf/0.1.1")]
|
||||
#![deny(missing_docs, missing_debug_implementations, unreachable_pub)]
|
||||
#![cfg_attr(test, deny(warnings))]
|
||||
|
||||
//! Asynchronous stream of bytes.
|
||||
//!
|
||||
|
||||
@@ -4,4 +4,4 @@ use tokio_buf::BufStream;
|
||||
|
||||
// Ensures that `BufStream` can be a trait object
|
||||
#[allow(dead_code)]
|
||||
fn obj(_: &mut BufStream<Item = u32, Error = ()>) {}
|
||||
fn obj(_: &mut dyn BufStream<Item = u32, Error = ()>) {}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#![deny(missing_docs, missing_debug_implementations, warnings)]
|
||||
#![deny(missing_docs, missing_debug_implementations)]
|
||||
#![doc(html_root_url = "https://docs.rs/tokio-codec/0.1.1")]
|
||||
|
||||
//! Utilities for encoding and decoding frames.
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
#![doc(html_root_url = "https://docs.rs/tokio-current-thread/0.1.6")]
|
||||
#![deny(warnings, missing_docs, missing_debug_implementations)]
|
||||
#![deny(missing_docs, missing_debug_implementations)]
|
||||
|
||||
//! A single-threaded executor which executes tasks on the same thread from which
|
||||
//! they are spawned.
|
||||
@@ -64,7 +64,7 @@ pub struct CurrentThread<P: Park = ParkThread> {
|
||||
spawn_handle: Handle,
|
||||
|
||||
/// Receiver for futures spawned from other threads
|
||||
spawn_receiver: mpsc::Receiver<Box<Future<Item = (), Error = ()> + Send + 'static>>,
|
||||
spawn_receiver: mpsc::Receiver<Box<dyn Future<Item = (), Error = ()> + Send + 'static>>,
|
||||
|
||||
/// The thread-local ID assigned to this executor.
|
||||
id: u64,
|
||||
@@ -186,11 +186,15 @@ struct Borrow<'a, U: 'a> {
|
||||
}
|
||||
|
||||
trait SpawnLocal {
|
||||
fn spawn_local(&mut self, future: Box<Future<Item = (), Error = ()>>, already_counted: bool);
|
||||
fn spawn_local(
|
||||
&mut self,
|
||||
future: Box<dyn Future<Item = (), Error = ()>>,
|
||||
already_counted: bool,
|
||||
);
|
||||
}
|
||||
|
||||
struct CurrentRunner {
|
||||
spawn: Cell<Option<*mut SpawnLocal>>,
|
||||
spawn: Cell<Option<*mut dyn SpawnLocal>>,
|
||||
id: Cell<Option<u64>>,
|
||||
}
|
||||
|
||||
@@ -424,7 +428,7 @@ impl<P: Park> Drop for CurrentThread<P> {
|
||||
impl tokio_executor::Executor for CurrentThread {
|
||||
fn spawn(
|
||||
&mut self,
|
||||
future: Box<Future<Item = (), Error = ()> + Send>,
|
||||
future: Box<dyn Future<Item = (), Error = ()> + Send>,
|
||||
) -> Result<(), SpawnError> {
|
||||
self.borrow().spawn_local(future, false);
|
||||
Ok(())
|
||||
@@ -629,7 +633,7 @@ impl<'a, P: Park> fmt::Debug for Entered<'a, P> {
|
||||
/// Handle to spawn a future on the corresponding `CurrentThread` instance
|
||||
#[derive(Clone)]
|
||||
pub struct Handle {
|
||||
sender: mpsc::Sender<Box<Future<Item = (), Error = ()> + Send + 'static>>,
|
||||
sender: mpsc::Sender<Box<dyn Future<Item = (), Error = ()> + Send + 'static>>,
|
||||
num_futures: Arc<atomic::AtomicUsize>,
|
||||
shut_down: Cell<bool>,
|
||||
notify: executor::NotifyHandle,
|
||||
@@ -731,7 +735,7 @@ impl TaskExecutor {
|
||||
/// Spawn a future onto the current `CurrentThread` instance.
|
||||
pub fn spawn_local(
|
||||
&mut self,
|
||||
future: Box<Future<Item = (), Error = ()>>,
|
||||
future: Box<dyn Future<Item = (), Error = ()>>,
|
||||
) -> Result<(), SpawnError> {
|
||||
CURRENT.with(|current| match current.spawn.get() {
|
||||
Some(spawn) => {
|
||||
@@ -746,7 +750,7 @@ impl TaskExecutor {
|
||||
impl tokio_executor::Executor for TaskExecutor {
|
||||
fn spawn(
|
||||
&mut self,
|
||||
future: Box<Future<Item = (), Error = ()> + Send>,
|
||||
future: Box<dyn Future<Item = (), Error = ()> + Send>,
|
||||
) -> Result<(), SpawnError> {
|
||||
self.spawn_local(future)
|
||||
}
|
||||
@@ -791,7 +795,11 @@ impl<'a, U: Unpark> Borrow<'a, U> {
|
||||
}
|
||||
|
||||
impl<'a, U: Unpark> SpawnLocal for Borrow<'a, U> {
|
||||
fn spawn_local(&mut self, future: Box<Future<Item = (), Error = ()>>, already_counted: bool) {
|
||||
fn spawn_local(
|
||||
&mut self,
|
||||
future: Box<dyn Future<Item = (), Error = ()>>,
|
||||
already_counted: bool,
|
||||
) {
|
||||
if !already_counted {
|
||||
// NOTE: we have a borrow of the Runtime, so we know that it isn't shut down.
|
||||
// NOTE: += 2 since LSB is the shutdown bit
|
||||
@@ -804,7 +812,7 @@ impl<'a, U: Unpark> SpawnLocal for Borrow<'a, U> {
|
||||
// ===== impl CurrentRunner =====
|
||||
|
||||
impl CurrentRunner {
|
||||
fn set_spawn<F, R>(&self, spawn: &mut SpawnLocal, f: F) -> R
|
||||
fn set_spawn<F, R>(&self, spawn: &mut dyn SpawnLocal, f: F) -> R
|
||||
where
|
||||
F: FnOnce() -> R,
|
||||
{
|
||||
@@ -819,14 +827,14 @@ impl CurrentRunner {
|
||||
|
||||
let _reset = Reset(self);
|
||||
|
||||
let spawn = unsafe { hide_lt(spawn as *mut SpawnLocal) };
|
||||
let spawn = unsafe { hide_lt(spawn as *mut dyn SpawnLocal) };
|
||||
self.spawn.set(Some(spawn));
|
||||
|
||||
f()
|
||||
}
|
||||
}
|
||||
|
||||
unsafe fn hide_lt<'a>(p: *mut (SpawnLocal + 'a)) -> *mut (SpawnLocal + 'static) {
|
||||
unsafe fn hide_lt<'a>(p: *mut (dyn SpawnLocal + 'a)) -> *mut (dyn SpawnLocal + 'static) {
|
||||
use std::mem;
|
||||
mem::transmute(p)
|
||||
}
|
||||
|
||||
@@ -125,7 +125,7 @@ enum Dequeue<U> {
|
||||
}
|
||||
|
||||
/// Wraps a spawned boxed future
|
||||
struct Task(Spawn<Box<Future<Item = (), Error = ()>>>);
|
||||
struct Task(Spawn<Box<dyn Future<Item = (), Error = ()>>>);
|
||||
|
||||
/// A task that is scheduled. `turn` must be called
|
||||
pub struct Scheduled<'a, U: 'a> {
|
||||
@@ -171,7 +171,7 @@ where
|
||||
self.inner.clone().into()
|
||||
}
|
||||
|
||||
pub fn schedule(&mut self, item: Box<Future<Item = (), Error = ()>>) {
|
||||
pub fn schedule(&mut self, item: Box<dyn Future<Item = (), Error = ()>>) {
|
||||
// Get the current scheduler tick
|
||||
let tick_num = self.inner.tick_num.load(SeqCst);
|
||||
|
||||
@@ -359,7 +359,7 @@ impl<'a, U: Unpark> Scheduled<'a, U> {
|
||||
}
|
||||
|
||||
impl Task {
|
||||
pub fn new(future: Box<Future<Item = (), Error = ()> + 'static>) -> Self {
|
||||
pub fn new(future: Box<dyn Future<Item = (), Error = ()> + 'static>) -> Self {
|
||||
Task(executor::spawn(future))
|
||||
}
|
||||
}
|
||||
@@ -687,8 +687,8 @@ unsafe impl<U: Unpark> UnsafeNotify for ArcNode<U> {
|
||||
}
|
||||
}
|
||||
|
||||
unsafe fn hide_lt<U: Unpark>(p: *mut ArcNode<U>) -> *mut UnsafeNotify {
|
||||
mem::transmute(p as *mut UnsafeNotify)
|
||||
unsafe fn hide_lt<U: Unpark>(p: *mut ArcNode<U>) -> *mut dyn UnsafeNotify {
|
||||
mem::transmute(p as *mut dyn UnsafeNotify)
|
||||
}
|
||||
|
||||
impl<U: Unpark> Node<U> {
|
||||
|
||||
@@ -22,7 +22,7 @@ use futures::sync::oneshot;
|
||||
|
||||
mod from_block_on_all {
|
||||
use super::*;
|
||||
fn test<F: Fn(Box<Future<Item = (), Error = ()>>) + 'static>(spawn: F) {
|
||||
fn test<F: Fn(Box<dyn Future<Item = (), Error = ()>>) + 'static>(spawn: F) {
|
||||
let cnt = Rc::new(Cell::new(0));
|
||||
let c = cnt.clone();
|
||||
|
||||
@@ -102,7 +102,7 @@ fn spawn_many() {
|
||||
mod does_not_set_global_executor_by_default {
|
||||
use super::*;
|
||||
|
||||
fn test<F: Fn(Box<Future<Item = (), Error = ()> + Send>) -> Result<(), E> + 'static, E>(
|
||||
fn test<F: Fn(Box<dyn Future<Item = (), Error = ()> + Send>) -> Result<(), E> + 'static, E>(
|
||||
spawn: F,
|
||||
) {
|
||||
block_on_all(lazy(|| {
|
||||
@@ -127,7 +127,7 @@ mod does_not_set_global_executor_by_default {
|
||||
mod from_block_on_future {
|
||||
use super::*;
|
||||
|
||||
fn test<F: Fn(Box<Future<Item = (), Error = ()>>)>(spawn: F) {
|
||||
fn test<F: Fn(Box<dyn Future<Item = (), Error = ()>>)>(spawn: F) {
|
||||
let cnt = Rc::new(Cell::new(0));
|
||||
|
||||
let mut tokio_current_thread = CurrentThread::new();
|
||||
@@ -181,8 +181,8 @@ mod outstanding_tasks_are_dropped_when_executor_is_dropped {
|
||||
|
||||
fn test<F, G>(spawn: F, dotspawn: G)
|
||||
where
|
||||
F: Fn(Box<Future<Item = (), Error = ()>>) + 'static,
|
||||
G: Fn(&mut CurrentThread, Box<Future<Item = (), Error = ()>>),
|
||||
F: Fn(Box<dyn Future<Item = (), Error = ()>>) + 'static,
|
||||
G: Fn(&mut CurrentThread, Box<dyn Future<Item = (), Error = ()>>),
|
||||
{
|
||||
let mut rc = Rc::new(());
|
||||
|
||||
@@ -383,8 +383,8 @@ mod and_turn {
|
||||
|
||||
fn test<F, G>(spawn: F, dotspawn: G)
|
||||
where
|
||||
F: Fn(Box<Future<Item = (), Error = ()>>) + 'static,
|
||||
G: Fn(&mut CurrentThread, Box<Future<Item = (), Error = ()>>),
|
||||
F: Fn(Box<dyn Future<Item = (), Error = ()>>) + 'static,
|
||||
G: Fn(&mut CurrentThread, Box<dyn Future<Item = (), Error = ()>>),
|
||||
{
|
||||
let cnt = Rc::new(Cell::new(0));
|
||||
let c = cnt.clone();
|
||||
@@ -445,7 +445,6 @@ mod and_turn {
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
mod in_drop {
|
||||
@@ -459,7 +458,7 @@ mod in_drop {
|
||||
}
|
||||
|
||||
struct MyFuture {
|
||||
_data: Box<Any>,
|
||||
_data: Box<dyn Any>,
|
||||
}
|
||||
|
||||
impl Future for MyFuture {
|
||||
@@ -473,8 +472,8 @@ mod in_drop {
|
||||
|
||||
fn test<F, G>(spawn: F, dotspawn: G)
|
||||
where
|
||||
F: Fn(Box<Future<Item = (), Error = ()>>) + 'static,
|
||||
G: Fn(&mut CurrentThread, Box<Future<Item = (), Error = ()>>),
|
||||
F: Fn(Box<dyn Future<Item = (), Error = ()>>) + 'static,
|
||||
G: Fn(&mut CurrentThread, Box<dyn Future<Item = (), Error = ()>>),
|
||||
{
|
||||
let mut tokio_current_thread = CurrentThread::new();
|
||||
|
||||
@@ -520,7 +519,6 @@ mod in_drop {
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
||||
@@ -1,3 +1,8 @@
|
||||
# 0.1.8 (June 2, 2019)
|
||||
|
||||
### Added
|
||||
- Add `executor::exit` to allow other executors inside `threadpool::blocking` (#1155).
|
||||
|
||||
# 0.1.7 (March 22, 2019)
|
||||
|
||||
### Added
|
||||
|
||||
@@ -8,7 +8,7 @@ name = "tokio-executor"
|
||||
# - README.md
|
||||
# - Update CHANGELOG.md.
|
||||
# - Create "v0.1.x" git tag.
|
||||
version = "0.1.7"
|
||||
version = "0.1.8"
|
||||
documentation = "https://docs.rs/tokio-executor/0.1.7/tokio_executor"
|
||||
repository = "https://github.com/tokio-rs/tokio"
|
||||
homepage = "https://github.com/tokio-rs/tokio"
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
Task execution related traits and utilities.
|
||||
|
||||
[Documentation](https://docs.rs/tokio-executor/0.1.7/tokio_executor)
|
||||
[Documentation](https://docs.rs/tokio-executor/0.1.8/tokio_executor)
|
||||
|
||||
## Overview
|
||||
|
||||
@@ -31,10 +31,10 @@ executor, including:
|
||||
|
||||
* [`Park`] abstracts over blocking and unblocking the current thread.
|
||||
|
||||
[`Executor`]: https://docs.rs/tokio-executor/0.1.7/tokio_executor/trait.Executor.html
|
||||
[`enter`]: https://docs.rs/tokio-executor/0.1.7/tokio_executor/fn.enter.html
|
||||
[`DefaultExecutor`]: https://docs.rs/tokio-executor/0.1.7/tokio_executor/struct.DefaultExecutor.html
|
||||
[`Park`]: https://docs.rs/tokio-executor/0.1.7/tokio_executor/park/trait.Park.html
|
||||
[`Executor`]: https://docs.rs/tokio-executor/0.1.8/tokio_executor/trait.Executor.html
|
||||
[`enter`]: https://docs.rs/tokio-executor/0.1.8/tokio_executor/fn.enter.html
|
||||
[`DefaultExecutor`]: https://docs.rs/tokio-executor/0.1.8/tokio_executor/struct.DefaultExecutor.html
|
||||
[`Park`]: https://docs.rs/tokio-executor/0.1.8/tokio_executor/park/trait.Park.html
|
||||
|
||||
## License
|
||||
|
||||
|
||||
@@ -11,7 +11,7 @@ thread_local!(static ENTERED: Cell<bool> = Cell::new(false));
|
||||
///
|
||||
/// For more details, see [`enter` documentation](fn.enter.html)
|
||||
pub struct Enter {
|
||||
on_exit: Vec<Box<Callback>>,
|
||||
on_exit: Vec<Box<dyn Callback>>,
|
||||
permanent: bool,
|
||||
}
|
||||
|
||||
@@ -67,6 +67,42 @@ pub fn enter() -> Result<Enter, EnterError> {
|
||||
})
|
||||
}
|
||||
|
||||
// Forces the current "entered" state to be cleared while the closure
|
||||
// is executed.
|
||||
//
|
||||
// # Warning
|
||||
//
|
||||
// This is hidden for a reason. Do not use without fully understanding
|
||||
// executors. Misuing can easily cause your program to deadlock.
|
||||
#[doc(hidden)]
|
||||
pub fn exit<F: FnOnce() -> R, R>(f: F) -> R {
|
||||
// Reset in case the closure panics
|
||||
struct Reset;
|
||||
impl Drop for Reset {
|
||||
fn drop(&mut self) {
|
||||
ENTERED.with(|c| {
|
||||
c.set(true);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
ENTERED.with(|c| {
|
||||
debug_assert!(c.get());
|
||||
c.set(false);
|
||||
});
|
||||
|
||||
let reset = Reset;
|
||||
let ret = f();
|
||||
::std::mem::forget(reset);
|
||||
|
||||
ENTERED.with(|c| {
|
||||
assert!(!c.get(), "closure claimed permanent executor");
|
||||
c.set(true);
|
||||
});
|
||||
|
||||
ret
|
||||
}
|
||||
|
||||
impl Enter {
|
||||
/// Register a callback to be invoked if and when the thread
|
||||
/// ceased to act as an executor.
|
||||
|
||||
@@ -94,7 +94,7 @@ pub trait Executor {
|
||||
/// ```
|
||||
fn spawn(
|
||||
&mut self,
|
||||
future: Box<Future<Item = (), Error = ()> + Send>,
|
||||
future: Box<dyn Future<Item = (), Error = ()> + Send>,
|
||||
) -> Result<(), SpawnError>;
|
||||
|
||||
/// Provides a best effort **hint** to whether or not `spawn` will succeed.
|
||||
@@ -140,7 +140,7 @@ pub trait Executor {
|
||||
impl<E: Executor + ?Sized> Executor for Box<E> {
|
||||
fn spawn(
|
||||
&mut self,
|
||||
future: Box<Future<Item = (), Error = ()> + Send>,
|
||||
future: Box<dyn Future<Item = (), Error = ()> + Send>,
|
||||
) -> Result<(), SpawnError> {
|
||||
(**self).spawn(future)
|
||||
}
|
||||
|
||||
@@ -37,7 +37,7 @@ impl DefaultExecutor {
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn with_current<F: FnOnce(&mut Executor) -> R, R>(f: F) -> Option<R> {
|
||||
fn with_current<F: FnOnce(&mut dyn Executor) -> R, R>(f: F) -> Option<R> {
|
||||
EXECUTOR.with(
|
||||
|current_executor| match current_executor.replace(State::Active) {
|
||||
State::Ready(executor_ptr) => {
|
||||
@@ -57,7 +57,7 @@ enum State {
|
||||
// default executor not defined
|
||||
Empty,
|
||||
// default executor is defined and ready to be used
|
||||
Ready(*mut Executor),
|
||||
Ready(*mut dyn Executor),
|
||||
// default executor is currently active (used to detect recursive calls)
|
||||
Active,
|
||||
}
|
||||
@@ -72,7 +72,7 @@ thread_local! {
|
||||
impl super::Executor for DefaultExecutor {
|
||||
fn spawn(
|
||||
&mut self,
|
||||
future: Box<Future<Item = (), Error = ()> + Send>,
|
||||
future: Box<dyn Future<Item = (), Error = ()> + Send>,
|
||||
) -> Result<(), SpawnError> {
|
||||
DefaultExecutor::with_current(|executor| executor.spawn(future))
|
||||
.unwrap_or_else(|| Err(SpawnError::shutdown()))
|
||||
@@ -210,7 +210,7 @@ where
|
||||
})
|
||||
}
|
||||
|
||||
unsafe fn hide_lt<'a>(p: *mut (Executor + 'a)) -> *mut (Executor + 'static) {
|
||||
unsafe fn hide_lt<'a>(p: *mut (dyn Executor + 'a)) -> *mut (dyn Executor + 'static) {
|
||||
use std::mem;
|
||||
mem::transmute(p)
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
#![deny(missing_docs, missing_debug_implementations, warnings)]
|
||||
#![doc(html_root_url = "https://docs.rs/tokio-executor/0.1.7")]
|
||||
#![deny(missing_docs, missing_debug_implementations)]
|
||||
#![doc(html_root_url = "https://docs.rs/tokio-executor/0.1.8")]
|
||||
|
||||
//! Task execution related traits and utilities.
|
||||
//!
|
||||
@@ -61,7 +61,7 @@ mod global;
|
||||
pub mod park;
|
||||
mod typed;
|
||||
|
||||
pub use enter::{enter, Enter, EnterError};
|
||||
pub use enter::{enter, exit, Enter, EnterError};
|
||||
pub use error::SpawnError;
|
||||
pub use executor::Executor;
|
||||
pub use global::{spawn, with_default, DefaultExecutor};
|
||||
|
||||
@@ -128,13 +128,13 @@ pub trait Unpark: Sync + Send + 'static {
|
||||
fn unpark(&self);
|
||||
}
|
||||
|
||||
impl Unpark for Box<Unpark> {
|
||||
impl Unpark for Box<dyn Unpark> {
|
||||
fn unpark(&self) {
|
||||
(**self).unpark()
|
||||
}
|
||||
}
|
||||
|
||||
impl Unpark for Arc<Unpark> {
|
||||
impl Unpark for Arc<dyn Unpark> {
|
||||
fn unpark(&self) {
|
||||
(**self).unpark()
|
||||
}
|
||||
|
||||
@@ -10,7 +10,7 @@ mod out_of_executor_context {
|
||||
|
||||
fn test<F, E>(spawn: F)
|
||||
where
|
||||
F: Fn(Box<Future<Item = (), Error = ()> + Send>) -> Result<(), E>,
|
||||
F: Fn(Box<dyn Future<Item = (), Error = ()> + Send>) -> Result<(), E>,
|
||||
{
|
||||
let res = spawn(Box::new(lazy(|| Ok(()))));
|
||||
assert!(res.is_err());
|
||||
|
||||
+2
-3
@@ -27,9 +27,8 @@ tokio-threadpool = "0.1.3"
|
||||
tokio-io = "0.1.6"
|
||||
|
||||
[dev-dependencies]
|
||||
rand = "0.6"
|
||||
tempfile = "3"
|
||||
tempdir = "0.3"
|
||||
rand = "0.7"
|
||||
tempfile = "~3.1.0"
|
||||
tokio-io = "0.1.6"
|
||||
tokio-codec = "0.1.0"
|
||||
tokio = "0.1.7"
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
//! Echo everything received on STDIN to STDOUT.
|
||||
#![deny(deprecated, warnings)]
|
||||
#![deny(deprecated)]
|
||||
|
||||
extern crate futures;
|
||||
extern crate tokio_codec;
|
||||
@@ -14,7 +14,7 @@ use futures::{Future, Sink, Stream};
|
||||
|
||||
use std::io;
|
||||
|
||||
pub fn main() -> Result<(), Box<std::error::Error>> {
|
||||
pub fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
let pool = Builder::new().pool_size(1).build();
|
||||
|
||||
pool.spawn({
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
#![deny(missing_docs, missing_debug_implementations, warnings)]
|
||||
#![deny(missing_docs, missing_debug_implementations)]
|
||||
#![doc(html_root_url = "https://docs.rs/tokio-fs/0.1.6")]
|
||||
|
||||
//! Asynchronous file and standard stream adaptation.
|
||||
|
||||
@@ -1,18 +1,18 @@
|
||||
extern crate futures;
|
||||
extern crate tempdir;
|
||||
extern crate tempfile;
|
||||
extern crate tokio_fs;
|
||||
|
||||
use futures::{Future, Stream};
|
||||
use std::fs;
|
||||
use std::sync::{Arc, Mutex};
|
||||
use tempdir::TempDir;
|
||||
use tempfile::tempdir;
|
||||
use tokio_fs::*;
|
||||
|
||||
mod pool;
|
||||
|
||||
#[test]
|
||||
fn create() {
|
||||
let base_dir = TempDir::new("base").unwrap();
|
||||
let base_dir = tempdir().unwrap();
|
||||
let new_dir = base_dir.path().join("foo");
|
||||
|
||||
pool::run({ create_dir(new_dir.clone()) });
|
||||
@@ -22,7 +22,7 @@ fn create() {
|
||||
|
||||
#[test]
|
||||
fn create_all() {
|
||||
let base_dir = TempDir::new("base").unwrap();
|
||||
let base_dir = tempdir().unwrap();
|
||||
let new_dir = base_dir.path().join("foo").join("bar");
|
||||
|
||||
pool::run({ create_dir_all(new_dir.clone()) });
|
||||
@@ -32,7 +32,7 @@ fn create_all() {
|
||||
|
||||
#[test]
|
||||
fn remove() {
|
||||
let base_dir = TempDir::new("base").unwrap();
|
||||
let base_dir = tempdir().unwrap();
|
||||
let new_dir = base_dir.path().join("foo");
|
||||
|
||||
fs::create_dir(new_dir.clone()).unwrap();
|
||||
@@ -44,7 +44,7 @@ fn remove() {
|
||||
|
||||
#[test]
|
||||
fn read() {
|
||||
let base_dir = TempDir::new("base").unwrap();
|
||||
let base_dir = tempdir().unwrap();
|
||||
|
||||
let p = base_dir.path();
|
||||
fs::create_dir(p.join("aa")).unwrap();
|
||||
|
||||
@@ -1,19 +1,19 @@
|
||||
extern crate futures;
|
||||
extern crate tempdir;
|
||||
extern crate tempfile;
|
||||
extern crate tokio_fs;
|
||||
|
||||
use futures::Future;
|
||||
use std::fs;
|
||||
use std::io::prelude::*;
|
||||
use std::io::BufReader;
|
||||
use tempdir::TempDir;
|
||||
use tempfile::tempdir;
|
||||
use tokio_fs::*;
|
||||
|
||||
mod pool;
|
||||
|
||||
#[test]
|
||||
fn test_hard_link() {
|
||||
let dir = TempDir::new("base").unwrap();
|
||||
let dir = tempdir().unwrap();
|
||||
let src = dir.path().join("src.txt");
|
||||
let dst = dir.path().join("dst.txt");
|
||||
|
||||
@@ -38,7 +38,7 @@ fn test_hard_link() {
|
||||
#[cfg(unix)]
|
||||
#[test]
|
||||
fn test_symlink() {
|
||||
let dir = TempDir::new("base").unwrap();
|
||||
let dir = tempdir().unwrap();
|
||||
let src = dir.path().join("src.txt");
|
||||
let dst = dir.path().join("dst.txt");
|
||||
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
#![feature(await_macro)]
|
||||
#![doc(html_root_url = "https://docs.rs/tokio-futures/0.1.0")]
|
||||
#![deny(missing_docs, missing_debug_implementations)]
|
||||
#![cfg_attr(test, deny(warnings))]
|
||||
|
||||
//! A preview of Tokio w/ `async` / `await` support.
|
||||
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
//! [`Stream`]: #
|
||||
//! [transports]: #
|
||||
|
||||
#![deny(missing_docs, missing_debug_implementations, warnings)]
|
||||
#![deny(missing_docs, missing_debug_implementations)]
|
||||
#![doc(hidden, html_root_url = "https://docs.rs/tokio-codec/0.1.0")]
|
||||
|
||||
// _tokio_codec are the items that belong in the `tokio_codec` crate. However, because we need to
|
||||
|
||||
+5
-5
@@ -1,4 +1,4 @@
|
||||
#![deny(missing_docs, missing_debug_implementations, warnings)]
|
||||
#![deny(missing_docs, missing_debug_implementations)]
|
||||
#![doc(html_root_url = "https://docs.rs/tokio-io/0.1.12")]
|
||||
|
||||
//! Core I/O traits and combinators when working with Tokio.
|
||||
@@ -21,10 +21,10 @@ use std::io as std_io;
|
||||
use futures::{Future, Stream};
|
||||
|
||||
/// A convenience typedef around a `Future` whose error component is `io::Error`
|
||||
pub type IoFuture<T> = Box<Future<Item = T, Error = std_io::Error> + Send>;
|
||||
pub type IoFuture<T> = Box<dyn Future<Item = T, Error = std_io::Error> + Send>;
|
||||
|
||||
/// A convenience typedef around a `Stream` whose error component is `io::Error`
|
||||
pub type IoStream<T> = Box<Stream<Item = T, Error = std_io::Error> + Send>;
|
||||
pub type IoStream<T> = Box<dyn Stream<Item = T, Error = std_io::Error> + Send>;
|
||||
|
||||
/// A convenience macro for working with `io::Result<T>` from the `Read` and
|
||||
/// `Write` traits.
|
||||
@@ -65,6 +65,6 @@ pub use self::async_write::AsyncWrite;
|
||||
|
||||
fn _assert_objects() {
|
||||
fn _assert<T>() {}
|
||||
_assert::<Box<AsyncRead>>();
|
||||
_assert::<Box<AsyncWrite>>();
|
||||
_assert::<Box<dyn AsyncRead>>();
|
||||
_assert::<Box<dyn AsyncWrite>>();
|
||||
}
|
||||
|
||||
@@ -1,3 +1,9 @@
|
||||
# 0.1.10 (September 25, 2019)
|
||||
|
||||
### Changed
|
||||
- Upgrade to parking_lot 0.9.0 (#1298 backport)
|
||||
- The minimum supported rust version (MSRV) is now 1.31.0. (#1358)
|
||||
|
||||
# 0.1.9 (March 1, 2019)
|
||||
|
||||
### Added
|
||||
|
||||
@@ -8,13 +8,13 @@ name = "tokio-reactor"
|
||||
# - README.md
|
||||
# - Update CHANGELOG.md.
|
||||
# - Create "v0.1.x" git tag.
|
||||
version = "0.1.9"
|
||||
version = "0.1.10"
|
||||
authors = ["Carl Lerche <[email protected]>"]
|
||||
license = "MIT"
|
||||
readme = "README.md"
|
||||
repository = "https://github.com/tokio-rs/tokio"
|
||||
homepage = "https://tokio.rs"
|
||||
documentation = "https://docs.rs/tokio-reactor/0.1.9/tokio_reactor"
|
||||
documentation = "https://docs.rs/tokio-reactor/0.1.10/tokio_reactor"
|
||||
description = """
|
||||
Event loop that drives Tokio I/O resources.
|
||||
"""
|
||||
@@ -27,7 +27,7 @@ lazy_static = "1.0.2"
|
||||
log = "0.4.1"
|
||||
mio = "0.6.14"
|
||||
num_cpus = "1.8.0"
|
||||
parking_lot = "0.7.0"
|
||||
parking_lot = "0.9.0"
|
||||
slab = "0.4.0"
|
||||
tokio-executor = "0.1.1"
|
||||
tokio-io = "0.1.6"
|
||||
@@ -36,4 +36,4 @@ tokio-sync = "0.1.1"
|
||||
[dev-dependencies]
|
||||
num_cpus = "1.8.0"
|
||||
tokio = "0.1.7"
|
||||
tokio-io-pool = "0.1.4"
|
||||
tokio-io-pool = "=0.1.4"
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
Event loop that drives Tokio I/O resources.
|
||||
|
||||
[Documentation](https://docs.rs/tokio-reactor/0.1.9/tokio_reactor)
|
||||
[Documentation](https://docs.rs/tokio-reactor/0.1.10/tokio_reactor)
|
||||
|
||||
## Overview
|
||||
|
||||
@@ -25,10 +25,10 @@ are building a custom I/O resource.
|
||||
|
||||
[`mio`]: http://github.com/carllerche/mio
|
||||
[`futures`]: http://github.com/rust-lang-nursery/futures-rs
|
||||
[`Reactor`]: https://docs.rs/tokio-reactor/0.1.9/tokio_reactor/struct.Reactor.html
|
||||
[`Handle`]: https://docs.rs/tokio-reactor/0.1.9/tokio_reactor/struct.Handle.html
|
||||
[`Registration`]: https://docs.rs/tokio-reactor/0.1.9/tokio_reactor/struct.Registration.html
|
||||
[`PollEvented`]: https://docs.rs/tokio-reactor/0.1.9/tokio_reactor/struct.PollEvented.html
|
||||
[`Reactor`]: https://docs.rs/tokio-reactor/0.1.10/tokio_reactor/struct.Reactor.html
|
||||
[`Handle`]: https://docs.rs/tokio-reactor/0.1.10/tokio_reactor/struct.Handle.html
|
||||
[`Registration`]: https://docs.rs/tokio-reactor/0.1.10/tokio_reactor/struct.Registration.html
|
||||
[`PollEvented`]: https://docs.rs/tokio-reactor/0.1.10/tokio_reactor/struct.PollEvented.html
|
||||
[`tokio`]: ../
|
||||
|
||||
## License
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
#![feature(test)]
|
||||
#![deny(warnings)]
|
||||
|
||||
extern crate futures;
|
||||
extern crate mio;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
#![doc(html_root_url = "https://docs.rs/tokio-reactor/0.1.9")]
|
||||
#![deny(missing_docs, warnings, missing_debug_implementations)]
|
||||
#![doc(html_root_url = "https://docs.rs/tokio-reactor/0.1.10")]
|
||||
#![deny(missing_docs, missing_debug_implementations)]
|
||||
|
||||
//! Event loop that drives Tokio I/O resources.
|
||||
//!
|
||||
@@ -631,7 +631,7 @@ impl HandlePriv {
|
||||
}
|
||||
|
||||
unsafe fn from_usize(val: usize) -> HandlePriv {
|
||||
let inner = mem::transmute::<usize, Weak<Inner>>(val);;
|
||||
let inner = mem::transmute::<usize, Weak<Inner>>(val);
|
||||
HandlePriv { inner }
|
||||
}
|
||||
|
||||
@@ -652,7 +652,7 @@ impl Inner {
|
||||
/// Register an I/O resource with the reactor.
|
||||
///
|
||||
/// The registration token is returned.
|
||||
fn add_source(&self, source: &Evented) -> io::Result<usize> {
|
||||
fn add_source(&self, source: &dyn Evented) -> io::Result<usize> {
|
||||
// Get an ABA guard value
|
||||
let aba_guard = self.next_aba_guard.fetch_add(1 << TOKEN_SHIFT, Relaxed);
|
||||
|
||||
@@ -690,7 +690,7 @@ impl Inner {
|
||||
}
|
||||
|
||||
/// Deregisters an I/O resource from the reactor.
|
||||
fn deregister_source(&self, source: &Evented) -> io::Result<()> {
|
||||
fn deregister_source(&self, source: &dyn Evented) -> io::Result<()> {
|
||||
self.io.deregister(source)
|
||||
}
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@ use futures::{Future, Stream};
|
||||
/// how many signals to handle before exiting
|
||||
const STOP_AFTER: u64 = 10;
|
||||
|
||||
fn main() -> Result<(), Box<std::error::Error>> {
|
||||
fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
// tokio_signal provides a convenience builder for Ctrl+C
|
||||
// this even works cross-platform: linux and windows!
|
||||
//
|
||||
|
||||
@@ -11,7 +11,7 @@ mod platform {
|
||||
use futures::{Future, Stream};
|
||||
use tokio_signal::unix::{Signal, SIGINT, SIGTERM};
|
||||
|
||||
pub fn main() -> Result<(), Box<::std::error::Error>> {
|
||||
pub fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
// Create a stream for each of the signals we'd like to handle.
|
||||
let sigint = Signal::new(SIGINT).flatten_stream();
|
||||
let sigterm = Signal::new(SIGTERM).flatten_stream();
|
||||
@@ -39,7 +39,6 @@ mod platform {
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#[cfg(not(unix))]
|
||||
@@ -49,6 +48,6 @@ mod platform {
|
||||
}
|
||||
}
|
||||
|
||||
fn main() -> Result<(), Box<std::error::Error>> {
|
||||
fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
platform::main()
|
||||
}
|
||||
|
||||
@@ -9,7 +9,7 @@ mod platform {
|
||||
use futures::{Future, Stream};
|
||||
use tokio_signal::unix::{Signal, SIGHUP};
|
||||
|
||||
pub fn main() -> Result<(), Box<::std::error::Error>> {
|
||||
pub fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
// on Unix, we can listen to whatever signal we want, in this case: SIGHUP
|
||||
let stream = Signal::new(SIGHUP).flatten_stream();
|
||||
|
||||
@@ -38,7 +38,6 @@ mod platform {
|
||||
::tokio::runtime::current_thread::block_on_all(future)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#[cfg(not(unix))]
|
||||
@@ -48,6 +47,6 @@ mod platform {
|
||||
}
|
||||
}
|
||||
|
||||
fn main() -> Result<(), Box<std::error::Error>> {
|
||||
fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
platform::main()
|
||||
}
|
||||
|
||||
@@ -86,9 +86,9 @@ pub mod unix;
|
||||
pub mod windows;
|
||||
|
||||
/// A future whose error is `io::Error`
|
||||
pub type IoFuture<T> = Box<Future<Item = T, Error = io::Error> + Send>;
|
||||
pub type IoFuture<T> = Box<dyn Future<Item = T, Error = io::Error> + Send>;
|
||||
/// A stream whose error is `io::Error`
|
||||
pub type IoStream<T> = Box<Stream<Item = T, Error = io::Error> + Send>;
|
||||
pub type IoStream<T> = Box<dyn Stream<Item = T, Error = io::Error> + Send>;
|
||||
|
||||
/// Creates a stream which receives "ctrl-c" notifications sent to a process.
|
||||
///
|
||||
@@ -125,7 +125,7 @@ pub fn ctrl_c_handle(handle: &Handle) -> IoFuture<IoStream<()>> {
|
||||
let handle = handle.clone();
|
||||
Box::new(future::lazy(move || {
|
||||
unix::Signal::with_handle(unix::libc::SIGINT, &handle)
|
||||
.map(|x| Box::new(x.map(|_| ())) as Box<Stream<Item = _, Error = _> + Send>)
|
||||
.map(|x| Box::new(x.map(|_| ())) as Box<dyn Stream<Item = _, Error = _> + Send>)
|
||||
}))
|
||||
}
|
||||
|
||||
|
||||
@@ -1,3 +1,8 @@
|
||||
# 0.1.7 (October 10, 2019)
|
||||
|
||||
### Fixed
|
||||
- memory leak when polling oneshot handle from more than one task (#1649).
|
||||
|
||||
# 0.1.6 (June 4, 2019)
|
||||
|
||||
### Added
|
||||
|
||||
@@ -8,12 +8,12 @@ name = "tokio-sync"
|
||||
# - README.md
|
||||
# - Update CHANGELOG.md.
|
||||
# - Create "v0.1.x" git tag.
|
||||
version = "0.1.6"
|
||||
version = "0.1.7"
|
||||
authors = ["Carl Lerche <[email protected]>"]
|
||||
license = "MIT"
|
||||
repository = "https://github.com/tokio-rs/tokio"
|
||||
homepage = "https://tokio.rs"
|
||||
documentation = "https://docs.rs/tokio-sync/0.1.6/tokio_sync"
|
||||
documentation = "https://docs.rs/tokio-sync/0.1.7/tokio_sync"
|
||||
description = """
|
||||
Synchronization utilities.
|
||||
"""
|
||||
@@ -24,7 +24,7 @@ fnv = "1.0.6"
|
||||
futures = "0.1.19"
|
||||
|
||||
[dev-dependencies]
|
||||
env_logger = { version = "0.5", default-features = false }
|
||||
env_logger = { version = "0.6", default-features = false }
|
||||
tokio = { version = "0.1.15", path = "../tokio" }
|
||||
tokio-mock-task = "0.1.1"
|
||||
loom = { version = "0.1.1", features = ["futures"] }
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
#![feature(test)]
|
||||
#![cfg_attr(test, deny(warnings))]
|
||||
|
||||
extern crate futures;
|
||||
extern crate test;
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
#![feature(test)]
|
||||
#![cfg_attr(test, deny(warnings))]
|
||||
|
||||
extern crate futures;
|
||||
extern crate test;
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
#![doc(html_root_url = "https://docs.rs/tokio-sync/0.1.6")]
|
||||
#![doc(html_root_url = "https://docs.rs/tokio-sync/0.1.7")]
|
||||
#![deny(missing_debug_implementations, missing_docs, unreachable_pub)]
|
||||
#![cfg_attr(test, deny(warnings))]
|
||||
|
||||
//! Asynchronous synchronization primitives.
|
||||
//!
|
||||
|
||||
@@ -198,6 +198,8 @@ impl<T> Sender<T> {
|
||||
state = State::unset_tx_task(&inner.state);
|
||||
|
||||
if state.is_closed() {
|
||||
// Set the flag again so that the waker is released in drop
|
||||
State::set_tx_task(&inner.state);
|
||||
return Ok(Async::Ready(()));
|
||||
} else {
|
||||
unsafe { inner.drop_tx_task() };
|
||||
@@ -363,6 +365,9 @@ impl<T> Inner<T> {
|
||||
// Unset the task
|
||||
state = State::unset_rx_task(&self.state);
|
||||
if state.is_complete() {
|
||||
// Set the flag again so that the waker is released in drop
|
||||
State::set_rx_task(&self.state);
|
||||
|
||||
return match unsafe { self.consume_value() } {
|
||||
Some(value) => Ok(Ready(value)),
|
||||
None => Err(RecvError(())),
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
#![deny(warnings)]
|
||||
|
||||
extern crate futures;
|
||||
extern crate tokio_mock_task;
|
||||
extern crate tokio_sync;
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
#![deny(warnings)]
|
||||
|
||||
extern crate tokio_sync;
|
||||
|
||||
fn is_error<T: ::std::error::Error + Send + Sync>() {}
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
#![deny(warnings)]
|
||||
|
||||
extern crate futures;
|
||||
#[macro_use]
|
||||
extern crate loom;
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
#![deny(warnings)]
|
||||
|
||||
extern crate futures;
|
||||
extern crate loom;
|
||||
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
#![deny(warnings)]
|
||||
|
||||
#[macro_use]
|
||||
extern crate futures;
|
||||
#[macro_use]
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
#![deny(warnings)]
|
||||
|
||||
extern crate futures;
|
||||
extern crate tokio_mock_task;
|
||||
extern crate tokio_sync;
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
#![deny(warnings)]
|
||||
|
||||
extern crate futures;
|
||||
extern crate tokio_mock_task;
|
||||
extern crate tokio_sync;
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
#![deny(warnings)]
|
||||
|
||||
extern crate futures;
|
||||
extern crate tokio_mock_task;
|
||||
extern crate tokio_sync;
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
#![deny(warnings)]
|
||||
|
||||
extern crate futures;
|
||||
extern crate tokio_mock_task;
|
||||
extern crate tokio_sync;
|
||||
|
||||
@@ -28,6 +28,6 @@ iovec = "0.1"
|
||||
futures = "0.1.19"
|
||||
|
||||
[dev-dependencies]
|
||||
env_logger = { version = "0.5", default-features = false }
|
||||
env_logger = { version = "0.6", default-features = false }
|
||||
net2 = "0.2"
|
||||
tokio = "0.1.13"
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
#![doc(html_root_url = "https://docs.rs/tokio-tcp/0.1.3")]
|
||||
#![deny(missing_docs, warnings, missing_debug_implementations)]
|
||||
#![deny(missing_docs, missing_debug_implementations)]
|
||||
|
||||
//! TCP bindings for `tokio`.
|
||||
//!
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
#![doc(html_root_url = "https://docs.rs/tokio-test/0.1.0")]
|
||||
#![deny(missing_docs, missing_debug_implementations, unreachable_pub)]
|
||||
#![cfg_attr(test, deny(warnings))]
|
||||
|
||||
//! Tokio and Futures based testing utilites
|
||||
//!
|
||||
|
||||
@@ -127,5 +127,4 @@ mod tests {
|
||||
let mut fut = future::ok::<(), ()>(());
|
||||
assert_ready_eq!(fut.poll(), ());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,3 +1,16 @@
|
||||
# 0.1.16 (September 25, 2019)
|
||||
|
||||
### Changed
|
||||
- Remove last non-dev dependency on rand crate by seeding PRNG via libstd
|
||||
`RandomState` (#1324 backport)
|
||||
- Upgrade (dev-only dependency) rand to 0.7.0 (#1302 backport)
|
||||
- The minimum supported rust version (MSRV) is now 1.31.0 (#1358)
|
||||
|
||||
# 0.1.15 (June 2, 2019)
|
||||
|
||||
### Changed
|
||||
- Allow other executors inside `threadpool::blocking` (#1155).
|
||||
|
||||
# 0.1.14 (April 22, 2019)
|
||||
|
||||
### Added
|
||||
|
||||
@@ -8,8 +8,8 @@ name = "tokio-threadpool"
|
||||
# - README.md
|
||||
# - Update CHANGELOG.md.
|
||||
# - Create "v0.1.x" git tag.
|
||||
version = "0.1.14"
|
||||
documentation = "https://docs.rs/tokio-threadpool/0.1.14/tokio_threadpool"
|
||||
version = "0.1.16"
|
||||
documentation = "https://docs.rs/tokio-threadpool/0.1.16/tokio_threadpool"
|
||||
repository = "https://github.com/tokio-rs/tokio"
|
||||
homepage = "https://github.com/tokio-rs/tokio"
|
||||
license = "MIT"
|
||||
@@ -21,18 +21,19 @@ keywords = ["futures", "tokio"]
|
||||
categories = ["concurrency", "asynchronous"]
|
||||
|
||||
[dependencies]
|
||||
tokio-executor = "0.1.7"
|
||||
tokio-executor = "0.1.8"
|
||||
futures = "0.1.19"
|
||||
crossbeam-deque = "0.7.0"
|
||||
crossbeam-queue = "0.1.0"
|
||||
crossbeam-utils = "0.6.4"
|
||||
num_cpus = "1.2"
|
||||
rand = "0.6"
|
||||
slab = "0.4.1"
|
||||
log = "0.4"
|
||||
lazy_static = "1"
|
||||
|
||||
[dev-dependencies]
|
||||
env_logger = "0.5"
|
||||
rand = "0.7"
|
||||
env_logger = { version = "0.6", default-features = false }
|
||||
|
||||
# For comparison benchmarks
|
||||
futures-cpupool = "0.1.7"
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
A library for scheduling execution of futures concurrently across a pool of
|
||||
threads.
|
||||
|
||||
[Documentation](https://docs.rs/tokio-threadpool/0.1.14/tokio_threadpool)
|
||||
[Documentation](https://docs.rs/tokio-threadpool/0.1.16/tokio_threadpool)
|
||||
|
||||
### Why not Rayon?
|
||||
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
#![feature(test)]
|
||||
#![deny(warnings)]
|
||||
|
||||
extern crate futures;
|
||||
extern crate futures_cpupool;
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
#![feature(test)]
|
||||
#![deny(warnings)]
|
||||
|
||||
extern crate futures;
|
||||
extern crate rand;
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
#![feature(test)]
|
||||
#![deny(warnings)]
|
||||
|
||||
extern crate futures;
|
||||
extern crate futures_cpupool;
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
use worker::Worker;
|
||||
|
||||
use futures::Poll;
|
||||
use tokio_executor;
|
||||
|
||||
use std::error::Error;
|
||||
use std::fmt;
|
||||
@@ -142,8 +143,11 @@ where
|
||||
// If the transition cannot happen, exit early
|
||||
try_ready!(res);
|
||||
|
||||
// Currently in blocking mode, so call the inner closure
|
||||
let ret = f();
|
||||
// Currently in blocking mode, so call the inner closure.
|
||||
//
|
||||
// "Exit" the current executor in case the blocking function wants
|
||||
// to call a different executor.
|
||||
let ret = tokio_executor::exit(move || f());
|
||||
|
||||
// Try to transition out of blocking mode. This is a fast path that takes
|
||||
// back ownership of the worker if the worker handoff didn't complete yet.
|
||||
|
||||
@@ -67,7 +67,7 @@ pub struct Builder {
|
||||
max_blocking: usize,
|
||||
|
||||
/// Generates the `Park` instances
|
||||
new_park: Box<Fn(&WorkerId) -> BoxPark>,
|
||||
new_park: Box<dyn Fn(&WorkerId) -> BoxPark>,
|
||||
}
|
||||
|
||||
impl Builder {
|
||||
@@ -223,7 +223,7 @@ impl Builder {
|
||||
/// ```
|
||||
pub fn panic_handler<F>(&mut self, f: F) -> &mut Self
|
||||
where
|
||||
F: Fn(Box<Any + Send>) + Send + Sync + 'static,
|
||||
F: Fn(Box<dyn Any + Send>) + Send + Sync + 'static,
|
||||
{
|
||||
self.config.panic_handler = Some(Arc::new(f));
|
||||
self
|
||||
|
||||
@@ -7,7 +7,7 @@ use tokio_executor::Enter;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub(crate) struct Callback {
|
||||
f: Arc<Fn(&Worker, &mut Enter) + Send + Sync>,
|
||||
f: Arc<dyn Fn(&Worker, &mut Enter) + Send + Sync>,
|
||||
}
|
||||
|
||||
impl Callback {
|
||||
|
||||
@@ -13,9 +13,9 @@ pub(crate) struct Config {
|
||||
pub name_prefix: Option<String>,
|
||||
pub stack_size: Option<usize>,
|
||||
pub around_worker: Option<Callback>,
|
||||
pub after_start: Option<Arc<Fn() + Send + Sync>>,
|
||||
pub before_stop: Option<Arc<Fn() + Send + Sync>>,
|
||||
pub panic_handler: Option<Arc<Fn(Box<Any + Send>) + Send + Sync>>,
|
||||
pub after_start: Option<Arc<dyn Fn() + Send + Sync>>,
|
||||
pub before_stop: Option<Arc<dyn Fn() + Send + Sync>>,
|
||||
pub panic_handler: Option<Arc<dyn Fn(Box<dyn Any + Send>) + Send + Sync>>,
|
||||
}
|
||||
|
||||
/// Max number of workers that can be part of a pool. This is the most that can
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
#![doc(html_root_url = "https://docs.rs/tokio-threadpool/0.1.14")]
|
||||
#![deny(warnings, missing_docs, missing_debug_implementations)]
|
||||
#![doc(html_root_url = "https://docs.rs/tokio-threadpool/0.1.16")]
|
||||
#![deny(missing_docs, missing_debug_implementations)]
|
||||
|
||||
//! A work-stealing based thread pool for executing futures.
|
||||
//!
|
||||
@@ -84,8 +84,9 @@ extern crate crossbeam_queue;
|
||||
extern crate crossbeam_utils;
|
||||
#[macro_use]
|
||||
extern crate futures;
|
||||
#[macro_use]
|
||||
extern crate lazy_static;
|
||||
extern crate num_cpus;
|
||||
extern crate rand;
|
||||
extern crate slab;
|
||||
|
||||
#[macro_use]
|
||||
|
||||
@@ -3,8 +3,8 @@ use tokio_executor::park::{Park, Unpark};
|
||||
use std::error::Error;
|
||||
use std::time::Duration;
|
||||
|
||||
pub(crate) type BoxPark = Box<Park<Unpark = BoxUnpark, Error = ()> + Send>;
|
||||
pub(crate) type BoxUnpark = Box<Unpark>;
|
||||
pub(crate) type BoxPark = Box<dyn Park<Unpark = BoxUnpark, Error = ()> + Send>;
|
||||
pub(crate) type BoxUnpark = Box<dyn Unpark>;
|
||||
|
||||
pub(crate) struct BoxedPark<T>(T);
|
||||
|
||||
|
||||
@@ -17,6 +17,8 @@ use worker::{self, Worker, WorkerId};
|
||||
use futures::Poll;
|
||||
|
||||
use std::cell::Cell;
|
||||
use std::collections::hash_map::RandomState;
|
||||
use std::hash::{BuildHasher, Hash, Hasher};
|
||||
use std::num::Wrapping;
|
||||
use std::sync::atomic::AtomicUsize;
|
||||
use std::sync::atomic::Ordering::{AcqRel, Acquire};
|
||||
@@ -25,7 +27,6 @@ use std::thread;
|
||||
|
||||
use crossbeam_deque::Injector;
|
||||
use crossbeam_utils::CachePadded;
|
||||
use rand;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub(crate) struct Pool {
|
||||
@@ -420,11 +421,7 @@ impl Pool {
|
||||
/// Uses a thread-local random number generator based on XorShift.
|
||||
pub fn rand_usize(&self) -> usize {
|
||||
thread_local! {
|
||||
static RNG: Cell<Wrapping<u32>> = {
|
||||
// The initial seed must be non-zero.
|
||||
let init = rand::random::<u32>() | 1;
|
||||
Cell::new(Wrapping(init))
|
||||
}
|
||||
static RNG: Cell<Wrapping<u32>> = Cell::new(Wrapping(prng_seed()));
|
||||
}
|
||||
|
||||
RNG.with(|rng| {
|
||||
@@ -448,3 +445,31 @@ impl PartialEq for Pool {
|
||||
|
||||
unsafe impl Send for Pool {}
|
||||
unsafe impl Sync for Pool {}
|
||||
|
||||
// Return a thread-specific, 32-bit, non-zero seed value suitable for a 32-bit
|
||||
// PRNG. This uses one libstd RandomState for a default hasher and hashes on
|
||||
// the current thread ID to obtain an unpredictable, collision resistant seed.
|
||||
fn prng_seed() -> u32 {
|
||||
// This obtains a small number of random bytes from the host system (for
|
||||
// example, on unix via getrandom(2)) in order to seed an unpredictable and
|
||||
// HashDoS resistant 64-bit hash function (currently: `SipHasher13` with
|
||||
// 128-bit state). We only need one of these, to make the seeds for all
|
||||
// process threads different via hashed IDs, collision resistant, and
|
||||
// unpredictable.
|
||||
lazy_static! {
|
||||
static ref RND_STATE: RandomState = RandomState::new();
|
||||
}
|
||||
|
||||
// Hash the current thread ID to produce a u32 value
|
||||
let mut hasher = RND_STATE.build_hasher();
|
||||
thread::current().id().hash(&mut hasher);
|
||||
let hash: u64 = hasher.finish();
|
||||
let seed = (hash as u32) ^ ((hash >> 32) as u32);
|
||||
|
||||
// Ensure non-zero seed (Xorshift yields only zero's for that seed)
|
||||
if seed == 0 {
|
||||
0x9b4e_6d25 // misc bits, could be any non-zero
|
||||
} else {
|
||||
seed
|
||||
}
|
||||
}
|
||||
|
||||
@@ -131,7 +131,7 @@ impl tokio_executor::Executor for Sender {
|
||||
|
||||
fn spawn(
|
||||
&mut self,
|
||||
future: Box<Future<Item = (), Error = ()> + Send>,
|
||||
future: Box<dyn Future<Item = (), Error = ()> + Send>,
|
||||
) -> Result<(), SpawnError> {
|
||||
let mut s = &*self;
|
||||
tokio_executor::Executor::spawn(&mut s, future)
|
||||
@@ -157,7 +157,7 @@ impl<'a> tokio_executor::Executor for &'a Sender {
|
||||
|
||||
fn spawn(
|
||||
&mut self,
|
||||
future: Box<Future<Item = (), Error = ()> + Send>,
|
||||
future: Box<dyn Future<Item = (), Error = ()> + Send>,
|
||||
) -> Result<(), SpawnError> {
|
||||
self.prepare_for_spawn()?;
|
||||
|
||||
|
||||
@@ -60,7 +60,7 @@ pub(crate) enum Run {
|
||||
Complete,
|
||||
}
|
||||
|
||||
type BoxFuture = Box<Future<Item = (), Error = ()> + Send + 'static>;
|
||||
type BoxFuture = Box<dyn Future<Item = (), Error = ()> + Send + 'static>;
|
||||
|
||||
// ===== impl Task =====
|
||||
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
extern crate tokio_executor;
|
||||
extern crate tokio_threadpool;
|
||||
|
||||
extern crate env_logger;
|
||||
@@ -44,6 +45,28 @@ fn basic() {
|
||||
rx2.recv().unwrap();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn other_executors_can_run_inside_blocking() {
|
||||
let _ = ::env_logger::try_init();
|
||||
|
||||
let pool = Builder::new().pool_size(1).max_blocking(1).build();
|
||||
|
||||
let (tx, rx) = mpsc::channel();
|
||||
|
||||
pool.spawn(lazy(move || {
|
||||
let res = blocking(|| {
|
||||
let _e = tokio_executor::enter().expect("nested blocking enter");
|
||||
tx.send(()).unwrap();
|
||||
})
|
||||
.unwrap();
|
||||
|
||||
assert!(res.is_ready());
|
||||
Ok(().into())
|
||||
}));
|
||||
|
||||
rx.recv().unwrap();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn notify_task_on_capacity() {
|
||||
const BLOCKING: usize = 10;
|
||||
|
||||
@@ -18,7 +18,9 @@ use std::time::Duration;
|
||||
|
||||
thread_local!(static FOO: Cell<u32> = Cell::new(0));
|
||||
|
||||
fn ignore_results<F: Future + Send + 'static>(f: F) -> Box<Future<Item = (), Error = ()> + Send> {
|
||||
fn ignore_results<F: Future + Send + 'static>(
|
||||
f: F,
|
||||
) -> Box<dyn Future<Item = (), Error = ()> + Send> {
|
||||
Box::new(f.map(|_| ()).map_err(|_| ()))
|
||||
}
|
||||
|
||||
|
||||
@@ -28,6 +28,6 @@ crossbeam-utils = "0.6.0"
|
||||
slab = "0.4.1"
|
||||
|
||||
[dev-dependencies]
|
||||
rand = "0.6"
|
||||
rand = "0.7"
|
||||
tokio-mock-task = "0.1.0"
|
||||
tokio = "0.1.7"
|
||||
|
||||
@@ -17,7 +17,7 @@ use std::time::Instant;
|
||||
/// [`Instant::now`]: https://doc.rust-lang.org/std/time/struct.Instant.html#method.now
|
||||
#[derive(Default, Clone)]
|
||||
pub struct Clock {
|
||||
now: Option<Arc<Now>>,
|
||||
now: Option<Arc<dyn Now>>,
|
||||
}
|
||||
|
||||
thread_local! {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
#![doc(html_root_url = "https://docs.rs/tokio-timer/0.2.11")]
|
||||
#![deny(missing_docs, warnings, missing_debug_implementations)]
|
||||
#![deny(missing_docs, missing_debug_implementations)]
|
||||
|
||||
//! Utilities for tracking time.
|
||||
//!
|
||||
|
||||
@@ -158,7 +158,7 @@ impl<T: StdError + 'static> StdError for ThrottleError<T> {
|
||||
// FIXME(taiki-e): When the minimum support version of tokio reaches Rust 1.30,
|
||||
// replace this with Error::source.
|
||||
#[allow(deprecated)]
|
||||
fn cause(&self) -> Option<&StdError> {
|
||||
fn cause(&self) -> Option<&dyn StdError> {
|
||||
match self.0 {
|
||||
Either::A(ref err) => Some(err),
|
||||
Either::B(ref err) => Some(err),
|
||||
|
||||
@@ -162,7 +162,7 @@ pub(crate) struct Inner {
|
||||
process: AtomicStack,
|
||||
|
||||
/// Unparks the timer thread.
|
||||
unpark: Box<Unpark>,
|
||||
unpark: Box<dyn Unpark>,
|
||||
}
|
||||
|
||||
/// Maximum number of timeouts the system can handle concurrently.
|
||||
@@ -426,7 +426,7 @@ impl<T, N> Drop for Timer<T, N> {
|
||||
// ===== impl Inner =====
|
||||
|
||||
impl Inner {
|
||||
fn new(start: Instant, unpark: Box<Unpark>) -> Inner {
|
||||
fn new(start: Instant, unpark: Box<dyn Unpark>) -> Inner {
|
||||
Inner {
|
||||
num: AtomicUsize::new(0),
|
||||
elapsed: AtomicU64::new(0),
|
||||
|
||||
@@ -31,7 +31,7 @@ tokio-io = "0.1.7"
|
||||
[dev-dependencies]
|
||||
tokio = "0.1"
|
||||
cfg-if = "0.1"
|
||||
env_logger = { version = "0.5", default-features = false }
|
||||
env_logger = { version = "0.6", default-features = false }
|
||||
|
||||
[target.'cfg(all(not(target_os = "macos"), not(windows), not(target_os = "ios")))'.dev-dependencies]
|
||||
openssl = "0.10"
|
||||
|
||||
@@ -12,7 +12,7 @@ use native_tls::TlsConnector;
|
||||
use tokio::net::TcpStream;
|
||||
use tokio::runtime::Runtime;
|
||||
|
||||
fn main() -> Result<(), Box<std::error::Error>> {
|
||||
fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
let mut runtime = Runtime::new()?;
|
||||
let addr = "www.rust-lang.org:443"
|
||||
.to_socket_addrs()?
|
||||
|
||||
@@ -8,7 +8,7 @@ use tokio::io;
|
||||
use tokio::net::TcpListener;
|
||||
use tokio::prelude::*;
|
||||
|
||||
fn main() -> Result<(), Box<std::error::Error>> {
|
||||
fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
// Bind the server's socket
|
||||
let addr = "127.0.0.1:12345".parse()?;
|
||||
let tcp = TcpListener::bind(&addr)?;
|
||||
|
||||
@@ -1,3 +0,0 @@
|
||||
# 0.1.0 (April 22, 2019)
|
||||
|
||||
- Initial release
|
||||
@@ -1,53 +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"
|
||||
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 = "0.2"
|
||||
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"
|
||||
# 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 = []
|
||||
|
||||
@@ -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,171 +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.enter(|| {
|
||||
// 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)).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 = 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.
|
||||
|
||||
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;
|
||||
|
||||
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. Alternatively, a single subscriber may be
|
||||
constructed by the `main` function and all subsequent code executed 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.
|
||||
|
||||
[`log`]: https://docs.rs/log/0.4.6/log/
|
||||
[`tokio-trace-nursery`]: https://github.com/tokio-rs/tokio-trace-nursery
|
||||
|
||||
## 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,46 +0,0 @@
|
||||
#![feature(test)]
|
||||
#[macro_use]
|
||||
extern crate tokio_trace;
|
||||
#[macro_use]
|
||||
extern crate log;
|
||||
extern crate test;
|
||||
use test::Bencher;
|
||||
use tokio_trace::Level;
|
||||
|
||||
#[bench]
|
||||
fn bench_span_no_subscriber(b: &mut Bencher) {
|
||||
b.iter(|| {
|
||||
span!(Level::TRACE, "span");
|
||||
});
|
||||
}
|
||||
|
||||
#[bench]
|
||||
fn bench_log_no_logger(b: &mut Bencher) {
|
||||
b.iter(|| {
|
||||
log!(log::Level::Info, "log");
|
||||
});
|
||||
}
|
||||
|
||||
#[bench]
|
||||
fn bench_costly_field_no_subscriber(b: &mut Bencher) {
|
||||
b.iter(|| {
|
||||
span!(
|
||||
Level::TRACE,
|
||||
"span",
|
||||
foo = tokio_trace::field::display(format!("bar {:?}", 2))
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
#[bench]
|
||||
fn bench_no_span_no_subscriber(b: &mut Bencher) {
|
||||
b.iter(|| {});
|
||||
}
|
||||
|
||||
#[bench]
|
||||
fn bench_1_atomic_load(b: &mut Bencher) {
|
||||
// This is just included as a baseline.
|
||||
use std::sync::atomic::{AtomicUsize, Ordering};
|
||||
let foo = AtomicUsize::new(1);
|
||||
b.iter(|| foo.load(Ordering::Relaxed));
|
||||
}
|
||||
@@ -1,158 +0,0 @@
|
||||
#![feature(test)]
|
||||
|
||||
#[macro_use]
|
||||
extern crate tokio_trace;
|
||||
extern crate test;
|
||||
use test::Bencher;
|
||||
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;
|
||||
|
||||
#[bench]
|
||||
fn span_no_fields(b: &mut Bencher) {
|
||||
tokio_trace::subscriber::with_default(EnabledSubscriber, || {
|
||||
b.iter(|| span!(Level::TRACE, "span"))
|
||||
});
|
||||
}
|
||||
|
||||
#[bench]
|
||||
fn enter_span(b: &mut Bencher) {
|
||||
tokio_trace::subscriber::with_default(EnabledSubscriber, || {
|
||||
let span = span!(Level::TRACE, "span");
|
||||
b.iter(|| test::black_box(span.enter(|| {})))
|
||||
});
|
||||
}
|
||||
|
||||
#[bench]
|
||||
fn span_repeatedly(b: &mut Bencher) {
|
||||
#[inline]
|
||||
fn mk_span(i: u64) -> tokio_trace::Span {
|
||||
span!(Level::TRACE, "span", i = i)
|
||||
}
|
||||
|
||||
let n = test::black_box(N_SPANS);
|
||||
tokio_trace::subscriber::with_default(EnabledSubscriber, || {
|
||||
b.iter(|| (0..n).fold(mk_span(0), |_, i| mk_span(i as u64)))
|
||||
});
|
||||
}
|
||||
|
||||
#[bench]
|
||||
fn span_with_fields(b: &mut Bencher) {
|
||||
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)
|
||||
)
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
#[bench]
|
||||
fn span_with_fields_record(b: &mut Bencher) {
|
||||
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)
|
||||
)
|
||||
})
|
||||
});
|
||||
}
|
||||
@@ -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).enter(|| {
|
||||
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
|
||||
)
|
||||
.enter(|| {
|
||||
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)).enter(|| {
|
||||
span!(Level::TRACE, "server", host = "localhost", port = 8080).enter(|| {
|
||||
info!("starting");
|
||||
info!("listening");
|
||||
let peer1 = span!(Level::TRACE, "conn", peer_addr = "82.9.9.9", port = 42381);
|
||||
peer1.enter(|| {
|
||||
debug!("connected");
|
||||
debug!({ length = 2 }, "message received");
|
||||
});
|
||||
let peer2 = span!(Level::TRACE, "conn", peer_addr = "8.8.8.8", port = 18230);
|
||||
peer2.enter(|| {
|
||||
debug!("connected");
|
||||
});
|
||||
peer1.enter(|| {
|
||||
warn!({ algo = "xor" }, "weak encryption requested");
|
||||
debug!({ length = 8 }, "response sent");
|
||||
debug!("disconnected");
|
||||
});
|
||||
peer2.enter(|| {
|
||||
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,437 +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 `Event`s, `Span`s, and
|
||||
//! `Subscriber`s. We'll cover these in turn.
|
||||
//!
|
||||
//! ## `Span`s
|
||||
//!
|
||||
//! 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() {
|
||||
//! span!(Level::TRACE, "my_span").enter(|| {
|
||||
//! // perform some work in the context of `my_span`...
|
||||
//! });
|
||||
//! # }
|
||||
//!```
|
||||
//!
|
||||
//! 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").enter(|| {
|
||||
//! // since we are now inside "root", this span is considered a child
|
||||
//! // of "root":
|
||||
//! span!(Level::DEBUG, "outer_child").enter(|| {
|
||||
//! // this span is a child of "outer_child", which is in turn a
|
||||
//! // child of "root":
|
||||
//! span!(Level::TRACE, "inner_child").enter(|| {
|
||||
//! // and so on...
|
||||
//! });
|
||||
//! });
|
||||
//! });
|
||||
//! # }
|
||||
//!```
|
||||
//!
|
||||
//! In addition, data may be associated with spans. A span may have _fields_ —
|
||||
//! a set of key-value pairs describing the state of the program during that
|
||||
//! span; an optional name, and metadata describing the source code location
|
||||
//! where the span was originally entered.
|
||||
//! ```
|
||||
//! # #[macro_use] extern crate tokio_trace;
|
||||
//! # use tokio_trace::Level;
|
||||
//! # fn main() {
|
||||
//! // construct a new span with three fields:
|
||||
//! // - "foo", with a value of 42,
|
||||
//! // - "bar", with the value "false"
|
||||
//! // - "baz", with no initial value
|
||||
//! let my_span = span!(Level::INFO, "my_span", foo = 42, bar = false, baz);
|
||||
//!
|
||||
//! // record a value for the field "baz" declared above:
|
||||
//! my_span.record("baz", &"hello world");
|
||||
//! # }
|
||||
//!```
|
||||
//!
|
||||
//! ### 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;
|
||||
//! span!(Level::TRACE, "my loop").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 _ = i;
|
||||
//! span!(Level::TRACE, "my loop", iteration = i).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.
|
||||
//!
|
||||
//! ## 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").enter(|| {
|
||||
//! // 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");
|
||||
//! span.enter(|| {
|
||||
//! // 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.
|
||||
//! # }
|
||||
//! ```
|
||||
//!
|
||||
//! `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::{field, 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) {
|
||||
//! // Create a new span for this invocation of `shave_the_yak`, annotated
|
||||
//! // with the yak being shaved as a *field* on the span.
|
||||
//! span!(Level::TRACE, "shave_the_yak", yak = field::debug(&yak)).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 = 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.
|
||||
//!
|
||||
//! 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. Alternatively, a single subscriber
|
||||
//! may be constructed by the `main` function and all subsequent code executed
|
||||
//! 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/struct.Span
|
||||
//! [`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
|
||||
//! [`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,609 +0,0 @@
|
||||
//! Spans represent periods of time in the execution of a program.
|
||||
//!
|
||||
//! # 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`] method, which enters the target span,
|
||||
//! performs a given function (either a closure or a function pointer), exits
|
||||
//! the span, and then returns the result.
|
||||
//!
|
||||
//! Calling `enter` on a span handle enters the span that handle corresponds to,
|
||||
//! if the span exists:
|
||||
//! ```
|
||||
//! # #[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.enter(|| {
|
||||
//! // perform some work in the context of `my_span`...
|
||||
//! });
|
||||
//!
|
||||
//! // Perform some work outside of the context of `my_span`...
|
||||
//!
|
||||
//! my_span.enter(|| {
|
||||
//! // Perform some more work in the context of `my_span`.
|
||||
//! });
|
||||
//! # }
|
||||
//! ```
|
||||
//!
|
||||
//! # The Span Lifecycle
|
||||
//!
|
||||
//! 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> {
|
||||
//! 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 "close" 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").enter(|| {
|
||||
//! // 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.
|
||||
//!
|
||||
//! # Accessing a Span's Attributes
|
||||
//!
|
||||
//! The [`Attributes`] type represents a *non-entering* reference to a `Span`'s data
|
||||
//! — a set of key-value pairs (known as _fields_), a creation timestamp,
|
||||
//! a reference to the span's parent in the trace tree, and metadata describing
|
||||
//! the source code location where the span was created. This data is provided
|
||||
//! to the [`Subscriber`] when the span is created; it may then choose to cache
|
||||
//! the data for future use, record it in some manner, or discard it completely.
|
||||
//!
|
||||
//! [`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
|
||||
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.
|
||||
///
|
||||
/// This guard may be used to exit the span, returning an `Enter` to
|
||||
/// re-enter it.
|
||||
///
|
||||
/// This type is primarily used for implementing span handles; users should
|
||||
/// typically not need to interact with it directly.
|
||||
#[derive(Debug)]
|
||||
#[must_use = "once a span has been entered, it should be exited"]
|
||||
struct Entered<'a> {
|
||||
inner: &'a Inner,
|
||||
}
|
||||
|
||||
// ===== 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<I>(
|
||||
parent: I,
|
||||
meta: &'static Metadata<'static>,
|
||||
values: &field::ValueSet,
|
||||
) -> Span
|
||||
where
|
||||
I: AsId,
|
||||
{
|
||||
let new_span = match parent.as_id() {
|
||||
Some(parent) => Attributes::child_of(parent.clone(), meta, values),
|
||||
None => Attributes::new_root(meta, values),
|
||||
};
|
||||
Self::make(meta, new_span)
|
||||
}
|
||||
|
||||
/// 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
|
||||
}
|
||||
|
||||
/// Executes the given function in the context of this span.
|
||||
///
|
||||
/// If this span is enabled, then this function enters the span, invokes
|
||||
/// 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`.
|
||||
pub fn enter<F: FnOnce() -> T, T>(&self, f: F) -> T {
|
||||
self.log(format_args!("-> {}", self.meta.name));
|
||||
let _enter = self.inner.as_ref().map(Inner::enter);
|
||||
let result = f();
|
||||
self.log(format_args!("<- {}", self.meta.name));
|
||||
result
|
||||
}
|
||||
|
||||
/// 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<I>(&self, from: I) -> &Self
|
||||
where
|
||||
I: AsId,
|
||||
{
|
||||
if let Some(ref inner) = self.inner {
|
||||
if let Some(from) = from.as_id() {
|
||||
inner.follows_from(from);
|
||||
}
|
||||
}
|
||||
self
|
||||
}
|
||||
|
||||
/// 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 Inner =====
|
||||
|
||||
impl Inner {
|
||||
/// Enters the span, returning a guard that may be used to exit the span and
|
||||
/// re-enter the prior span.
|
||||
///
|
||||
/// This is used internally to implement `Span::enter`. It may be used for
|
||||
/// writing custom span handles, but should generally not be called directly
|
||||
/// when entering a span.
|
||||
#[inline]
|
||||
fn enter(&self) -> Entered {
|
||||
self.subscriber.enter(&self.id);
|
||||
Entered { inner: self }
|
||||
}
|
||||
|
||||
/// 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.
|
||||
self.inner.subscriber.exit(&self.inner.id);
|
||||
}
|
||||
}
|
||||
|
||||
struct FmtValues<'a>(&'a Record<'a>);
|
||||
|
||||
impl<'a> fmt::Display for FmtValues<'a> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
let mut res = Ok(());
|
||||
self.0.record(&mut |k: &field::Field, v: &fmt::Debug| {
|
||||
res = write!(f, "{}={:?} ", k, v);
|
||||
});
|
||||
res
|
||||
}
|
||||
}
|
||||
|
||||
struct FmtAttrs<'a>(&'a Attributes<'a>);
|
||||
|
||||
impl<'a> fmt::Display for FmtAttrs<'a> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
let mut res = Ok(());
|
||||
self.0.record(&mut |k: &field::Field, v: &fmt::Debug| {
|
||||
res = write!(f, "{}={:?} ", k, v);
|
||||
});
|
||||
res
|
||||
}
|
||||
}
|
||||
|
||||
// ===== impl AsId =====
|
||||
|
||||
impl ::sealed::Sealed for Span {}
|
||||
|
||||
impl AsId for Span {
|
||||
fn as_id(&self) -> Option<&Id> {
|
||||
self.inner.as_ref().map(|inner| &inner.id)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> ::sealed::Sealed for &'a Span {}
|
||||
|
||||
impl<'a> AsId for &'a Span {
|
||||
fn as_id(&self) -> Option<&Id> {
|
||||
self.inner.as_ref().map(|inner| &inner.id)
|
||||
}
|
||||
}
|
||||
|
||||
impl ::sealed::Sealed for Id {}
|
||||
|
||||
impl AsId for Id {
|
||||
fn as_id(&self) -> Option<&Id> {
|
||||
Some(self)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> ::sealed::Sealed for &'a Id {}
|
||||
|
||||
impl<'a> AsId for &'a Id {
|
||||
fn as_id(&self) -> Option<&Id> {
|
||||
Some(self)
|
||||
}
|
||||
}
|
||||
|
||||
impl ::sealed::Sealed for Option<Id> {}
|
||||
|
||||
impl AsId for Option<Id> {
|
||||
fn as_id(&self) -> Option<&Id> {
|
||||
self.as_ref()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> ::sealed::Sealed for &'a Option<Id> {}
|
||||
|
||||
impl<'a> AsId for &'a Option<Id> {
|
||||
fn as_id(&self) -> Option<&Id> {
|
||||
self.as_ref()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::*;
|
||||
|
||||
trait AssertSend: Send {}
|
||||
impl AssertSend for Span {}
|
||||
|
||||
trait AssertSync: Sync {}
|
||||
impl AssertSync for Span {}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user