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