rs_nomad/
metrics.rs

1#[cfg(feature = "metrics")]
2use http::StatusCode;
3
4#[cfg(feature = "metrics")]
5use std::time::Duration;
6
7/// A struct to hold information about a calls to consul for metrics.
8#[cfg(feature = "metrics")]
9#[derive(Debug, Clone, Copy)]
10pub struct MetricInfo {
11    /// The HTTP method used in the call.
12    pub method: HttpMethod,
13    /// The function called in the consul client.
14    pub function: Function,
15    /// The status code returned by the call if any.
16    pub status: Option<StatusCode>,
17    /// The duration of the call.
18    pub duration: Option<Duration>,
19}
20
21#[cfg(feature = "metrics")]
22impl MetricInfo {
23    fn new(method: HttpMethod, function: Function, status: Option<StatusCode>) -> Self {
24        Self {
25            method,
26            function,
27            status,
28            duration: None,
29        }
30    }
31
32    /// Get the labels for the metric as an array of `&str`.
33    pub fn labels(&self) -> [&str; 3] {
34        if let Some(status) = self.status.and_then(|o| o.canonical_reason()) {
35            [self.method.as_str(), self.function.as_str(), status]
36        } else {
37            [self.method.as_str(), self.function.as_str(), "unknown"]
38        }
39    }
40}
41
42#[cfg(feature = "metrics")]
43#[derive(Debug, Clone)]
44pub(crate) struct MetricInfoWrapper {
45    metrics: MetricInfo,
46    sender: Option<tokio::sync::mpsc::UnboundedSender<MetricInfo>>,
47    start: std::time::Instant,
48}
49
50#[cfg(feature = "metrics")]
51impl MetricInfoWrapper {
52    pub fn new(
53        method: HttpMethod,
54        function: Function,
55        status: Option<StatusCode>,
56        sender: tokio::sync::mpsc::UnboundedSender<MetricInfo>,
57    ) -> Self {
58        Self {
59            metrics: MetricInfo::new(method, function, status),
60            sender: Some(sender),
61            start: std::time::Instant::now(),
62        }
63    }
64
65    pub fn set_status(&mut self, status: StatusCode) {
66        self.metrics.status = Some(status);
67    }
68
69    pub fn emit_metrics(&mut self) {
70        if let Some(sender) = self.sender.take() {
71            let mut metrics = self.metrics;
72            metrics.duration = Some(self.start.elapsed());
73            let _ = sender.send(metrics);
74        }
75    }
76}
77
78#[cfg(feature = "metrics")]
79impl Drop for MetricInfoWrapper {
80    fn drop(&mut self) {
81        self.emit_metrics();
82    }
83}
84
85/// The HTTP methods supported by the consul API.
86#[derive(Debug, Clone, Copy)]
87pub enum HttpMethod {
88    /// The OPTIONS method.
89    Options,
90    /// The GET method.
91    Get,
92    /// The POST method.
93    Post,
94    /// The PUT method.
95    Put,
96    /// The DELETE method.
97    Delete,
98    /// The HEAD method.
99    Head,
100    /// The TRACE method.
101    Trace,
102    /// The CONNECT method.
103    Connect,
104    /// The PATCH method.
105    Patch,
106    /// Extensions to the HTTP methods.
107    Extensions,
108}
109
110impl HttpMethod {
111    #[cfg(feature = "metrics")]
112    fn as_str(&self) -> &'static str {
113        match self {
114            HttpMethod::Options => "options",
115            HttpMethod::Get => "get",
116            HttpMethod::Post => "post",
117            HttpMethod::Put => "put",
118            HttpMethod::Delete => "delete",
119            HttpMethod::Head => "head",
120            HttpMethod::Trace => "trace",
121            HttpMethod::Connect => "connect",
122            HttpMethod::Patch => "patch",
123            HttpMethod::Extensions => "extensions",
124        }
125    }
126}
127
128#[cfg(feature = "metrics")]
129impl From<http::Method> for HttpMethod {
130    fn from(method: http::Method) -> Self {
131        match method {
132            http::Method::OPTIONS => HttpMethod::Options,
133            http::Method::GET => HttpMethod::Get,
134            http::Method::POST => HttpMethod::Post,
135            http::Method::PUT => HttpMethod::Put,
136            http::Method::DELETE => HttpMethod::Delete,
137            http::Method::HEAD => HttpMethod::Head,
138            http::Method::TRACE => HttpMethod::Trace,
139            http::Method::CONNECT => HttpMethod::Connect,
140            http::Method::PATCH => HttpMethod::Patch,
141            _ => HttpMethod::Extensions,
142        }
143    }
144}
145
146/// The functions supported by the consul client.
147#[derive(Debug, Clone, Copy)]
148pub enum Function {
149    /// The list_jobs function.
150    ListJobs,
151    /// The create_job_plan function.
152    CreateJobPlan,
153    /// The retrieve_most_recent_job_deployment function.
154    RetrieveMostRecentJobDeployment,
155    /// The create_or_update_job function.
156    CreateOrUpdateJob,
157    /// The read_job function.
158    ReadJob,
159    /// The stop_job function.
160    StopJob,
161}
162
163impl Function {
164    /// Get the function as a string.
165    #[cfg(feature = "metrics")]
166    pub fn as_str(&self) -> &'static str {
167        match self {
168            Function::ListJobs => "list_jobs",
169            Function::CreateJobPlan => "create_job_plan",
170            Function::RetrieveMostRecentJobDeployment => "retrieve_most_recent_job_deployment",
171            Function::CreateOrUpdateJob => "create_or_update_job",
172            Function::ReadJob => "read_job",
173            Function::StopJob => "stop_job",
174        }
175    }
176}