Struct tokio_metrics::TaskMetrics

source ·
#[non_exhaustive]
pub struct TaskMetrics {
Show 18 fields pub instrumented_count: u64, pub dropped_count: u64, pub first_poll_count: u64, pub total_first_poll_delay: Duration, pub total_idled_count: u64, pub total_idle_duration: Duration, pub total_scheduled_count: u64, pub total_scheduled_duration: Duration, pub total_poll_count: u64, pub total_poll_duration: Duration, pub total_fast_poll_count: u64, pub total_fast_poll_duration: Duration, pub total_slow_poll_count: u64, pub total_slow_poll_duration: Duration, pub total_short_delay_count: u64, pub total_long_delay_count: u64, pub total_short_delay_duration: Duration, pub total_long_delay_duration: Duration,
}
Expand description

Key metrics of instrumented tasks.

Fields (Non-exhaustive)§

This struct is marked as non-exhaustive
Non-exhaustive structs could have additional fields added in future. Therefore, non-exhaustive structs cannot be constructed in external crates using the traditional Struct { .. } syntax; cannot be matched against without a wildcard ..; and struct update syntax will not work.
§instrumented_count: u64

The number of tasks instrumented.

§Examples
#[tokio::main]
async fn main() {
    let monitor = tokio_metrics::TaskMonitor::new();
    let mut interval = monitor.intervals();
    let mut next_interval = || interval.next().unwrap();

    // 0 tasks have been instrumented
    assert_eq!(next_interval().instrumented_count, 0);

    monitor.instrument(async {});

    // 1 task has been instrumented
    assert_eq!(next_interval().instrumented_count, 1);

    monitor.instrument(async {});
    monitor.instrument(async {});

    // 2 tasks have been instrumented
    assert_eq!(next_interval().instrumented_count, 2);

    // since the last interval was produced, 0 tasks have been instrumented
    assert_eq!(next_interval().instrumented_count, 0);
}
§dropped_count: u64

The number of tasks dropped.

§Examples
#[tokio::main]
async fn main() {
    let monitor = tokio_metrics::TaskMonitor::new();
    let mut interval = monitor.intervals();
    let mut next_interval = || interval.next().unwrap();

    // 0 tasks have been dropped
    assert_eq!(next_interval().dropped_count, 0);

    let _task = monitor.instrument(async {});

    // 0 tasks have been dropped
    assert_eq!(next_interval().dropped_count, 0);

    monitor.instrument(async {}).await;
    drop(monitor.instrument(async {}));

    // 2 tasks have been dropped
    assert_eq!(next_interval().dropped_count, 2);

    // since the last interval was produced, 0 tasks have been dropped
    assert_eq!(next_interval().dropped_count, 0);
}
§first_poll_count: u64

The number of tasks polled for the first time.

§Derived metrics
  • mean_first_poll_delay
    The mean duration elapsed between the instant tasks are instrumented, and the instant they are first polled.
§Examples

In the below example, no tasks are instrumented or polled in the first sampling interval; one task is instrumented (but not polled) in the second sampling interval; that task is awaited to completion (and, thus, polled at least once) in the third sampling interval; no additional tasks are polled for the first time within the fourth sampling interval:

#[tokio::main]
async fn main() {
    let metrics_monitor = tokio_metrics::TaskMonitor::new();
    let mut interval = metrics_monitor.intervals();
    let mut next_interval = || interval.next().unwrap();

    // no tasks have been constructed, instrumented, and polled at least once
    assert_eq!(next_interval().first_poll_count, 0);

    let task = metrics_monitor.instrument(async {});

    // `task` has been constructed and instrumented, but has not yet been polled
    assert_eq!(next_interval().first_poll_count, 0);

    // poll `task` to completion
    task.await;

    // `task` has been constructed, instrumented, and polled at least once
    assert_eq!(next_interval().first_poll_count, 1);

    // since the last interval was produced, 0 tasks have been constructed, instrumented and polled
    assert_eq!(next_interval().first_poll_count, 0);

}
§total_first_poll_delay: Duration

The total duration elapsed between the instant tasks are instrumented, and the instant they are first polled.

§Derived metrics
  • mean_first_poll_delay
    The mean duration elapsed between the instant tasks are instrumented, and the instant they are first polled.
§Examples

In the below example, 0 tasks have been instrumented or polled within the first sampling interval, a total of 500ms elapse between the instrumentation and polling of tasks within the second sampling interval, and a total of 350ms elapse between the instrumentation and polling of tasks within the third sampling interval:

use tokio::time::Duration;

