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    ($label:ty { $($variant:ident => $index:expr),+ $(,)? }) => {
9        impl MetricLabel for $label {
10            const VARIANTS: &'static [Self] = &[$(Self::$variant),+];
11            const COUNT: usize = <[()]>::len(&[$(impl_metric_label!(@unit $variant)),+]);
12
13            fn as_index(self) -> usize {
14                match self {
15                    $(Self::$variant => $index),+
16                }
17            }
18        }
19    };
20    (@unit $_variant:ident) => {
21        ()
22    };
23}
24
25macro_rules! impl_exported_metric_label {
26    ($label:ty { $($variant:ident => ($index:expr, $label_value:literal)),+ $(,)? }) => {
27        impl_metric_label!($label {
28            $($variant => $index),+
29        });
30
31        impl ExportedMetricLabel for $label {
32            fn label_value(self) -> &'static str {
33                match self {
34                    $(Self::$variant => $label_value),+
35                }
36            }
37        }
38    };
39}
40
41macro_rules! impl_exported_metric_label_pair {
42    ($label:ty { $($variant:ident => ($index:expr, [($first_name:literal, $first_value:literal), ($second_name:literal, $second_value:literal)])),+ $(,)? }) => {
43        impl_metric_label!($label {
44            $($variant => $index),+
45        });
46
47        impl ExportedMetricLabelPair for $label {
48            fn label_pair(self) -> [(&'static str, &'static str); 2] {
49                match self {
50                    $(Self::$variant => [($first_name, $first_value), ($second_name, $second_value)]),+
51                }
52            }
53        }
54    };
55}
56
57pub(super) trait ExportedMetricLabelPair: MetricLabel {
58    fn label_pair(self) -> [(&'static str, &'static str); 2];
59}
60
61#[derive(Debug, Clone, Copy, PartialEq, Eq)]
62pub enum WsSessionLoopExitReason {
63    RuntimeShutdown,
64    UserClosed,
65    ReaderError,
66    BusBreak,
67    PingTimeout,
68    TransportDisconnected,
69    OutboundChannelClosed,
70    OutboundCloseSignal,
71    OutboundMessageSendFailure,
72    OutboundQueueOverflow,
73}
74
75#[derive(Debug, Clone, Copy, PartialEq, Eq)]
76pub enum HttpRoute {
77    Noop,
78    Stats,
79    Room,
80    Disconnect,
81    Metrics,
82}
83
84#[derive(Debug, Clone, Copy, PartialEq, Eq)]
85pub(super) enum HttpRoomResponseStatus {
86    Success,
87    Unauthorized,
88    Forbidden,
89    BadRequest,
90    Conflict,
91}
92
93#[derive(Debug, Clone, Copy, PartialEq, Eq)]
94pub(super) enum HttpDisconnectResponseStatus {
95    Success,
96    BadRequest,
97    UnprocessableEntity,
98}
99
100#[derive(Debug, Clone, Copy, PartialEq, Eq)]
101pub(super) enum ControlPlaneDurationBucket {
102    Le10Millis,
103    Le50Millis,
104    Le100Millis,
105    Le250Millis,
106    Le500Millis,
107    Le1Second,
108    Le5Seconds,
109}
110
111#[derive(Debug, Clone, Copy, PartialEq, Eq)]
112pub(super) enum WsConnectionStage {
113    Accepted,
114    CredentialsReceived,
115    Joined,
116}
117
118#[derive(Debug, Clone, Copy, PartialEq, Eq)]
119pub(super) enum WsStartupFailureKind {
120    StartupSend,
121    SessionInitialize,
122}
123
124#[derive(Debug, Clone, Copy, PartialEq, Eq)]
125pub(super) enum WsBusDirection {
126    Received,
127    Sent,
128}
129
130#[derive(Debug, Clone, Copy, PartialEq, Eq)]
131pub(super) enum WsBusFailureKind {
132    InvalidInput,
133    UnsupportedFeature,
134    Send,
135}
136
137#[derive(Debug, Clone, Copy, PartialEq, Eq)]
138pub(super) enum WsBusClientFrameKind {
139    Request,
140    Message,
141}
142
143#[derive(Debug, Clone, Copy, PartialEq, Eq)]
144pub(super) enum RtpFlowDirection {
145    Ingress,
146    Egress,
147}
148
149#[derive(Debug, Clone, Copy, PartialEq, Eq)]
150pub enum RtpForwardDestinationKind {
151    LocalRtc,
152    Recording,
153    IntraNodeRelay,
154}
155
156#[derive(Debug, Clone, Copy, PartialEq, Eq)]
157pub enum RtpDecoderRefreshScope {
158    Rid,
159    Source,
160}
161
162#[derive(Debug, Clone, Copy, PartialEq, Eq)]
163pub enum RtpRelayDropKind {
164    IntraNodeRelay,
165}
166
167#[derive(Debug, Clone, Copy, PartialEq, Eq)]
168pub enum RtcDatagramRoutePath {
169    Indexed,
170    Scan,
171}
172
173#[derive(Debug, Clone, Copy, PartialEq, Eq)]
174pub enum RtcDatagramDropReason {
175    RecentMissCache,
176    SourceRateLimited,
177    NoUser,
178    Malformed,
179}
180
181#[derive(Debug, Clone, Copy, PartialEq, Eq)]
182pub enum RtcNackDirection {
183    SentToPublisher,
184    ReceivedFromSubscriber,
185}
186
187#[derive(Debug, Clone, Copy, PartialEq, Eq)]
188pub enum RtcOutputBudgetLimit {
189    Packets,
190    PayloadBytes,
191    PacketsAndPayloadBytes,
192}
193
194#[derive(Debug, Clone, Copy, PartialEq, Eq)]
195pub enum RtcRouteControlOutcome {
196    Absorbed,
197    Forwarded,
198    RouteGatedRelayDrop,
199    LayerAllowed,
200    LayerDropped,
201}
202
203#[derive(Debug, Clone, Copy, PartialEq, Eq)]
204pub enum RtcKeyframeRequestOutcome {
205    Forwarded,
206    Absorbed,
207    Retry,
208    Cleared,
209}
210
211#[derive(Debug, Clone, Copy, PartialEq, Eq)]
212pub enum RtcRelayEnqueueResult {
213    IntraNodeEnqueued,
214    IntraNodeOverloaded,
215    IntraNodeClosed,
216}
217
218impl RtcRelayEnqueueResult {
219    #[must_use]
220    pub const fn target_label(self) -> &'static str {
221        match self {
222            Self::IntraNodeEnqueued | Self::IntraNodeOverloaded | Self::IntraNodeClosed => {
223                "intra_node_relay"
224            }
225        }
226    }
227
228    #[must_use]
229    pub const fn outcome_label(self) -> &'static str {
230        match self {
231            Self::IntraNodeEnqueued => "enqueued",
232            Self::IntraNodeOverloaded => "overloaded",
233            Self::IntraNodeClosed => "closed",
234        }
235    }
236}
237
238#[derive(Debug, Clone, Copy, PartialEq, Eq)]
239pub enum RtcRemoteControlDropKind {
240    Keyframe,
241    PacketGate,
242}
243
244#[derive(Debug, Clone, Copy, PartialEq, Eq)]
245pub enum RtcRemotePacketGateConvergence {
246    Retry,
247    Flushed,
248}
249
250#[derive(Debug, Clone, Copy, PartialEq, Eq)]
251pub enum SourceSelectionKind {
252    Open,
253    Encoding,
254}
255
256#[derive(Debug, Clone, Copy, PartialEq, Eq)]
257pub enum BudgetSolverOutcome {
258    Degraded,
259    Paused,
260    Resumed,
261}
262
263#[derive(Debug, Clone, Copy, PartialEq, Eq)]
264pub enum TransportIceState {
265    New,
266    Checking,
267    Connected,
268    Completed,
269    Disconnected,
270}
271
272#[derive(Debug, Clone, Copy, PartialEq, Eq)]
273pub enum TransportHealthState {
274    Connected,
275    Disconnected,
276}
277
278#[derive(Debug, Clone, Copy, PartialEq, Eq)]
279pub(super) enum TransportHealthTransition {
280    UnsetToConnected,
281    UnsetToDisconnected,
282    ConnectedToDisconnected,
283    DisconnectedToConnected,
284    ConnectedToUnset,
285    DisconnectedToUnset,
286}
287
288#[derive(Debug, Clone, Copy, PartialEq, Eq)]
289pub(super) enum TransportUserLifetimeBucket {
290    Le1Second,
291    Le10Seconds,
292    Le60Seconds,
293    Le300Seconds,
294}
295
296#[derive(Debug, Clone, Copy, PartialEq, Eq)]
297pub enum MediaQualitySample {
298    Peer,
299    MediaIngress,
300    MediaEgress,
301}
302
303#[derive(Debug, Clone, Copy, PartialEq, Eq)]
304pub enum MediaQualityLossDirection {
305    Ingress,
306    Egress,
307}
308
309#[derive(Debug, Clone, Copy, PartialEq, Eq)]
310pub(super) enum MediaQualityRttBucket {
311    Le50Millis,
312    Le100Millis,
313    Le250Millis,
314    Le500Millis,
315    Le1Second,
316    Le2Seconds,
317    Le5Seconds,
318}
319
320#[derive(Debug, Clone, Copy, PartialEq, Eq)]
321pub(super) enum RecordingActionOutcome {
322    StartAccepted,
323    StartRejected,
324    StopAccepted,
325    StopRejected,
326}
327
328impl_exported_metric_label!(HttpRoute {
329    Noop => (0, "noop"),
330    Stats => (1, "stats"),
331    Room => (2, "room"),
332    Disconnect => (3, "disconnect"),
333    Metrics => (4, "metrics"),
334});
335
336impl_exported_metric_label!(HttpRoomResponseStatus {
337    Success => (0, "success"),
338    Unauthorized => (1, "unauthorized"),
339    Forbidden => (2, "forbidden"),
340    BadRequest => (3, "bad_request"),
341    Conflict => (4, "conflict"),
342});
343
344impl_exported_metric_label!(HttpDisconnectResponseStatus {
345    Success => (0, "success"),
346    BadRequest => (1, "bad_request"),
347    UnprocessableEntity => (2, "unprocessable_entity"),
348});
349
350impl MetricLabel for ControlPlaneDurationBucket {
351    const VARIANTS: &'static [Self] = &[
352        Self::Le10Millis,
353        Self::Le50Millis,
354        Self::Le100Millis,
355        Self::Le250Millis,
356        Self::Le500Millis,
357        Self::Le1Second,
358        Self::Le5Seconds,
359    ];
360    const COUNT: usize = 7;
361
362    fn as_index(self) -> usize {
363        match self {
364            Self::Le10Millis => 0,
365            Self::Le50Millis => 1,
366            Self::Le100Millis => 2,
367            Self::Le250Millis => 3,
368            Self::Le500Millis => 4,
369            Self::Le1Second => 5,
370            Self::Le5Seconds => 6,
371        }
372    }
373}
374
375impl MetricBucketLabel for ControlPlaneDurationBucket {
376    fn upper_bound(self) -> &'static str {
377        match self {
378            Self::Le10Millis => "0.01",
379            Self::Le50Millis => "0.05",
380            Self::Le100Millis => "0.1",
381            Self::Le250Millis => "0.25",
382            Self::Le500Millis => "0.5",
383            Self::Le1Second => "1",
384            Self::Le5Seconds => "5",
385        }
386    }
387}
388
389impl HistogramBucketLabel for ControlPlaneDurationBucket {
390    fn from_duration(duration: Duration) -> Self {
391        if duration <= Duration::from_millis(10) {
392            return Self::Le10Millis;
393        }
394        if duration <= Duration::from_millis(50) {
395            return Self::Le50Millis;
396        }
397        if duration <= Duration::from_millis(100) {
398            return Self::Le100Millis;
399        }
400        if duration <= Duration::from_millis(250) {
401            return Self::Le250Millis;
402        }
403        if duration <= Duration::from_millis(500) {
404            return Self::Le500Millis;
405        }
406        if duration <= Duration::from_secs(1) {
407            return Self::Le1Second;
408        }
409        Self::Le5Seconds
410    }
411}
412
413impl_exported_metric_label!(WsConnectionStage {
414    Accepted => (0, "accepted"),
415    CredentialsReceived => (1, "credentials_received"),
416    Joined => (2, "joined"),
417});
418
419impl_exported_metric_label!(WebSocketCloseCode {
420    AuthTimeout => (0, "auth_timeout"),
421    AuthFailed => (1, "auth_failed"),
422    ProtocolError => (2, "protocol_error"),
423    RoomFull => (3, "room_full"),
424    Error => (4, "error"),
425    Clean => (5, "clean"),
426    Leaving => (6, "leaving"),
427    Kicked => (7, "kicked"),
428});
429
430impl_exported_metric_label!(WsStartupFailureKind {
431    StartupSend => (0, "startup_send"),
432    SessionInitialize => (1, "user_initialize"),
433});
434
435impl_exported_metric_label!(WsSessionLoopExitReason {
436    UserClosed => (0, "user_closed"),
437    ReaderError => (1, "reader_error"),
438    BusBreak => (2, "bus_break"),
439    PingTimeout => (3, "ping_timeout"),
440    TransportDisconnected => (4, "transport_disconnected"),
441    OutboundChannelClosed => (5, "outbound_room_closed"),
442    OutboundCloseSignal => (6, "outbound_close_signal"),
443    OutboundMessageSendFailure => (7, "outbound_message_send_failure"),
444    OutboundQueueOverflow => (8, "outbound_queue_overflow"),
445    RuntimeShutdown => (9, "runtime_shutdown"),
446});
447
448impl_exported_metric_label!(WsBusDirection {
449    Received => (0, "received"),
450    Sent => (1, "sent"),
451});
452
453impl_exported_metric_label!(WsBusFailureKind {
454    InvalidInput => (0, "invalid_input"),
455    UnsupportedFeature => (1, "unsupported_feature"),
456    Send => (2, "send"),
457});
458
459impl_exported_metric_label!(WsBusClientFrameKind {
460    Request => (0, "request"),
461    Message => (1, "message"),
462});
463
464impl_exported_metric_label!(RtpFlowDirection {
465    Ingress => (0, "ingress"),
466    Egress => (1, "egress"),
467});
468
469impl_exported_metric_label!(RtpForwardDestinationKind {
470    LocalRtc => (0, "local_rtc"),
471    Recording => (1, "recording"),
472    IntraNodeRelay => (2, "intra_node_relay"),
473});
474
475impl_exported_metric_label!(RtpDecoderRefreshScope {
476    Rid => (0, "rid"),
477    Source => (1, "source"),
478});
479
480impl_exported_metric_label!(RtpRelayDropKind {
481    IntraNodeRelay => (0, "intra_node_relay"),
482});
483
484impl_exported_metric_label!(RtcDatagramRoutePath {
485    Indexed => (0, "indexed"),
486    Scan => (1, "scan"),
487});
488
489impl_exported_metric_label!(RtcDatagramDropReason {
490    RecentMissCache => (0, "recent_miss_cache"),
491    SourceRateLimited => (1, "source_rate_limited"),
492    NoUser => (2, "no_user"),
493    Malformed => (3, "malformed"),
494});
495
496impl_exported_metric_label!(RtcNackDirection {
497    SentToPublisher => (0, "sent_to_publisher"),
498    ReceivedFromSubscriber => (1, "received_from_subscriber"),
499});
500
501impl_exported_metric_label!(RtcOutputBudgetLimit {
502    Packets => (0, "packets"),
503    PayloadBytes => (1, "payload_bytes"),
504    PacketsAndPayloadBytes => (2, "packets_and_payload_bytes"),
505});
506
507impl_exported_metric_label!(RtcRouteControlOutcome {
508    Absorbed => (0, "absorbed"),
509    Forwarded => (1, "forwarded"),
510    RouteGatedRelayDrop => (2, "route_gated_relay_drop"),
511    LayerAllowed => (3, "layer_allowed"),
512    LayerDropped => (4, "layer_dropped"),
513});
514
515impl_exported_metric_label!(RtcKeyframeRequestOutcome {
516    Forwarded => (0, "forwarded"),
517    Absorbed => (1, "absorbed"),
518    Retry => (2, "retry"),
519    Cleared => (3, "cleared"),
520});
521
522impl_metric_label!(RtcRelayEnqueueResult {
523    IntraNodeEnqueued => 0,
524    IntraNodeOverloaded => 1,
525    IntraNodeClosed => 2,
526});
527
528impl ExportedMetricLabelPair for RtcRelayEnqueueResult {
529    fn label_pair(self) -> [(&'static str, &'static str); 2] {
530        [
531            ("target", self.target_label()),
532            ("outcome", self.outcome_label()),
533        ]
534    }
535}
536
537impl_exported_metric_label!(RtcRemoteControlDropKind {
538    Keyframe => (0, "keyframe"),
539    PacketGate => (1, "packet_gate"),
540});
541
542impl_exported_metric_label!(RtcRemotePacketGateConvergence {
543    Retry => (0, "retry"),
544    Flushed => (1, "flushed"),
545});
546
547impl_exported_metric_label!(SourceSelectionKind {
548    Open => (0, "open"),
549    Encoding => (1, "encoding"),
550});
551
552impl_exported_metric_label!(BudgetSolverOutcome {
553    Degraded => (0, "degraded"),
554    Paused => (1, "paused"),
555    Resumed => (2, "resumed"),
556});
557
558impl_exported_metric_label!(TransportIceState {
559    New => (0, "new"),
560    Checking => (1, "checking"),
561    Connected => (2, "connected"),
562    Completed => (3, "completed"),
563    Disconnected => (4, "disconnected"),
564});
565
566impl_exported_metric_label!(TransportHealthState {
567    Connected => (0, "connected"),
568    Disconnected => (1, "disconnected"),
569});
570
571impl_exported_metric_label_pair!(TransportHealthTransition {
572    UnsetToConnected => (0, [("from", "unset"), ("to", "connected")]),
573    UnsetToDisconnected => (1, [("from", "unset"), ("to", "disconnected")]),
574    ConnectedToDisconnected => (2, [("from", "connected"), ("to", "disconnected")]),
575    DisconnectedToConnected => (3, [("from", "disconnected"), ("to", "connected")]),
576    ConnectedToUnset => (4, [("from", "connected"), ("to", "unset")]),
577    DisconnectedToUnset => (5, [("from", "disconnected"), ("to", "unset")]),
578});
579
580impl_metric_label!(TransportUserLifetimeBucket {
581    Le1Second => 0,
582    Le10Seconds => 1,
583    Le60Seconds => 2,
584    Le300Seconds => 3,
585});
586
587impl MetricBucketLabel for TransportUserLifetimeBucket {
588    fn upper_bound(self) -> &'static str {
589        match self {
590            Self::Le1Second => "1",
591            Self::Le10Seconds => "10",
592            Self::Le60Seconds => "60",
593            Self::Le300Seconds => "300",
594        }
595    }
596}
597
598impl_exported_metric_label!(MediaQualitySample {
599    Peer => (0, "peer"),
600    MediaIngress => (1, "media_ingress"),
601    MediaEgress => (2, "media_egress"),
602});
603
604impl_exported_metric_label!(MediaQualityLossDirection {
605    Ingress => (0, "ingress"),
606    Egress => (1, "egress"),
607});
608
609impl_metric_label!(MediaQualityRttBucket {
610    Le50Millis => 0,
611    Le100Millis => 1,
612    Le250Millis => 2,
613    Le500Millis => 3,
614    Le1Second => 4,
615    Le2Seconds => 5,
616    Le5Seconds => 6,
617});
618
619impl MetricBucketLabel for MediaQualityRttBucket {
620    fn upper_bound(self) -> &'static str {
621        match self {
622            Self::Le50Millis => "0.05",
623            Self::Le100Millis => "0.1",
624            Self::Le250Millis => "0.25",
625            Self::Le500Millis => "0.5",
626            Self::Le1Second => "1",
627            Self::Le2Seconds => "2",
628            Self::Le5Seconds => "5",
629        }
630    }
631}
632
633impl HistogramBucketLabel for MediaQualityRttBucket {
634    fn from_duration(duration: Duration) -> Self {
635        if duration <= Duration::from_millis(50) {
636            return Self::Le50Millis;
637        }
638        if duration <= Duration::from_millis(100) {
639            return Self::Le100Millis;
640        }
641        if duration <= Duration::from_millis(250) {
642            return Self::Le250Millis;
643        }
644        if duration <= Duration::from_millis(500) {
645            return Self::Le500Millis;
646        }
647        if duration <= Duration::from_secs(1) {
648            return Self::Le1Second;
649        }
650        if duration <= Duration::from_secs(2) {
651            return Self::Le2Seconds;
652        }
653        Self::Le5Seconds
654    }
655}
656
657impl_exported_metric_label_pair!(RecordingActionOutcome {
658    StartAccepted => (0, [("action", "start"), ("outcome", "accepted")]),
659    StartRejected => (1, [("action", "start"), ("outcome", "rejected")]),
660    StopAccepted => (2, [("action", "stop"), ("outcome", "accepted")]),
661    StopRejected => (3, [("action", "stop"), ("outcome", "rejected")]),
662});