1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
//! # Event subscriber trait
//!
//! [`Subscribe`] is the extension point for observing runtime events.
//!
//! Each registered subscriber gets:
//! - a bounded queue,
//! - a dedicated queue worker task,
//! - panic isolation from the runtime and other subscribers.
//!
//! Delivery is best-effort.
//! If a subscriber queue is full, new events may be dropped for that subscriber.
//! Events may also be lost earlier if the shared event bus lags.
//!
//! ## Flow
//!
//! ```text
//! event ──► [bounded queue] ──► one queue worker ──► blocking pool ──► on_event
//! └── full: drop event and try to report SubscriberOverflow
//! ```
//!
//! ## Rules
//!
//! - Taskvisor tries to report ordinary overflows as [`EventKind::SubscriberOverflow`](crate::EventKind::SubscriberOverflow).
//! - Taskvisor tries to report ordinary panics as [`EventKind::SubscriberPanicked`](crate::EventKind::SubscriberPanicked).
//! - Successfully queued events are processed one at a time and in FIFO order for each subscriber.
//! - Diagnostic events are not re-reported if they overflow or panic, to avoid feedback loops.
//! - There is no processing order guarantee between different subscribers.
//! - Queue overflow drops the event for this subscriber only.
//! - A slow subscriber can fill only its own queue.
//!
//! ## Example
//!
//! ```rust
//! use std::num::NonZeroUsize;
//! use taskvisor::{Event, EventKind, Subscribe};
//!
//! struct Metrics;
//!
//! impl Subscribe for Metrics {
//! fn on_event(&self, ev: &Event) {
//! if matches!(ev.kind, EventKind::TaskFailed) {
//! // update counters, push to a channel, etc.
//! }
//! }
//!
//! fn name(&self) -> &str { "metrics" }
//! fn queue_capacity(&self) -> NonZeroUsize {
//! NonZeroUsize::new(2048).unwrap()
//! }
//! }
//! ```
use NonZeroUsize;
use crateEvent;
const DEFAULT_QUEUE_CAPACITY: NonZeroUsize = new.unwrap;
/// Synchronous handler for best-effort runtime events.
///
/// `Subscribe` is synchronous by design.
/// A dedicated queue worker schedules one callback at a time on Tokio's blocking pool.
///
/// Keep [`on_event`](Self::on_event) fast.
/// For async I/O or work that may wait a long time, send the event data to your own channel and process it elsewhere.
///
/// During shutdown, Taskvisor gives all subscriber queues one shared drain timeout.
/// At the deadline, queued events are dropped.
/// A callback that is already running cannot be aborted and may continue after Taskvisor returns.
/// Tokio runtime shutdown may still wait for that blocking callback.
///
/// Unwinding panics are caught and isolated.
/// A build with `panic = "abort"` cannot isolate panics because the process exits immediately.
/// Taskvisor tries to report a panic on an ordinary event as `SubscriberPanicked`.
/// A panic while handling an internal diagnostic event is not reported again, to avoid a feedback loop.