#[tokio::main(flavor = "current_thread", start_paused = true)]
async fn main() {
    let monitor = tokio_metrics::TaskMonitor::new();
    let mut interval = monitor.intervals();
    let mut next_interval = || interval.next().unwrap();

    // no tasks have yet been created, instrumented, or polled
    assert_eq!(monitor.cumulative().total_first_poll_delay, Duration::ZERO);
    assert_eq!(next_interval().total_first_poll_delay, Duration::ZERO);

    // constructs and instruments a task, pauses a given duration, then awaits the task
    async fn instrument_pause_await(monitor: &tokio_metrics::TaskMonitor, pause: Duration) {
        let task = monitor.instrument(async move {});
        tokio::time::sleep(pause).await;
        task.await;
    }

    // construct and await a task that pauses for 500ms between instrumentation and first poll
    let task_a_pause_time = Duration::from_millis(500);
    instrument_pause_await(&monitor, task_a_pause_time).await;

    assert_eq!(next_interval().total_first_poll_delay, task_a_pause_time);
    assert_eq!(monitor.cumulative().total_first_poll_delay, task_a_pause_time);

    // construct and await a task that pauses for 250ms between instrumentation and first poll
    let task_b_pause_time = Duration::from_millis(250);
    instrument_pause_await(&monitor, task_b_pause_time).await;

    // construct and await a task that pauses for 100ms between instrumentation and first poll
    let task_c_pause_time = Duration::from_millis(100);
    instrument_pause_await(&monitor, task_c_pause_time).await;

    assert_eq!(
        next_interval().total_first_poll_delay,
        task_b_pause_time + task_c_pause_time
    );
    assert_eq!(
        monitor.cumulative().total_first_poll_delay,
        task_a_pause_time + task_b_pause_time + task_c_pause_time
    );
}
§When is this metric recorded?

The delay between instrumentation and first poll is not recorded until the first poll actually occurs:

// we construct and instrument a task, but do not `await` it
let task = monitor.instrument(async {});

// let's sleep for 1s before we poll `task`
let one_sec = Duration::from_secs(1);
let _ = tokio::time::sleep(one_sec).await;

// although 1s has now elapsed since the instrumentation of `task`,
// this is not reflected in `total_first_poll_delay`...
assert_eq!(next_interval().total_first_poll_delay, Duration::ZERO);
assert_eq!(monitor.cumulative().total_first_poll_delay, Duration::ZERO);

// ...and won't be until `task` is actually polled
task.await;

// now, the 1s delay is reflected in `total_first_poll_delay`:
assert_eq!(next_interval().total_first_poll_delay, one_sec);
assert_eq!(monitor.cumulative().total_first_poll_delay, one_sec);
§What if first-poll-delay is very large?

The first-poll-delay of individual tasks saturates at u64::MAX nanoseconds. However, if the total first-poll-delay across monitored tasks exceeds u64::MAX nanoseconds, this metric will wrap around:

use tokio::time::Duration;

#[tokio::main(flavor = "current_thread", start_paused = true)]
async fn main() {
    let monitor = tokio_metrics::TaskMonitor::new();

    // construct and instrument a task, but do not `await` it
    let task = monitor.instrument(async {});

    // this is the maximum duration representable by tokio_metrics
    let max_duration = Duration::from_nanos(u64::MAX);

    // let's advance the clock by double this amount and await `task`
    let _ = tokio::time::advance(max_duration * 2).await;
    task.await;

    // the time-to-first-poll of `task` saturates at `max_duration`
    assert_eq!(monitor.cumulative().total_first_poll_delay, max_duration);

    // ...but note that the metric *will* wrap around if more tasks are involved
    let task = monitor.instrument(async {});
    let _ = tokio::time::advance(Duration::from_nanos(1)).await;
    task.await;
    assert_eq!(monitor.cumulative().total_first_poll_delay, Duration::ZERO);
}
§total_idled_count: u64

The total number of times that tasks idled, waiting to be awoken.

An idle is recorded as occurring if a non-zero duration elapses between the instant a task completes a poll, and the instant that it is next awoken.

§Derived metrics
§Examples
#[tokio::main(flavor = "current_thread", start_paused = true)]
async fn main() {
    let monitor = tokio_metrics::TaskMonitor::new();
    let mut interval = monitor.intervals();
    let mut next_interval = move || interval.next().unwrap();
    let one_sec = std::time::Duration::from_secs(1);

    monitor.instrument(async {}).await;

    assert_eq!(next_interval().total_idled_count, 0);
    assert_eq!(monitor.cumulative().total_idled_count, 0);

    monitor.instrument(async move {
        tokio::time::sleep(one_sec).await;
    }).await;

    assert_eq!(next_interval().total_idled_count, 1);
    assert_eq!(monitor.cumulative().total_idled_count, 1);

    monitor.instrument(async {
        tokio::time::sleep(one_sec).await;
        tokio::time::sleep(one_sec).await;
    }).await;

    assert_eq!(next_interval().total_idled_count, 2);
    assert_eq!(monitor.cumulative().total_idled_count, 3);
}
§total_idle_duration: Duration

The total duration that tasks idled.

An idle is recorded as occurring if a non-zero duration elapses between the instant a task completes a poll, and the instant that it is next awoken.

§Derived metrics
§Examples
#[tokio::main(flavor = "current_thread", start_paused = true)]
async fn main() {
    let monitor = tokio_metrics::TaskMonitor::new();
    let mut interval = monitor.intervals();
    let mut next_interval = move || interval.next().unwrap();
    let one_sec = std::time::Duration::from_secs(1);
    let two_sec = std::time::Duration::from_secs(2);

    assert_eq!(next_interval().total_idle_duration.as_nanos(), 0);
    assert_eq!(monitor.cumulative().total_idle_duration.as_nanos(), 0);

    monitor.instrument(async move {
        tokio::time::sleep(one_sec).await;
    }).await;

    assert_eq!(next_interval().total_idle_duration, one_sec);
    assert_eq!(monitor.cumulative().total_idle_duration, one_sec);

    monitor.instrument(async move {
        tokio::time::sleep(two_sec).await;
    }).await;

    assert_eq!(next_interval().total_idle_duration, two_sec);
    assert_eq!(monitor.cumulative().total_idle_duration, one_sec + two_sec);
}
§total_scheduled_count: u64

