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#[derive(Debug, Clone, Copy, PartialEq, Eq)]
323pub enum RtcProducerSsrcBindingOutcome {
324 Learned,
326 Replaced,
328 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});