Files
rustfs/crates/trusted-proxies/src/proxy/metrics.rs
T
houseme 9059a9c68d refactor(logging): standardize concurrency and trusted proxy events (#3417)
* refactor(logging): standardize concurrency and proxy events

* chore(logging): extend guardrails for concurrency and proxies

* feat(skill): add rustfs logging governance skill
2026-06-14 01:00:26 +08:00

252 lines
8.1 KiB
Rust

// Copyright 2024 RustFS Team
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//! Metrics and monitoring for proxy validation performance and results.
use crate::{ProxyError, ValidationMode};
use metrics::{counter, describe_counter, describe_gauge, describe_histogram, gauge, histogram};
use std::time::Duration;
use tracing::info;
/// Collector for proxy validation metrics.
#[derive(Debug, Clone)]
pub struct ProxyMetrics {
/// Whether metrics collection is enabled.
enabled: bool,
/// Application name used as a label for metrics.
app_name: String,
}
impl ProxyMetrics {
/// Creates a new `ProxyMetrics` collector.
pub fn new(app_name: &str, enabled: bool) -> Self {
let metrics = Self {
enabled,
app_name: app_name.to_string(),
};
// Register metric descriptions for Prometheus.
metrics.register_descriptions();
metrics
}
/// Registers descriptions for all metrics.
fn register_descriptions(&self) {
if !self.enabled {
return;
}
describe_counter!(
"rustfs_trusted_proxy_validation_attempts_total",
"Total number of proxy validation attempts"
);
describe_counter!(
"rustfs_trusted_proxy_validation_success_total",
"Total number of successful proxy validations"
);
describe_counter!(
"rustfs_trusted_proxy_validation_failure_total",
"Total number of failed proxy validations"
);
describe_counter!(
"rustfs_trusted_proxy_validation_failure_by_type_total",
"Total number of failed proxy validations categorized by error type"
);
describe_gauge!("rustfs_trusted_proxy_chain_length", "Current length of proxy chains being validated");
describe_histogram!(
"rustfs_trusted_proxy_validation_duration_seconds",
"Time taken to validate a proxy chain in seconds"
);
describe_gauge!(
"rustfs_trusted_proxy_cache_size",
"Current number of entries in the proxy validation cache"
);
describe_counter!("rustfs_trusted_proxy_cache_hits_total", "Total number of cache hits for proxy validation");
describe_counter!(
"rustfs_trusted_proxy_cache_misses_total",
"Total number of cache misses for proxy validation"
);
}
/// Increments the total number of validation attempts.
pub fn increment_validation_attempts(&self) {
if !self.enabled {
return;
}
counter!(
"rustfs_trusted_proxy_validation_attempts_total",
"app" => self.app_name.clone()
)
.increment(1);
}
/// Records a successful validation.
pub fn record_validation_success(&self, from_trusted_proxy: bool, proxy_hops: usize, duration: Duration) {
if !self.enabled {
return;
}
counter!(
"rustfs_trusted_proxy_validation_success_total",
"app" => self.app_name.clone(),
"trusted" => from_trusted_proxy.to_string()
)
.increment(1);
gauge!(
"rustfs_trusted_proxy_chain_length",
"app" => self.app_name.clone()
)
.set(proxy_hops as f64);
histogram!(
"rustfs_trusted_proxy_validation_duration_seconds",
"app" => self.app_name.clone()
)
.record(duration.as_secs_f64());
}
/// Records a failed validation with the specific error type.
pub fn record_validation_failure(&self, error: &ProxyError, duration: Duration) {
if !self.enabled {
return;
}
let error_type = match error {
ProxyError::InvalidXForwardedFor(_) => "invalid_x_forwarded_for",
ProxyError::InvalidForwardedHeader(_) => "invalid_forwarded_header",
ProxyError::ChainValidationFailed(_) => "chain_validation_failed",
ProxyError::ChainTooLong(_, _) => "chain_too_long",
ProxyError::UntrustedProxy(_) => "untrusted_proxy",
ProxyError::ChainNotContinuous => "chain_not_continuous",
ProxyError::IpParseError(_) => "ip_parse_error",
ProxyError::HeaderParseError(_) => "header_parse_error",
ProxyError::Timeout => "timeout",
ProxyError::Internal(_) => "internal",
};
counter!(
"rustfs_trusted_proxy_validation_failure_total",
"app" => self.app_name.clone(),
"error_type" => error_type
)
.increment(1);
counter!(
"rustfs_trusted_proxy_validation_failure_by_type_total",
"app" => self.app_name.clone(),
"error_type" => error_type
)
.increment(1);
histogram!(
"rustfs_trusted_proxy_validation_duration_seconds",
"app" => self.app_name.clone(),
"error_type" => error_type
)
.record(duration.as_secs_f64());
}
/// Records the validation mode currently in use.
pub fn record_validation_mode(&self, mode: ValidationMode) {
if !self.enabled {
return;
}
gauge!(
"rustfs_trusted_proxy_validation_mode",
"app" => self.app_name.clone(),
"mode" => mode.as_str()
)
.set(match mode {
ValidationMode::Lenient => 0.0,
ValidationMode::Strict => 1.0,
ValidationMode::HopByHop => 2.0,
});
}
/// Records a cache hit.
pub fn record_cache_hit(&self) {
if !self.enabled {
return;
}
counter!("rustfs_trusted_proxy_cache_hits_total", "app" => self.app_name.clone()).increment(1);
}
/// Records a cache miss.
pub fn record_cache_miss(&self) {
if !self.enabled {
return;
}
counter!("rustfs_trusted_proxy_cache_misses_total", "app" => self.app_name.clone()).increment(1);
}
/// Updates only the cache size gauge.
pub fn set_cache_size(&self, size: usize) {
if !self.enabled {
return;
}
gauge!("rustfs_trusted_proxy_cache_size", "app" => self.app_name.clone()).set(size as f64);
}
/// Records cache performance metrics.
pub fn record_cache_metrics(&self, hits: u64, misses: u64, size: usize) {
if !self.enabled {
return;
}
counter!("rustfs_trusted_proxy_cache_hits_total", "app" => self.app_name.clone()).increment(hits);
counter!("rustfs_trusted_proxy_cache_misses_total", "app" => self.app_name.clone()).increment(misses);
gauge!("rustfs_trusted_proxy_cache_size", "app" => self.app_name.clone()).set(size as f64);
}
/// Prints a summary of enabled metrics to the log.
pub fn print_summary(&self) {
if !self.enabled {
info!(
event = "trusted_proxies.metrics",
component = "trusted_proxies",
subsystem = "metrics",
state = "disabled",
app = %self.app_name,
"trusted proxies metrics state changed"
);
return;
}
info!(
event = "trusted_proxies.metrics",
component = "trusted_proxies",
subsystem = "metrics",
state = "enabled",
app = %self.app_name,
metric_count = 9,
"trusted proxies metrics state changed"
);
}
}
/// Default application name for metrics.
const DEFAULT_APP_NAME: &str = "trusted-proxy";
/// Creates a default `ProxyMetrics` collector.
pub fn default_proxy_metrics(enabled: bool) -> ProxyMetrics {
ProxyMetrics::new(DEFAULT_APP_NAME, enabled)
}