Skip to main content

servers/
metrics.rs

1// Copyright 2023 Greptime Team
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//     http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15#[cfg(not(windows))]
16pub(crate) mod jemalloc;
17
18use std::task::{Context, Poll};
19use std::time::Instant;
20
21use axum::extract::{MatchedPath, Request};
22use axum::middleware::Next;
23use axum::response::IntoResponse;
24use lazy_static::lazy_static;
25use prometheus::{
26    Histogram, HistogramVec, IntCounter, IntCounterVec, IntGauge, register_histogram,
27    register_histogram_vec, register_int_counter, register_int_counter_vec, register_int_gauge,
28};
29use session::context::QueryContext;
30use tonic::body::Body;
31use tower::{Layer, Service};
32
33pub(crate) const METRIC_DB_LABEL: &str = "db";
34pub(crate) const METRIC_CODE_LABEL: &str = "code";
35pub(crate) const METRIC_TYPE_LABEL: &str = "type";
36pub(crate) const METRIC_PROTOCOL_LABEL: &str = "protocol";
37pub(crate) const METRIC_ERROR_COUNTER_LABEL_MYSQL: &str = "mysql";
38pub(crate) const METRIC_MYSQL_SUBPROTOCOL_LABEL: &str = "subprotocol";
39pub(crate) const METRIC_MYSQL_BINQUERY: &str = "binquery";
40pub(crate) const METRIC_MYSQL_TEXTQUERY: &str = "textquery";
41pub(crate) const METRIC_POSTGRES_SUBPROTOCOL_LABEL: &str = "subprotocol";
42pub(crate) const METRIC_POSTGRES_SIMPLE_QUERY: &str = "simple";
43pub(crate) const METRIC_POSTGRES_EXTENDED_QUERY: &str = "extended";
44pub(crate) const METRIC_METHOD_LABEL: &str = "method";
45pub(crate) const METRIC_PATH_LABEL: &str = "path";
46pub(crate) const METRIC_RESULT_LABEL: &str = "result";
47pub(crate) const METRIC_VERSION_LABEL: &str = "version";
48
49pub(crate) const METRIC_SUCCESS_VALUE: &str = "success";
50pub(crate) const METRIC_FAILURE_VALUE: &str = "failure";
51
52lazy_static! {
53
54    pub static ref HTTP_REQUEST_COUNTER: IntCounterVec = register_int_counter_vec!(
55        "greptime_servers_http_request_counter",
56        "servers http request counter",
57        &[METRIC_METHOD_LABEL, METRIC_PATH_LABEL, METRIC_CODE_LABEL, METRIC_DB_LABEL]
58    ).unwrap();
59
60    pub static ref METRIC_ERROR_COUNTER: IntCounterVec = register_int_counter_vec!(
61        "greptime_servers_error",
62        "servers error",
63        &[METRIC_PROTOCOL_LABEL]
64    )
65    .unwrap();
66    /// Http SQL query duration per database.
67    pub static ref METRIC_HTTP_SQL_ELAPSED: HistogramVec = register_histogram_vec!(
68        "greptime_servers_http_sql_elapsed",
69        "servers http sql elapsed",
70        &[METRIC_DB_LABEL],
71        vec![0.005, 0.01, 0.05, 0.1, 0.5, 1.0, 5.0, 10.0, 60.0, 300.0]
72    )
73    .unwrap();
74    /// Http pql query duration per database.
75    pub static ref METRIC_HTTP_PROMQL_ELAPSED: HistogramVec = register_histogram_vec!(
76        "greptime_servers_http_promql_elapsed",
77        "servers http promql elapsed",
78        &[METRIC_DB_LABEL],
79        vec![0.005, 0.01, 0.05, 0.1, 0.5, 1.0, 5.0, 10.0, 60.0, 300.0]
80    )
81    .unwrap();
82    /// Http logs query duration per database.
83    pub static ref METRIC_HTTP_LOGS_ELAPSED: HistogramVec = register_histogram_vec!(
84        "greptime_servers_http_logs_elapsed",
85        "servers http logs elapsed",
86        &[METRIC_DB_LABEL],
87        vec![0.005, 0.01, 0.05, 0.1, 0.5, 1.0, 5.0, 10.0, 60.0, 300.0]
88    )
89    .unwrap();
90    pub static ref METRIC_AUTH_FAILURE: IntCounterVec = register_int_counter_vec!(
91        "greptime_servers_auth_failure_count",
92        "servers auth failure count",
93        &[METRIC_CODE_LABEL]
94    )
95    .unwrap();
96    /// Http influxdb write duration per database.
97    pub static ref METRIC_HTTP_INFLUXDB_WRITE_ELAPSED: HistogramVec = register_histogram_vec!(
98        "greptime_servers_http_influxdb_write_elapsed",
99        "servers http influxdb write elapsed",
100        &[METRIC_DB_LABEL],
101        vec![0.005, 0.01, 0.05, 0.1, 0.5, 1.0, 5.0, 10.0, 60.0, 300.0]
102    )
103    .unwrap();
104    /// Http prometheus write duration per database and remote write protocol version.
105    pub static ref METRIC_HTTP_PROM_STORE_WRITE_ELAPSED: HistogramVec = register_histogram_vec!(
106        "greptime_servers_http_prometheus_write_elapsed",
107        "servers http prometheus write elapsed",
108        &[METRIC_DB_LABEL, METRIC_VERSION_LABEL],
109        vec![0.005, 0.01, 0.05, 0.1, 0.5, 1.0, 5.0, 10.0, 60.0, 300.0]
110    )
111    .unwrap();
112    /// Prometheus remote write codec duration per protocol version.
113    pub static ref METRIC_HTTP_PROM_STORE_CODEC_ELAPSED: HistogramVec = register_histogram_vec!(
114        "greptime_servers_http_prometheus_codec_elapsed",
115        "servers http prometheus request codec duration",
116        &["type", METRIC_VERSION_LABEL],
117    )
118    .unwrap();
119    /// The samples count of Prometheus remote write per protocol version.
120    pub static ref PROM_STORE_REMOTE_WRITE_SAMPLES: IntCounterVec = register_int_counter_vec!(
121        "greptime_servers_prometheus_remote_write_samples",
122        "frontend prometheus remote write samples",
123        &[METRIC_DB_LABEL, METRIC_VERSION_LABEL]
124    )
125    .unwrap();
126    pub static ref PROM_STORE_REMOTE_WRITE_EXEMPLARS: IntCounterVec = register_int_counter_vec!(
127        "greptime_servers_prometheus_remote_write_exemplars",
128        "Acknowledged Prometheus remote write exemplars",
129        &[METRIC_DB_LABEL, METRIC_VERSION_LABEL]
130    ).unwrap();
131    pub static ref EXEMPLARS_DROPPED_TOTAL: IntCounterVec = register_int_counter_vec!(
132        "greptime_servers_exemplars_dropped_total",
133        "Exemplars dropped during ingestion",
134        &["protocol"]
135    ).unwrap();
136    /// The native histograms count of Prometheus remote write per protocol version.
137    pub static ref PROM_STORE_REMOTE_WRITE_HISTOGRAMS: IntCounterVec = register_int_counter_vec!(
138        "greptime_servers_prometheus_remote_write_histograms",
139        "frontend prometheus remote write native histograms",
140        &[METRIC_DB_LABEL, METRIC_VERSION_LABEL]
141    )
142    .unwrap();
143    /// OTLP exponential-histogram data points rejected during conversion, by reason.
144    pub static ref OTLP_EXPONENTIAL_HISTOGRAM_REJECTED_DATA_POINTS: IntCounterVec = register_int_counter_vec!(
145        "greptime_servers_otlp_exponential_histogram_rejected_data_points_total",
146        "Number of OTLP exponential histogram data points rejected during conversion",
147        &["reason"]
148    )
149    .unwrap();
150    pub static ref PENDING_BATCHES: IntGauge = register_int_gauge!(
151        "greptime_prom_store_pending_batches",
152        "Number of pending batches waiting to be flushed"
153    )
154    .unwrap();
155    pub static ref PENDING_ROWS: IntGauge = register_int_gauge!(
156        "greptime_prom_store_pending_rows",
157        "Number of pending rows waiting to be flushed"
158    )
159    .unwrap();
160    pub static ref PENDING_WORKERS: IntGauge = register_int_gauge!(
161        "greptime_prom_store_pending_workers",
162        "Number of active pending rows batch workers"
163    )
164    .unwrap();
165    pub static ref FLUSH_TOTAL: IntCounter = register_int_counter!(
166        "greptime_prom_store_flush_total",
167        "Total number of successful batch flushes"
168    )
169    .unwrap();
170    pub static ref FLUSH_ROWS: Histogram = register_histogram!(
171        "greptime_prom_store_flush_rows",
172        "Number of rows per successful flush",
173        vec![100.0, 1000.0, 10000.0, 50000.0, 100000.0, 500000.0]
174    )
175    .unwrap();
176    pub static ref FLUSH_ELAPSED: Histogram = register_histogram!(
177        "greptime_prom_store_flush_elapsed",
178        "Elapsed time of pending rows batch flush in seconds",
179        vec![0.005, 0.01, 0.05, 0.1, 0.5, 1.0, 5.0, 10.0, 60.0, 300.0]
180    )
181    .unwrap();
182    pub static ref FLUSH_DROPPED_ROWS: IntCounter = register_int_counter!(
183        "greptime_pending_rows_flush_dropped_rows",
184        "Total rows dropped due to pending rows flush failures"
185    )
186    .unwrap();
187    pub static ref FLUSH_FAILURES: IntCounter = register_int_counter!(
188        "greptime_pending_rows_flush_failures",
189        "Total pending rows flush failures"
190    )
191    .unwrap();
192    pub static ref FLOW_NOTIFICATION_DROPPED: IntCounterVec = register_int_counter_vec!(
193        "greptime_prom_store_flow_notification_dropped_total",
194        "Total flow notifications dropped by the pending rows batcher",
195        &["reason"]
196    )
197    .unwrap();
198    pub static ref PENDING_ROWS_BATCH_INGEST_STAGE_ELAPSED: HistogramVec = register_histogram_vec!(
199        "greptime_prom_store_pending_rows_batch_ingest_stage_elapsed",
200        "Elapsed time of pending rows batch ingestion stages in seconds",
201        &["stage"],
202        vec![0.0005, 0.001, 0.005, 0.01, 0.05, 0.1, 0.5, 1.0, 5.0, 10.0, 60.0]
203    )
204    .unwrap();
205    pub static ref PENDING_ROWS_BATCH_FLUSH_STAGE_ELAPSED: HistogramVec = register_histogram_vec!(
206        "greptime_prom_store_pending_rows_batch_flush_stage_elapsed",
207        "Elapsed time of pending rows batch flush stages in seconds",
208        &["stage"],
209        vec![0.0005, 0.001, 0.005, 0.01, 0.05, 0.1, 0.5, 1.0, 5.0, 10.0, 60.0]
210    )
211    .unwrap();
212    /// Http prometheus read duration per database.
213    pub static ref METRIC_HTTP_PROM_STORE_READ_ELAPSED: HistogramVec = register_histogram_vec!(
214        "greptime_servers_http_prometheus_read_elapsed",
215        "servers http prometheus read elapsed",
216        &[METRIC_DB_LABEL]
217    )
218    .unwrap();
219    /// Http prometheus endpoint query duration per database.
220    pub static ref METRIC_HTTP_PROMETHEUS_PROMQL_ELAPSED: HistogramVec = register_histogram_vec!(
221        "greptime_servers_http_prometheus_promql_elapsed",
222        "servers http prometheus promql elapsed",
223        &[METRIC_DB_LABEL, METRIC_METHOD_LABEL]
224    )
225    .unwrap();
226    pub static ref METRIC_HTTP_OPENTELEMETRY_METRICS_ELAPSED: HistogramVec =
227        register_histogram_vec!(
228            "greptime_servers_http_otlp_metrics_elapsed",
229            "servers_http_otlp_metrics_elapsed",
230            &[METRIC_DB_LABEL]
231        )
232        .unwrap();
233    pub static ref METRIC_HTTP_OPENTELEMETRY_TRACES_ELAPSED: HistogramVec =
234        register_histogram_vec!(
235            "greptime_servers_http_otlp_traces_elapsed",
236            "servers http otlp traces elapsed",
237            &[METRIC_DB_LABEL]
238        )
239        .unwrap();
240    pub static ref METRIC_HTTP_OPENTELEMETRY_LOGS_ELAPSED: HistogramVec =
241    register_histogram_vec!(
242        "greptime_servers_http_otlp_logs_elapsed",
243        "servers http otlp logs elapsed",
244        &[METRIC_DB_LABEL]
245    )
246    .unwrap();
247    pub static ref METRIC_HTTP_LOGS_INGESTION_COUNTER: IntCounterVec = register_int_counter_vec!(
248        "greptime_servers_http_logs_ingestion_counter",
249        "servers http logs ingestion counter",
250        &[METRIC_DB_LABEL]
251    )
252    .unwrap();
253    pub static ref METRIC_HTTP_LOGS_INGESTION_ELAPSED: HistogramVec =
254        register_histogram_vec!(
255            "greptime_servers_http_logs_ingestion_elapsed",
256            "servers http logs ingestion elapsed",
257            &[METRIC_DB_LABEL, METRIC_RESULT_LABEL]
258        )
259        .unwrap();
260    pub static ref METRIC_HTTP_LOGS_UTF8_RETRIES_TOTAL: IntCounterVec = register_int_counter_vec!(
261        "greptime_servers_http_logs_utf8_retries_total",
262        "UTF-8 parse retry attempts for HTTP logs and pipeline dry-runs, per JSON/JSON stream payload or NDJSON line, regardless of outcome",
263        &["format"]
264    )
265    .unwrap();
266
267    /// Count of logs ingested into Loki.
268    pub static ref METRIC_LOKI_LOGS_INGESTION_COUNTER: IntCounterVec = register_int_counter_vec!(
269        "greptime_servers_loki_logs_ingestion_counter",
270        "servers loki logs ingestion counter",
271        &[METRIC_DB_LABEL]
272    )
273    .unwrap();
274    pub static ref METRIC_LOKI_LOGS_INGESTION_ELAPSED: HistogramVec =
275        register_histogram_vec!(
276            "greptime_servers_loki_logs_ingestion_elapsed",
277            "servers loki logs ingestion elapsed",
278            &[METRIC_DB_LABEL, METRIC_RESULT_LABEL]
279        )
280        .unwrap();
281    pub static ref METRIC_ELASTICSEARCH_LOGS_INGESTION_ELAPSED: HistogramVec =
282        register_histogram_vec!(
283            "greptime_servers_elasticsearch_logs_ingestion_elapsed",
284            "servers elasticsearch logs ingestion elapsed",
285            &[METRIC_DB_LABEL]
286        )
287        .unwrap();
288
289    /// Count of documents ingested into Elasticsearch logs.
290    pub static ref METRIC_ELASTICSEARCH_LOGS_DOCS_COUNT: IntCounterVec = register_int_counter_vec!(
291        "greptime_servers_elasticsearch_logs_docs_count",
292        "servers elasticsearch ingest logs docs count",
293        &[METRIC_DB_LABEL]
294    )
295    .unwrap();
296
297    pub static ref METRIC_HTTP_LOGS_TRANSFORM_ELAPSED: HistogramVec =
298        register_histogram_vec!(
299            "greptime_servers_http_logs_transform_elapsed",
300            "servers http logs transform elapsed",
301            &[METRIC_DB_LABEL, METRIC_RESULT_LABEL]
302        )
303        .unwrap();
304    pub static ref METRIC_MYSQL_CONNECTIONS: IntGauge = register_int_gauge!(
305        "greptime_servers_mysql_connection_count",
306        "servers mysql connection count"
307    )
308    .unwrap();
309    pub static ref METRIC_MYSQL_QUERY_TIMER: HistogramVec = register_histogram_vec!(
310        "greptime_servers_mysql_query_elapsed",
311        "servers mysql query elapsed",
312        &[METRIC_MYSQL_SUBPROTOCOL_LABEL, METRIC_DB_LABEL],
313        vec![0.005, 0.01, 0.05, 0.1, 0.5, 1.0, 5.0, 10.0, 60.0, 300.0]
314    )
315    .unwrap();
316    pub static ref METRIC_MYSQL_PREPARED_COUNT: IntCounterVec = register_int_counter_vec!(
317        "greptime_servers_mysql_prepared_count",
318        "servers mysql prepared count",
319        &[METRIC_DB_LABEL]
320    )
321    .unwrap();
322    pub static ref METRIC_POSTGRES_CONNECTIONS: IntGauge = register_int_gauge!(
323        "greptime_servers_postgres_connection_count",
324        "servers postgres connection count"
325    )
326    .unwrap();
327    pub static ref METRIC_POSTGRES_QUERY_TIMER: HistogramVec = register_histogram_vec!(
328        "greptime_servers_postgres_query_elapsed",
329        "servers postgres query elapsed",
330        &[METRIC_POSTGRES_SUBPROTOCOL_LABEL, METRIC_DB_LABEL],
331        vec![0.005, 0.01, 0.05, 0.1, 0.5, 1.0, 5.0, 10.0, 60.0, 300.0]
332    )
333    .unwrap();
334    pub static ref METRIC_POSTGRES_PREPARED_COUNT: IntCounter = register_int_counter!(
335        "greptime_servers_postgres_prepared_count",
336        "servers postgres prepared count"
337    )
338    .unwrap();
339    pub static ref METRIC_POSTGRES_COPY_IN_ROWS: IntCounter = register_int_counter!(
340        "greptime_servers_postgres_copy_in_rows",
341        "servers postgres COPY FROM STDIN rows written"
342    )
343    .unwrap();
344    pub static ref METRIC_SERVER_GRPC_DB_REQUEST_TIMER: HistogramVec = register_histogram_vec!(
345        "greptime_servers_grpc_db_request_elapsed",
346        "servers grpc db request elapsed",
347        &[METRIC_DB_LABEL, METRIC_TYPE_LABEL, METRIC_CODE_LABEL]
348    )
349    .unwrap();
350    pub static ref METRIC_SERVER_GRPC_PROM_REQUEST_TIMER: HistogramVec = register_histogram_vec!(
351        "greptime_servers_grpc_prom_request_elapsed",
352        "servers grpc prom request elapsed",
353        &[METRIC_DB_LABEL],
354        vec![0.005, 0.01, 0.05, 0.1, 0.5, 1.0, 5.0, 10.0, 60.0, 300.0]
355    )
356    .unwrap();
357    pub static ref METRIC_HTTP_REQUESTS_TOTAL: IntCounterVec = register_int_counter_vec!(
358        "greptime_servers_http_requests_total",
359        "servers http requests total",
360        &[METRIC_METHOD_LABEL, METRIC_PATH_LABEL, METRIC_CODE_LABEL, METRIC_DB_LABEL]
361    )
362    .unwrap();
363    pub static ref METRIC_HTTP_REQUESTS_ELAPSED: HistogramVec = register_histogram_vec!(
364        "greptime_servers_http_requests_elapsed",
365        "servers http requests elapsed",
366        &[METRIC_METHOD_LABEL, METRIC_PATH_LABEL, METRIC_CODE_LABEL, METRIC_DB_LABEL],
367        vec![0.005, 0.01, 0.05, 0.1, 0.5, 1.0, 5.0, 10.0, 60.0, 300.0]
368    )
369    .unwrap();
370    pub static ref METRIC_GRPC_REQUESTS_TOTAL: IntCounterVec = register_int_counter_vec!(
371        "greptime_servers_grpc_requests_total",
372        "servers grpc requests total",
373        &[METRIC_PATH_LABEL, METRIC_CODE_LABEL]
374    )
375    .unwrap();
376    pub static ref METRIC_GRPC_REQUESTS_ELAPSED: HistogramVec = register_histogram_vec!(
377        "greptime_servers_grpc_requests_elapsed",
378        "servers grpc requests elapsed",
379        &[METRIC_PATH_LABEL, METRIC_CODE_LABEL],
380        vec![0.005, 0.01, 0.05, 0.1, 0.5, 1.0, 5.0, 10.0, 60.0, 300.0]
381    )
382    .unwrap();
383    pub static ref METRIC_JAEGER_QUERY_ELAPSED: HistogramVec = register_histogram_vec!(
384        "greptime_servers_jaeger_query_elapsed",
385        "servers jaeger query elapsed",
386        &[METRIC_DB_LABEL, METRIC_PATH_LABEL]
387    ).unwrap();
388
389    pub static ref GRPC_BULK_INSERT_ELAPSED: Histogram = register_histogram!(
390        "greptime_servers_bulk_insert_elapsed",
391        "servers handle bulk insert elapsed",
392    ).unwrap();
393
394    // Unified request memory metrics
395    /// Current memory in use by all concurrent requests (HTTP, gRPC, Flight).
396    pub static ref REQUEST_MEMORY_IN_USE: IntGauge = register_int_gauge!(
397        "greptime_servers_request_memory_in_use_bytes",
398        "bytes currently reserved for all concurrent request bodies and messages"
399    ).unwrap();
400
401    /// Maximum configured memory for all concurrent requests.
402    pub static ref REQUEST_MEMORY_LIMIT: IntGauge = register_int_gauge!(
403        "greptime_servers_request_memory_limit_bytes",
404        "maximum bytes allowed for all concurrent request bodies and messages"
405    ).unwrap();
406
407    /// Total number of requests rejected due to memory exhaustion.
408    pub static ref REQUEST_MEMORY_REJECTED: IntCounterVec = register_int_counter_vec!(
409        "greptime_servers_request_memory_rejected_total",
410        "number of requests rejected due to memory limit",
411        &["reason"]
412    ).unwrap();
413}
414
415// Based on https://github.com/hyperium/tonic/blob/master/examples/src/tower/server.rs
416// See https://github.com/hyperium/tonic/issues/242
417/// A metrics middleware.
418#[derive(Debug, Clone, Default)]
419pub(crate) struct MetricsMiddlewareLayer;
420
421impl<S> Layer<S> for MetricsMiddlewareLayer {
422    type Service = MetricsMiddleware<S>;
423
424    fn layer(&self, service: S) -> Self::Service {
425        MetricsMiddleware { inner: service }
426    }
427}
428
429#[derive(Debug, Clone)]
430pub(crate) struct MetricsMiddleware<S> {
431    inner: S,
432}
433
434impl<S> Service<http::Request<Body>> for MetricsMiddleware<S>
435where
436    S: Service<http::Request<Body>, Response = http::Response<Body>> + Clone + Send + 'static,
437    S::Future: Send + 'static,
438{
439    type Response = S::Response;
440    type Error = S::Error;
441    type Future = futures::future::BoxFuture<'static, Result<Self::Response, Self::Error>>;
442
443    fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
444        self.inner.poll_ready(cx)
445    }
446
447    fn call(&mut self, req: http::Request<Body>) -> Self::Future {
448        // This is necessary because tonic internally uses `tower::buffer::Buffer`.
449        // See https://github.com/tower-rs/tower/issues/547#issuecomment-767629149
450        // for details on why this is necessary
451        let clone = self.inner.clone();
452        let mut inner = std::mem::replace(&mut self.inner, clone);
453
454        Box::pin(async move {
455            let start = Instant::now();
456            let path = req.uri().path().to_string();
457
458            // Do extra async work here...
459            let response = inner.call(req).await?;
460
461            let latency = start.elapsed().as_secs_f64();
462            let status = response.status().as_u16().to_string();
463
464            let labels = [path.as_str(), status.as_str()];
465            METRIC_GRPC_REQUESTS_TOTAL.with_label_values(&labels).inc();
466            METRIC_GRPC_REQUESTS_ELAPSED
467                .with_label_values(&labels)
468                .observe(latency);
469
470            Ok(response)
471        })
472    }
473}
474
475/// A middleware to record metrics for HTTP.
476// Based on https://github.com/tokio-rs/axum/blob/axum-v0.6.16/examples/prometheus-metrics/src/main.rs
477pub(crate) async fn http_metrics_layer(req: Request, next: Next) -> impl IntoResponse {
478    let start = Instant::now();
479    let path = if let Some(matched_path) = req.extensions().get::<MatchedPath>() {
480        matched_path.as_str().to_string()
481    } else {
482        req.uri().path().to_string()
483    };
484    let method = req.method().clone();
485
486    let db = req
487        .extensions()
488        .get::<QueryContext>()
489        .map(|ctx| ctx.get_db_string())
490        .unwrap_or_else(|| "unknown".to_string());
491
492    let response = next.run(req).await;
493
494    let latency = start.elapsed().as_secs_f64();
495    let status = response.status();
496    let status = status.as_str();
497    let method_str = method.as_str();
498
499    let labels = [method_str, &path, status, db.as_str()];
500    METRIC_HTTP_REQUESTS_TOTAL.with_label_values(&labels).inc();
501    METRIC_HTTP_REQUESTS_ELAPSED
502        .with_label_values(&labels)
503        .observe(latency);
504
505    response
506}