diff --git a/crates/obs/src/metrics/audit.rs b/crates/obs/src/metrics/audit.rs new file mode 100644 index 000000000..a92c98024 --- /dev/null +++ b/crates/obs/src/metrics/audit.rs @@ -0,0 +1,30 @@ +use crate::metrics::{new_counter_md, new_gauge_md, subsystems, MetricDescriptor, MetricName}; + +const TARGET_ID: &str = "target_id"; + +/// audit related metric descriptors +lazy_static::lazy_static! { + pub static ref AUDIT_FAILED_MESSAGES_MD: MetricDescriptor = + new_counter_md( + MetricName::AuditFailedMessages, + "Total number of messages that failed to send since start", + &[TARGET_ID], + subsystems::AUDIT + ); + + pub static ref AUDIT_TARGET_QUEUE_LENGTH_MD: MetricDescriptor = + new_gauge_md( + MetricName::AuditTargetQueueLength, + "Number of unsent messages in queue for target", + &[TARGET_ID], + subsystems::AUDIT + ); + + pub static ref AUDIT_TOTAL_MESSAGES_MD: MetricDescriptor = + new_counter_md( + MetricName::AuditTotalMessages, + "Total number of messages sent since start", + &[TARGET_ID], + subsystems::AUDIT + ); +} diff --git a/crates/obs/src/metrics/bucket.rs b/crates/obs/src/metrics/bucket.rs new file mode 100644 index 000000000..fda403477 --- /dev/null +++ b/crates/obs/src/metrics/bucket.rs @@ -0,0 +1,68 @@ +use crate::metrics::{new_counter_md, new_gauge_md, new_histogram_md, subsystems, MetricDescriptor, MetricName}; + +/// Bucket 级别 S3 指标描述符 +lazy_static::lazy_static! { + pub static ref BUCKET_API_TRAFFIC_SENT_BYTES_MD: MetricDescriptor = + new_counter_md( + MetricName::ApiTrafficSentBytes, + "Total number of bytes received for a bucket", + &["bucket", "type"], + subsystems::BUCKET_API + ); + + pub static ref BUCKET_API_TRAFFIC_RECV_BYTES_MD: MetricDescriptor = + new_counter_md( + MetricName::ApiTrafficRecvBytes, + "Total number of bytes sent for a bucket", + &["bucket", "type"], + subsystems::BUCKET_API + ); + + pub static ref BUCKET_API_REQUESTS_IN_FLIGHT_MD: MetricDescriptor = + new_gauge_md( + MetricName::ApiRequestsInFlightTotal, + "Total number of requests currently in flight for a bucket", + &["bucket", "name", "type"], + subsystems::BUCKET_API + ); + + pub static ref BUCKET_API_REQUESTS_TOTAL_MD: MetricDescriptor = + new_counter_md( + MetricName::ApiRequestsTotal, + "Total number of requests for a bucket", + &["bucket", "name", "type"], + subsystems::BUCKET_API + ); + + pub static ref BUCKET_API_REQUESTS_CANCELED_MD: MetricDescriptor = + new_counter_md( + MetricName::ApiRequestsCanceledTotal, + "Total number of requests canceled by the client for a bucket", + &["bucket", "name", "type"], + subsystems::BUCKET_API + ); + + pub static ref BUCKET_API_REQUESTS_4XX_ERRORS_MD: MetricDescriptor = + new_counter_md( + MetricName::ApiRequests4xxErrorsTotal, + "Total number of requests with 4xx errors for a bucket", + &["bucket", "name", "type"], + subsystems::BUCKET_API + ); + + pub static ref BUCKET_API_REQUESTS_5XX_ERRORS_MD: MetricDescriptor = + new_counter_md( + MetricName::ApiRequests5xxErrorsTotal, + "Total number of requests with 5xx errors for a bucket", + &["bucket", "name", "type"], + subsystems::BUCKET_API + ); + + pub static ref BUCKET_API_REQUESTS_TTFB_SECONDS_DISTRIBUTION_MD: MetricDescriptor = + new_histogram_md( + MetricName::ApiRequestsTTFBSecondsDistribution, + "Distribution of time to first byte across API calls for a bucket", + &["bucket", "name", "le", "type"], + subsystems::BUCKET_API + ); +} diff --git a/crates/obs/src/metrics/bucket_replication.rs b/crates/obs/src/metrics/bucket_replication.rs new file mode 100644 index 000000000..acfbd2dde --- /dev/null +++ b/crates/obs/src/metrics/bucket_replication.rs @@ -0,0 +1,165 @@ +use crate::metrics::{new_counter_md, new_gauge_md, subsystems, MetricDescriptor, MetricName}; + +// Label constants +pub const BUCKET_L: &str = "bucket"; +pub const OPERATION_L: &str = "operation"; +pub const TARGET_ARN_L: &str = "targetArn"; +pub const RANGE_L: &str = "range"; + +/// Bucket copy metric descriptor +lazy_static::lazy_static! { + pub static ref BUCKET_REPL_LAST_HR_FAILED_BYTES_MD: MetricDescriptor = + new_gauge_md( + MetricName::LastHourFailedBytes, + "Total number of bytes failed at least once to replicate in the last hour on a bucket", + &[BUCKET_L], + subsystems::BUCKET_REPLICATION + ); + + pub static ref BUCKET_REPL_LAST_HR_FAILED_COUNT_MD: MetricDescriptor = + new_gauge_md( + MetricName::LastHourFailedCount, + "Total number of objects which failed replication in the last hour on a bucket", + &[BUCKET_L], + subsystems::BUCKET_REPLICATION + ); + + pub static ref BUCKET_REPL_LAST_MIN_FAILED_BYTES_MD: MetricDescriptor = + new_gauge_md( + MetricName::LastMinFailedBytes, + "Total number of bytes failed at least once to replicate in the last full minute on a bucket", + &[BUCKET_L], + subsystems::BUCKET_REPLICATION + ); + + pub static ref BUCKET_REPL_LAST_MIN_FAILED_COUNT_MD: MetricDescriptor = + new_gauge_md( + MetricName::LastMinFailedCount, + "Total number of objects which failed replication in the last full minute on a bucket", + &[BUCKET_L], + subsystems::BUCKET_REPLICATION + ); + + pub static ref BUCKET_REPL_LATENCY_MS_MD: MetricDescriptor = + new_gauge_md( + MetricName::LatencyMilliSec, + "Replication latency on a bucket in milliseconds", + &[BUCKET_L, OPERATION_L, RANGE_L, TARGET_ARN_L], + subsystems::BUCKET_REPLICATION + ); + + pub static ref BUCKET_REPL_PROXIED_DELETE_TAGGING_REQUESTS_TOTAL_MD: MetricDescriptor = + new_counter_md( + MetricName::ProxiedDeleteTaggingRequestsTotal, + "Number of DELETE tagging requests proxied to replication target", + &[BUCKET_L], + subsystems::BUCKET_REPLICATION + ); + + pub static ref BUCKET_REPL_PROXIED_GET_REQUESTS_FAILURES_MD: MetricDescriptor = + new_counter_md( + MetricName::ProxiedGetRequestsFailures, + "Number of failures in GET requests proxied to replication target", + &[BUCKET_L], + subsystems::BUCKET_REPLICATION + ); + + pub static ref BUCKET_REPL_PROXIED_GET_REQUESTS_TOTAL_MD: MetricDescriptor = + new_counter_md( + MetricName::ProxiedGetRequestsTotal, + "Number of GET requests proxied to replication target", + &[BUCKET_L], + subsystems::BUCKET_REPLICATION + ); + + // TODO - add a metric for the number of PUT requests proxied to replication target + pub static ref BUCKET_REPL_PROXIED_GET_TAGGING_REQUESTS_FAILURES_MD: MetricDescriptor = + new_counter_md( + MetricName::ProxiedGetTaggingRequestFailures, + "Number of failures in GET tagging requests proxied to replication target", + &[BUCKET_L], + subsystems::BUCKET_REPLICATION + ); + + pub static ref BUCKET_REPL_PROXIED_GET_TAGGING_REQUESTS_TOTAL_MD: MetricDescriptor = + new_counter_md( + MetricName::ProxiedGetTaggingRequestsTotal, + "Number of GET tagging requests proxied to replication target", + &[BUCKET_L], + subsystems::BUCKET_REPLICATION + ); + + pub static ref BUCKET_REPL_PROXIED_HEAD_REQUESTS_FAILURES_MD: MetricDescriptor = + new_counter_md( + MetricName::ProxiedHeadRequestsFailures, + "Number of failures in HEAD requests proxied to replication target", + &[BUCKET_L], + subsystems::BUCKET_REPLICATION + ); + + pub static ref BUCKET_REPL_PROXIED_HEAD_REQUESTS_TOTAL_MD: MetricDescriptor = + new_counter_md( + MetricName::ProxiedHeadRequestsTotal, + "Number of HEAD requests proxied to replication target", + &[BUCKET_L], + subsystems::BUCKET_REPLICATION + ); + + // TODO - add a metric for the number of PUT requests proxied to replication target + pub static ref BUCKET_REPL_PROXIED_PUT_TAGGING_REQUESTS_FAILURES_MD: MetricDescriptor = + new_counter_md( + MetricName::ProxiedPutTaggingRequestFailures, + "Number of failures in PUT tagging requests proxied to replication target", + &[BUCKET_L], + subsystems::BUCKET_REPLICATION + ); + + pub static ref BUCKET_REPL_PROXIED_PUT_TAGGING_REQUESTS_TOTAL_MD: MetricDescriptor = + new_counter_md( + MetricName::ProxiedPutTaggingRequestsTotal, + "Number of PUT tagging requests proxied to replication target", + &[BUCKET_L], + subsystems::BUCKET_REPLICATION + ); + + pub static ref BUCKET_REPL_SENT_BYTES_MD: MetricDescriptor = + new_counter_md( + MetricName::SentBytes, + "Total number of bytes replicated to the target", + &[BUCKET_L], + subsystems::BUCKET_REPLICATION + ); + + pub static ref BUCKET_REPL_SENT_COUNT_MD: MetricDescriptor = + new_counter_md( + MetricName::SentCount, + "Total number of objects replicated to the target", + &[BUCKET_L], + subsystems::BUCKET_REPLICATION + ); + + pub static ref BUCKET_REPL_TOTAL_FAILED_BYTES_MD: MetricDescriptor = + new_counter_md( + MetricName::TotalFailedBytes, + "Total number of bytes failed at least once to replicate since server start", + &[BUCKET_L], + subsystems::BUCKET_REPLICATION + ); + + pub static ref BUCKET_REPL_TOTAL_FAILED_COUNT_MD: MetricDescriptor = + new_counter_md( + MetricName::TotalFailedCount, + "Total number of objects which failed replication since server start", + &[BUCKET_L], + subsystems::BUCKET_REPLICATION + ); + + // TODO - add a metric for the number of DELETE requests proxied to replication target + pub static ref BUCKET_REPL_PROXIED_DELETE_TAGGING_REQUESTS_FAILURES_MD: MetricDescriptor = + new_counter_md( + MetricName::ProxiedDeleteTaggingRequestFailures, + "Number of failures in DELETE tagging requests proxied to replication target", + &[BUCKET_L], + subsystems::BUCKET_REPLICATION + ); +} diff --git a/crates/obs/src/metrics/cluster_config.rs b/crates/obs/src/metrics/cluster_config.rs new file mode 100644 index 000000000..ee262f692 --- /dev/null +++ b/crates/obs/src/metrics/cluster_config.rs @@ -0,0 +1,20 @@ +use crate::metrics::{new_gauge_md, subsystems, MetricDescriptor, MetricName}; + +/// 集群配置相关指标描述符 +lazy_static::lazy_static! { + pub static ref CONFIG_RRS_PARITY_MD: MetricDescriptor = + new_gauge_md( + MetricName::ConfigRRSParity, + "Reduced redundancy storage class parity", + &[], // 无标签 + subsystems::CLUSTER_CONFIG + ); + + pub static ref CONFIG_STANDARD_PARITY_MD: MetricDescriptor = + new_gauge_md( + MetricName::ConfigStandardParity, + "Standard storage class parity", + &[], // 无标签 + subsystems::CLUSTER_CONFIG + ); +} diff --git a/crates/obs/src/metrics/entry/description.rs b/crates/obs/src/metrics/entry/description.rs deleted file mode 100644 index c7d1becff..000000000 --- a/crates/obs/src/metrics/entry/description.rs +++ /dev/null @@ -1,12 +0,0 @@ -use crate::metrics::{MetricName, MetricNamespace, MetricSubsystem, MetricType}; - -/// The metric description describes the metric -/// It contains the namespace, subsystem, name, help text, and type of the metric -#[derive(Debug, Clone)] -pub struct MetricDescription { - pub namespace: MetricNamespace, - pub subsystem: MetricSubsystem, - pub name: MetricName, - pub help: String, - pub metric_type: MetricType, -} diff --git a/crates/obs/src/metrics/entry/descriptor.rs b/crates/obs/src/metrics/entry/descriptor.rs index 808c0e37a..a5232c87d 100644 --- a/crates/obs/src/metrics/entry/descriptor.rs +++ b/crates/obs/src/metrics/entry/descriptor.rs @@ -1,36 +1,58 @@ -use crate::metrics::{MetricName, MetricType}; +use crate::metrics::{MetricName, MetricNamespace, MetricSubsystem, MetricType}; use std::collections::HashSet; -/// MetricDescriptor - Indicates the metric descriptor +/// MetricDescriptor - 指标描述符 +#[allow(dead_code)] #[derive(Debug, Clone)] pub struct MetricDescriptor { pub name: MetricName, pub metric_type: MetricType, pub help: String, pub variable_labels: Vec, + pub namespace: MetricNamespace, + pub subsystem: MetricSubsystem, // 从 String 修改为 MetricSubsystem - // internal values for management: + // 内部管理值 label_set: Option>, } impl MetricDescriptor { - /// Create a new metric descriptor - pub fn new(name: MetricName, metric_type: MetricType, help: String, variable_labels: Vec) -> Self { + /// 创建新的指标描述符 + pub fn new( + name: MetricName, + metric_type: MetricType, + help: String, + variable_labels: Vec, + namespace: MetricNamespace, + subsystem: impl Into, // 修改参数类型 + ) -> Self { Self { name, metric_type, help, variable_labels, + namespace, + subsystem: subsystem.into(), label_set: None, } } - /// Check whether the label is in the label set + /// 获取完整的指标名称,包含前缀和格式化路径 + pub fn get_full_metric_name(&self) -> String { + let prefix = self.metric_type.to_prom(); + let namespace = self.namespace.as_str(); + let formatted_subsystem = self.subsystem.as_str(); + + format!("{}{}_{}_{}", prefix, namespace, formatted_subsystem, self.name.as_str()) + } + + /// 检查标签是否在标签集中 + #[allow(dead_code)] pub fn has_label(&mut self, label: &str) -> bool { self.get_label_set().contains(label) } - /// Gets a collection of tags, and creates if it doesn't exist + /// 获取标签集合,如果不存在则创建 pub fn get_label_set(&mut self) -> &HashSet { if self.label_set.is_none() { let mut set = HashSet::with_capacity(self.variable_labels.len()); diff --git a/crates/obs/src/metrics/entry/metric_name.rs b/crates/obs/src/metrics/entry/metric_name.rs index 842d707c9..62b9f19f7 100644 --- a/crates/obs/src/metrics/entry/metric_name.rs +++ b/crates/obs/src/metrics/entry/metric_name.rs @@ -1,4 +1,5 @@ /// The metric name is the individual name of the metric +#[allow(dead_code)] #[derive(Debug, Clone, PartialEq, Eq)] pub enum MetricName { // 通用指标名称 @@ -158,6 +159,15 @@ pub enum MetricName { ApiTrafficSentBytes, ApiTrafficRecvBytes, + // 审计指标 + AuditFailedMessages, + AuditTargetQueueLength, + AuditTotalMessages, + + // 集群配置相关指标 + ConfigRRSParity, + ConfigStandardParity, + // 自定义指标 Custom(String), } @@ -303,6 +313,14 @@ impl MetricName { Self::ApiTrafficSentBytes => "traffic_sent_bytes".to_string(), Self::ApiTrafficRecvBytes => "traffic_received_bytes".to_string(), + Self::AuditFailedMessages => "failed_messages".to_string(), + Self::AuditTargetQueueLength => "target_queue_length".to_string(), + Self::AuditTotalMessages => "total_messages".to_string(), + + /// metrics related to cluster configurations + Self::ConfigRRSParity => "rrs_parity".to_string(), + Self::ConfigStandardParity => "standard_parity".to_string(), + Self::Custom(name) => name.clone(), } } diff --git a/crates/obs/src/metrics/entry/metric_type.rs b/crates/obs/src/metrics/entry/metric_type.rs index 163cc9099..614a8539d 100644 --- a/crates/obs/src/metrics/entry/metric_type.rs +++ b/crates/obs/src/metrics/entry/metric_type.rs @@ -1,4 +1,5 @@ /// MetricType - Indicates the type of indicator +#[allow(dead_code)] #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub enum MetricType { Counter, @@ -8,6 +9,7 @@ pub enum MetricType { impl MetricType { /// convert the metric type to a string representation + #[allow(dead_code)] pub fn as_str(&self) -> &'static str { match self { Self::Counter => "counter", @@ -20,9 +22,9 @@ impl MetricType { /// In a Rust implementation, this might return the corresponding Prometheus Rust client type pub fn to_prom(&self) -> &'static str { match self { - Self::Counter => "counter_value", - Self::Gauge => "gauge_value", - Self::Histogram => "counter_value", // 直方图在 Prometheus 中仍使用 counter 值 + Self::Counter => "counter.", + Self::Gauge => "gauge.", + Self::Histogram => "histogram.", // Histograms still use the counter value in Prometheus } } } diff --git a/crates/obs/src/metrics/entry/mod.rs b/crates/obs/src/metrics/entry/mod.rs index 440eb7f00..937435538 100644 --- a/crates/obs/src/metrics/entry/mod.rs +++ b/crates/obs/src/metrics/entry/mod.rs @@ -1,32 +1,115 @@ -use crate::metrics::{MetricDescriptor, MetricName, MetricType}; +use crate::metrics::{MetricDescriptor, MetricName, MetricNamespace, MetricSubsystem, MetricType}; -pub(crate) mod description; pub(crate) mod descriptor; pub(crate) mod metric_name; pub(crate) mod metric_type; pub(crate) mod namespace; +mod path_utils; pub(crate) mod subsystem; -/// Represents a range label constant -pub const RANGE_LABEL: &str = "range"; -pub const SERVER_NAME: &str = "server"; - /// Create a new counter metric descriptor -pub fn new_counter_md(name: impl Into, help: impl Into, labels: &[&str]) -> MetricDescriptor { +pub fn new_counter_md( + name: impl Into, + help: impl Into, + labels: &[&str], + subsystem: impl Into, +) -> MetricDescriptor { MetricDescriptor::new( name.into(), MetricType::Counter, help.into(), labels.iter().map(|&s| s.to_string()).collect(), + MetricNamespace::RustFS, + subsystem, ) } -/// Create a new gauge metric descriptor -pub fn new_gauge_md(name: impl Into, help: impl Into, labels: &[&str]) -> MetricDescriptor { +/// create a new dashboard metric descriptor +pub fn new_gauge_md( + name: impl Into, + help: impl Into, + labels: &[&str], + subsystem: impl Into, +) -> MetricDescriptor { MetricDescriptor::new( name.into(), MetricType::Gauge, help.into(), labels.iter().map(|&s| s.to_string()).collect(), + MetricNamespace::RustFS, + subsystem, ) } + +/// create a new histogram indicator descriptor +#[allow(dead_code)] +pub fn new_histogram_md( + name: impl Into, + help: impl Into, + labels: &[&str], + subsystem: impl Into, +) -> MetricDescriptor { + MetricDescriptor::new( + name.into(), + MetricType::Histogram, + help.into(), + labels.iter().map(|&s| s.to_string()).collect(), + MetricNamespace::RustFS, + subsystem, + ) +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::metrics::subsystems; + + #[test] + fn test_new_histogram_md() { + // create a histogram indicator descriptor + let histogram_md = new_histogram_md( + MetricName::TtfbDistribution, + "test the response time distribution", + &["api", "method", "le"], + subsystems::API_REQUESTS, + ); + + // verify that the metric type is correct + assert_eq!(histogram_md.metric_type, MetricType::Histogram); + + // verify that the metric name is correct + assert_eq!(histogram_md.name.as_str(), "seconds_distribution"); + + // verify that the help information is correct + assert_eq!(histogram_md.help, "test the response time distribution"); + + // Verify that the label is correct + assert_eq!(histogram_md.variable_labels.len(), 3); + assert!(histogram_md.variable_labels.contains(&"api".to_string())); + assert!(histogram_md.variable_labels.contains(&"method".to_string())); + assert!(histogram_md.variable_labels.contains(&"le".to_string())); + + // Verify that the namespace is correct + assert_eq!(histogram_md.namespace, MetricNamespace::RustFS); + + // Verify that the subsystem is correct + assert_eq!(histogram_md.subsystem, MetricSubsystem::ApiRequests); + + // Verify that the full metric name generated is formatted correctly + assert_eq!(histogram_md.get_full_metric_name(), "histogram.rustfs_api_requests_seconds_distribution"); + + // Tests use custom subsystems + let custom_histogram_md = new_histogram_md( + "custom_latency_distribution", + "custom latency distribution", + &["endpoint", "le"], + MetricSubsystem::new("/custom/path-metrics"), + ); + + // Verify the custom name and subsystem + assert_eq!( + custom_histogram_md.get_full_metric_name(), + "histogram.rustfs_custom_path_metrics_custom_latency_distribution" + ); + } +} diff --git a/crates/obs/src/metrics/entry/namespace.rs b/crates/obs/src/metrics/entry/namespace.rs index 85c487b4f..851ba0b82 100644 --- a/crates/obs/src/metrics/entry/namespace.rs +++ b/crates/obs/src/metrics/entry/namespace.rs @@ -1,25 +1,13 @@ -/// The metric namespace is the top-level grouping created by the metric name +/// The metric namespace, which represents the top-level grouping of the metric #[derive(Debug, Clone, PartialEq, Eq)] pub enum MetricNamespace { - Bucket, - Cluster, - Heal, - InterNode, - Node, RustFS, - S3, } impl MetricNamespace { pub fn as_str(&self) -> &'static str { match self { - Self::Bucket => "rustfs_bucket", - Self::Cluster => "rustfs_cluster", - Self::Heal => "rustfs_heal", - Self::InterNode => "rustfs_inter_node", - Self::Node => "rustfs_node", Self::RustFS => "rustfs", - Self::S3 => "rustfs_s3", } } } diff --git a/crates/obs/src/metrics/entry/path_utils.rs b/crates/obs/src/metrics/entry/path_utils.rs new file mode 100644 index 000000000..3b83df3eb --- /dev/null +++ b/crates/obs/src/metrics/entry/path_utils.rs @@ -0,0 +1,18 @@ +/// Format the path to the metric name format +/// Replace '/' and '-' with '_' +pub fn format_path_to_metric_name(path: &str) -> String { + path.trim_start_matches('/').replace('/', "_").replace('-', "_") +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_format_path_to_metric_name() { + assert_eq!(format_path_to_metric_name("/api/requests"), "api_requests"); + assert_eq!(format_path_to_metric_name("/system/network/internode"), "system_network_internode"); + assert_eq!(format_path_to_metric_name("/bucket-api"), "bucket_api"); + assert_eq!(format_path_to_metric_name("cluster/health"), "cluster_health"); + } +} diff --git a/crates/obs/src/metrics/entry/subsystem.rs b/crates/obs/src/metrics/entry/subsystem.rs index 91d3f9e3d..715088299 100644 --- a/crates/obs/src/metrics/entry/subsystem.rs +++ b/crates/obs/src/metrics/entry/subsystem.rs @@ -1,79 +1,230 @@ +use crate::metrics::entry::path_utils::format_path_to_metric_name; + /// The metrics subsystem is a subgroup of metrics within a namespace -#[derive(Debug, Clone, PartialEq, Eq)] +/// 指标子系统,表示命名空间内指标的子分组 +#[derive(Debug, Clone, PartialEq, Eq, Hash)] pub enum MetricSubsystem { - Cache, - CapacityRaw, - CapacityUsable, - Drive, - Interface, - Memory, - CpuAvg, - StorageClass, - FileDescriptor, - GoRoutine, - Io, - Nodes, - Objects, - Bucket, - Process, - Replication, - Requests, - RequestsRejected, - Time, - Ttfb, - Traffic, - Software, - Syscall, - Usage, - Quota, + // API 相关子系统 + ApiRequests, + + // 桶相关子系统 + BucketApi, + BucketReplication, + + // 系统相关子系统 + SystemNetworkInternode, + SystemDrive, + SystemMemory, + SystemCpu, + SystemProcess, + + // 调试相关子系统 + DebugGo, + + // 集群相关子系统 + ClusterHealth, + ClusterUsageObjects, + ClusterUsageBuckets, + ClusterErasureSet, + ClusterIam, + ClusterConfig, + + // 其他服务相关子系统 Ilm, - Tier, - Scanner, - Iam, - Kms, - Notify, - Lambda, Audit, - Webhook, + LoggerWebhook, + Replication, + Notification, + Scanner, + + // 自定义路径 + Custom(String), } impl MetricSubsystem { - pub fn as_str(&self) -> &'static str { + /// 获取原始路径字符串 + pub fn path(&self) -> &str { match self { - Self::Cache => "cache", - Self::CapacityRaw => "capacity_raw", - Self::CapacityUsable => "capacity_usable", - Self::Drive => "drive", - Self::Interface => "if", - Self::Memory => "mem", - Self::CpuAvg => "cpu_avg", - Self::StorageClass => "storage_class", - Self::FileDescriptor => "file_descriptor", - Self::GoRoutine => "go_routine", - Self::Io => "io", - Self::Nodes => "nodes", - Self::Objects => "objects", - Self::Bucket => "bucket", - Self::Process => "process", - Self::Replication => "replication", - Self::Requests => "requests", - Self::RequestsRejected => "requests_rejected", - Self::Time => "time", - Self::Ttfb => "requests_ttfb", - Self::Traffic => "traffic", - Self::Software => "software", - Self::Syscall => "syscall", - Self::Usage => "usage", - Self::Quota => "quota", - Self::Ilm => "ilm", - Self::Tier => "tier", - Self::Scanner => "scanner", - Self::Iam => "iam", - Self::Kms => "kms", - Self::Notify => "notify", - Self::Lambda => "lambda", - Self::Audit => "audit", - Self::Webhook => "webhook", + // API 相关子系统 + Self::ApiRequests => "/api/requests", + + // 桶相关子系统 + Self::BucketApi => "/bucket/api", + Self::BucketReplication => "/bucket/replication", + + // 系统相关子系统 + Self::SystemNetworkInternode => "/system/network/internode", + Self::SystemDrive => "/system/drive", + Self::SystemMemory => "/system/memory", + Self::SystemCpu => "/system/cpu", + Self::SystemProcess => "/system/process", + + // 调试相关子系统 + Self::DebugGo => "/debug/go", + + // 集群相关子系统 + Self::ClusterHealth => "/cluster/health", + Self::ClusterUsageObjects => "/cluster/usage/objects", + Self::ClusterUsageBuckets => "/cluster/usage/buckets", + Self::ClusterErasureSet => "/cluster/erasure-set", + Self::ClusterIam => "/cluster/iam", + Self::ClusterConfig => "/cluster/config", + + // 其他服务相关子系统 + Self::Ilm => "/ilm", + Self::Audit => "/audit", + Self::LoggerWebhook => "/logger/webhook", + Self::Replication => "/replication", + Self::Notification => "/notification", + Self::Scanner => "/scanner", + + // 自定义路径 + Self::Custom(path) => path, } } + + /// 获取格式化后的指标名称格式字符串 + pub fn as_str(&self) -> String { + format_path_to_metric_name(self.path()) + } + + /// 从路径字符串创建子系统枚举 + pub fn from_path(path: &str) -> Self { + match path { + // API 相关子系统 + "/api/requests" => Self::ApiRequests, + + // 桶相关子系统 + "/bucket/api" => Self::BucketApi, + "/bucket/replication" => Self::BucketReplication, + + // 系统相关子系统 + "/system/network/internode" => Self::SystemNetworkInternode, + "/system/drive" => Self::SystemDrive, + "/system/memory" => Self::SystemMemory, + "/system/cpu" => Self::SystemCpu, + "/system/process" => Self::SystemProcess, + + // 调试相关子系统 + "/debug/go" => Self::DebugGo, + + // 集群相关子系统 + "/cluster/health" => Self::ClusterHealth, + "/cluster/usage/objects" => Self::ClusterUsageObjects, + "/cluster/usage/buckets" => Self::ClusterUsageBuckets, + "/cluster/erasure-set" => Self::ClusterErasureSet, + "/cluster/iam" => Self::ClusterIam, + "/cluster/config" => Self::ClusterConfig, + + // 其他服务相关子系统 + "/ilm" => Self::Ilm, + "/audit" => Self::Audit, + "/logger/webhook" => Self::LoggerWebhook, + "/replication" => Self::Replication, + "/notification" => Self::Notification, + "/scanner" => Self::Scanner, + + // 其他路径作为自定义处理 + _ => Self::Custom(path.to_string()), + } + } + + // 便利方法,直接创建自定义子系统 + pub fn new(path: impl Into) -> Self { + Self::Custom(path.into()) + } +} + +// 便于与字符串相互转换的实现 +impl From<&str> for MetricSubsystem { + fn from(s: &str) -> Self { + Self::from_path(s) + } +} + +impl From for MetricSubsystem { + fn from(s: String) -> Self { + Self::from_path(&s) + } +} + +impl std::fmt::Display for MetricSubsystem { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{}", self.path()) + } +} + +#[allow(dead_code)] +pub mod subsystems { + use super::MetricSubsystem; + + // 集群基本路径常量 + pub const CLUSTER_BASE_PATH: &str = "/cluster"; + + // 快捷访问各子系统的常量 + pub const API_REQUESTS: MetricSubsystem = MetricSubsystem::ApiRequests; + pub const BUCKET_API: MetricSubsystem = MetricSubsystem::BucketApi; + pub const BUCKET_REPLICATION: MetricSubsystem = MetricSubsystem::BucketReplication; + pub const SYSTEM_NETWORK_INTERNODE: MetricSubsystem = MetricSubsystem::SystemNetworkInternode; + pub const SYSTEM_DRIVE: MetricSubsystem = MetricSubsystem::SystemDrive; + pub const SYSTEM_MEMORY: MetricSubsystem = MetricSubsystem::SystemMemory; + pub const SYSTEM_CPU: MetricSubsystem = MetricSubsystem::SystemCpu; + pub const SYSTEM_PROCESS: MetricSubsystem = MetricSubsystem::SystemProcess; + pub const DEBUG_GO: MetricSubsystem = MetricSubsystem::DebugGo; + pub const CLUSTER_HEALTH: MetricSubsystem = MetricSubsystem::ClusterHealth; + pub const CLUSTER_USAGE_OBJECTS: MetricSubsystem = MetricSubsystem::ClusterUsageObjects; + pub const CLUSTER_USAGE_BUCKETS: MetricSubsystem = MetricSubsystem::ClusterUsageBuckets; + pub const CLUSTER_ERASURE_SET: MetricSubsystem = MetricSubsystem::ClusterErasureSet; + pub const CLUSTER_IAM: MetricSubsystem = MetricSubsystem::ClusterIam; + pub const CLUSTER_CONFIG: MetricSubsystem = MetricSubsystem::ClusterConfig; + pub const ILM: MetricSubsystem = MetricSubsystem::Ilm; + pub const AUDIT: MetricSubsystem = MetricSubsystem::Audit; + pub const LOGGER_WEBHOOK: MetricSubsystem = MetricSubsystem::LoggerWebhook; + pub const REPLICATION: MetricSubsystem = MetricSubsystem::Replication; + pub const NOTIFICATION: MetricSubsystem = MetricSubsystem::Notification; + pub const SCANNER: MetricSubsystem = MetricSubsystem::Scanner; +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::metrics::MetricType; + use crate::metrics::{MetricDescriptor, MetricName, MetricNamespace}; + + #[test] + fn test_metric_subsystem_formatting() { + assert_eq!(MetricSubsystem::ApiRequests.as_str(), "api_requests"); + assert_eq!(MetricSubsystem::SystemNetworkInternode.as_str(), "system_network_internode"); + assert_eq!(MetricSubsystem::BucketApi.as_str(), "bucket_api"); + assert_eq!(MetricSubsystem::ClusterHealth.as_str(), "cluster_health"); + + // 测试自定义路径 + let custom = MetricSubsystem::new("/custom/path-test"); + assert_eq!(custom.as_str(), "custom_path_test"); + } + + #[test] + fn test_metric_descriptor_name_generation() { + let md = MetricDescriptor::new( + MetricName::ApiRequestsTotal, + MetricType::Counter, + "Test help".to_string(), + vec!["label1".to_string(), "label2".to_string()], + MetricNamespace::RustFS, + MetricSubsystem::ApiRequests, + ); + + assert_eq!(md.get_full_metric_name(), "counter.rustfs_api_requests_total"); + + let custom_md = MetricDescriptor::new( + MetricName::Custom("test_metric".to_string()), + MetricType::Gauge, + "Test help".to_string(), + vec!["label1".to_string()], + MetricNamespace::RustFS, + MetricSubsystem::new("/custom/path-with-dash"), + ); + + assert_eq!(custom_md.get_full_metric_name(), "gauge.rustfs_custom_path_with_dash_test_metric"); + } } diff --git a/crates/obs/src/metrics/mod.rs b/crates/obs/src/metrics/mod.rs index 37d684a1e..ad1cba98f 100644 --- a/crates/obs/src/metrics/mod.rs +++ b/crates/obs/src/metrics/mod.rs @@ -1,10 +1,13 @@ +mod audit; +mod bucket; +mod bucket_replication; mod entry; mod request; -pub use entry::description::MetricDescription; pub use entry::descriptor::MetricDescriptor; pub use entry::metric_name::MetricName; pub use entry::metric_type::MetricType; pub use entry::namespace::MetricNamespace; +pub use entry::subsystem::subsystems; pub use entry::subsystem::MetricSubsystem; -pub use entry::{new_counter_md, new_gauge_md}; +pub use entry::{new_counter_md, new_gauge_md, new_histogram_md}; diff --git a/crates/obs/src/metrics/request.rs b/crates/obs/src/metrics/request.rs index 5b9d0f3b2..c508db64b 100644 --- a/crates/obs/src/metrics/request.rs +++ b/crates/obs/src/metrics/request.rs @@ -1,109 +1,123 @@ -use crate::metrics::{new_counter_md, new_gauge_md, MetricDescriptor, MetricName}; +use crate::metrics::{new_counter_md, new_gauge_md, subsystems, MetricDescriptor, MetricName, MetricSubsystem}; -/// Predefined API metric descriptors lazy_static::lazy_static! { pub static ref API_REJECTED_AUTH_TOTAL_MD: MetricDescriptor = new_counter_md( - MetricName::ApiRejectedAuthTotal.as_str(), + MetricName::ApiRejectedAuthTotal, "Total number of requests rejected for auth failure", - &["type"] + &["type"], + subsystems::API_REQUESTS ); pub static ref API_REJECTED_HEADER_TOTAL_MD: MetricDescriptor = new_counter_md( - MetricName::ApiRejectedHeaderTotal.as_str(), + MetricName::ApiRejectedHeaderTotal, "Total number of requests rejected for invalid header", - &["type"] + &["type"], + MetricSubsystem::ApiRequests ); pub static ref API_REJECTED_TIMESTAMP_TOTAL_MD: MetricDescriptor = new_counter_md( - MetricName::ApiRejectedTimestampTotal.as_str(), + MetricName::ApiRejectedTimestampTotal, "Total number of requests rejected for invalid timestamp", - &["type"] + &["type"], + MetricSubsystem::ApiRequests ); pub static ref API_REJECTED_INVALID_TOTAL_MD: MetricDescriptor = new_counter_md( - MetricName::ApiRejectedInvalidTotal.as_str(), + MetricName::ApiRejectedInvalidTotal, "Total number of invalid requests", - &["type"] + &["type"], + MetricSubsystem::ApiRequests ); pub static ref API_REQUESTS_WAITING_TOTAL_MD: MetricDescriptor = new_gauge_md( - MetricName::ApiRequestsWaitingTotal.as_str(), + MetricName::ApiRequestsWaitingTotal, "Total number of requests in the waiting queue", - &["type"] + &["type"], + MetricSubsystem::ApiRequests ); pub static ref API_REQUESTS_INCOMING_TOTAL_MD: MetricDescriptor = new_gauge_md( - MetricName::ApiRequestsIncomingTotal.as_str(), + MetricName::ApiRequestsIncomingTotal, "Total number of incoming requests", - &["type"] + &["type"], + MetricSubsystem::ApiRequests ); pub static ref API_REQUESTS_IN_FLIGHT_TOTAL_MD: MetricDescriptor = new_gauge_md( - MetricName::ApiRequestsInFlightTotal.as_str(), + MetricName::ApiRequestsInFlightTotal, "Total number of requests currently in flight", - &["name", "type"] + &["name", "type"], + MetricSubsystem::ApiRequests ); pub static ref API_REQUESTS_TOTAL_MD: MetricDescriptor = new_counter_md( - MetricName::ApiRequestsTotal.as_str(), + MetricName::ApiRequestsTotal, "Total number of requests", - &["name", "type"] + &["name", "type"], + MetricSubsystem::ApiRequests ); pub static ref API_REQUESTS_ERRORS_TOTAL_MD: MetricDescriptor = new_counter_md( - MetricName::ApiRequestsErrorsTotal.as_str(), + MetricName::ApiRequestsErrorsTotal, "Total number of requests with (4xx and 5xx) errors", - &["name", "type"] + &["name", "type"], + MetricSubsystem::ApiRequests ); pub static ref API_REQUESTS_5XX_ERRORS_TOTAL_MD: MetricDescriptor = new_counter_md( - MetricName::ApiRequests5xxErrorsTotal.as_str(), + MetricName::ApiRequests5xxErrorsTotal, "Total number of requests with 5xx errors", - &["name", "type"] + &["name", "type"], + MetricSubsystem::ApiRequests ); pub static ref API_REQUESTS_4XX_ERRORS_TOTAL_MD: MetricDescriptor = new_counter_md( - MetricName::ApiRequests4xxErrorsTotal.as_str(), + MetricName::ApiRequests4xxErrorsTotal, "Total number of requests with 4xx errors", - &["name", "type"] + &["name", "type"], + MetricSubsystem::ApiRequests ); pub static ref API_REQUESTS_CANCELED_TOTAL_MD: MetricDescriptor = new_counter_md( - MetricName::ApiRequestsCanceledTotal.as_str(), + MetricName::ApiRequestsCanceledTotal, "Total number of requests canceled by the client", - &["name", "type"] + &["name", "type"], + MetricSubsystem::ApiRequests ); pub static ref API_REQUESTS_TTFB_SECONDS_DISTRIBUTION_MD: MetricDescriptor = new_counter_md( - MetricName::ApiRequestsTTFBSecondsDistribution.as_str(), + MetricName::ApiRequestsTTFBSecondsDistribution, "Distribution of time to first byte across API calls", - &["name", "type", "le"] + &["name", "type", "le"], + MetricSubsystem::ApiRequests ); pub static ref API_TRAFFIC_SENT_BYTES_MD: MetricDescriptor = new_counter_md( - MetricName::ApiTrafficSentBytes.as_str(), + MetricName::ApiTrafficSentBytes, "Total number of bytes sent", - &["type"] + &["type"], + MetricSubsystem::ApiRequests ); pub static ref API_TRAFFIC_RECV_BYTES_MD: MetricDescriptor = new_counter_md( - MetricName::ApiTrafficRecvBytes.as_str(), + MetricName::ApiTrafficRecvBytes, "Total number of bytes received", - &["type"] + &["type"], + MetricSubsystem::ApiRequests ); } diff --git a/crates/utils/src/lib.rs b/crates/utils/src/lib.rs index cda53d088..9fd21edb5 100644 --- a/crates/utils/src/lib.rs +++ b/crates/utils/src/lib.rs @@ -1,8 +1,11 @@ +#[cfg(feature = "tls")] mod certs; +#[cfg(feature = "ip")] mod ip; +#[cfg(feature = "net")] mod net; -#[cfg(feature = "ip")] +#[cfg(feature = "tls")] pub use certs::*; #[cfg(feature = "ip")] pub use ip::*;