The total number of times that tasks were awoken (and then, presumably, scheduled for execution).

§Definition

This metric is equal to total_short_delay_duration

§Derived metrics
§Examples

In the below example, a task yields to the scheduler a varying number of times between sampling intervals; this metric is equal to the number of times the task yielded:

#[tokio::main]
async fn main(){
    let metrics_monitor = tokio_metrics::TaskMonitor::new();

    // [A] no tasks have been created, instrumented, and polled more than once
    assert_eq!(metrics_monitor.cumulative().total_scheduled_count, 0);

    // [B] a `task` is created and instrumented
    let task = {
        let monitor = metrics_monitor.clone();
        metrics_monitor.instrument(async move {
            let mut interval = monitor.intervals();
            let mut next_interval = move || interval.next().unwrap();

            // [E] `task` has not yet yielded to the scheduler, and
            // thus has not yet been scheduled since its first `poll`
            assert_eq!(next_interval().total_scheduled_count, 0);

            tokio::task::yield_now().await; // yield to the scheduler

            // [F] `task` has yielded to the scheduler once (and thus been
            // scheduled once) since the last sampling interval
            assert_eq!(next_interval().total_scheduled_count, 1);

            tokio::task::yield_now().await; // yield to the scheduler
            tokio::task::yield_now().await; // yield to the scheduler
            tokio::task::yield_now().await; // yield to the scheduler

            // [G] `task` has yielded to the scheduler thrice (and thus been
            // scheduled thrice) since the last sampling interval
            assert_eq!(next_interval().total_scheduled_count, 3);

            tokio::task::yield_now().await; // yield to the scheduler

            next_interval
        })
    };

    // [C] `task` has not yet been polled at all
    assert_eq!(metrics_monitor.cumulative().first_poll_count, 0);
    assert_eq!(metrics_monitor.cumulative().total_scheduled_count, 0);

    // [D] poll `task` to completion
    let mut next_interval = task.await;

    // [H] `task` has been polled 1 times since the last sample
    assert_eq!(next_interval().total_scheduled_count, 1);

    // [I] `task` has been polled 0 times since the last sample
    assert_eq!(next_interval().total_scheduled_count, 0);

    // [J] `task` has yielded to the scheduler a total of five times
    assert_eq!(metrics_monitor.cumulative().total_scheduled_count, 5);
}
§total_scheduled_duration: Duration

The total duration that tasks spent waiting to be polled after awakening.

§Definition

This metric is equal to total_short_delay_count

§Derived metrics
§Examples
use tokio::time::Duration;

#[tokio::main(flavor = "current_thread")]
async fn main() {
    let metrics_monitor = tokio_metrics::TaskMonitor::new();
    let mut interval = metrics_monitor.intervals();
    let mut next_interval = || interval.next().unwrap();

    // construct and instrument and spawn a task that yields endlessly
    tokio::spawn(metrics_monitor.instrument(async {
        loop { tokio::task::yield_now().await }
    }));

    tokio::task::yield_now().await;

    // block the executor for 1 second
    std::thread::sleep(Duration::from_millis(1000));

    tokio::task::yield_now().await;

    // `endless_task` will have spent approximately one second waiting
    let total_scheduled_duration = next_interval().total_scheduled_duration;
    assert!(total_scheduled_duration >= Duration::from_millis(1000));
    assert!(total_scheduled_duration <= Duration::from_millis(1100));
}
§total_poll_count: u64

The total number of times that tasks were polled.

§Definition

This metric is equal to total_fast_poll_count

§Derived metrics
§Examples

In the below example, a task with multiple yield points is await’ed to completion; this metric reflects the number of awaits within each sampling interval:

#[tokio::main]
async fn main() {
    let metrics_monitor = tokio_metrics::TaskMonitor::new();

    // [A] no tasks have been created, instrumented, and polled more than once
    assert_eq!(metrics_monitor.cumulative().first_poll_count, 0);

    // [B] a `task` is created and instrumented
    let task = {
        let monitor = metrics_monitor.clone();
        metrics_monitor.instrument(async move {
            let mut interval = monitor.intervals();
            let mut next_interval = move || interval.next().unwrap();

            // [E] task is in the midst of its first poll
            assert_eq!(next_interval().total_poll_count, 0);

            tokio::task::yield_now().await; // poll 1

            // [F] task has been polled 1 time
            assert_eq!(next_interval().total_poll_count, 1);

            tokio::task::yield_now().await; // poll 2
            tokio::task::yield_now().await; // poll 3
            tokio::task::yield_now().await; // poll 4

            // [G] task has been polled 3 times
            assert_eq!(next_interval().total_poll_count, 3);

            tokio::task::yield_now().await; // poll 5

            next_interval                   // poll 6
        })
    };

    // [C] `task` has not yet been polled at all
    assert_eq!(metrics_monitor.cumulative().total_poll_count, 0);

    // [D] poll `task` to completion
    let mut next_interval = task.await;

    // [H] `task` has been polled 2 times since the last sample
    assert_eq!(next_interval().total_poll_count, 2);

    // [I] `task` has been polled 0 times since the last sample
    assert_eq!(next_interval().total_poll_count, 0);

    // [J] `task` has been polled 6 times
    assert_eq!(metrics_monitor.cumulative().total_poll_count, 6);
}
§total_poll_duration: Duration

