Skip to main content

o_sfu_telemetry/metrics/
labels.rs

1use std::time::Duration;
2
3use o_sfu_model::WebSocketCloseCode;
4
5use super::counter::{ExportedMetricLabel, HistogramBucketLabel, MetricBucketLabel, MetricLabel};
6
7macro_rules! impl_metric_label {
8    ($visibility:vis enum $label:ident { $($variant:ident => $value:tt),+ $(,)? }) => {
9        #[derive(Debug, Clone, Copy, PartialEq, Eq)]
10        $visibility enum $label {
11            $($variant),+
12        }
13
14        impl_metric_label!($label { $($variant => $value),+ });
15    };
16    ($label:ty { $($variant:ident => $index:expr),+ $(,)? }) => {
17        impl MetricLabel for $label {
18            const VARIANTS: &'static [Self] = &[$(Self::$variant),+];
19            const COUNT: usize = Self::VARIANTS.len();
20
21            fn as_index(self) -> usize {
22                match self {
23                    $(Self::$variant => $index),+
24                }
25            }
26        }
27    };
28}
29
30macro_rules! impl_exported_metric_label {
31    ($visibility:vis enum $label:ident { $($variant:ident => $value:tt),+ $(,)? }) => {
32        #[derive(Debug, Clone, Copy, PartialEq, Eq)]
33        $visibility enum $label {
34            $($variant),+
35        }
36
37        impl_exported_metric_label!($label { $($variant => $value),+ });
38    };
39    ($label:ty { $($variant:ident => ($index:expr, $label_value:literal)),+ $(,)? }) => {
40        impl_metric_label!($label {
41            $($variant => $index),+
42        });
43
44        impl ExportedMetricLabel for $label {
45            fn label_value(self) -> &'static str {
46                match self {
47                    $(Self::$variant => $label_value),+
48                }
49            }
50        }
51    };
52}
53
54macro_rules! impl_exported_metric_label_pair {
55    ($visibility:vis enum $label:ident { $($variant:ident => $value:tt),+ $(,)? }) => {
56        #[derive(Debug, Clone, Copy, PartialEq, Eq)]
57        $visibility enum $label {
58            $($variant),+
59        }
60
61        impl_exported_metric_label_pair!($label { $($variant => $value),+ });
62    };
63    ($label:ty { $($variant:ident => ($index:expr, [($first_name:literal, $first_value:literal), ($second_name:literal, $second_value:literal)])),+ $(,)? }) => {
64        impl_metric_label!($label {
65            $($variant => $index),+
66        });
67
68        impl ExportedMetricLabelPair for $label {
69            fn label_pair(self) -> [(&'static str, &'static str); 2] {
70                match self {
71                    $(Self::$variant => [($first_name, $first_value), ($second_name, $second_value)]),+
72                }
73            }
74        }
75    };
76}
77
78pub(super) trait ExportedMetricLabelPair: MetricLabel {
79    fn label_pair(self) -> [(&'static str, &'static str); 2];
80}
81
82#[derive(Debug, Clone, Copy, PartialEq, Eq)]
83pub enum WsSessionLoopExitReason {
84    RuntimeShutdown,
85    UserClosed,
86    ReaderError,
87    BusBreak,
88    PingTimeout,
89    TransportDisconnected,
90    OutboundChannelClosed,
91    OutboundCloseSignal,
92    OutboundMessageSendFailure,
93    OutboundQueueOverflow,
94}
95
96impl RtcRelayEnqueueResult {
97    #[must_use]
98    pub const fn target_label(self) -> &'static str {
99        match self {
100            Self::IntraNodeEnqueued | Self::IntraNodeOverloaded | Self::IntraNodeClosed => {
101                "intra_node_relay"
102            }
103        }
104    }
105
106    #[must_use]
107    pub const fn outcome_label(self) -> &'static str {
108        match self {
109            Self::IntraNodeEnqueued => "enqueued",
110            Self::IntraNodeOverloaded => "overloaded",
111            Self::IntraNodeClosed => "closed",
112        }
113    }
114}
115
116impl_exported_metric_label!(pub enum HttpRoute {
117    Noop => (0, "noop"),
118    Stats => (1, "stats"),
119    Room => (2, "room"),
120    Disconnect => (3, "disconnect"),
121    Metrics => (4, "metrics"),
122});
123
124impl_exported_metric_label!(pub(super) enum HttpRoomResponseStatus {
125    Success => (0, "success"),
126    Unauthorized => (1, "unauthorized"),
127    Forbidden => (2, "forbidden"),
128    BadRequest => (3, "bad_request"),
129    Conflict => (4, "conflict"),
130});
131
132impl_exported_metric_label!(pub(super) enum HttpDisconnectResponseStatus {
133    Success => (0, "success"),
134    BadRequest => (1, "bad_request"),
135    UnprocessableEntity => (2, "unprocessable_entity"),
136});
137
138impl_metric_label!(pub(super) enum ControlPlaneDurationBucket {
139    Le10Millis => 0,
140    Le50Millis => 1,
141    Le100Millis => 2,
142    Le250Millis => 3,
143    Le500Millis => 4,
144    Le1Second => 5,
145    Le5Seconds => 6,
146});
147
148impl MetricBucketLabel for ControlPlaneDurationBucket {
149    fn upper_bound(self) -> &'static str {
150        match self {
151            Self::Le10Millis => "0.01",
152            Self::Le50Millis => "0.05",
153            Self::Le100Millis => "0.1",
154            Self::Le250Millis => "0.25",
155            Self::Le500Millis => "0.5",
156            Self::Le1Second => "1",
157            Self::Le5Seconds => "5",
158        }
159    }
160}
161
162impl HistogramBucketLabel for ControlPlaneDurationBucket {
163    fn from_duration(duration: Duration) -> Option<Self> {
164        if duration <= Duration::from_millis(10) {
165            return Some(Self::Le10Millis);
166        }
167        if duration <= Duration::from_millis(50) {
168            return Some(Self::Le50Millis);
169        }
170        if duration <= Duration::from_millis(100) {
171            return Some(Self::Le100Millis);
172        }
173        if duration <= Duration::from_millis(250) {
174            return Some(Self::Le250Millis);
175        }
176        if duration <= Duration::from_millis(500) {
177            return Some(Self::Le500Millis);
178        }
179        if duration <= Duration::from_secs(1) {
180            return Some(Self::Le1Second);
181        }
182        (duration <= Duration::from_secs(5)).then_some(Self::Le5Seconds)
183    }
184}
185
186impl_exported_metric_label!(pub(super) enum WsConnectionStage {
187    Accepted => (0, "accepted"),
188    CredentialsReceived => (1, "credentials_received"),
189    Joined => (2, "joined"),
190});
191
192impl_exported_metric_label!(WebSocketCloseCode {
193    AuthTimeout => (0, "auth_timeout"),
194    AuthFailed => (1, "auth_failed"),
195    ProtocolError => (2, "protocol_error"),
196    RoomFull => (3, "room_full"),
197    Error => (4, "error"),
198    Clean => (5, "clean"),
199    Leaving => (6, "leaving"),
200    Kicked => (7, "kicked"),
201    Overloaded => (8, "overloaded"),
202});
203
204impl_exported_metric_label!(pub enum WsPreAuthRejection {
205    Global => (0, "global"),
206    Origin => (1, "origin"),
207});
208
209impl_exported_metric_label!(pub(super) enum WsStartupFailureKind {
210    StartupSend => (0, "startup_send"),
211    SessionInitialize => (1, "user_initialize"),
212});
213
214impl_exported_metric_label!(WsSessionLoopExitReason {
215    UserClosed => (0, "user_closed"),
216    ReaderError => (1, "reader_error"),
217    BusBreak => (2, "bus_break"),
218    PingTimeout => (3, "ping_timeout"),
219    TransportDisconnected => (4, "transport_disconnected"),
220    OutboundChannelClosed => (5, "outbound_room_closed"),
221    OutboundCloseSignal => (6, "outbound_close_signal"),
222    OutboundMessageSendFailure => (7, "outbound_message_send_failure"),
223    OutboundQueueOverflow => (8, "outbound_queue_overflow"),
224    RuntimeShutdown => (9, "runtime_shutdown"),
225});
226
227impl_exported_metric_label!(pub(super) enum WsBusDirection {
228    Received => (0, "received"),
229    Sent => (1, "sent"),
230});
231
232impl_exported_metric_label!(pub(super) enum WsBusFailureKind {
233    InvalidInput => (0, "invalid_input"),
234    UnsupportedFeature => (1, "unsupported_feature"),
235    Send => (2, "send"),
236});
237
238impl_exported_metric_label!(pub(super) enum WsBusClientFrameKind {
239    Request => (0, "request"),
240    Message => (1, "message"),
241});
242
243impl_exported_metric_label!(pub(super) enum RtpFlowDirection {
244    Ingress => (0, "ingress"),
245    Egress => (1, "egress"),
246});
247
248impl_exported_metric_label!(pub enum RtpForwardDestinationKind {
249    LocalRtc => (0, "local_rtc"),
250    Recording => (1, "recording"),
251    IntraNodeRelay => (2, "intra_node_relay"),
252});
253
254impl_exported_metric_label!(pub enum RtpDecoderRefreshScope {
255    Rid => (0, "rid"),
256    Source => (1, "source"),
257});
258
259impl_exported_metric_label!(pub enum RtpRelayDropKind {
260    IntraNodeRelay => (0, "intra_node_relay"),
261});
262
263impl_exported_metric_label!(pub enum RtcDatagramRoutePath {
264    Indexed => (0, "indexed"),
265    Scan => (1, "scan"),
266});
267
268impl_exported_metric_label!(pub enum RtcDatagramDropReason {
269    RecentMissCache => (0, "recent_miss_cache"),
270    SourceRateLimited => (1, "source_rate_limited"),
271    NoUser => (2, "no_user"),
272    Malformed => (3, "malformed"),
273});
274
275impl_exported_metric_label!(pub enum RtcNackDirection {
276    SentToPublisher => (0, "sent_to_publisher"),
277    ReceivedFromSubscriber => (1, "received_from_subscriber"),
278});
279
280impl_exported_metric_label_pair!(pub enum RtcTransportIoFailure {
281    ReceivePermissionDenied => (0, [("direction", "receive"), ("category", "permission_denied")]),
282    ReceiveNetworkUnavailable => (1, [("direction", "receive"), ("category", "network_unavailable")]),
283    ReceiveWouldBlock => (2, [("direction", "receive"), ("category", "would_block")]),
284    ReceiveOther => (3, [("direction", "receive"), ("category", "other")]),
285    SendPermissionDenied => (4, [("direction", "send"), ("category", "permission_denied")]),
286    SendNetworkUnavailable => (5, [("direction", "send"), ("category", "network_unavailable")]),
287    SendWouldBlock => (6, [("direction", "send"), ("category", "would_block")]),
288    SendOther => (7, [("direction", "send"), ("category", "other")]),
289});
290
291impl_exported_metric_label!(pub enum RtcInputFailure {
292    Io => (0, "io"),
293    Dtls => (1, "dtls"),
294    Net => (2, "net"),
295    Ice => (3, "ice"),
296    Sctp => (4, "sctp"),
297    Other => (5, "other"),
298});
299
300impl_exported_metric_label!(pub enum RtcDrainFailureStage {
301    PollOutput => (0, "poll_output"),
302    TimeoutInput => (1, "timeout_input"),
303});
304
305impl_exported_metric_label!(pub enum RtcWorkerObservationKind {
306    ActiveSpeakerSources => (0, "active_speaker_sources"),
307    SourceDiagnostics => (1, "source_diagnostics"),
308    ResolveMediaMid => (2, "resolve_media_mid"),
309    NegotiatedProducerParameters => (3, "negotiated_producer_parameters"),
310});
311
312impl_exported_metric_label!(pub enum RtcOutputBudgetLimit {
313    Packets => (0, "packets"),
314    PayloadBytes => (1, "payload_bytes"),
315    PacketsAndPayloadBytes => (2, "packets_and_payload_bytes"),
316});
317
318/// Outcomes of authenticated producer SSRC binding changes.
319///
320/// Labels contain no session, media, RID or SSRC identity so source churn cannot
321/// increase the number of exported time series.
322#[derive(Debug, Clone, Copy, PartialEq, Eq)]
323pub enum RtcProducerSsrcBindingOutcome {
324    /// A negotiated encoding acquired its first SSRC binding.
325    Learned,
326    /// A negotiated encoding replaced an earlier SSRC binding.
327    Replaced,
328    /// A packet could not claim a negotiated encoding or its SSRC was occupied.
329    Rejected,
330}
331
332impl_exported_metric_label!(RtcProducerSsrcBindingOutcome {
333    Learned => (0, "learned"),
334    Replaced => (1, "replaced"),
335    Rejected => (2, "rejected"),
336});
337
338impl_exported_metric_label!(pub enum RtcRouteControlOutcome {
339    Absorbed => (0, "absorbed"),
340    Forwarded => (1, "forwarded"),
341    RouteGatedRelayDrop => (2, "route_gated_relay_drop"),
342    LayerAllowed => (3, "layer_allowed"),
343    LayerDropped => (4, "layer_dropped"),
344});
345
346impl_exported_metric_label!(pub enum RtcKeyframeRequestOutcome {
347    Forwarded => (0, "forwarded"),
348    Absorbed => (1, "absorbed"),
349    Retry => (2, "retry"),
350    Cleared => (3, "cleared"),
351    Deferred => (4, "deferred"),
352});
353
354impl_metric_label!(pub enum RtcRelayEnqueueResult {
355    IntraNodeEnqueued => 0,
356    IntraNodeOverloaded => 1,
357    IntraNodeClosed => 2,
358});
359
360impl ExportedMetricLabelPair for RtcRelayEnqueueResult {
361    fn label_pair(self) -> [(&'static str, &'static str); 2] {
362        [
363            ("target", self.target_label()),
364            ("outcome", self.outcome_label()),
365        ]
366    }
367}
368
369impl_exported_metric_label!(pub enum RtcRemoteControlDropKind {
370    Keyframe => (0, "keyframe"),
371    PacketGate => (1, "packet_gate"),
372});
373
374impl_exported_metric_label!(pub enum RtcRemotePacketGateConvergence {
375    Retry => (0, "retry"),
376    Flushed => (1, "flushed"),
377});
378
379impl_exported_metric_label!(pub enum SourceSelectionKind {
380    Open => (0, "open"),
381    Encoding => (1, "encoding"),
382});
383
384impl_exported_metric_label!(pub enum BudgetSolverOutcome {
385    Degraded => (0, "degraded"),
386    Paused => (1, "paused"),
387    Resumed => (2, "resumed"),
388});
389
390impl_exported_metric_label!(pub enum TransportIceState {
391    New => (0, "new"),
392    Checking => (1, "checking"),
393    Connected => (2, "connected"),
394    Completed => (3, "completed"),
395    Disconnected => (4, "disconnected"),
396});
397
398impl_exported_metric_label!(pub enum TransportHealthState {
399    Connected => (0, "connected"),
400    Disconnected => (1, "disconnected"),
401});
402
403impl_exported_metric_label_pair!(pub(super) enum TransportHealthTransition {
404    UnsetToConnected => (0, [("from", "unset"), ("to", "connected")]),
405    UnsetToDisconnected => (1, [("from", "unset"), ("to", "disconnected")]),
406    ConnectedToDisconnected => (2, [("from", "connected"), ("to", "disconnected")]),
407    DisconnectedToConnected => (3, [("from", "disconnected"), ("to", "connected")]),
408    ConnectedToUnset => (4, [("from", "connected"), ("to", "unset")]),
409    DisconnectedToUnset => (5, [("from", "disconnected"), ("to", "unset")]),
410});
411
412impl_metric_label!(pub(super) enum TransportUserLifetimeBucket {
413    Le1Second => 0,
414    Le10Seconds => 1,
415    Le60Seconds => 2,
416    Le300Seconds => 3,
417});
418
419impl MetricBucketLabel for TransportUserLifetimeBucket {
420    fn upper_bound(self) -> &'static str {
421        match self {
422            Self::Le1Second => "1",
423            Self::Le10Seconds => "10",
424            Self::Le60Seconds => "60",
425            Self::Le300Seconds => "300",
426        }
427    }
428}
429
430impl_exported_metric_label!(pub enum MediaQualitySample {
431    Peer => (0, "peer"),
432    MediaIngress => (1, "media_ingress"),
433    MediaEgress => (2, "media_egress"),
434});
435
436impl_exported_metric_label!(pub enum MediaQualityLossDirection {
437    Ingress => (0, "ingress"),
438    Egress => (1, "egress"),
439});
440
441impl_metric_label!(pub(super) enum MediaQualityRttBucket {
442    Le50Millis => 0,
443    Le100Millis => 1,
444    Le250Millis => 2,
445    Le500Millis => 3,
446    Le1Second => 4,
447    Le2Seconds => 5,
448    Le5Seconds => 6,
449});
450
451impl MetricBucketLabel for MediaQualityRttBucket {
452    fn upper_bound(self) -> &'static str {
453        match self {
454            Self::Le50Millis => "0.05",
455            Self::Le100Millis => "0.1",
456            Self::Le250Millis => "0.25",
457            Self::Le500Millis => "0.5",
458            Self::Le1Second => "1",
459            Self::Le2Seconds => "2",
460            Self::Le5Seconds => "5",
461        }
462    }
463}
464
465impl HistogramBucketLabel for MediaQualityRttBucket {
466    fn from_duration(duration: Duration) -> Option<Self> {
467        if duration <= Duration::from_millis(50) {
468            return Some(Self::Le50Millis);
469        }
470        if duration <= Duration::from_millis(100) {
471            return Some(Self::Le100Millis);
472        }
473        if duration <= Duration::from_millis(250) {
474            return Some(Self::Le250Millis);
475        }
476        if duration <= Duration::from_millis(500) {
477            return Some(Self::Le500Millis);
478        }
479        if duration <= Duration::from_secs(1) {
480            return Some(Self::Le1Second);
481        }
482        if duration <= Duration::from_secs(2) {
483            return Some(Self::Le2Seconds);
484        }
485        (duration <= Duration::from_secs(5)).then_some(Self::Le5Seconds)
486    }
487}
488
489impl_exported_metric_label_pair!(pub(super) enum RecordingActionOutcome {
490    StartAccepted => (0, [("action", "start"), ("outcome", "accepted")]),
491    StartRejected => (1, [("action", "start"), ("outcome", "rejected")]),
492    StopAccepted => (2, [("action", "stop"), ("outcome", "accepted")]),
493    StopRejected => (3, [("action", "stop"), ("outcome", "rejected")]),
494});