Skip to main content

sozu_lib/metrics/
names.rs

1//! Canonical metric-name constants.
2//!
3//! Every metric string emitted by Sōzu and consumed by the StatsD/Prometheus/
4//! TUI surface should reference a constant declared here rather than being
5//! repeated as a literal. The constants intentionally preserve the dotted
6//! string values byte-for-byte so dashboards and scrapers stay valid.
7//!
8//! Layout: one submodule per metric family (`http`, `h2`, `tcp`, `tls`, …),
9//! constants inside are `UPPER_SNAKE_CASE` derived from the dotted suffix.
10//! E.g. `h2.frames.tx.data` lives at [`h2::FRAMES_TX_DATA`].
11//!
12//! When adding a new metric:
13//! 1. Add the constant in the matching submodule (create one if needed).
14//! 2. Reference the constant from the emission site
15//!    (`incr!(names::h2::FRAMES_TX_DATA)`) instead of repeating the literal.
16//! 3. If the TUI or any scraper reads this metric, reference the same
17//!    constant on the read side too.
18
19/// Accept-queue counters and gauges, fed by the worker accept loop in
20/// `lib/src/server.rs`.
21pub mod accept_queue {
22    pub const BACKPRESSURE: &str = "accept_queue.backpressure";
23    pub const CONNECTIONS: &str = "accept_queue.connections";
24    pub const SATURATED_SECONDS: &str = "accept_queue.saturated_seconds";
25    pub const TIMEOUT: &str = "accept_queue.timeout";
26    pub const WAIT_TIME: &str = "accept_queue.wait_time";
27}
28
29/// Access-log infrastructure counters.
30pub mod access_logs {
31    pub const COUNT: &str = "access_logs.count";
32    pub const UNSENT: &str = "unsent-access-logs";
33}
34
35/// Per-backend bandwidth + connection counters emitted by both H1 and the
36/// H2 mux. Front-bytes and back-bytes are accounted separately so dashboards
37/// can compare upstream vs downstream traffic.
38pub mod backend {
39    pub const BYTES_IN: &str = "bytes_in";
40    pub const BYTES_OUT: &str = "bytes_out";
41    pub const BACK_BYTES_IN: &str = "back_bytes_in";
42    pub const BACK_BYTES_OUT: &str = "back_bytes_out";
43    pub const AVAILABLE: &str = "backend.available";
44    pub const CONNECTIONS: &str = "backend.connections";
45    pub const FLOW_CONTROL_PAUSED: &str = "backend.flow_control.paused";
46    pub const POOL_HIT: &str = "backend.pool.hit";
47    pub const POOL_MISS: &str = "backend.pool.miss";
48    pub const POOL_SIZE: &str = "backend.pool.size";
49    pub const CONNECTIONS_PER_BACKEND: &str = "connections_per_backend";
50    pub const CONNECTION_TIME: &str = "backend_connection_time";
51    pub const RESPONSE_TIME: &str = "backend_response_time";
52    pub const REQUESTS: &str = "requests";
53    pub const FAIL_OPEN: &str = "backends.fail_open";
54}
55
56/// Buffer-pool gauges and counters.
57pub mod buffer {
58    pub const CAPACITY: &str = "buffer.capacity";
59    pub const IN_USE: &str = "buffer.in_use";
60    pub const USAGE_PERCENT: &str = "buffer.usage_percent";
61}
62
63/// Client-side connection gauges, populated by the worker accept loop.
64pub mod client {
65    pub const CONNECTIONS: &str = "client.connections";
66    pub const CONNECTIONS_MAX: &str = "client.connections_max";
67}
68
69/// Per-cluster aggregate gauges.
70pub mod cluster {
71    pub const AVAILABLE_BACKENDS: &str = "cluster.available_backends";
72    pub const AVAILABLE_RECOVERED: &str = "cluster.available_recovered";
73    pub const NO_AVAILABLE_BACKENDS: &str = "cluster.no_available_backends";
74    pub const TOTAL_BACKENDS: &str = "cluster.total_backends";
75}
76
77/// Configuration-state inventory gauges, refreshed when the master
78/// fans out a state change.
79pub mod configuration {
80    pub const BACKENDS: &str = "configuration.backends";
81    pub const CLUSTERS: &str = "configuration.clusters";
82    pub const FRONTENDS: &str = "configuration.frontends";
83}
84
85/// Event-loop timing counters.
86pub mod event_loop {
87    pub const EPOLL_TIME: &str = "epoll_time";
88    pub const EVENT_LOOP_TIME: &str = "event_loop_time";
89    pub const FRONTEND_MATCHING_TIME: &str = "frontend_matching_time";
90    pub const REGEX_MATCHING_TIME: &str = "regex_matching_time";
91    pub const REQUEST_TIME: &str = "request_time";
92    pub const SERVICE_TIME: &str = "service_time";
93}
94
95/// Health-check transition counters.
96pub mod health_check {
97    pub const UP: &str = "health_check.up";
98    pub const DOWN: &str = "health_check.down";
99    pub const SUCCESS: &str = "health_check.success";
100    pub const FAILURE: &str = "health_check.failure";
101}
102
103/// H1 protocol counters.
104pub mod h1 {
105    pub const BACKEND_EOF_BEFORE_MESSAGE_COMPLETE: &str = "h1.backend_eof_before_message_complete";
106}
107
108/// H2 multiplexer counters — frame TX, flood mitigations, header policy
109/// rejections, signal-writable rearm sites, and other H2-specific buckets.
110pub mod h2 {
111    pub const CLOSE_WITH_ACTIVE_STREAMS: &str = "h2.close_with_active_streams";
112    pub const COALESCING_ACCEPTED: &str = "h2.coalescing.accepted";
113    pub const CONNECTION_ACTIVE_STREAMS: &str = "h2.connection.active_streams";
114    pub const CONNECTION_PENDING_WINDOW_UPDATES: &str = "h2.connection.pending_window_updates";
115    pub const CONNECTION_WINDOW_BYTES: &str = "h2.connection.window_bytes";
116    pub const FLOW_CONTROL_STALL: &str = "h2.flow_control_stall";
117
118    // Per-stream reap counters — `cancel_timed_out_streams` breaks reaps down by
119    // which guard tripped (M1), plus a stall-budget subset for the
120    // `WINDOW_UPDATE`-drip vector the cumulative-stall budget closes (M2).
121    // `reaped.stall_budget` is a subset of `reaped.window_stall`.
122    pub const STREAMS_REAPED_IDLE_TIMEOUT: &str = "h2.streams.reaped.idle_timeout";
123    pub const STREAMS_REAPED_WINDOW_STALL: &str = "h2.streams.reaped.window_stall";
124    pub const STREAMS_REAPED_STALL_BUDGET: &str = "h2.streams.reaped.stall_budget";
125
126    // Frame-TX counters (frame type fanout).
127    pub const FRAMES_TX_CONTINUATION: &str = "h2.frames.tx.continuation";
128    pub const FRAMES_TX_DATA: &str = "h2.frames.tx.data";
129    pub const FRAMES_TX_GOAWAY: &str = "h2.frames.tx.goaway";
130    pub const FRAMES_TX_HEADERS: &str = "h2.frames.tx.headers";
131    pub const FRAMES_TX_PING_ACK: &str = "h2.frames.tx.ping_ack";
132    pub const FRAMES_TX_RST_STREAM: &str = "h2.frames.tx.rst_stream";
133    pub const FRAMES_TX_SETTINGS: &str = "h2.frames.tx.settings";
134    pub const FRAMES_TX_WINDOW_UPDATE: &str = "h2.frames.tx.window_update";
135
136    // Header-policy rejections (the `h2.headers.rejected.*` family).
137    pub const HEADERS_NO_STREAM_ERROR: &str = "h2.headers_no_stream.error";
138    pub const HEADERS_REJECTED_BUDGET_OVERRUN: &str = "h2.headers.rejected.budget_overrun";
139    pub const HEADERS_REJECTED_TOTAL: &str = "h2.headers.rejected.total";
140
141    pub const RST_STREAM_RECEIVED_PRE_RESPONSE_START: &str =
142        "h2.rst_stream.received.pre_response_start";
143
144    // Writable-rearm signal counters — one per rearm reason.
145    pub const SIGNAL_WRITABLE_REARMED_CONTROL_QUEUE: &str =
146        "h2.signal.writable.rearmed.control_queue";
147    pub const SIGNAL_WRITABLE_REARMED_DEFAULT_ANSWER: &str =
148        "h2.signal.writable.rearmed.default_answer";
149    pub const SIGNAL_WRITABLE_REARMED_FORCEFULLY_TERMINATE_ANSWER: &str =
150        "h2.signal.writable.rearmed.forcefully_terminate_answer";
151    pub const SIGNAL_WRITABLE_REARMED_PEER_DATA: &str = "h2.signal.writable.rearmed.peer_data";
152    pub const SIGNAL_WRITABLE_REARMED_PEER_HEADERS: &str =
153        "h2.signal.writable.rearmed.peer_headers";
154    pub const SIGNAL_WRITABLE_REARMED_PRIORITY_UPDATE: &str =
155        "h2.signal.writable.rearmed.priority_update";
156
157    pub const TRAILERS_DROPPED_CONTENT_LENGTH: &str = "h2.trailers_dropped_content_length";
158    pub const TRAILER_SPOOF_VECTOR_ELIDED: &str = "h2.trailer.spoof_vector_elided";
159    pub const WINDOW_UPDATE_DROPPED: &str = "h2.window_update_dropped";
160
161    // Flood-mitigation violation counters — one per flood class the H2
162    // mux's `H2FloodDetector` recognises. Surfaced in the TUI's H2 pane
163    // so operators can spot a flood-pattern before it pages.
164    pub const FLOOD_VIOLATION_CONTINUATION: &str = "h2.flood.violation.continuation";
165    pub const FLOOD_VIOLATION_GLITCH_WINDOW: &str = "h2.flood.violation.glitch_window";
166    pub const FLOOD_VIOLATION_MADE_YOU_RESET: &str = "h2.flood.violation.made_you_reset";
167    pub const FLOOD_VIOLATION_PING: &str = "h2.flood.violation.ping";
168    pub const FLOOD_VIOLATION_PRIORITY: &str = "h2.flood.violation.priority";
169    pub const FLOOD_VIOLATION_RAPID_RESET: &str = "h2.flood.violation.rapid_reset";
170    pub const FLOOD_VIOLATION_SETTINGS: &str = "h2.flood.violation.settings";
171}
172
173/// HTTP counters (H1 + H2 share these); see `https` for the HTTPS-specific
174/// variants and `h2` for H2-frame-level counters.
175pub mod http {
176    pub const ERR_400: &str = "http.400.errors";
177    pub const ERR_404: &str = "http.404.errors";
178    pub const ACTIVE_REQUESTS: &str = "http.active_requests";
179    pub const ALPN_H2: &str = "http.alpn.h2";
180    pub const ALPN_HTTP11: &str = "http.alpn.http11";
181    pub const BACKEND_PARSE_ERRORS: &str = "http.backend_parse_errors";
182    pub const E2E_H2: &str = "http.e2e.h2";
183    pub const E2E_HTTP11: &str = "http.e2e.http11";
184    pub const EARLY_RESPONSE_CLOSE: &str = "http.early_response_close";
185    pub const FAILED_BACKEND_MATCHING: &str = "http.failed_backend_matching";
186    pub const FRONTEND_PARSE_ERRORS: &str = "http.frontend_parse_errors";
187    pub const HSTS_FRONTEND_ADDED: &str = "http.hsts.frontend_added";
188    pub const HSTS_FRONTEND_REFRESHED: &str = "http.hsts.frontend_refreshed";
189    pub const HSTS_LISTENER_DEFAULT_PATCHED: &str = "http.hsts.listener_default_patched";
190    pub const HSTS_SUPPRESSED_PLAINTEXT: &str = "http.hsts.suppressed_plaintext";
191    pub const HSTS_UNRENDERED: &str = "http.hsts.unrendered";
192    pub const INFINITE_LOOP_ERROR: &str = "http.infinite_loop.error";
193    pub const REDIRECT_TEMPLATE_COMPILE_ERROR: &str = "http.redirect_template.compile_error";
194    pub const REQUESTS: &str = "http.requests";
195    pub const SNI_AUTHORITY_MISMATCH: &str = "http.sni_authority_mismatch";
196    pub const STATUS_1XX: &str = "http.status.1xx";
197    pub const STATUS_2XX: &str = "http.status.2xx";
198    pub const STATUS_3XX: &str = "http.status.3xx";
199    pub const STATUS_4XX: &str = "http.status.4xx";
200    pub const STATUS_5XX: &str = "http.status.5xx";
201    pub const STATUS_OTHER: &str = "http.status.other";
202    pub const TRUSTING_X_PORT: &str = "http.trusting.x_port";
203    pub const TRUSTING_X_PORT_DIFF: &str = "http.trusting.x_port.diff";
204    pub const TRUSTING_X_PROTO: &str = "http.trusting.x_proto";
205    pub const TRUSTING_X_PROTO_DIFF: &str = "http.trusting.x_proto.diff";
206    pub const UPGRADE_EXPECT_FAILED: &str = "http.upgrade.expect.failed";
207    pub const UPGRADE_MUX_FAILED: &str = "http.upgrade.mux.failed";
208    pub const UPGRADE_WS_FAILED: &str = "http.upgrade.ws.failed";
209    pub const X_REQUEST_ID_GENERATED: &str = "http.x_request_id.generated";
210    pub const X_REQUEST_ID_PROPAGATED: &str = "http.x_request_id.propagated";
211}
212
213/// Per-status-code HTTP counters. Only the codes Sōzu either generates as
214/// a default answer or that operators routinely chart get a dedicated bucket;
215/// the rest fold into the `http::STATUS_*XX` parent buckets.
216pub mod http_status {
217    pub const S200: &str = "http.status.200";
218    pub const S201: &str = "http.status.201";
219    pub const S204: &str = "http.status.204";
220    pub const S301: &str = "http.status.301";
221    pub const S302: &str = "http.status.302";
222    pub const S304: &str = "http.status.304";
223    pub const S400: &str = "http.status.400";
224    pub const S401: &str = "http.status.401";
225    pub const S403: &str = "http.status.403";
226    pub const S404: &str = "http.status.404";
227    pub const S408: &str = "http.status.408";
228    pub const S413: &str = "http.status.413";
229    pub const S429: &str = "http.status.429";
230    pub const S500: &str = "http.status.500";
231    pub const S502: &str = "http.status.502";
232    pub const S503: &str = "http.status.503";
233    pub const S504: &str = "http.status.504";
234    pub const S507: &str = "http.status.507";
235}
236
237/// HTTPS-specific counters; see `http` for the HTTP family these complement.
238pub mod https {
239    pub const ALPN_REJECTED_HTTP11_DISABLED: &str = "https.alpn.rejected.http11_disabled";
240    pub const ALPN_REJECTED_UNSUPPORTED: &str = "https.alpn.rejected.unsupported";
241    pub const UPGRADE_EXPECT_FAILED: &str = "https.upgrade.expect.failed";
242    pub const UPGRADE_HANDSHAKE_FAILED: &str = "https.upgrade.handshake.failed";
243    pub const UPGRADE_MUX_FAILED: &str = "https.upgrade.mux.failed";
244    pub const UPGRADE_WSS_FAILED: &str = "https.upgrade.wss.failed";
245}
246
247/// Per-listener counters.
248pub mod listener {
249    pub const ACCEPTED_TOTAL: &str = "listener.accepted.total";
250    pub const CONNECTION_CAPPED: &str = "listener.connection_capped";
251}
252
253/// Pipe-protocol counters.
254pub mod pipe {
255    pub const ERRORS: &str = "pipe.errors";
256}
257
258/// Protocol-type counters that increment once per session and track which
259/// protocol carried it end-to-end.
260pub mod protocol {
261    pub const HTTP: &str = "protocol.http";
262    pub const HTTPS: &str = "protocol.https";
263    pub const PROXY_EXPECT: &str = "protocol.proxy.expect";
264    pub const PROXY_RELAY: &str = "protocol.proxy.relay";
265    pub const PROXY_SEND: &str = "protocol.proxy.send";
266    pub const TCP: &str = "protocol.tcp";
267    pub const TLS_HANDSHAKE: &str = "protocol.tls.handshake";
268    pub const WS: &str = "protocol.ws";
269    pub const WSS: &str = "protocol.wss";
270}
271
272/// PROXY-protocol counters.
273pub mod proxy_protocol {
274    pub const ERRORS: &str = "proxy_protocol.errors";
275}
276
277/// `rustls`-specific counters for read/write infinite-loop guards.
278pub mod rustls {
279    pub const READ_ERROR: &str = "rustls.read.error";
280    pub const READ_INFINITE_LOOP_ERROR: &str = "rustls.read.infinite_loop.error";
281    pub const WRITE_ERROR: &str = "rustls.write.error";
282    pub const WRITE_INFINITE_LOOP_ERROR: &str = "rustls.write.infinite_loop.error";
283}
284
285/// Generic session-level counters.
286pub mod sessions {
287    pub const EVICTED: &str = "sessions.evicted";
288}
289
290/// Slab-allocator gauges.
291pub mod slab {
292    pub const CAPACITY: &str = "slab.capacity";
293    pub const ENTRIES: &str = "slab.entries";
294    pub const USAGE_PERCENT: &str = "slab.usage_percent";
295}
296
297/// Raw-socket counters.
298pub mod socket {
299    pub const READ_INFINITE_LOOP_ERROR: &str = "socket.read.infinite_loop.error";
300    pub const WRITE_INFINITE_LOOP_ERROR: &str = "socket.write.infinite_loop.error";
301}
302
303/// TCP protocol counters.
304pub mod tcp {
305    pub const INFINITE_LOOP_ERROR: &str = "tcp.infinite_loop.error";
306    pub const READ_ERROR: &str = "tcp.read.error";
307    pub const REQUESTS: &str = "tcp.requests";
308    pub const UPGRADE_EXPECT_FAILED: &str = "tcp.upgrade.expect.failed";
309    pub const UPGRADE_PIPE_FAILED: &str = "tcp.upgrade.pipe.failed";
310    pub const UPGRADE_RELAY_FAILED: &str = "tcp.upgrade.relay.failed";
311    pub const UPGRADE_SEND_FAILED: &str = "tcp.upgrade.send.failed";
312    pub const WRITE_ERROR: &str = "tcp.write.error";
313}
314
315/// UDP datapath counters/gauges, fed by the UDP I/O shell in `lib/src/udp.rs`.
316///
317/// `IN`/`OUT` follow the client perspective: `IN` = client→backend
318/// (request), `OUT` = backend→client (reply). `ACTIVE_FLOWS` is a gauge that
319/// must never underflow — it is incremented exactly once per admitted flow
320/// (`FLOWS_CREATED`) and decremented exactly once per close.
321pub mod udp {
322    /// Client→backend datagrams forwarded.
323    pub const DATAGRAMS_IN: &str = "udp.datagrams.in";
324    /// Backend→client datagrams returned.
325    pub const DATAGRAMS_OUT: &str = "udp.datagrams.out";
326    /// Payload bytes forwarded client→backend (excludes PPv2 prefix).
327    pub const BYTES_IN: &str = "udp.bytes.in";
328    /// Payload bytes returned backend→client.
329    pub const BYTES_OUT: &str = "udp.bytes.out";
330    /// Gauge: currently admitted flows. Never underflows.
331    pub const ACTIVE_FLOWS: &str = "udp.active_flows";
332    /// Flows admitted since boot.
333    pub const FLOWS_CREATED: &str = "udp.flows.created";
334    /// Flows torn down (idle / teardown / drain).
335    pub const FLOWS_EVICTED: &str = "udp.flows.evicted";
336    /// New flows shed at the `max_flows` cap or under fd pressure.
337    pub const FLOWS_SHED: &str = "udp.flows.shed";
338    /// Datagrams dropped before allocation (aggregate). Reason-specific
339    /// counters below carry the dotted `.<reason>` suffix; both are emitted
340    /// so dashboards can chart the total or break it down. The `incr!` macro
341    /// needs `&'static str`, so the by-reason names are fixed constants
342    /// rather than a runtime-formatted suffix.
343    pub const DATAGRAMS_DROPPED: &str = "udp.datagrams.dropped";
344    /// Dropped: failed validation (empty / extractor rejected).
345    pub const DROPPED_INVALID: &str = "udp.datagrams.dropped.invalid";
346    /// Dropped: exceeded `max_rx_datagram_size` (truncation surrogate).
347    pub const DROPPED_TRUNCATED: &str = "udp.datagrams.dropped.truncated";
348    /// Dropped: no cluster/backend configured for the listener.
349    pub const DROPPED_NO_BACKEND: &str = "udp.datagrams.dropped.no_backend";
350    /// Dropped: flow-table cap reached (shed).
351    pub const DROPPED_SHED: &str = "udp.datagrams.dropped.shed";
352    /// Dropped: referenced an unknown / already-closed flow.
353    pub const DROPPED_UNKNOWN_FLOW: &str = "udp.datagrams.dropped.unknown_flow";
354    /// Dropped: the bounded per-flow / client-return write queue was at
355    /// capacity (genuine egress-queue overflow under sustained `WouldBlock`).
356    pub const DROPPED_WQ_FULL: &str = "udp.datagrams.dropped.wq_full";
357    /// Dropped: a hard `send`/`send_to` error (e.g. ECONNREFUSED) on the egress
358    /// path, distinct from a queue-full overflow. The egress "no resolved flow /
359    /// socket gone" case routes to [`DROPPED_UNKNOWN_FLOW`] instead, so
360    /// `wq_full` only counts genuine bounded-queue overflow.
361    pub const DROPPED_SEND_ERROR: &str = "udp.datagrams.dropped.send_error";
362    /// Backend health transitions observed by the UDP health prober.
363    pub const BACKEND_HEALTH: &str = "udp.backend.health";
364    /// `time!` of a flow's lifetime, recorded on close.
365    pub const FLOW_DURATION: &str = "udp.flow.duration";
366}
367
368/// TLS counters (certificate inventory, handshake timing).
369pub mod tls {
370    pub const CERT_MIN_EXPIRES_AT_SECONDS: &str = "tls.cert.min_expires_at_seconds";
371    pub const DEFAULT_CERT_USED: &str = "tls.default_cert_used";
372    pub const HANDSHAKE_MS: &str = "tls.handshake_ms";
373}
374
375/// WebSocket counters.
376pub mod websocket {
377    pub const ACTIVE_REQUESTS: &str = "websocket.active_requests";
378}
379
380/// Miscellaneous counters that don't fit a richer family.
381pub mod misc {
382    pub const PANIC: &str = "panic";
383    pub const ZOMBIES: &str = "zombies";
384}