The total duration elapsed during polls.

§Definition

This metric is equal to total_fast_poll_duration

§Derived metrics
§Examples
use tokio::time::Duration;

#[tokio::main(flavor = "current_thread", start_paused = true)]
async fn main() {
    let monitor = tokio_metrics::TaskMonitor::new();
    let mut interval = monitor.intervals();
    let mut next_interval = move || interval.next().unwrap();

    assert_eq!(next_interval().total_poll_duration, Duration::ZERO);

    monitor.instrument(async {
        tokio::time::advance(Duration::from_secs(1)).await; // poll 1 (1s)
        tokio::time::advance(Duration::from_secs(1)).await; // poll 2 (1s)
        ()                                                  // poll 3 (0s)
    }).await;

    assert_eq!(next_interval().total_poll_duration, Duration::from_secs(2));
}
§total_fast_poll_count: u64

The total number of times that polling tasks completed swiftly.

Here, ‘swiftly’ is defined as completing in strictly less time than slow_poll_threshold.

§Derived metrics
§Examples

In the below example, 0 polls occur within the first sampling interval, 3 fast polls occur within the second sampling interval, and 2 fast polls occur within the third sampling interval:

use std::future::Future;
use std::time::Duration;

#[tokio::main]
async fn main() {
    let metrics_monitor = tokio_metrics::TaskMonitor::new();
    let mut interval = metrics_monitor.intervals();
    let mut next_interval = || interval.next().unwrap();

    // no tasks have been constructed, instrumented, or polled
    assert_eq!(next_interval().total_fast_poll_count, 0);

    let fast = Duration::ZERO;

    // this task completes in three fast polls
    let _ = metrics_monitor.instrument(async {
        spin_for(fast).await; // fast poll 1
        spin_for(fast).await; // fast poll 2
        spin_for(fast)        // fast poll 3
    }).await;

    assert_eq!(next_interval().total_fast_poll_count, 3);

    // this task completes in two fast polls
    let _ = metrics_monitor.instrument(async {
        spin_for(fast).await; // fast poll 1
        spin_for(fast)        // fast poll 2
    }).await;

    assert_eq!(next_interval().total_fast_poll_count, 2);
}

/// Block the current thread for a given `duration`, then (optionally) yield to the scheduler.
fn spin_for(duration: Duration) -> impl Future<Output=()> {
    let start = tokio::time::Instant::now();
    while start.elapsed() <= duration {}
    tokio::task::yield_now()
}
§total_fast_poll_duration: Duration

The total duration of fast polls.

Here, ‘fast’ is defined as completing in strictly less time than slow_poll_threshold.

§Derived metrics
§Examples

In the below example, no tasks are polled in the first sampling interval; three fast polls consume a total of 3μs time in the second sampling interval; and two fast polls consume a total of 2μs time in the third sampling interval:

use std::future::Future;
use std::time::Duration;

#[tokio::main]
async fn main() {
    let metrics_monitor = tokio_metrics::TaskMonitor::new();
    let mut interval = metrics_monitor.intervals();
    let mut next_interval = || interval.next().unwrap();

    // no tasks have been constructed, instrumented, or polled
    let interval = next_interval();
    assert_eq!(interval.total_fast_poll_duration, Duration::ZERO);

    let fast = Duration::from_micros(1);

    // this task completes in three fast polls
    let task_a_time = time(metrics_monitor.instrument(async {
        spin_for(fast).await; // fast poll 1
        spin_for(fast).await; // fast poll 2
        spin_for(fast)        // fast poll 3
    })).await;

    let interval = next_interval();
    assert!(interval.total_fast_poll_duration >= fast * 3);
    assert!(interval.total_fast_poll_duration <= task_a_time);

    // this task completes in two fast polls
    let task_b_time = time(metrics_monitor.instrument(async {
        spin_for(fast).await; // fast poll 1
        spin_for(fast)        // fast poll 2
    })).await;

    let interval = next_interval();
    assert!(interval.total_fast_poll_duration >= fast * 2);
    assert!(interval.total_fast_poll_duration <= task_b_time);
}

/// Produces the amount of time it took to await a given async task.
async fn time(task: impl Future) -> Duration {
    let start = tokio::time::Instant::now();
    task.await;
    start.elapsed()
}

/// Block the current thread for a given `duration`, then (optionally) yield to the scheduler.
fn spin_for(duration: Duration) -> impl Future<Output=()> {
    let start = tokio::time::Instant::now();
    while start.elapsed() <= duration {}
    tokio::task::yield_now()
}
§total_slow_poll_count: u64

The total number of times that polling tasks completed slowly.

Here, ‘slowly’ is defined as completing in at least as much time as slow_poll_threshold.

§Derived metrics
§Examples

In the below example, 0 polls occur within the first sampling interval, 3 slow polls occur within the second sampling interval, and 2 slow polls occur within the third sampling interval:

use std::future::Future;
use std::time::Duration;

