opendal_layer_prometheus/
lib.rs1#![doc = include_str!("../README.md")]
19#![cfg_attr(docsrs, feature(doc_cfg))]
20#![cfg_attr(docsrs, doc(auto_cfg))]
21#![deny(missing_docs)]
22use opendal_core::raw::*;
23use opendal_core::*;
24use opendal_layer_observe_metrics_common as observe;
25use prometheus::HistogramVec;
26use prometheus::Registry;
27use prometheus::core::AtomicI64;
28use prometheus::core::AtomicU64;
29use prometheus::core::GenericCounterVec;
30use prometheus::core::GenericGaugeVec;
31use prometheus::register_histogram_vec_with_registry;
32use prometheus::register_int_counter_vec_with_registry;
33use prometheus::register_int_gauge_vec_with_registry;
34
35#[derive(Clone, Debug)]
141pub struct PrometheusLayer {
142 interceptor: PrometheusInterceptor,
143}
144
145impl PrometheusLayer {
146 pub fn builder() -> PrometheusLayerBuilder {
176 PrometheusLayerBuilder::default()
177 }
178}
179
180impl Layer for PrometheusLayer {
181 fn apply_service(&self, inner: Servicer) -> Servicer {
182 observe::MetricsLayer::new(self.interceptor.clone()).apply_service(inner)
183 }
184
185 fn apply_context(&self, srv: Servicer, inner: OperationContext) -> OperationContext {
187 observe::MetricsLayer::new(self.interceptor.clone()).apply_context(srv, inner)
188 }
189}
190
191pub struct PrometheusLayerBuilder {
193 bytes_buckets: Vec<f64>,
194 bytes_rate_buckets: Vec<f64>,
195 entries_buckets: Vec<f64>,
196 entries_rate_buckets: Vec<f64>,
197 duration_seconds_buckets: Vec<f64>,
198 ttfb_buckets: Vec<f64>,
199}
200
201impl Default for PrometheusLayerBuilder {
202 fn default() -> Self {
203 Self {
204 bytes_buckets: observe::DEFAULT_BYTES_BUCKETS.to_vec(),
205 bytes_rate_buckets: observe::DEFAULT_BYTES_RATE_BUCKETS.to_vec(),
206 entries_buckets: observe::DEFAULT_ENTRIES_BUCKETS.to_vec(),
207 entries_rate_buckets: observe::DEFAULT_ENTRIES_RATE_BUCKETS.to_vec(),
208 duration_seconds_buckets: observe::DEFAULT_DURATION_SECONDS_BUCKETS.to_vec(),
209 ttfb_buckets: observe::DEFAULT_TTFB_BUCKETS.to_vec(),
210 }
211 }
212}
213
214impl PrometheusLayerBuilder {
215 pub fn bytes_buckets(mut self, buckets: Vec<f64>) -> Self {
217 if !buckets.is_empty() {
218 self.bytes_buckets = buckets;
219 }
220 self
221 }
222
223 pub fn bytes_rate_buckets(mut self, buckets: Vec<f64>) -> Self {
225 if !buckets.is_empty() {
226 self.bytes_rate_buckets = buckets;
227 }
228 self
229 }
230
231 pub fn entries_buckets(mut self, buckets: Vec<f64>) -> Self {
233 if !buckets.is_empty() {
234 self.entries_buckets = buckets;
235 }
236 self
237 }
238
239 pub fn entries_rate_buckets(mut self, buckets: Vec<f64>) -> Self {
241 if !buckets.is_empty() {
242 self.entries_rate_buckets = buckets;
243 }
244 self
245 }
246
247 pub fn duration_seconds_buckets(mut self, buckets: Vec<f64>) -> Self {
249 if !buckets.is_empty() {
250 self.duration_seconds_buckets = buckets;
251 }
252 self
253 }
254
255 pub fn ttfb_buckets(mut self, buckets: Vec<f64>) -> Self {
257 if !buckets.is_empty() {
258 self.ttfb_buckets = buckets;
259 }
260 self
261 }
262
263 pub fn register(self, registry: &Registry) -> Result<PrometheusLayer> {
287 let labels = OperationLabels::names();
288 let operation_bytes = {
289 let metric = observe::MetricValue::OperationBytes(0);
290 register_histogram_vec_with_registry!(
291 metric.name(),
292 metric.help(),
293 labels.as_ref(),
294 self.bytes_buckets.clone(),
295 registry
296 )
297 .map_err(parse_prometheus_error)?
298 };
299 let operation_bytes_rate = {
300 let metric = observe::MetricValue::OperationBytesRate(0.0);
301 register_histogram_vec_with_registry!(
302 metric.name(),
303 metric.help(),
304 labels.as_ref(),
305 self.bytes_rate_buckets.clone(),
306 registry
307 )
308 .map_err(parse_prometheus_error)?
309 };
310 let operation_entries = {
311 let metric = observe::MetricValue::OperationEntries(0);
312 register_histogram_vec_with_registry!(
313 metric.name(),
314 metric.help(),
315 labels.as_ref(),
316 self.entries_buckets,
317 registry
318 )
319 .map_err(parse_prometheus_error)?
320 };
321 let operation_entries_rate = {
322 let metric = observe::MetricValue::OperationEntriesRate(0.0);
323 register_histogram_vec_with_registry!(
324 metric.name(),
325 metric.help(),
326 labels.as_ref(),
327 self.entries_rate_buckets,
328 registry
329 )
330 .map_err(parse_prometheus_error)?
331 };
332 let operation_duration_seconds = {
333 let metric = observe::MetricValue::OperationDurationSeconds(Duration::default());
334 register_histogram_vec_with_registry!(
335 metric.name(),
336 metric.help(),
337 labels.as_ref(),
338 self.duration_seconds_buckets.clone(),
339 registry
340 )
341 .map_err(parse_prometheus_error)?
342 };
343 let operation_executing = {
344 let metric = observe::MetricValue::OperationExecuting(0);
345 register_int_gauge_vec_with_registry!(
346 metric.name(),
347 metric.help(),
348 labels.as_ref(),
349 registry
350 )
351 .map_err(parse_prometheus_error)?
352 };
353 let operation_ttfb_seconds = {
354 let metric = observe::MetricValue::OperationTtfbSeconds(Duration::default());
355 register_histogram_vec_with_registry!(
356 metric.name(),
357 metric.help(),
358 labels.as_ref(),
359 self.ttfb_buckets.clone(),
360 registry
361 )
362 .map_err(parse_prometheus_error)?
363 };
364
365 let labels_with_error = OperationLabels::names().with_error();
366 let operation_errors_total = {
367 let metric = observe::MetricValue::OperationErrorsTotal;
368 register_int_counter_vec_with_registry!(
369 metric.name(),
370 metric.help(),
371 labels_with_error.as_ref(),
372 registry
373 )
374 .map_err(parse_prometheus_error)?
375 };
376
377 let http_labels = OperationLabels::names().with_service_operation();
378 let http_executing = {
379 let metric = observe::MetricValue::HttpExecuting(0);
380 register_int_gauge_vec_with_registry!(
381 metric.name(),
382 metric.help(),
383 http_labels.as_ref(),
384 registry
385 )
386 .map_err(parse_prometheus_error)?
387 };
388 let http_request_bytes = {
389 let metric = observe::MetricValue::HttpRequestBytes(0);
390 register_histogram_vec_with_registry!(
391 metric.name(),
392 metric.help(),
393 http_labels.as_ref(),
394 self.bytes_buckets.clone(),
395 registry
396 )
397 .map_err(parse_prometheus_error)?
398 };
399 let http_request_bytes_rate = {
400 let metric = observe::MetricValue::HttpRequestBytesRate(0.0);
401 register_histogram_vec_with_registry!(
402 metric.name(),
403 metric.help(),
404 http_labels.as_ref(),
405 self.bytes_rate_buckets.clone(),
406 registry
407 )
408 .map_err(parse_prometheus_error)?
409 };
410 let http_request_duration_seconds = {
411 let metric = observe::MetricValue::HttpRequestDurationSeconds(Duration::default());
412 register_histogram_vec_with_registry!(
413 metric.name(),
414 metric.help(),
415 http_labels.as_ref(),
416 self.duration_seconds_buckets.clone(),
417 registry
418 )
419 .map_err(parse_prometheus_error)?
420 };
421 let http_response_bytes = {
422 let metric = observe::MetricValue::HttpResponseBytes(0);
423 register_histogram_vec_with_registry!(
424 metric.name(),
425 metric.help(),
426 http_labels.as_ref(),
427 self.bytes_buckets,
428 registry
429 )
430 .map_err(parse_prometheus_error)?
431 };
432 let http_response_bytes_rate = {
433 let metric = observe::MetricValue::HttpResponseBytesRate(0.0);
434 register_histogram_vec_with_registry!(
435 metric.name(),
436 metric.help(),
437 http_labels.as_ref(),
438 self.bytes_rate_buckets,
439 registry
440 )
441 .map_err(parse_prometheus_error)?
442 };
443 let http_response_duration_seconds = {
444 let metric = observe::MetricValue::HttpResponseDurationSeconds(Duration::default());
445 register_histogram_vec_with_registry!(
446 metric.name(),
447 metric.help(),
448 http_labels.as_ref(),
449 self.duration_seconds_buckets,
450 registry
451 )
452 .map_err(parse_prometheus_error)?
453 };
454 let http_connection_errors_total = {
455 let metric = observe::MetricValue::HttpConnectionErrorsTotal;
456 register_int_counter_vec_with_registry!(
457 metric.name(),
458 metric.help(),
459 http_labels.as_ref(),
460 registry
461 )
462 .map_err(parse_prometheus_error)?
463 };
464
465 let http_labels_with_status_code = OperationLabels::names()
466 .with_service_operation()
467 .with_status_code();
468 let http_status_errors_total = {
469 let metric = observe::MetricValue::HttpStatusErrorsTotal;
470 register_int_counter_vec_with_registry!(
471 metric.name(),
472 metric.help(),
473 http_labels_with_status_code.as_ref(),
474 registry
475 )
476 .map_err(parse_prometheus_error)?
477 };
478
479 Ok(PrometheusLayer {
480 interceptor: PrometheusInterceptor {
481 operation_bytes,
482 operation_bytes_rate,
483 operation_entries,
484 operation_entries_rate,
485 operation_duration_seconds,
486 operation_errors_total,
487 operation_executing,
488 operation_ttfb_seconds,
489
490 http_executing,
491 http_request_bytes,
492 http_request_bytes_rate,
493 http_request_duration_seconds,
494 http_response_bytes,
495 http_response_bytes_rate,
496 http_response_duration_seconds,
497 http_connection_errors_total,
498 http_status_errors_total,
499 },
500 })
501 }
502
503 pub fn register_default(self) -> Result<PrometheusLayer> {
527 let registry = prometheus::default_registry();
528 self.register(registry)
529 }
530}
531
532fn parse_prometheus_error(err: prometheus::Error) -> Error {
534 Error::new(ErrorKind::Unexpected, err.to_string()).set_source(err)
535}
536
537#[doc(hidden)]
538#[derive(Clone, Debug)]
539pub struct PrometheusInterceptor {
540 operation_bytes: HistogramVec,
541 operation_bytes_rate: HistogramVec,
542 operation_entries: HistogramVec,
543 operation_entries_rate: HistogramVec,
544 operation_duration_seconds: HistogramVec,
545 operation_errors_total: GenericCounterVec<AtomicU64>,
546 operation_executing: GenericGaugeVec<AtomicI64>,
547 operation_ttfb_seconds: HistogramVec,
548
549 http_executing: GenericGaugeVec<AtomicI64>,
550 http_request_bytes: HistogramVec,
551 http_request_bytes_rate: HistogramVec,
552 http_request_duration_seconds: HistogramVec,
553 http_response_bytes: HistogramVec,
554 http_response_bytes_rate: HistogramVec,
555 http_response_duration_seconds: HistogramVec,
556 http_connection_errors_total: GenericCounterVec<AtomicU64>,
557 http_status_errors_total: GenericCounterVec<AtomicU64>,
558}
559
560impl observe::MetricsIntercept for PrometheusInterceptor {
561 fn observe(&self, labels: observe::MetricLabels, value: observe::MetricValue) {
562 let labels = OperationLabels(labels);
563 match value {
564 observe::MetricValue::OperationBytes(v) => self
565 .operation_bytes
566 .with_label_values(&labels.op_values())
567 .observe(v as f64),
568 observe::MetricValue::OperationBytesRate(v) => self
569 .operation_bytes_rate
570 .with_label_values(&labels.op_values())
571 .observe(v),
572 observe::MetricValue::OperationEntries(v) => self
573 .operation_entries
574 .with_label_values(&labels.op_values())
575 .observe(v as f64),
576 observe::MetricValue::OperationEntriesRate(v) => self
577 .operation_entries_rate
578 .with_label_values(&labels.op_values())
579 .observe(v),
580 observe::MetricValue::OperationDurationSeconds(v) => self
581 .operation_duration_seconds
582 .with_label_values(&labels.op_values())
583 .observe(v.as_secs_f64()),
584 observe::MetricValue::OperationErrorsTotal => self
585 .operation_errors_total
586 .with_label_values(&labels.op_values())
587 .inc(),
588 observe::MetricValue::OperationExecuting(v) => self
589 .operation_executing
590 .with_label_values(&labels.op_values())
591 .add(v as i64),
592 observe::MetricValue::OperationTtfbSeconds(v) => self
593 .operation_ttfb_seconds
594 .with_label_values(&labels.op_values())
595 .observe(v.as_secs_f64()),
596
597 observe::MetricValue::HttpExecuting(v) => self
598 .http_executing
599 .with_label_values(&labels.http_values())
600 .add(v as i64),
601 observe::MetricValue::HttpRequestBytes(v) => self
602 .http_request_bytes
603 .with_label_values(&labels.http_values())
604 .observe(v as f64),
605 observe::MetricValue::HttpRequestBytesRate(v) => self
606 .http_request_bytes_rate
607 .with_label_values(&labels.http_values())
608 .observe(v),
609 observe::MetricValue::HttpRequestDurationSeconds(v) => self
610 .http_request_duration_seconds
611 .with_label_values(&labels.http_values())
612 .observe(v.as_secs_f64()),
613 observe::MetricValue::HttpResponseBytes(v) => self
614 .http_response_bytes
615 .with_label_values(&labels.http_values())
616 .observe(v as f64),
617 observe::MetricValue::HttpResponseBytesRate(v) => self
618 .http_response_bytes_rate
619 .with_label_values(&labels.http_values())
620 .observe(v),
621 observe::MetricValue::HttpResponseDurationSeconds(v) => self
622 .http_response_duration_seconds
623 .with_label_values(&labels.http_values())
624 .observe(v.as_secs_f64()),
625 observe::MetricValue::HttpConnectionErrorsTotal => self
626 .http_connection_errors_total
627 .with_label_values(&labels.http_values())
628 .inc(),
629 observe::MetricValue::HttpStatusErrorsTotal => self
630 .http_status_errors_total
631 .with_label_values(&labels.http_values())
632 .inc(),
633 _ => {}
634 }
635 }
636}
637
638struct OperationLabelNames(Vec<&'static str>);
639
640impl AsRef<[&'static str]> for OperationLabelNames {
641 fn as_ref(&self) -> &[&'static str] {
642 &self.0
643 }
644}
645
646impl OperationLabelNames {
647 fn with_error(mut self) -> Self {
648 self.0.push(observe::LABEL_ERROR);
649 self
650 }
651
652 fn with_status_code(mut self) -> Self {
653 self.0.push(observe::LABEL_STATUS_CODE);
654 self
655 }
656
657 fn with_service_operation(mut self) -> Self {
658 self.0.push(observe::LABEL_SERVICE_OPERATION);
659 self
660 }
661}
662
663#[derive(Clone, Debug, PartialEq, Eq, Hash)]
664struct OperationLabels(observe::MetricLabels);
665
666impl OperationLabels {
667 fn names() -> OperationLabelNames {
668 OperationLabelNames(vec![
669 observe::LABEL_SCHEME,
670 observe::LABEL_NAMESPACE,
671 observe::LABEL_ROOT,
672 observe::LABEL_OPERATION,
673 ])
674 }
675
676 fn op_values(&self) -> Vec<&str> {
677 let mut labels = vec![
678 self.0.scheme,
679 self.0.namespace.as_ref(),
680 self.0.root.as_ref(),
681 self.0.operation,
682 ];
683
684 if let Some(error) = self.0.error {
685 labels.push(error.into_static());
686 }
687
688 labels
689 }
690
691 fn http_values(&self) -> Vec<&str> {
692 let mut labels = vec![
693 self.0.scheme,
694 self.0.namespace.as_ref(),
695 self.0.root.as_ref(),
696 self.0.operation,
697 self.0.service_operation.unwrap_or("unknown"),
698 ];
699
700 if let Some(error) = self.0.error {
701 labels.push(error.into_static());
702 }
703
704 if let Some(status_code) = &self.0.status_code {
705 labels.push(status_code.as_str());
706 }
707
708 labels
709 }
710}