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});