#[tokio::main]
async fn main() {
    let metrics_monitor = tokio_metrics::TaskMonitor::new();
    let mut interval = metrics_monitor.intervals();
    let mut next_interval = || interval.next().unwrap();

    // no tasks have been constructed, instrumented, or polled
    assert_eq!(next_interval().total_slow_poll_count, 0);

    let slow = 10 * metrics_monitor.slow_poll_threshold();

    // this task completes in three slow polls
    let _ = metrics_monitor.instrument(async {
        spin_for(slow).await; // slow poll 1
        spin_for(slow).await; // slow poll 2
        spin_for(slow)        // slow poll 3
    }).await;

    assert_eq!(next_interval().total_slow_poll_count, 3);

    // this task completes in two slow polls
    let _ = metrics_monitor.instrument(async {
        spin_for(slow).await; // slow poll 1
        spin_for(slow)        // slow poll 2
    }).await;

    assert_eq!(next_interval().total_slow_poll_count, 2);
}

/// Block the current thread for a given `duration`, then (optionally) yield to the scheduler.
fn spin_for(duration: Duration) -> impl Future<Output=()> {
    let start = tokio::time::Instant::now();
    while start.elapsed() <= duration {}
    tokio::task::yield_now()
}
§total_slow_poll_duration: Duration

The total duration of slow polls.

Here, ‘slowly’ is defined as completing in at least as much time as slow_poll_threshold.

§Derived metrics
§Examples

In the below example, no tasks are polled in the first sampling interval; three slow polls consume a total of 30 × DEFAULT_SLOW_POLL_THRESHOLD time in the second sampling interval; and two slow polls consume a total of 20 × DEFAULT_SLOW_POLL_THRESHOLD time in the third sampling interval:

use std::future::Future;
use std::time::Duration;

#[tokio::main]
async fn main() {
    let metrics_monitor = tokio_metrics::TaskMonitor::new();
    let mut interval = metrics_monitor.intervals();
    let mut next_interval = || interval.next().unwrap();

    // no tasks have been constructed, instrumented, or polled
    let interval = next_interval();
    assert_eq!(interval.total_slow_poll_duration, Duration::ZERO);

    let slow = 10 * metrics_monitor.slow_poll_threshold();

    // this task completes in three slow polls
    let task_a_time = time(metrics_monitor.instrument(async {
        spin_for(slow).await; // slow poll 1
        spin_for(slow).await; // slow poll 2
        spin_for(slow)        // slow poll 3
    })).await;

    let interval = next_interval();
    assert!(interval.total_slow_poll_duration >= slow * 3);
    assert!(interval.total_slow_poll_duration <= task_a_time);

    // this task completes in two slow polls
    let task_b_time = time(metrics_monitor.instrument(async {
        spin_for(slow).await; // slow poll 1
        spin_for(slow)        // slow poll 2
    })).await;

    let interval = next_interval();
    assert!(interval.total_slow_poll_duration >= slow * 2);
    assert!(interval.total_slow_poll_duration <= task_b_time);
}

/// Produces the amount of time it took to await a given async task.
async fn time(task: impl Future) -> Duration {
    let start = tokio::time::Instant::now();
    task.await;
    start.elapsed()
}

/// Block the current thread for a given `duration`, then (optionally) yield to the scheduler.
fn spin_for(duration: Duration) -> impl Future<Output=()> {
    let start = tokio::time::Instant::now();
    while start.elapsed() <= duration {}
    tokio::task::yield_now()
}
§total_short_delay_count: u64

The total count of tasks with short scheduling delays.

This is defined as tasks taking strictly less than long_delay_threshold to be executed after being scheduled.

§Derived metrics
§total_long_delay_count: u64

The total count of tasks with long scheduling delays.

This is defined as tasks taking long_delay_threshold or longer to be executed after being scheduled.

§Derived metrics
§total_short_delay_duration: Duration

The total duration of tasks with short scheduling delays.

This is defined as tasks taking strictly less than long_delay_threshold to be executed after being scheduled.

§Derived metrics
§total_long_delay_duration: Duration

The total number of times that a task had a long scheduling duration.

Here, a long scheduling duration is defined as taking longer to start execution after scheduling than long_delay_threshold.

§Derived metrics

Implementations§

source§

impl TaskMetrics

source

pub fn mean_first_poll_delay(&self) -> Duration

The mean duration elapsed between the instant tasks are instrumented, and the instant they are first polled.

§Definition

This metric is derived from total_first_poll_delay ÷ first_poll_count.

§Interpretation

If this metric increases, it means that, on average, tasks spent longer waiting to be initially polled.

§See also
§Examples

In the below example, no tasks are instrumented or polled within the first sampling interval; in the second sampling interval, 500ms elapse between the instrumentation of a task and its first poll; in the third sampling interval, a mean of 750ms elapse between the instrumentation and first poll of two tasks:

use std::time::Duration;

