This is an automated email from the ASF dual-hosted git repository. xuanwo pushed a commit to branch main in repository https://gitbox.apache.org/repos/asf/opendal.git
The following commit(s) were added to refs/heads/main by this push: new 9731a7a3b refactor(layer/otelmetrics): Migrate OtelMetricsLayer to support IO metrics (#5901) 9731a7a3b is described below commit 9731a7a3b8ce981f752905f36c628d22fecd125a Author: Qinxuan Chen <koushiro....@gmail.com> AuthorDate: Fri Mar 28 13:05:44 2025 +0800 refactor(layer/otelmetrics): Migrate OtelMetricsLayer to support IO metrics (#5901) * refactor(layer/otelmetrics): Migrate OtelMetricsLayer to support IO metrics * return Vec<f64> instead of iterator * cleanup * specify name explictly * remove exponential_boundaries --- core/src/layers/observe/metrics.rs | 50 ---- core/src/layers/observe/mod.rs | 41 --- core/src/layers/otelmetrics.rs | 589 ++++++++++++++++++++----------------- core/src/layers/prometheus.rs | 6 - 4 files changed, 326 insertions(+), 360 deletions(-) diff --git a/core/src/layers/observe/metrics.rs b/core/src/layers/observe/metrics.rs index e8d165713..d908ce61e 100644 --- a/core/src/layers/observe/metrics.rs +++ b/core/src/layers/observe/metrics.rs @@ -133,8 +133,6 @@ pub static LABEL_ROOT: &str = "root"; pub static LABEL_OPERATION: &str = "operation"; /// The metric label for the error. pub static LABEL_ERROR: &str = "error"; -/// The metrics label for the path. (will be removed) -pub static LABEL_PATH: &str = "path"; /// The metric label for the http code. pub static LABEL_STATUS_CODE: &str = "status_code"; @@ -395,54 +393,6 @@ pub trait MetricsIntercept: Debug + Clone + Send + Sync + Unpin + 'static { fn observe(&self, labels: MetricLabels, value: MetricValue) { let _ = (labels, value); } - - /// Observe the operation duration in seconds. - fn observe_operation_duration_seconds( - &self, - info: Arc<AccessorInfo>, - path: &str, - op: Operation, - duration: Duration, - ) { - let _ = (info, path, op, duration); - } - - /// Observe the operation bytes happened in IO like read and write. - fn observe_operation_bytes( - &self, - info: Arc<AccessorInfo>, - path: &str, - op: Operation, - bytes: usize, - ) { - let _ = (info, path, op, bytes); - } - - /// Observe the operation errors total. - fn observe_operation_errors_total( - &self, - info: Arc<AccessorInfo>, - path: &str, - op: Operation, - error: ErrorKind, - ) { - let _ = (info, path, op, error); - } - - /// Observe the http request duration in seconds. - fn observe_http_request_duration_seconds( - &self, - info: Arc<AccessorInfo>, - op: Operation, - duration: Duration, - ) { - let _ = (info, op, duration); - } - - /// Observe the operation bytes happened in http request like read and write. - fn observe_http_request_bytes(&self, info: Arc<AccessorInfo>, op: Operation, bytes: usize) { - let _ = (info, op, bytes); - } } /// The metrics layer for opendal. diff --git a/core/src/layers/observe/mod.rs b/core/src/layers/observe/mod.rs index dd8185ef6..86959b3ca 100644 --- a/core/src/layers/observe/mod.rs +++ b/core/src/layers/observe/mod.rs @@ -89,7 +89,6 @@ pub use metrics::DEFAULT_TTFB_BUCKETS; pub use metrics::LABEL_ERROR; pub use metrics::LABEL_NAMESPACE; pub use metrics::LABEL_OPERATION; -pub use metrics::LABEL_PATH; pub use metrics::LABEL_ROOT; pub use metrics::LABEL_SCHEME; pub use metrics::LABEL_STATUS_CODE; @@ -98,43 +97,3 @@ pub use metrics::METRIC_HTTP_REQUEST_DURATION_SECONDS; pub use metrics::METRIC_OPERATION_BYTES; pub use metrics::METRIC_OPERATION_DURATION_SECONDS; pub use metrics::METRIC_OPERATION_ERRORS_TOTAL; - -/// Return the path label value according to the given `path` and `level`. -/// -/// - level = 0: return `None`, which means we ignore the path label. -/// - level > 0: the path label will be the path split by "/" and get the last n level, -/// if n=1 and input path is "abc/def/ghi", and then we'll use "abc/" as the path label. -pub fn path_label_value(path: &str, level: usize) -> Option<&str> { - if level > 0 { - if path.is_empty() { - return Some(""); - } - - let label_value = path - .char_indices() - .filter(|&(_, c)| c == '/') - .nth(level - 1) - .map_or(path, |(i, _)| &path[..i]); - Some(label_value) - } else { - None - } -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn test_path_label_value() { - let path = "abc/def/ghi"; - assert_eq!(path_label_value(path, 0), None); - assert_eq!(path_label_value(path, 1), Some("abc")); - assert_eq!(path_label_value(path, 2), Some("abc/def")); - assert_eq!(path_label_value(path, 3), Some("abc/def/ghi")); - assert_eq!(path_label_value(path, usize::MAX), Some("abc/def/ghi")); - - assert_eq!(path_label_value("", 0), None); - assert_eq!(path_label_value("", 1), Some("")); - } -} diff --git a/core/src/layers/otelmetrics.rs b/core/src/layers/otelmetrics.rs index ffac5fc1c..3f3ca4121 100644 --- a/core/src/layers/otelmetrics.rs +++ b/core/src/layers/otelmetrics.rs @@ -15,17 +15,16 @@ // specific language governing permissions and limitations // under the License. -use std::sync::Arc; use std::time::Duration; use opentelemetry::metrics::Counter; use opentelemetry::metrics::Histogram; use opentelemetry::metrics::Meter; +use opentelemetry::metrics::UpDownCounter; use opentelemetry::KeyValue; use crate::layers::observe; use crate::raw::*; -use crate::*; /// Add [opentelemetry::metrics](https://docs.rs/opentelemetry/latest/opentelemetry/metrics/index.html) for every operation. /// @@ -53,10 +52,6 @@ pub struct OtelMetricsLayer { impl OtelMetricsLayer { /// Create a [`OtelMetricsLayerBuilder`] to set the configuration of metrics. /// - /// # Default Configuration - /// - /// - `path_label`: `0` - /// /// # Examples /// /// ```no_run @@ -69,196 +64,85 @@ impl OtelMetricsLayer { /// # async fn main() -> Result<()> { /// let meter = opentelemetry::global::meter("opendal"); /// let op = Operator::new(services::Memory::default())? - /// .layer(OtelMetricsLayer::builder().path_label(1).register(&meter)) + /// .layer(OtelMetricsLayer::builder().register(&meter)) /// .finish(); /// /// Ok(()) /// # } /// ``` pub fn builder() -> OtelMetricsLayerBuilder { - OtelMetricsLayerBuilder::new() + OtelMetricsLayerBuilder::default() } } /// [`OtelMetricsLayerBuilder`] is a config builder to build a [`OtelMetricsLayer`]. pub struct OtelMetricsLayerBuilder { - operation_duration_seconds_boundaries: Vec<f64>, - operation_bytes_boundaries: Vec<f64>, - http_request_duration_seconds_boundaries: Vec<f64>, - http_request_bytes_boundaries: Vec<f64>, - path_label_level: usize, + bytes_boundaries: Vec<f64>, + bytes_rate_boundaries: Vec<f64>, + entries_boundaries: Vec<f64>, + entries_rate_boundaries: Vec<f64>, + duration_seconds_boundaries: Vec<f64>, + ttfb_boundaries: Vec<f64>, } -impl OtelMetricsLayerBuilder { - fn new() -> Self { +impl Default for OtelMetricsLayerBuilder { + fn default() -> Self { Self { - operation_duration_seconds_boundaries: exponential_boundary(0.01, 2.0, 16), - operation_bytes_boundaries: exponential_boundary(1.0, 2.0, 16), - http_request_duration_seconds_boundaries: exponential_boundary(0.01, 2.0, 16), - http_request_bytes_boundaries: exponential_boundary(1.0, 2.0, 16), - path_label_level: 0, + bytes_boundaries: observe::DEFAULT_BYTES_BUCKETS.to_vec(), + bytes_rate_boundaries: observe::DEFAULT_BYTES_RATE_BUCKETS.to_vec(), + entries_boundaries: observe::DEFAULT_ENTRIES_BUCKETS.to_vec(), + entries_rate_boundaries: observe::DEFAULT_ENTRIES_RATE_BUCKETS.to_vec(), + duration_seconds_boundaries: observe::DEFAULT_DURATION_SECONDS_BUCKETS.to_vec(), + ttfb_boundaries: observe::DEFAULT_TTFB_BUCKETS.to_vec(), } } +} - /// Set the level of path label. - /// - /// - level = 0: we will ignore the path label. - /// - level > 0: the path label will be the path split by "/" and get the last n level, - /// if n=1 and input path is "abc/def/ghi", and then we will get "abc/" as the path label. - /// - /// # Examples - /// - /// ```no_run - /// # use log::debug; - /// # use opendal::layers::OtelMetricsLayer; - /// # use opendal::services; - /// # use opendal::Operator; - /// # use opendal::Result; - /// - /// # #[tokio::main] - /// # async fn main() -> Result<()> { - /// let meter = opentelemetry::global::meter("opendal"); - /// let op = Operator::new(services::Memory::default())? - /// .layer(OtelMetricsLayer::builder().path_label(1).register(&meter)) - /// .finish(); - /// debug!("operator: {op:?}"); - /// - /// Ok(()) - /// # } - /// ``` - pub fn path_label(mut self, level: usize) -> Self { - self.path_label_level = level; +impl OtelMetricsLayerBuilder { + /// Set boundaries for bytes related histogram like `operation_bytes`. + pub fn bytes_boundaries(mut self, boundaries: Vec<f64>) -> Self { + if !boundaries.is_empty() { + self.bytes_boundaries = boundaries; + } self } - /// Set boundaries for `operation_duration_seconds` histogram. - /// - /// # Examples - /// - /// ```no_run - /// # use log::debug; - /// # use opendal::layers::OtelMetricsLayer; - /// # use opendal::services; - /// # use opendal::Operator; - /// # use opendal::Result; - /// - /// # #[tokio::main] - /// # async fn main() -> Result<()> { - /// let meter = opentelemetry::global::meter("opendal"); - /// let op = Operator::new(services::Memory::default())? - /// .layer( - /// OtelMetricsLayer::builder() - /// .operation_duration_seconds_boundaries(vec![0.01, 0.02, 0.05, 0.1, 0.2, 0.5]) - /// .register(&meter) - /// ) - /// .finish(); - /// debug!("operator: {op:?}"); - /// - /// Ok(()) - /// # } - /// ``` - pub fn operation_duration_seconds_boundaries(mut self, boundaries: Vec<f64>) -> Self { + /// Set boundaries for bytes rate related histogram like `operation_bytes_rate`. + pub fn bytes_rate_boundaries(mut self, boundaries: Vec<f64>) -> Self { if !boundaries.is_empty() { - self.operation_duration_seconds_boundaries = boundaries; + self.bytes_rate_boundaries = boundaries; } self } - /// Set boundaries for `operation_bytes` histogram. - /// - /// # Examples - /// - /// ```no_run - /// # use log::debug; - /// # use opendal::layers::OtelMetricsLayer; - /// # use opendal::services; - /// # use opendal::Operator; - /// # use opendal::Result; - /// - /// # #[tokio::main] - /// # async fn main() -> Result<()> { - /// let meter = opentelemetry::global::meter("opendal"); - /// let op = Operator::new(services::Memory::default())? - /// .layer( - /// OtelMetricsLayer::builder() - /// .operation_bytes_boundaries(vec![1.0, 2.0, 5.0, 10.0, 20.0, 50.0]) - /// .register(&meter) - /// ) - /// .finish(); - /// debug!("operator: {op:?}"); - /// - /// Ok(()) - /// # } - /// ``` - pub fn operation_bytes_boundaries(mut self, boundaries: Vec<f64>) -> Self { + /// Set boundaries for entries related histogram like `operation_entries`. + pub fn entries_boundaries(mut self, boundaries: Vec<f64>) -> Self { if !boundaries.is_empty() { - self.operation_bytes_boundaries = boundaries; + self.entries_boundaries = boundaries; } self } - /// Set boundaries for `http_request_duration_seconds` histogram. - /// - /// # Examples - /// - /// ```no_run - /// # use log::debug; - /// # use opendal::layers::OtelMetricsLayer; - /// # use opendal::services; - /// # use opendal::Operator; - /// # use opendal::Result; - /// - /// # #[tokio::main] - /// # async fn main() -> Result<()> { - /// let meter = opentelemetry::global::meter("opendal"); - /// let op = Operator::new(services::Memory::default())? - /// .layer( - /// OtelMetricsLayer::builder() - /// .http_request_duration_seconds_boundaries(vec![0.01, 0.02, 0.05, 0.1, 0.2, 0.5]) - /// .register(&meter) - /// ) - /// .finish(); - /// debug!("operator: {op:?}"); - /// - /// Ok(()) - /// # } - /// ``` - pub fn http_request_duration_seconds_boundaries(mut self, boundaries: Vec<f64>) -> Self { + /// Set boundaries for entries rate related histogram like `operation_entries_rate`. + pub fn entries_rate_boundaries(mut self, boundaries: Vec<f64>) -> Self { if !boundaries.is_empty() { - self.http_request_duration_seconds_boundaries = boundaries; + self.entries_rate_boundaries = boundaries; } self } - /// Set boundaries for `http_request_bytes` histogram. - /// - /// # Examples - /// - /// ```no_run - /// # use log::debug; - /// # use opendal::layers::OtelMetricsLayer; - /// # use opendal::services; - /// # use opendal::Operator; - /// # use opendal::Result; - /// - /// # #[tokio::main] - /// # async fn main() -> Result<()> { - /// let meter = opentelemetry::global::meter("opendal"); - /// let op = Operator::new(services::Memory::default())? - /// .layer( - /// OtelMetricsLayer::builder() - /// .http_request_bytes_boundaries(vec![1.0, 2.0, 5.0, 10.0, 20.0, 50.0]) - /// .register(&meter) - /// ) - /// .finish(); - /// debug!("operator: {op:?}"); - /// - /// Ok(()) - /// # } - /// ``` - pub fn http_request_bytes_boundaries(mut self, boundaries: Vec<f64>) -> Self { + /// Set boundaries for duration seconds related histogram like `operation_duration_seconds`. + pub fn duration_seconds_boundaries(mut self, boundaries: Vec<f64>) -> Self { if !boundaries.is_empty() { - self.http_request_bytes_boundaries = boundaries; + self.duration_seconds_boundaries = boundaries; + } + self + } + + /// Set boundaries for ttfb related histogram like `operation_ttfb_seconds`. + pub fn ttfb_boundaries(mut self, boundaries: Vec<f64>) -> Self { + if !boundaries.is_empty() { + self.ttfb_boundaries = boundaries; } self } @@ -277,48 +161,179 @@ impl OtelMetricsLayerBuilder { /// # async fn main() -> Result<()> { /// let meter = opentelemetry::global::meter("opendal"); /// let op = Operator::new(services::Memory::default())? - /// .layer(OtelMetricsLayer::builder().register(&meter)) + /// .layer(OtelMetricsLayer::builder() + /// .register(&meter)) /// .finish(); /// /// Ok(()) /// # } /// ``` pub fn register(self, meter: &Meter) -> OtelMetricsLayer { - let operation_duration_seconds = meter - .f64_histogram("opendal.operation.duration") - .with_description("Duration of operations") - .with_unit("second") - .with_boundaries(self.operation_duration_seconds_boundaries) - .build(); - let operation_bytes = meter - .u64_histogram("opendal.operation.size") - .with_description("Size of operations") - .with_unit("byte") - .with_boundaries(self.operation_bytes_boundaries) - .build(); - let http_request_duration_seconds = meter - .f64_histogram("opendal.http.request.duration") - .with_description("Duration of http requests") - .with_unit("second") - .build(); - let http_request_bytes = meter - .u64_histogram("opendal.http.request.size") - .with_description("Size of http requests") - .with_unit("byte") - .build(); - let errors = meter - .u64_counter("opendal.operation.errors") - .with_description("Number of operation errors") - .build(); + let operation_bytes = { + let metric = observe::MetricValue::OperationBytes(0); + register_u64_histogram_meter( + meter, + "opendal.operation.bytes", + metric, + self.bytes_boundaries.clone(), + ) + }; + let operation_bytes_rate = { + let metric = observe::MetricValue::OperationBytesRate(0.0); + register_f64_histogram_meter( + meter, + "opendal.operation.bytes_rate", + metric, + self.bytes_rate_boundaries.clone(), + ) + }; + let operation_entries = { + let metric = observe::MetricValue::OperationEntries(0); + register_u64_histogram_meter( + meter, + "opendal.operation.entries", + metric, + self.entries_boundaries.clone(), + ) + }; + let operation_entries_rate = { + let metric = observe::MetricValue::OperationEntriesRate(0.0); + register_f64_histogram_meter( + meter, + "opendal.operation.entries_rate", + metric, + self.entries_rate_boundaries.clone(), + ) + }; + let operation_duration_seconds = { + let metric = observe::MetricValue::OperationDurationSeconds(Duration::default()); + register_f64_histogram_meter( + meter, + "opendal.operation.duration", + metric, + self.duration_seconds_boundaries.clone(), + ) + }; + let operation_errors_total = { + let metric = observe::MetricValue::OperationErrorsTotal; + meter + .u64_counter("opendal.operation.errors") + .with_description(metric.help()) + .build() + }; + let operation_executing = { + let metric = observe::MetricValue::OperationExecuting(0); + meter + .i64_up_down_counter("opendal.operation.executing") + .with_description(metric.help()) + .build() + }; + let operation_ttfb_seconds = { + let metric = observe::MetricValue::OperationTtfbSeconds(Duration::default()); + register_f64_histogram_meter( + meter, + "opendal.operation.ttfb", + metric, + self.duration_seconds_boundaries.clone(), + ) + }; + + let http_executing = { + let metric = observe::MetricValue::HttpExecuting(0); + meter + .i64_up_down_counter("opendal.http.executing") + .with_description(metric.help()) + .build() + }; + let http_request_bytes = { + let metric = observe::MetricValue::HttpRequestBytes(0); + register_u64_histogram_meter( + meter, + "opendal.http.request.bytes", + metric, + self.bytes_boundaries.clone(), + ) + }; + let http_request_bytes_rate = { + let metric = observe::MetricValue::HttpRequestBytesRate(0.0); + register_f64_histogram_meter( + meter, + "opendal.http.request.bytes_rate", + metric, + self.bytes_rate_boundaries.clone(), + ) + }; + let http_request_duration_seconds = { + let metric = observe::MetricValue::HttpRequestDurationSeconds(Duration::default()); + register_f64_histogram_meter( + meter, + "opendal.http.request.duration", + metric, + self.duration_seconds_boundaries.clone(), + ) + }; + let http_response_bytes = { + let metric = observe::MetricValue::HttpResponseBytes(0); + register_u64_histogram_meter( + meter, + "opendal.http.response.bytes", + metric, + self.bytes_boundaries.clone(), + ) + }; + let http_response_bytes_rate = { + let metric = observe::MetricValue::HttpResponseBytesRate(0.0); + register_f64_histogram_meter( + meter, + "opendal.http.response.bytes_rate", + metric, + self.bytes_rate_boundaries.clone(), + ) + }; + let http_response_duration_seconds = { + let metric = observe::MetricValue::HttpResponseDurationSeconds(Duration::default()); + register_f64_histogram_meter( + meter, + "opendal.http.response.duration", + metric, + self.duration_seconds_boundaries.clone(), + ) + }; + let http_connection_errors_total = { + let metric = observe::MetricValue::HttpConnectionErrorsTotal; + meter + .u64_counter("opendal.http.connection_errors") + .with_description(metric.help()) + .build() + }; + let http_status_errors_total = { + let metric = observe::MetricValue::HttpStatusErrorsTotal; + meter + .u64_counter("opendal.http.status_errors") + .with_description(metric.help()) + .build() + }; OtelMetricsLayer { interceptor: OtelMetricsInterceptor { - operation_duration_seconds, operation_bytes, - http_request_duration_seconds, + operation_bytes_rate, + operation_entries, + operation_entries_rate, + operation_duration_seconds, + operation_errors_total, + operation_executing, + operation_ttfb_seconds, + + http_executing, http_request_bytes, - errors, - path_label_level: self.path_label_level, + http_request_bytes_rate, + http_request_duration_seconds, + http_response_bytes, + http_response_bytes_rate, + http_response_duration_seconds, + http_connection_errors_total, + http_status_errors_total, }, } } @@ -334,101 +349,149 @@ impl<A: Access> Layer<A> for OtelMetricsLayer { #[derive(Clone, Debug)] pub struct OtelMetricsInterceptor { - operation_duration_seconds: Histogram<f64>, operation_bytes: Histogram<u64>, - http_request_duration_seconds: Histogram<f64>, + operation_bytes_rate: Histogram<f64>, + operation_entries: Histogram<u64>, + operation_entries_rate: Histogram<f64>, + operation_duration_seconds: Histogram<f64>, + operation_errors_total: Counter<u64>, + operation_executing: UpDownCounter<i64>, + operation_ttfb_seconds: Histogram<f64>, + + http_executing: UpDownCounter<i64>, http_request_bytes: Histogram<u64>, - errors: Counter<u64>, - path_label_level: usize, + http_request_bytes_rate: Histogram<f64>, + http_request_duration_seconds: Histogram<f64>, + http_response_bytes: Histogram<u64>, + http_response_bytes_rate: Histogram<f64>, + http_response_duration_seconds: Histogram<f64>, + http_connection_errors_total: Counter<u64>, + http_status_errors_total: Counter<u64>, } impl observe::MetricsIntercept for OtelMetricsInterceptor { - fn observe_operation_duration_seconds( - &self, - info: Arc<AccessorInfo>, - path: &str, - op: Operation, - duration: Duration, - ) { - let attributes = self.create_attributes(info, path, op, None); - self.operation_duration_seconds - .record(duration.as_secs_f64(), &attributes); - } - - fn observe_operation_bytes( - &self, - info: Arc<AccessorInfo>, - path: &str, - op: Operation, - bytes: usize, - ) { - let attributes = self.create_attributes(info, path, op, None); - self.operation_bytes.record(bytes as u64, &attributes); - } - - fn observe_operation_errors_total( - &self, - info: Arc<AccessorInfo>, - path: &str, - op: Operation, - error: ErrorKind, - ) { - let attributes = self.create_attributes(info, path, op, Some(error)); - self.errors.add(1, &attributes); - } + fn observe(&self, labels: observe::MetricLabels, value: observe::MetricValue) { + let attributes = self.create_attributes(labels); - fn observe_http_request_duration_seconds( - &self, - info: Arc<AccessorInfo>, - op: Operation, - duration: Duration, - ) { - let attributes = self.create_attributes(info, "", op, None); - self.http_request_duration_seconds - .record(duration.as_secs_f64(), &attributes); - } + match value { + observe::MetricValue::OperationBytes(v) => self.operation_bytes.record(v, &attributes), + observe::MetricValue::OperationBytesRate(v) => { + self.operation_bytes_rate.record(v, &attributes) + } + observe::MetricValue::OperationEntries(v) => { + self.operation_entries.record(v, &attributes) + } + observe::MetricValue::OperationEntriesRate(v) => { + self.operation_entries_rate.record(v, &attributes) + } + observe::MetricValue::OperationDurationSeconds(v) => self + .operation_duration_seconds + .record(v.as_secs_f64(), &attributes), + observe::MetricValue::OperationErrorsTotal => { + self.operation_errors_total.add(1, &attributes) + } + observe::MetricValue::OperationExecuting(v) => { + self.operation_executing.add(v as i64, &attributes) + } + observe::MetricValue::OperationTtfbSeconds(v) => self + .operation_ttfb_seconds + .record(v.as_secs_f64(), &attributes), - fn observe_http_request_bytes(&self, info: Arc<AccessorInfo>, op: Operation, bytes: usize) { - let attributes = self.create_attributes(info, "", op, None); - self.http_request_bytes.record(bytes as u64, &attributes); + observe::MetricValue::HttpExecuting(v) => { + self.http_executing.add(v as i64, &attributes) + } + observe::MetricValue::HttpRequestBytes(v) => { + self.http_request_bytes.record(v, &attributes) + } + observe::MetricValue::HttpRequestBytesRate(v) => { + self.http_request_bytes_rate.record(v, &attributes) + } + observe::MetricValue::HttpRequestDurationSeconds(v) => self + .http_request_duration_seconds + .record(v.as_secs_f64(), &attributes), + observe::MetricValue::HttpResponseBytes(v) => { + self.http_response_bytes.record(v, &attributes) + } + observe::MetricValue::HttpResponseBytesRate(v) => { + self.http_response_bytes_rate.record(v, &attributes) + } + observe::MetricValue::HttpResponseDurationSeconds(v) => self + .http_response_duration_seconds + .record(v.as_secs_f64(), &attributes), + observe::MetricValue::HttpConnectionErrorsTotal => { + self.http_connection_errors_total.add(1, &attributes) + } + observe::MetricValue::HttpStatusErrorsTotal => { + self.http_status_errors_total.add(1, &attributes) + } + } } } impl OtelMetricsInterceptor { - fn create_attributes( - &self, - info: Arc<AccessorInfo>, - path: &str, - operation: Operation, - error: Option<ErrorKind>, - ) -> Vec<KeyValue> { + fn create_attributes(&self, attrs: observe::MetricLabels) -> Vec<KeyValue> { let mut attributes = Vec::with_capacity(6); attributes.extend([ - KeyValue::new(observe::LABEL_SCHEME, info.scheme().into_static()), - KeyValue::new(observe::LABEL_NAMESPACE, info.name().clone()), - KeyValue::new(observe::LABEL_ROOT, info.root().clone()), - KeyValue::new(observe::LABEL_OPERATION, operation.into_static()), + KeyValue::new(observe::LABEL_SCHEME, attrs.scheme.into_static()), + KeyValue::new(observe::LABEL_NAMESPACE, attrs.namespace), + KeyValue::new(observe::LABEL_ROOT, attrs.root), + KeyValue::new(observe::LABEL_OPERATION, attrs.operation), ]); - if let Some(path) = observe::path_label_value(path, self.path_label_level) { - attributes.push(KeyValue::new(observe::LABEL_PATH, path.to_owned())); + if let Some(error) = attrs.error { + attributes.push(KeyValue::new(observe::LABEL_ERROR, error.into_static())); } - if let Some(error) = error { - attributes.push(KeyValue::new(observe::LABEL_ERROR, error.into_static())); + if let Some(status_code) = attrs.status_code { + attributes.push(KeyValue::new( + observe::LABEL_STATUS_CODE, + status_code.as_u16() as i64, + )); } attributes } } -fn exponential_boundary(start: f64, factor: f64, count: usize) -> Vec<f64> { - let mut boundaries = Vec::with_capacity(count); - let mut current = start; - for _ in 0..count { - boundaries.push(current); - current *= factor; +fn register_u64_histogram_meter( + meter: &Meter, + name: &'static str, + metric: observe::MetricValue, + boundaries: Vec<f64>, +) -> Histogram<u64> { + let (_name, unit) = metric.name_with_unit(); + let description = metric.help(); + + let builder = meter + .u64_histogram(name) + .with_description(description) + .with_boundaries(boundaries); + + if let Some(unit) = unit { + builder.with_unit(unit).build() + } else { + builder.build() + } +} + +fn register_f64_histogram_meter( + meter: &Meter, + name: &'static str, + metric: observe::MetricValue, + boundaries: Vec<f64>, +) -> Histogram<f64> { + let (_name, unit) = metric.name_with_unit(); + let description = metric.help(); + + let builder = meter + .f64_histogram(name) + .with_description(description) + .with_boundaries(boundaries); + + if let Some(unit) = unit { + builder.with_unit(unit).build() + } else { + builder.build() } - boundaries } diff --git a/core/src/layers/prometheus.rs b/core/src/layers/prometheus.rs index 726759102..95b9d9e2f 100644 --- a/core/src/layers/prometheus.rs +++ b/core/src/layers/prometheus.rs @@ -91,12 +91,6 @@ pub struct PrometheusLayer { impl PrometheusLayer { /// Create a [`PrometheusLayerBuilder`] to set the configuration of metrics. /// - /// # Default Configuration - /// - /// - `operation_duration_seconds_buckets`: `exponential_buckets(0.01, 2.0, 16)` - /// - `operation_bytes_buckets`: `exponential_buckets(1.0, 2.0, 16)` - /// - `path_label`: `0` - /// /// # Example /// /// ```no_run