reactive_mutiny/instruments.rs
1//! Resting place for [Instruments] used across this module
2//! TODO 2023-08-02: eventually, merge this and `./src/ogre_std/instruments.rs` together, into OgreStd, probably... making the instrumentation options general
3
4
5/// Honors the *Zero-Cost Instrumentation Pattern* for the [stream_executors]:\
6/// Designed to be used as a const generic parameter for Structs,
7/// causes the conditional instrumentation code in client structs to
8/// be selectively compiled -- see the implemented *const methods* for
9/// documentation of each available instrument.
10///
11/// Note: Using this enum directly in generics --
12/// as in `struct S<const INSTUMENTS: Instruments = { Instruments::NoInstruments }> {...}`
13/// -- is not possible yet (as of Rust 1.64): *"adt const params is experimental [E0658]"*,
14/// so we'll use it as `struct S<const INSTRUMENTS: usize = 0>` instead, and use [Instruments::from(INSTRUMENTS)]
15/// in the instrumentable implementations & `let s = S::<Instruments::<YourOption>::into()> {};` in clients.
16#[derive(Debug,Clone,Copy,PartialEq)]
17#[repr(usize)]
18pub enum Instruments {
19 /// No conditional instrumentation code will be included -- bringing in the fastest
20 /// possible execution speeds at the expense of lowest operational control
21 NoInstruments = 0,
22
23 LogsWithoutMetrics = Self::LOG,
24 LogsWithMetrics = Self::LOG | Self::PERFORMANCE_LOG | Self::COUNTERS | Self::SATURATION | Self::CHEAP_PROFILING,
25 LogsWithExpensiveMetrics = Self::LOG | Self::PERFORMANCE_LOG | Self::COUNTERS | Self::SATURATION | Self::EXPENSIVE_PROFILING,
26 MetricsWithoutLogs = Self::COUNTERS | Self::SATURATION | Self::CHEAP_PROFILING,
27 ExpensiveMetricsWithoutLogs = Self::COUNTERS | Self::SATURATION | Self::EXPENSIVE_PROFILING,
28 Custom(usize),
29}
30
31impl Instruments {
32
33 /// metrics: counts every work done & outcome: `processed_events`, `untreated_failures`
34 const COUNTERS: usize = 1;
35 /// metrics: keeps track of `avg_buffer_depth`, `max_depth`, serving cheap euristics to avoid needing to use [Instruments::EXPENSIVE_PROFILING]
36 const SATURATION: usize = 2;
37 /// keeps track of averages for the execution duration of each received `Future`
38 /// -- from the moment the executor starts them to their completion\
39 /// (does nothing if the executor is not handling `Future`s)
40 const CHEAP_PROFILING: usize = 4;
41 /// keeps track of averages for the total time each event takes to complete
42 /// -- from the moment they were *produced* to the moment their *consumption* completes\
43 /// (causes an increase in the payload due to the addition of the `u64` *start_time* field)
44 const EXPENSIVE_PROFILING: usize = 8;
45 /// metrics: computes counters of logarithmic decreasing precision of processing times for [Instruments::CHEAP_PROFILING] & [Instruments::EXPENSIVE_PROFILING], if enabled
46 /// -- useful for computing processing/response time percentiles.\
47 /// The map is in the form:\
48 /// `counters := {[time] = count, ...}`\
49 /// where *time* increase with the power of 2 and counts all requests whose times are between the previous *time* and the current one.\
50 /// provides: `futures_times` && `duration_times`
51 const _PERCENTILES: usize = 16;
52 /// outputs `INFO` on stream's execution start & finish
53 const LOG: usize = 32;
54 /// if [Instruments::LOG] is enabled, periodically outputs information about all enabled metrics & profiling data collected
55 const PERFORMANCE_LOG: usize = 64;
56 /// if [Instruments::LOG] is enabled, outputs every event in the *TRACE* level
57 const TRACING: usize = 128;
58
59 /// To be used in if conditions, returns the enum variant
60 /// that corresponds to the given const generic numeric value as described in [Self]
61 pub const fn from(instruments: usize) -> Self {
62 Self::Custom(instruments)
63 }
64
65 /// designed to be used by clients of the implementor structs, returns the number to be used as a
66 /// const generic numeric value (when instantiating the implementor struct) that corresponds
67 /// to the given enum variant
68 pub const fn into(self) -> usize {
69 match self {
70 Self::NoInstruments => 0,
71 Self::LogsWithoutMetrics => Self::LOG,
72 Self::LogsWithMetrics => Self::LOG | Self::PERFORMANCE_LOG | Self::COUNTERS | Self::SATURATION | Self::CHEAP_PROFILING,
73 Self::LogsWithExpensiveMetrics => Self::LOG | Self::PERFORMANCE_LOG | Self::COUNTERS | Self::SATURATION | Self::EXPENSIVE_PROFILING,
74 Self::MetricsWithoutLogs => Self::COUNTERS | Self::SATURATION | Self::CHEAP_PROFILING,
75 Self::ExpensiveMetricsWithoutLogs => Self::COUNTERS | Self::SATURATION | Self::EXPENSIVE_PROFILING,
76 Self::Custom(instruments) => instruments,
77 }
78 }
79
80 /// returns whether executors should log start/stop in the `INFO` level OR events `TRACING`
81 pub const fn logging(self) -> bool {
82 self.into() & (Self::LOG | Self::TRACING) > 0
83 }
84
85 /// returns whether executors should log events in the `TRACING` level
86 pub const fn tracing(self) -> bool {
87 self.into() & Self::TRACING > 0
88 }
89
90 /// returns whether executors should compute the `CHEAP_PROFILING` metrics
91 pub const fn cheap_profiling(self) -> bool {
92 self.into() & (Self::COUNTERS | Self::SATURATION | Self::CHEAP_PROFILING | Self::EXPENSIVE_PROFILING) > 0
93 }
94
95 /// returns whether executors should compute any of the available metrics
96 pub const fn metrics(self) -> bool {
97 self.into() & (Self::COUNTERS | Self::SATURATION | Self::CHEAP_PROFILING | Self::EXPENSIVE_PROFILING) > 0
98 }
99
100
101
102}
103