#[tokio::main]
async fn main() {
    let metrics_monitor = tokio_metrics::TaskMonitor::new();
    let mut interval = metrics_monitor.intervals();
    let mut next_interval = || interval.next().unwrap();

    // no tasks have yet been created, instrumented, or polled
    assert_eq!(next_interval().mean_first_poll_delay(), Duration::ZERO);

    // constructs and instruments a task, pauses for `pause_time`, awaits the task, then
    // produces the total time it took to do all of the aforementioned
    async fn instrument_pause_await(
        metrics_monitor: &tokio_metrics::TaskMonitor,
        pause_time: Duration
    ) -> Duration
    {
        let before_instrumentation = tokio::time::Instant::now();
        let task = metrics_monitor.instrument(async move {});
        tokio::time::sleep(pause_time).await;
        task.await;
        before_instrumentation.elapsed()
    }

    // construct and await a task that pauses for 500ms between instrumentation and first poll
    let task_a_pause_time = Duration::from_millis(500);
    let task_a_total_time = instrument_pause_await(&metrics_monitor, task_a_pause_time).await;

    // the `mean_first_poll_delay` will be some duration greater-than-or-equal-to the
    // pause time of 500ms, and less-than-or-equal-to the total runtime of `task_a`
    let mean_first_poll_delay = next_interval().mean_first_poll_delay();
    assert!(mean_first_poll_delay >= task_a_pause_time);
    assert!(mean_first_poll_delay <= task_a_total_time);

    // construct and await a task that pauses for 500ms between instrumentation and first poll
    let task_b_pause_time = Duration::from_millis(500);
    let task_b_total_time = instrument_pause_await(&metrics_monitor, task_b_pause_time).await;

    // construct and await a task that pauses for 1000ms between instrumentation and first poll
    let task_c_pause_time = Duration::from_millis(1000);
    let task_c_total_time = instrument_pause_await(&metrics_monitor, task_c_pause_time).await;

    // the `mean_first_poll_delay` will be some duration greater-than-or-equal-to the
    // average pause time of 500ms, and less-than-or-equal-to the combined total runtime of
    // `task_b` and `task_c`
    let mean_first_poll_delay = next_interval().mean_first_poll_delay();
    assert!(mean_first_poll_delay >= (task_b_pause_time + task_c_pause_time) / 2);
    assert!(mean_first_poll_delay <= (task_b_total_time + task_c_total_time) / 2);
}
source

pub fn mean_idle_duration(&self) -> Duration

The mean duration of idles.

§Definition

This metric is derived from total_idle_duration ÷ total_idled_count.

§Interpretation

The idle state is the duration spanning the instant a task completes a poll, and the instant that it is next awoken. Tasks inhabit this state when they are waiting for task-external events to complete (e.g., an asynchronous sleep, a network request, file I/O, etc.). If this metric increases, it means that tasks, in aggregate, spent more time waiting for task-external events to complete.

§Examples
#[tokio::main]
async fn main() {
    let monitor = tokio_metrics::TaskMonitor::new();
    let one_sec = std::time::Duration::from_secs(1);

    monitor.instrument(async move {
        tokio::time::sleep(one_sec).await;
    }).await;

    assert!(monitor.cumulative().mean_idle_duration() >= one_sec);
}
source

pub fn mean_scheduled_duration(&self) -> Duration

The mean duration that tasks spent waiting to be executed after awakening.

§Definition

This metric is derived from total_scheduled_duration ÷ total_scheduled_count.

§Interpretation

If this metric increases, it means that, on average, tasks spent longer in the runtime’s queues before being polled.

§See also
  • mean_first_poll_delay
    The mean duration elapsed between the instant tasks are instrumented, and the instant they are first polled.
§Examples
use tokio::time::Duration;

#[tokio::main(flavor = "current_thread")]
async fn main() {
    let metrics_monitor = tokio_metrics::TaskMonitor::new();
    let mut interval = metrics_monitor.intervals();
    let mut next_interval = || interval.next().unwrap();

    // construct and instrument and spawn a task that yields endlessly
    tokio::spawn(metrics_monitor.instrument(async {
        loop { tokio::task::yield_now().await }
    }));

    tokio::task::yield_now().await;

    // block the executor for 1 second
    std::thread::sleep(Duration::from_millis(1000));

    // get the task to run twice
    // the first will have a 1 sec scheduling delay, the second will have almost none
    tokio::task::yield_now().await;
    tokio::task::yield_now().await;

    // `endless_task` will have spent approximately one second waiting
    let mean_scheduled_duration = next_interval().mean_scheduled_duration();
    assert!(mean_scheduled_duration >= Duration::from_millis(500), "{}", mean_scheduled_duration.as_secs_f64());
    assert!(mean_scheduled_duration <= Duration::from_millis(600), "{}", mean_scheduled_duration.as_secs_f64());
}
source

pub fn mean_poll_duration(&self) -> Duration

The mean duration of polls.

§Definition

This metric is derived from total_poll_duration ÷ total_poll_count.

§Interpretation

If this metric increases, it means that, on average, individual polls are tending to take longer. However, this does not necessarily imply increased task latency: An increase in poll durations could be offset by fewer polls.

§See also
§Examples
use std::time::Duration;

#[tokio::main(flavor = "current_thread", start_paused = true)]
async fn main() {
    let monitor = tokio_metrics::TaskMonitor::new();
    let mut interval = monitor.intervals();
    let mut next_interval = move || interval.next().unwrap();
  
    assert_eq!(next_interval().mean_poll_duration(), Duration::ZERO);
  
    monitor.instrument(async {
        tokio::time::advance(Duration::from_secs(1)).await; // poll 1 (1s)
        tokio::time::advance(Duration::from_secs(1)).await; // poll 2 (1s)
        ()                                                  // poll 3 (0s)
    }).await;
  
    assert_eq!(next_interval().mean_poll_duration(), Duration::from_secs(2) / 3);
}
source

pub fn slow_poll_ratio(&self) -> f64

The ratio between the number polls categorized as slow and fast.

§Definition

This metric is derived from total_slow_poll_count ÷ total_poll_count.

§Interpretation

