harness/payload/dogstatsd.rs
1//! `DogStatsD` payload generation.
2//!
3//! Dogstatsd is three message types: metric, event, service check.
4//!
5//! # Metrics
6//!
7//! ```text
8//! <NAME>:<VALUE>|<TYPE>|@<SAMPLE_RATE>|#<TAG>,<TAG>...|c:<CONTAINER>|T<TS>|e:<EXT>|card:<CARD>
9//!
10//! Required: <NAME>:<VALUE>|<TYPE>.
11//!
12//! <NAME> := [^:|\n]+
13//! <VALUE> := <NUMBER>(:<NUMBER>)* ':'-packed multi-value, non-set
14//! | [^|\n]+ raw string, set type
15//! <NUMBER> := [+-]?(\d+\.?\d*|\.\d+)([eE][+-]?\d+)? | [+-]?(inf|infinity|nan)
16//! <TYPE> := c|g|ms|h|s|d count gauge timer histogram set distribution
17//! <SAMPLE_RATE> := @<NUMBER>
18//! <TAG> := [^,|\n]+ conventionally <KEY>:<VALUE>, the ':' is not required
19//! <CONTAINER> := c:[^|\n]+ e.g. ci-<id>, in-<inode>
20//! <TS> := T\d+ unix seconds
21//! <EXT> := e:[^|\n]+ e.g. it-,cn-,pu-
22//! <CARD> := card:[^|\n]+ recognized: none|low|orchestrator|high
23//! ```
24//!
25//! # Events
26//!
27//! ```text
28//! _e{<TITLE_LEN>,<TEXT_LEN>}:<TITLE>|<TEXT>|d:<TS>|h:<HOST>|k:<AGGKEY>|p:<PRIO>|s:<SRC>|t:<ALERT>|#<TAGS>
29//!
30//! Required: _e{<TITLE_LEN>,<TEXT_LEN>}:<TITLE>|<TEXT>. c: / e: / card: are valid here too.
31//!
32//! <TITLE_LEN>,<TEXT_LEN> := \d+ byte length of TITLE / TEXT
33//! <TITLE>,<TEXT> := [^\n]{LEN} length-delimited, so '|' and ':' are allowed
34//! <TS> := d:\d+ unix seconds
35//! <HOST> := h:[^|\n]+
36//! <AGGKEY> := k:[^|\n]+
37//! <PRIO> := p:[^|\n]+ recognized: normal|low (else default)
38//! <SRC> := s:[^|\n]+
39//! <ALERT> := t:[^|\n]+ recognized: error|warning|info|success (else default)
40//! <TAGS> := #<TAG>(,<TAG>)*
41//! ```
42//!
43//! # Service checks
44//!
45//! ```text
46//! _sc|<NAME>|<STATUS>|d:<TS>|h:<HOST>|#<TAG>,<TAG>...|m:<MESSAGE>
47//!
48//! Required: _sc|<NAME>|<STATUS>. c: / e: / card: are valid here too.
49//!
50//! <NAME> := [^|\n]+
51//! <STATUS> := [0-3] OK warning critical unknown
52//! <TS> := d:\d+ unix seconds
53//! <HOST> := h:[^|\n]+
54//! <TAGS> := #<TAG>(,<TAG>)*
55//! <MESSAGE> := m:[^|\n]+
56//! ```
57//!
58//! # Name combinatorics
59//!
60//! A clean name is `c` segments from `COMPLIANT_WORD` (10 words) joined by
61//! `NAME_SEPARATORS` (4). Distinct names at count `c`: `10^c · 4^(c-1)`. `c` is
62//! sampled by vibe: clean draws from `ELEMENT_COUNTS_CLEAN` (a small body, no
63//! boundary cases); feral draws from `ELEMENT_COUNTS_FERAL`, which adds the `0`
64//! and large boundary counts as a tail.
65//!
66//! | `c` | P(c) clean | P(c) feral | distinct names |
67//! |-------|------------|------------|----------------|
68//! | 0 | — | 1/12 | 1 (empty) |
69//! | 1-3 | 6/9 | 6/12 | 10 .. ~16e3 |
70//! | 4-6 | 3/9 | 3/12 | ~640e3 .. ~1e9 |
71//! | 127 | — | 1/12 | ~10^203 |
72//! | 255 | — | 1/12 | ~10^408 |
73
74use rand::{Rng, RngExt};
75
76mod common;
77mod events;
78mod metrics;
79mod service_checks;
80
81pub use common::{sample_vibe, Vibe};
82
83/// The three `DogStatsD` message types.
84#[derive(Clone, Copy)]
85enum Message {
86 Metric,
87 Event,
88 ServiceCheck,
89}
90
91/// Sample a message type. The mix is heavily metric-weighted — 98% metric, 1%
92/// event, 1% service check. Metrics drive the aggregate context map and the
93/// sketch bin paths, the invariants worth exercising, so the bulk of load goes
94/// there while events and service checks still fire often enough to keep their
95/// own anchors non-vacuous.
96fn choose_message<R: Rng + ?Sized>(rng: &mut R) -> Message {
97 match rng.random_range(0..100u32) {
98 0 => Message::Event,
99 1 => Message::ServiceCheck,
100 _ => Message::Metric,
101 }
102}
103
104/// Write one `DogStatsD` message of a sampled type to `buf` at the given vibe.
105/// Returns the packed value count when a multi-value metric was emitted, else
106/// `None`.
107pub fn send<R: Rng + ?Sized>(rng: &mut R, buf: &mut Vec<u8>, vibe: Vibe) -> Option<usize> {
108 buf.clear();
109 match choose_message(rng) {
110 Message::Event => {
111 events::write(rng, buf, vibe);
112 None
113 }
114 Message::ServiceCheck => {
115 service_checks::write(rng, buf, vibe);
116 None
117 }
118 Message::Metric => metrics::write(rng, buf, vibe),
119 }
120}