switchyard_llm_client/
metrics.rs1use opentelemetry::{KeyValue, global};
7
8pub(crate) const fn is_retryable_http_status(status: u16) -> bool {
9 status == 408 || status == 429 || (status >= 500 && status <= 599)
10}
11
12pub const fn http_outcome_label(status: Option<u16>) -> &'static str {
13 match status {
14 Some(200..=299) => "success",
15 Some(status) if is_retryable_http_status(status) => "retryable_error",
16 None => "retryable_error",
17 Some(_) => "other_error",
18 }
19}
20
21pub const fn http_status_code_label(status: Option<u16>) -> &'static str {
25 match status {
26 None => "none",
27 Some(200) => "200",
28 Some(400) => "400",
29 Some(401) => "401",
30 Some(403) => "403",
31 Some(404) => "404",
32 Some(408) => "408",
33 Some(409) => "409",
34 Some(422) => "422",
35 Some(429) => "429",
36 Some(500) => "500",
37 Some(502) => "502",
38 Some(503) => "503",
39 Some(504) => "504",
40 Some(100..=199) => "1xx",
41 Some(200..=299) => "2xx",
42 Some(300..=399) => "3xx",
43 Some(400..=499) => "4xx",
44 Some(500..=599) => "5xx",
45 Some(_) => "other",
46 }
47}
48
49pub(crate) fn record_upstream_attempt(status: Option<u16>) {
50 global::meter("switchyard")
51 .u64_counter("switchyard.upstream_attempts")
52 .build()
53 .add(
54 1,
55 &[
56 KeyValue::new("outcome", http_outcome_label(status)),
57 KeyValue::new("code", http_status_code_label(status)),
58 ],
59 );
60}
61
62#[cfg(test)]
63mod tests {
64 use super::*;
65
66 #[test]
67 fn outcome_labels_match_the_retry_policy() {
68 for status in [408, 429, 500, 502, 599] {
69 assert!(is_retryable_http_status(status));
70 assert_eq!(http_outcome_label(Some(status)), "retryable_error");
71 }
72 for status in [400, 409, 499, 600] {
73 assert!(!is_retryable_http_status(status));
74 assert_eq!(http_outcome_label(Some(status)), "other_error");
75 }
76 assert_eq!(http_outcome_label(None), "retryable_error");
77 }
78}