If this metric increases, it means that a greater proportion of polls took excessively long before yielding to the scheduler. This does not necessarily imply increased task latency: An increase in the proportion of slow polls could be offset by fewer or faster polls. However, as a rule, should yield to the scheduler frequently.

§See also
§Examples

Changes in this metric may be observed by varying the ratio of slow and slow fast within sampling intervals; for instance:

use std::future::Future;
use std::time::Duration;

#[tokio::main]
async fn main() {
    let metrics_monitor = tokio_metrics::TaskMonitor::new();
    let mut interval = metrics_monitor.intervals();
    let mut next_interval = || interval.next().unwrap();

    // no tasks have been constructed, instrumented, or polled
    let interval = next_interval();
    assert_eq!(interval.total_fast_poll_count, 0);
    assert_eq!(interval.total_slow_poll_count, 0);
    assert!(interval.slow_poll_ratio().is_nan());

    let fast = Duration::ZERO;
    let slow = 10 * metrics_monitor.slow_poll_threshold();

    // this task completes in three fast polls
    metrics_monitor.instrument(async {
        spin_for(fast).await;   // fast poll 1
        spin_for(fast).await;   // fast poll 2
        spin_for(fast);         // fast poll 3
    }).await;

    // this task completes in two slow polls
    metrics_monitor.instrument(async {
        spin_for(slow).await;   // slow poll 1
        spin_for(slow);         // slow poll 2
    }).await;

    let interval = next_interval();
    assert_eq!(interval.total_fast_poll_count, 3);
    assert_eq!(interval.total_slow_poll_count, 2);
    assert_eq!(interval.slow_poll_ratio(), ratio(2., 3.));

    // this task completes in three slow polls
    metrics_monitor.instrument(async {
        spin_for(slow).await;   // slow poll 1
        spin_for(slow).await;   // slow poll 2
        spin_for(slow);         // slow poll 3
    }).await;

    // this task completes in two fast polls
    metrics_monitor.instrument(async {
        spin_for(fast).await; // fast poll 1
        spin_for(fast);       // fast poll 2
    }).await;

    let interval = next_interval();
    assert_eq!(interval.total_fast_poll_count, 2);
    assert_eq!(interval.total_slow_poll_count, 3);
    assert_eq!(interval.slow_poll_ratio(), ratio(3., 2.));
}

fn ratio(a: f64, b: f64) -> f64 {
    a / (a + b)
}

/// Block the current thread for a given `duration`, then (optionally) yield to the scheduler.
fn spin_for(duration: Duration) -> impl Future<Output=()> {
    let start = tokio::time::Instant::now();
    while start.elapsed() <= duration {}
    tokio::task::yield_now()
}
source

pub fn long_delay_ratio(&self) -> f64

The ratio of tasks exceeding long_delay_threshold.

§Definition

This metric is derived from total_long_delay_count ÷ total_scheduled_count.

source

pub fn mean_fast_poll_duration(&self) -> Duration

The mean duration of fast polls.

§Definition

This metric is derived from total_fast_poll_duration ÷ total_fast_poll_count.

§Examples

In the below example, no tasks are polled in the first sampling interval; three fast polls consume a mean of ⅜ × DEFAULT_SLOW_POLL_THRESHOLD time in the second sampling interval; and two fast polls consume a total of ½ × DEFAULT_SLOW_POLL_THRESHOLD time in the third sampling interval:

use std::future::Future;
use std::time::Duration;

#[tokio::main]
async fn main() {
    let metrics_monitor = tokio_metrics::TaskMonitor::new();
    let mut interval = metrics_monitor.intervals();
    let mut next_interval = || interval.next().unwrap();

    // no tasks have been constructed, instrumented, or polled
    assert_eq!(next_interval().mean_fast_poll_duration(), Duration::ZERO);

    let threshold = metrics_monitor.slow_poll_threshold();
    let fast_1 = 1 * Duration::from_micros(1);
    let fast_2 = 2 * Duration::from_micros(1);
    let fast_3 = 3 * Duration::from_micros(1);

    // this task completes in two fast polls
    let total_time = time(metrics_monitor.instrument(async {
        spin_for(fast_1).await; // fast poll 1
        spin_for(fast_2)        // fast poll 2
    })).await;

    // `mean_fast_poll_duration` ≈ the mean of `fast_1` and `fast_2`
    let mean_fast_poll_duration = next_interval().mean_fast_poll_duration();
    assert!(mean_fast_poll_duration >= (fast_1 + fast_2) / 2);
    assert!(mean_fast_poll_duration <= total_time / 2);

    // this task completes in three fast polls
    let total_time = time(metrics_monitor.instrument(async {
        spin_for(fast_1).await; // fast poll 1
        spin_for(fast_2).await; // fast poll 2
        spin_for(fast_3)        // fast poll 3
    })).await;

    // `mean_fast_poll_duration` ≈ the mean of `fast_1`, `fast_2`, `fast_3`
    let mean_fast_poll_duration = next_interval().mean_fast_poll_duration();
    assert!(mean_fast_poll_duration >= (fast_1 + fast_2 + fast_3) / 3);
    assert!(mean_fast_poll_duration <= total_time / 3);
}

/// Produces the amount of time it took to await a given task.
async fn time(task: impl Future) -> Duration {
    let start = tokio::time::Instant::now();
    task.await;
    start.elapsed()
}

