#![doc = include_str!("../README.md")]
#![cfg_attr(docsrs, feature(doc_cfg))]
#![cfg_attr(docsrs, doc(auto_cfg))]
#![deny(missing_docs)]
use metrics::Label;
use metrics::counter;
use metrics::gauge;
use metrics::histogram;
use opendal_core::OperationContext;
use opendal_core::raw::*;
use opendal_layer_observe_metrics_common as observe;
#[derive(Clone, Debug, Default)]
#[non_exhaustive]
pub struct MetricsLayer {}
impl MetricsLayer {
pub fn new() -> Self {
Self::default()
}
}
impl Layer for MetricsLayer {
fn apply_service(&self, inner: Servicer) -> Servicer {
let interceptor = MetricsInterceptor {};
observe::MetricsLayer::new(interceptor).apply_service(inner)
}
fn apply_context(&self, srv: Servicer, inner: OperationContext) -> OperationContext {
let interceptor = MetricsInterceptor {};
observe::MetricsLayer::new(interceptor).apply_context(srv, inner)
}
}
#[doc(hidden)]
#[derive(Clone, Debug)]
pub struct MetricsInterceptor;
impl observe::MetricsIntercept for MetricsInterceptor {
fn observe(&self, labels: observe::MetricLabels, value: observe::MetricValue) {
let labels = OperationLabels(labels).into_labels();
match value {
observe::MetricValue::OperationBytes(v) => {
histogram!(value.name(), labels).record(v as f64)
}
observe::MetricValue::OperationBytesRate(v) => {
histogram!(value.name(), labels).record(v)
}
observe::MetricValue::OperationEntries(v) => {
histogram!(value.name(), labels).record(v as f64)
}
observe::MetricValue::OperationEntriesRate(v) => {
histogram!(value.name(), labels).record(v)
}
observe::MetricValue::OperationDurationSeconds(v) => {
histogram!(value.name(), labels).record(v)
}
observe::MetricValue::OperationErrorsTotal => {
counter!(value.name(), labels).increment(1)
}
observe::MetricValue::OperationExecuting(v) => {
gauge!(value.name(), labels).increment(v as f64)
}
observe::MetricValue::OperationTtfbSeconds(v) => {
histogram!(value.name(), labels).record(v)
}
observe::MetricValue::HttpExecuting(v) => {
gauge!(value.name(), labels).increment(v as f64)
}
observe::MetricValue::HttpRequestBytes(v) => {
histogram!(value.name(), labels).record(v as f64)
}
observe::MetricValue::HttpRequestBytesRate(v) => {
histogram!(value.name(), labels).record(v)
}
observe::MetricValue::HttpRequestDurationSeconds(v) => {
histogram!(value.name(), labels).record(v)
}
observe::MetricValue::HttpResponseBytes(v) => {
histogram!(value.name(), labels).record(v as f64)
}
observe::MetricValue::HttpResponseBytesRate(v) => {
histogram!(value.name(), labels).record(v)
}
observe::MetricValue::HttpResponseDurationSeconds(v) => {
histogram!(value.name(), labels).record(v)
}
observe::MetricValue::HttpConnectionErrorsTotal => {
counter!(value.name(), labels).increment(1)
}
observe::MetricValue::HttpStatusErrorsTotal => {
counter!(value.name(), labels).increment(1)
}
_ => {}
}
}
}
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
struct OperationLabels(observe::MetricLabels);
impl OperationLabels {
fn into_labels(self) -> Vec<Label> {
let mut labels = Vec::with_capacity(6);
labels.extend([
Label::new(observe::LABEL_SCHEME, self.0.scheme),
Label::new(observe::LABEL_NAMESPACE, self.0.namespace),
Label::new(observe::LABEL_ROOT, self.0.root),
Label::new(observe::LABEL_OPERATION, self.0.operation),
]);
if let Some(error) = self.0.error {
labels.push(Label::new(observe::LABEL_ERROR, error.into_static()));
}
if let Some(status_code) = self.0.status_code {
labels.push(Label::new(
observe::LABEL_STATUS_CODE,
status_code.as_str().to_owned(),
));
}
if let Some(service_operation) = self.0.service_operation {
labels.push(Label::new(
observe::LABEL_SERVICE_OPERATION,
service_operation,
));
}
labels
}
}