/// Block the current thread for a given `duration`, then (optionally) yield to the scheduler.
fn spin_for(duration: Duration) -> impl Future<Output=()> {
    let start = tokio::time::Instant::now();
    while start.elapsed() <= duration {}
    tokio::task::yield_now()
}
source

pub fn mean_short_delay_duration(&self) -> Duration

The average time taken for a task with a short scheduling delay to be executed after being scheduled.

§Definition

This metric is derived from total_short_delay_duration ÷ total_short_delay_count.

source

pub fn mean_slow_poll_duration(&self) -> Duration

The mean duration of slow polls.

§Definition

This metric is derived from total_slow_poll_duration ÷ total_slow_poll_count.

§Interpretation

If this metric increases, it means that a greater proportion of polls took excessively long before yielding to the scheduler. This does not necessarily imply increased task latency: An increase in the proportion of slow polls could be offset by fewer or faster polls.

§See also
§Interpretation

If this metric increases, it means that, on average, slow polls got even slower. This does necessarily imply increased task latency: An increase in average slow poll duration could be offset by fewer or faster polls. However, as a rule, should yield to the scheduler frequently.

§Examples

In the below example, no tasks are polled in the first sampling interval; three slow polls consume a mean of 1.5 × DEFAULT_SLOW_POLL_THRESHOLD time in the second sampling interval; and two slow polls consume a total of 2 × DEFAULT_SLOW_POLL_THRESHOLD time in the third sampling interval:

use std::future::Future;
use std::time::Duration;

#[tokio::main]
async fn main() {
    let metrics_monitor = tokio_metrics::TaskMonitor::new();
    let mut interval = metrics_monitor.intervals();
    let mut next_interval = || interval.next().unwrap();

    // no tasks have been constructed, instrumented, or polled
    assert_eq!(next_interval().mean_slow_poll_duration(), Duration::ZERO);

    let threshold = metrics_monitor.slow_poll_threshold();
    let slow_1 = 1 * threshold;
    let slow_2 = 2 * threshold;
    let slow_3 = 3 * threshold;

    // this task completes in two slow polls
    let total_time = time(metrics_monitor.instrument(async {
        spin_for(slow_1).await; // slow poll 1
        spin_for(slow_2)        // slow poll 2
    })).await;

    // `mean_slow_poll_duration` ≈ the mean of `slow_1` and `slow_2`
    let mean_slow_poll_duration = next_interval().mean_slow_poll_duration();
    assert!(mean_slow_poll_duration >= (slow_1 + slow_2) / 2);
    assert!(mean_slow_poll_duration <= total_time / 2);

    // this task completes in three slow polls
    let total_time = time(metrics_monitor.instrument(async {
        spin_for(slow_1).await; // slow poll 1
        spin_for(slow_2).await; // slow poll 2
        spin_for(slow_3)        // slow poll 3
    })).await;

    // `mean_slow_poll_duration` ≈ the mean of `slow_1`, `slow_2`, `slow_3`
    let mean_slow_poll_duration = next_interval().mean_slow_poll_duration();
    assert!(mean_slow_poll_duration >= (slow_1 + slow_2 + slow_3) / 3);
    assert!(mean_slow_poll_duration <= total_time / 3);
}

/// Produces the amount of time it took to await a given task.
async fn time(task: impl Future) -> Duration {
    let start = tokio::time::Instant::now();
    task.await;
    start.elapsed()
}

/// Block the current thread for a given `duration`, then (optionally) yield to the scheduler.
fn spin_for(duration: Duration) -> impl Future<Output=()> {
    let start = tokio::time::Instant::now();
    while start.elapsed() <= duration {}
    tokio::task::yield_now()
}
source

pub fn mean_long_delay_duration(&self) -> Duration

The average scheduling delay for a task which takes a long time to start executing after being scheduled.

§Definition

This metric is derived from total_long_delay_duration ÷ total_long_delay_count.

Trait Implementations§

source§

impl Clone for TaskMetrics

source§

fn clone(&self) -> TaskMetrics

Returns a copy of the value. Read more
1.0.0 · source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
source§

impl Debug for TaskMetrics

source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
source§

impl Default for TaskMetrics

source§

fn default() -> TaskMetrics

Returns the “default value” for a type. Read more
source§

impl Copy for TaskMetrics

Auto Trait Implementations§

Blanket Implementations§

source§

impl<T> Any for T
where T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

impl<T> Borrow<T> for T
where T: ?Sized,

source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
source§

impl<T> CloneToUninit for T
where T: Clone,

source§

default unsafe fn clone_to_uninit(&self, dst: *mut T)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dst. Read more
source§

impl<T> CloneToUninit for T
where T: Copy,

source§

unsafe fn clone_to_uninit(&self, dst: *mut T)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dst. Read more
source§

impl<T> From<T> for T

source§

fn from(t: T) -> T

Returns the argument unchanged.

source§

impl<T> Instrument for T

source§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided Span, returning an Instrumented wrapper. Read more
source§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an Instrumented wrapper. Read more
source§

impl<T, U> Into<U> for T
where U: From<T>,

source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

source§

impl<T> ToOwned for T
where T: Clone,

§

type Owned = T

The resulting type after obtaining ownership.
source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
source§

impl<T> WithSubscriber for T

source§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a WithDispatch wrapper. Read more
source§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a WithDispatch wrapper. Read more