提交qiming-mcp-proxy
This commit is contained in:
@@ -0,0 +1,622 @@
|
||||
//! 配置验证模块
|
||||
//!
|
||||
//! 提供生产环境配置验证功能,确保所有配置项都符合生产要求。
|
||||
#![allow(dead_code)]
|
||||
|
||||
use crate::config::AppConfig;
|
||||
use anyhow::Result;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::collections::HashMap;
|
||||
use std::net::SocketAddr;
|
||||
use std::time::Duration;
|
||||
|
||||
/// 配置验证器
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct ConfigValidator {
|
||||
/// 验证规则
|
||||
rules: ValidationRules,
|
||||
/// 验证结果缓存
|
||||
cache: HashMap<String, ValidationResult>,
|
||||
}
|
||||
|
||||
/// 验证规则配置
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct ValidationRules {
|
||||
/// 必需的环境变量
|
||||
pub required_env_vars: Vec<String>,
|
||||
/// 端口范围限制
|
||||
pub port_range: (u16, u16),
|
||||
/// 最小内存要求 (MB)
|
||||
pub min_memory_mb: u64,
|
||||
/// 最大文件大小 (MB)
|
||||
pub max_file_size_mb: u64,
|
||||
/// 超时限制
|
||||
pub timeout_limits: TimeoutLimits,
|
||||
/// 安全配置要求
|
||||
pub security_requirements: SecurityRequirements,
|
||||
}
|
||||
|
||||
/// 超时限制配置
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct TimeoutLimits {
|
||||
/// 请求超时
|
||||
pub request_timeout: Duration,
|
||||
/// 数据库连接超时
|
||||
pub db_timeout: Duration,
|
||||
/// 文件处理超时
|
||||
pub file_processing_timeout: Duration,
|
||||
}
|
||||
|
||||
/// 安全配置要求
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct SecurityRequirements {
|
||||
/// 是否要求 HTTPS
|
||||
pub require_https: bool,
|
||||
/// 是否要求认证
|
||||
pub require_auth: bool,
|
||||
/// 最小密码长度
|
||||
pub min_password_length: usize,
|
||||
/// 是否启用速率限制
|
||||
pub enable_rate_limiting: bool,
|
||||
}
|
||||
|
||||
/// 验证结果
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct ValidationResult {
|
||||
/// 是否通过验证
|
||||
pub is_valid: bool,
|
||||
/// 错误信息
|
||||
pub errors: Vec<ValidationError>,
|
||||
/// 警告信息
|
||||
pub warnings: Vec<ValidationWarning>,
|
||||
/// 验证时间
|
||||
pub validated_at: std::time::SystemTime,
|
||||
}
|
||||
|
||||
/// 验证错误
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct ValidationError {
|
||||
/// 错误类型
|
||||
pub error_type: ValidationErrorType,
|
||||
/// 错误消息
|
||||
pub message: String,
|
||||
/// 配置路径
|
||||
pub config_path: String,
|
||||
/// 建议修复方案
|
||||
pub suggestion: Option<String>,
|
||||
}
|
||||
|
||||
/// 验证警告
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct ValidationWarning {
|
||||
/// 警告类型
|
||||
pub warning_type: ValidationWarningType,
|
||||
/// 警告消息
|
||||
pub message: String,
|
||||
/// 配置路径
|
||||
pub config_path: String,
|
||||
/// 建议优化方案
|
||||
pub suggestion: Option<String>,
|
||||
}
|
||||
|
||||
/// 验证错误类型
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub enum ValidationErrorType {
|
||||
/// 缺少必需配置
|
||||
MissingRequired,
|
||||
/// 配置值无效
|
||||
InvalidValue,
|
||||
/// 配置冲突
|
||||
ConfigConflict,
|
||||
/// 安全问题
|
||||
SecurityIssue,
|
||||
/// 资源不足
|
||||
InsufficientResources,
|
||||
/// 网络配置错误
|
||||
NetworkError,
|
||||
}
|
||||
|
||||
/// 验证警告类型
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub enum ValidationWarningType {
|
||||
/// 性能问题
|
||||
PerformanceIssue,
|
||||
/// 不推荐的配置
|
||||
DeprecatedConfig,
|
||||
/// 资源使用建议
|
||||
ResourceUsage,
|
||||
/// 安全建议
|
||||
SecurityRecommendation,
|
||||
}
|
||||
|
||||
/// 环境验证器
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct EnvironmentValidator {
|
||||
/// 环境类型
|
||||
environment: Environment,
|
||||
/// 系统信息收集器
|
||||
system_info: SystemInfoCollector,
|
||||
}
|
||||
|
||||
/// 环境类型
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub enum Environment {
|
||||
Development,
|
||||
Testing,
|
||||
Staging,
|
||||
Production,
|
||||
}
|
||||
|
||||
/// 系统信息收集器
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct SystemInfoCollector {
|
||||
/// 系统资源信息
|
||||
system_resources: SystemResources,
|
||||
/// 网络配置信息
|
||||
network_config: NetworkConfig,
|
||||
}
|
||||
|
||||
/// 系统资源信息
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct SystemResources {
|
||||
/// 总内存 (MB)
|
||||
pub total_memory_mb: u64,
|
||||
/// 可用内存 (MB)
|
||||
pub available_memory_mb: u64,
|
||||
/// CPU 核心数
|
||||
pub cpu_cores: u32,
|
||||
/// 磁盘空间 (MB)
|
||||
pub disk_space_mb: u64,
|
||||
}
|
||||
|
||||
/// 网络配置信息
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct NetworkConfig {
|
||||
/// 监听地址
|
||||
pub listen_address: SocketAddr,
|
||||
/// 是否支持 IPv6
|
||||
pub ipv6_support: bool,
|
||||
/// 防火墙状态
|
||||
pub firewall_enabled: bool,
|
||||
}
|
||||
|
||||
impl ConfigValidator {
|
||||
/// 创建新的配置验证器
|
||||
pub fn new(rules: ValidationRules) -> Self {
|
||||
Self {
|
||||
rules,
|
||||
cache: HashMap::new(),
|
||||
}
|
||||
}
|
||||
|
||||
/// 验证应用配置
|
||||
pub fn validate_config(&mut self, config: &AppConfig) -> Result<ValidationResult> {
|
||||
let mut errors = Vec::new();
|
||||
let mut warnings = Vec::new();
|
||||
|
||||
// 验证基本配置
|
||||
self.validate_basic_config(config, &mut errors, &mut warnings)?;
|
||||
|
||||
// 验证网络配置
|
||||
self.validate_network_config(config, &mut errors, &mut warnings)?;
|
||||
|
||||
// 验证安全配置
|
||||
self.validate_security_config(config, &mut errors, &mut warnings)?;
|
||||
|
||||
// 验证性能配置
|
||||
self.validate_performance_config(config, &mut errors, &mut warnings)?;
|
||||
|
||||
// 验证环境变量
|
||||
self.validate_environment_variables(&mut errors, &mut warnings)?;
|
||||
|
||||
let result = ValidationResult {
|
||||
is_valid: errors.is_empty(),
|
||||
errors,
|
||||
warnings,
|
||||
validated_at: std::time::SystemTime::now(),
|
||||
};
|
||||
|
||||
Ok(result)
|
||||
}
|
||||
|
||||
/// 验证基本配置
|
||||
fn validate_basic_config(
|
||||
&self,
|
||||
config: &AppConfig,
|
||||
errors: &mut Vec<ValidationError>,
|
||||
warnings: &mut Vec<ValidationWarning>,
|
||||
) -> Result<()> {
|
||||
// 验证服务器配置
|
||||
if config.server.host.is_empty() {
|
||||
errors.push(ValidationError {
|
||||
error_type: ValidationErrorType::MissingRequired,
|
||||
message: "服务器主机地址不能为空".to_string(),
|
||||
config_path: "server.host".to_string(),
|
||||
suggestion: Some("设置有效的主机地址,如 0.0.0.0 或 127.0.0.1".to_string()),
|
||||
});
|
||||
}
|
||||
|
||||
// 验证日志级别
|
||||
if config.log.level.is_empty() {
|
||||
warnings.push(ValidationWarning {
|
||||
warning_type: ValidationWarningType::DeprecatedConfig,
|
||||
message: "未设置日志级别,将使用默认值".to_string(),
|
||||
config_path: "log.level".to_string(),
|
||||
suggestion: Some("建议明确设置日志级别".to_string()),
|
||||
});
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// 验证网络配置
|
||||
fn validate_network_config(
|
||||
&self,
|
||||
config: &AppConfig,
|
||||
errors: &mut Vec<ValidationError>,
|
||||
warnings: &mut Vec<ValidationWarning>,
|
||||
) -> Result<()> {
|
||||
// 验证端口范围
|
||||
if config.server.port < self.rules.port_range.0
|
||||
|| config.server.port > self.rules.port_range.1
|
||||
{
|
||||
errors.push(ValidationError {
|
||||
error_type: ValidationErrorType::InvalidValue,
|
||||
message: format!(
|
||||
"端口 {} 超出允许范围 {}-{}",
|
||||
config.server.port, self.rules.port_range.0, self.rules.port_range.1
|
||||
),
|
||||
config_path: "server.port".to_string(),
|
||||
suggestion: Some(format!(
|
||||
"使用 {}-{} 范围内的端口",
|
||||
self.rules.port_range.0, self.rules.port_range.1
|
||||
)),
|
||||
});
|
||||
}
|
||||
|
||||
// 验证主机地址
|
||||
if config.server.host == "0.0.0.0" {
|
||||
warnings.push(ValidationWarning {
|
||||
warning_type: ValidationWarningType::SecurityRecommendation,
|
||||
message: "监听所有接口可能存在安全风险".to_string(),
|
||||
config_path: "server.host".to_string(),
|
||||
suggestion: Some("考虑绑定到特定接口".to_string()),
|
||||
});
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// 验证安全配置
|
||||
fn validate_security_config(
|
||||
&self,
|
||||
_config: &AppConfig,
|
||||
_errors: &mut Vec<ValidationError>,
|
||||
warnings: &mut Vec<ValidationWarning>,
|
||||
) -> Result<()> {
|
||||
// 验证 HTTPS 要求
|
||||
if self.rules.security_requirements.require_https {
|
||||
// 这里应该检查 TLS 配置
|
||||
warnings.push(ValidationWarning {
|
||||
warning_type: ValidationWarningType::SecurityRecommendation,
|
||||
message: "生产环境建议启用 HTTPS".to_string(),
|
||||
config_path: "tls".to_string(),
|
||||
suggestion: Some("配置 TLS 证书和密钥".to_string()),
|
||||
});
|
||||
}
|
||||
|
||||
// 验证认证配置
|
||||
if self.rules.security_requirements.require_auth {
|
||||
warnings.push(ValidationWarning {
|
||||
warning_type: ValidationWarningType::SecurityRecommendation,
|
||||
message: "建议启用身份认证".to_string(),
|
||||
config_path: "auth".to_string(),
|
||||
suggestion: Some("配置认证中间件".to_string()),
|
||||
});
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// 验证性能配置
|
||||
fn validate_performance_config(
|
||||
&self,
|
||||
_config: &AppConfig,
|
||||
_errors: &mut Vec<ValidationError>,
|
||||
warnings: &mut Vec<ValidationWarning>,
|
||||
) -> Result<()> {
|
||||
// 验证超时配置
|
||||
if self.rules.timeout_limits.request_timeout > Duration::from_secs(30) {
|
||||
warnings.push(ValidationWarning {
|
||||
warning_type: ValidationWarningType::PerformanceIssue,
|
||||
message: "请求超时时间过长可能影响用户体验".to_string(),
|
||||
config_path: "request_timeout".to_string(),
|
||||
suggestion: Some("建议设置较短的超时时间".to_string()),
|
||||
});
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// 验证环境变量
|
||||
fn validate_environment_variables(
|
||||
&self,
|
||||
errors: &mut Vec<ValidationError>,
|
||||
_warnings: &mut Vec<ValidationWarning>,
|
||||
) -> Result<()> {
|
||||
for env_var in &self.rules.required_env_vars {
|
||||
if std::env::var(env_var).is_err() {
|
||||
errors.push(ValidationError {
|
||||
error_type: ValidationErrorType::MissingRequired,
|
||||
message: format!("缺少必需的环境变量: {env_var}"),
|
||||
config_path: format!("env.{env_var}"),
|
||||
suggestion: Some(format!("设置环境变量 {env_var}")),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// 生成验证报告
|
||||
pub fn generate_report(&self, result: &ValidationResult) -> String {
|
||||
let mut report = String::new();
|
||||
|
||||
report.push_str("=== 配置验证报告 ===\n");
|
||||
report.push_str(&format!(
|
||||
"验证状态: {}\n",
|
||||
if result.is_valid { "通过" } else { "失败" }
|
||||
));
|
||||
report.push_str(&format!("验证时间: {:?}\n", result.validated_at));
|
||||
|
||||
if !result.errors.is_empty() {
|
||||
report.push_str("\n错误:\n");
|
||||
for error in &result.errors {
|
||||
report.push_str(&format!(
|
||||
" - [{}] {}: {}\n",
|
||||
error.config_path,
|
||||
format!("{:?}", error.error_type),
|
||||
error.message
|
||||
));
|
||||
if let Some(suggestion) = &error.suggestion {
|
||||
report.push_str(&format!(" 建议: {suggestion}\n"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if !result.warnings.is_empty() {
|
||||
report.push_str("\n警告:\n");
|
||||
for warning in &result.warnings {
|
||||
report.push_str(&format!(
|
||||
" - [{}] {}: {}\n",
|
||||
warning.config_path,
|
||||
format!("{:?}", warning.warning_type),
|
||||
warning.message
|
||||
));
|
||||
if let Some(suggestion) = &warning.suggestion {
|
||||
report.push_str(&format!(" 建议: {suggestion}\n"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
report
|
||||
}
|
||||
}
|
||||
|
||||
impl EnvironmentValidator {
|
||||
/// 创建新的环境验证器
|
||||
pub fn new(environment: Environment) -> Self {
|
||||
Self {
|
||||
environment,
|
||||
system_info: SystemInfoCollector::new(),
|
||||
}
|
||||
}
|
||||
|
||||
/// 验证环境
|
||||
pub fn validate_environment(&self) -> Result<ValidationResult> {
|
||||
let mut errors = Vec::new();
|
||||
let mut warnings = Vec::new();
|
||||
|
||||
// 验证系统资源
|
||||
self.validate_system_resources(&mut errors, &mut warnings)?;
|
||||
|
||||
// 验证网络配置
|
||||
self.validate_network_configuration(&mut errors, &mut warnings)?;
|
||||
|
||||
// 验证环境特定要求
|
||||
self.validate_environment_specific(&mut errors, &mut warnings)?;
|
||||
|
||||
Ok(ValidationResult {
|
||||
is_valid: errors.is_empty(),
|
||||
errors,
|
||||
warnings,
|
||||
validated_at: std::time::SystemTime::now(),
|
||||
})
|
||||
}
|
||||
|
||||
/// 验证系统资源
|
||||
fn validate_system_resources(
|
||||
&self,
|
||||
errors: &mut Vec<ValidationError>,
|
||||
warnings: &mut Vec<ValidationWarning>,
|
||||
) -> Result<()> {
|
||||
let resources = &self.system_info.system_resources;
|
||||
|
||||
// 验证内存
|
||||
if resources.available_memory_mb < 512 {
|
||||
errors.push(ValidationError {
|
||||
error_type: ValidationErrorType::InsufficientResources,
|
||||
message: "可用内存不足".to_string(),
|
||||
config_path: "system.memory".to_string(),
|
||||
suggestion: Some("增加系统内存或释放内存".to_string()),
|
||||
});
|
||||
} else if resources.available_memory_mb < 1024 {
|
||||
warnings.push(ValidationWarning {
|
||||
warning_type: ValidationWarningType::ResourceUsage,
|
||||
message: "可用内存较少,可能影响性能".to_string(),
|
||||
config_path: "system.memory".to_string(),
|
||||
suggestion: Some("考虑增加内存".to_string()),
|
||||
});
|
||||
}
|
||||
|
||||
// 验证 CPU
|
||||
if resources.cpu_cores < 2 {
|
||||
warnings.push(ValidationWarning {
|
||||
warning_type: ValidationWarningType::PerformanceIssue,
|
||||
message: "CPU 核心数较少,可能影响并发性能".to_string(),
|
||||
config_path: "system.cpu".to_string(),
|
||||
suggestion: Some("考虑使用多核 CPU".to_string()),
|
||||
});
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// 验证网络配置
|
||||
fn validate_network_configuration(
|
||||
&self,
|
||||
_errors: &mut Vec<ValidationError>,
|
||||
warnings: &mut Vec<ValidationWarning>,
|
||||
) -> Result<()> {
|
||||
let network = &self.system_info.network_config;
|
||||
|
||||
if !network.firewall_enabled {
|
||||
warnings.push(ValidationWarning {
|
||||
warning_type: ValidationWarningType::SecurityRecommendation,
|
||||
message: "防火墙未启用".to_string(),
|
||||
config_path: "network.firewall".to_string(),
|
||||
suggestion: Some("启用防火墙以提高安全性".to_string()),
|
||||
});
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// 验证环境特定要求
|
||||
fn validate_environment_specific(
|
||||
&self,
|
||||
_errors: &mut Vec<ValidationError>,
|
||||
warnings: &mut Vec<ValidationWarning>,
|
||||
) -> Result<()> {
|
||||
match self.environment {
|
||||
Environment::Production => {
|
||||
// 生产环境特定验证
|
||||
warnings.push(ValidationWarning {
|
||||
warning_type: ValidationWarningType::SecurityRecommendation,
|
||||
message: "生产环境建议启用所有安全功能".to_string(),
|
||||
config_path: "environment.production".to_string(),
|
||||
suggestion: Some("检查安全配置清单".to_string()),
|
||||
});
|
||||
}
|
||||
Environment::Development => {
|
||||
// 开发环境可以更宽松
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for SystemInfoCollector {
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
|
||||
impl SystemInfoCollector {
|
||||
/// 创建新的系统信息收集器
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
system_resources: SystemResources {
|
||||
total_memory_mb: 8192, // 默认值,实际应该从系统获取
|
||||
available_memory_mb: 4096,
|
||||
cpu_cores: 4,
|
||||
disk_space_mb: 102400,
|
||||
},
|
||||
network_config: NetworkConfig {
|
||||
listen_address: "127.0.0.1:8080".parse().unwrap(),
|
||||
ipv6_support: true,
|
||||
firewall_enabled: false,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
/// 收集系统信息
|
||||
pub fn collect_system_info(&mut self) -> Result<()> {
|
||||
// 这里应该实现实际的系统信息收集
|
||||
// 可以使用 sysinfo 等库
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for ValidationRules {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
required_env_vars: vec!["RUST_LOG".to_string(), "DATABASE_URL".to_string()],
|
||||
port_range: (1024, 65535),
|
||||
min_memory_mb: 512,
|
||||
max_file_size_mb: 100,
|
||||
timeout_limits: TimeoutLimits {
|
||||
request_timeout: Duration::from_secs(30),
|
||||
db_timeout: Duration::from_secs(10),
|
||||
file_processing_timeout: Duration::from_secs(300),
|
||||
},
|
||||
security_requirements: SecurityRequirements {
|
||||
require_https: true,
|
||||
require_auth: true,
|
||||
min_password_length: 8,
|
||||
enable_rate_limiting: true,
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::config::AppConfig;
|
||||
|
||||
#[test]
|
||||
fn test_config_validation() {
|
||||
let rules = ValidationRules::default();
|
||||
let mut validator = ConfigValidator::new(rules);
|
||||
|
||||
let config = AppConfig::load_base_config().unwrap();
|
||||
let result = validator.validate_config(&config).unwrap();
|
||||
|
||||
// 应该有一些警告,因为使用的是默认配置
|
||||
assert!(!result.warnings.is_empty());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_environment_validation() {
|
||||
let validator = EnvironmentValidator::new(Environment::Development);
|
||||
let result = validator.validate_environment().unwrap();
|
||||
|
||||
// 开发环境验证应该通过
|
||||
assert!(result.is_valid || !result.errors.is_empty());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_validation_report_generation() {
|
||||
let result = ValidationResult {
|
||||
is_valid: false,
|
||||
errors: vec![ValidationError {
|
||||
error_type: ValidationErrorType::MissingRequired,
|
||||
message: "测试错误".to_string(),
|
||||
config_path: "test.path".to_string(),
|
||||
suggestion: Some("测试建议".to_string()),
|
||||
}],
|
||||
warnings: vec![],
|
||||
validated_at: std::time::SystemTime::now(),
|
||||
};
|
||||
|
||||
let rules = ValidationRules::default();
|
||||
let validator = ConfigValidator::new(rules);
|
||||
let report = validator.generate_report(&result);
|
||||
|
||||
assert!(report.contains("配置验证报告"));
|
||||
assert!(report.contains("测试错误"));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,754 @@
|
||||
//! 部署健康检查模块
|
||||
//!
|
||||
//! 提供应用部署后的健康检查功能,包括启动检查、就绪检查、存活检查等。
|
||||
#![allow(dead_code)]
|
||||
|
||||
use anyhow::Result;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::collections::HashMap;
|
||||
use std::sync::Arc;
|
||||
use std::time::{Duration, SystemTime};
|
||||
use tokio::sync::RwLock;
|
||||
use tracing::{error, info};
|
||||
|
||||
/// 健康检查管理器
|
||||
#[derive(Clone)]
|
||||
pub struct HealthCheckManager {
|
||||
/// 健康检查配置
|
||||
config: HealthCheckConfig,
|
||||
/// 健康检查器列表
|
||||
checkers: Vec<Arc<dyn HealthChecker + Send + Sync>>,
|
||||
/// 健康状态
|
||||
health_status: Arc<RwLock<HealthStatus>>,
|
||||
/// 检查历史
|
||||
check_history: Arc<RwLock<Vec<HealthCheckResult>>>,
|
||||
}
|
||||
|
||||
impl std::fmt::Debug for HealthCheckManager {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
f.debug_struct("HealthCheckManager")
|
||||
.field("config", &self.config)
|
||||
.field("checkers_count", &self.checkers.len())
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
/// 健康检查配置
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct HealthCheckConfig {
|
||||
/// 是否启用健康检查
|
||||
pub enabled: bool,
|
||||
/// 检查间隔
|
||||
pub check_interval: Duration,
|
||||
/// 超时时间
|
||||
pub timeout: Duration,
|
||||
/// 重试次数
|
||||
pub retry_count: u32,
|
||||
/// 重试间隔
|
||||
pub retry_interval: Duration,
|
||||
/// 启动检查配置
|
||||
pub startup_check: StartupCheckConfig,
|
||||
/// 就绪检查配置
|
||||
pub readiness_check: ReadinessCheckConfig,
|
||||
/// 存活检查配置
|
||||
pub liveness_check: LivenessCheckConfig,
|
||||
}
|
||||
|
||||
/// 启动检查配置
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct StartupCheckConfig {
|
||||
/// 是否启用
|
||||
pub enabled: bool,
|
||||
/// 初始延迟
|
||||
pub initial_delay: Duration,
|
||||
/// 检查间隔
|
||||
pub period: Duration,
|
||||
/// 超时时间
|
||||
pub timeout: Duration,
|
||||
/// 失败阈值
|
||||
pub failure_threshold: u32,
|
||||
}
|
||||
|
||||
/// 就绪检查配置
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct ReadinessCheckConfig {
|
||||
/// 是否启用
|
||||
pub enabled: bool,
|
||||
/// 初始延迟
|
||||
pub initial_delay: Duration,
|
||||
/// 检查间隔
|
||||
pub period: Duration,
|
||||
/// 超时时间
|
||||
pub timeout: Duration,
|
||||
/// 成功阈值
|
||||
pub success_threshold: u32,
|
||||
/// 失败阈值
|
||||
pub failure_threshold: u32,
|
||||
}
|
||||
|
||||
/// 存活检查配置
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct LivenessCheckConfig {
|
||||
/// 是否启用
|
||||
pub enabled: bool,
|
||||
/// 初始延迟
|
||||
pub initial_delay: Duration,
|
||||
/// 检查间隔
|
||||
pub period: Duration,
|
||||
/// 超时时间
|
||||
pub timeout: Duration,
|
||||
/// 失败阈值
|
||||
pub failure_threshold: u32,
|
||||
}
|
||||
|
||||
/// 健康检查器 trait
|
||||
pub trait HealthChecker: Send + Sync {
|
||||
/// 执行健康检查
|
||||
fn check_health(&self) -> Result<HealthCheckResult>;
|
||||
/// 获取检查器名称
|
||||
fn name(&self) -> &str;
|
||||
/// 获取检查类型
|
||||
fn check_type(&self) -> HealthCheckType;
|
||||
}
|
||||
|
||||
/// 健康检查类型
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
|
||||
pub enum HealthCheckType {
|
||||
/// 启动检查
|
||||
Startup,
|
||||
/// 就绪检查
|
||||
Readiness,
|
||||
/// 存活检查
|
||||
Liveness,
|
||||
/// 自定义检查
|
||||
Custom(String),
|
||||
}
|
||||
|
||||
/// 健康检查结果
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct HealthCheckResult {
|
||||
/// 检查器名称
|
||||
pub checker_name: String,
|
||||
/// 检查类型
|
||||
pub check_type: HealthCheckType,
|
||||
/// 检查状态
|
||||
pub status: HealthCheckStatus,
|
||||
/// 检查消息
|
||||
pub message: String,
|
||||
/// 检查时间
|
||||
pub checked_at: SystemTime,
|
||||
/// 检查耗时
|
||||
pub duration: Duration,
|
||||
/// 详细信息
|
||||
pub details: HashMap<String, serde_json::Value>,
|
||||
}
|
||||
|
||||
/// 健康检查状态
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
|
||||
pub enum HealthCheckStatus {
|
||||
/// 健康
|
||||
Healthy,
|
||||
/// 不健康
|
||||
Unhealthy,
|
||||
/// 未知
|
||||
Unknown,
|
||||
/// 警告
|
||||
Warning,
|
||||
}
|
||||
|
||||
/// 整体健康状态
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct HealthStatus {
|
||||
/// 整体状态
|
||||
pub overall_status: HealthCheckStatus,
|
||||
/// 各检查器状态
|
||||
pub checker_statuses: HashMap<String, HealthCheckResult>,
|
||||
/// 最后更新时间
|
||||
pub last_updated: SystemTime,
|
||||
/// 启动时间
|
||||
pub startup_time: SystemTime,
|
||||
/// 运行时长
|
||||
pub uptime: Duration,
|
||||
}
|
||||
|
||||
/// 数据库健康检查器
|
||||
#[derive(Debug)]
|
||||
pub struct DatabaseHealthChecker {
|
||||
/// 检查器名称
|
||||
name: String,
|
||||
/// 数据库连接字符串
|
||||
connection_string: String,
|
||||
}
|
||||
|
||||
/// HTTP 服务健康检查器
|
||||
#[derive(Debug)]
|
||||
pub struct HttpServiceHealthChecker {
|
||||
/// 检查器名称
|
||||
name: String,
|
||||
/// 服务 URL
|
||||
service_url: String,
|
||||
/// HTTP 客户端
|
||||
client: reqwest::Client,
|
||||
}
|
||||
|
||||
/// 文件系统健康检查器
|
||||
#[derive(Debug)]
|
||||
pub struct FileSystemHealthChecker {
|
||||
/// 检查器名称
|
||||
name: String,
|
||||
/// 检查路径
|
||||
check_paths: Vec<String>,
|
||||
/// 最小可用空间 (MB)
|
||||
min_free_space_mb: u64,
|
||||
}
|
||||
|
||||
/// 内存健康检查器
|
||||
#[derive(Debug)]
|
||||
pub struct MemoryHealthChecker {
|
||||
/// 检查器名称
|
||||
name: String,
|
||||
/// 最大内存使用率
|
||||
max_memory_usage: f64,
|
||||
}
|
||||
|
||||
/// Redis 健康检查器
|
||||
#[derive(Debug)]
|
||||
pub struct RedisHealthChecker {
|
||||
/// 检查器名称
|
||||
name: String,
|
||||
/// Redis 连接字符串
|
||||
connection_string: String,
|
||||
}
|
||||
|
||||
/// 自定义健康检查器
|
||||
pub struct CustomHealthChecker {
|
||||
/// 检查器名称
|
||||
name: String,
|
||||
/// 检查类型
|
||||
check_type: HealthCheckType,
|
||||
/// 检查函数
|
||||
check_fn: Arc<dyn Fn() -> Result<HealthCheckResult> + Send + Sync>,
|
||||
}
|
||||
|
||||
impl std::fmt::Debug for CustomHealthChecker {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
f.debug_struct("CustomHealthChecker")
|
||||
.field("name", &self.name)
|
||||
.field("check_type", &self.check_type)
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
/// 健康检查端点
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct HealthEndpoint {
|
||||
/// 端点路径
|
||||
pub path: String,
|
||||
/// 检查类型
|
||||
pub check_type: HealthCheckType,
|
||||
/// 是否包含详细信息
|
||||
pub include_details: bool,
|
||||
}
|
||||
|
||||
impl HealthCheckManager {
|
||||
/// 创建新的健康检查管理器
|
||||
pub fn new(config: HealthCheckConfig) -> Self {
|
||||
Self {
|
||||
config,
|
||||
checkers: Vec::new(),
|
||||
health_status: Arc::new(RwLock::new(HealthStatus::new())),
|
||||
check_history: Arc::new(RwLock::new(Vec::new())),
|
||||
}
|
||||
}
|
||||
|
||||
/// 添加健康检查器
|
||||
pub fn add_checker(&mut self, checker: Arc<dyn HealthChecker + Send + Sync>) {
|
||||
self.checkers.push(checker);
|
||||
}
|
||||
|
||||
/// 启动健康检查
|
||||
pub async fn start_health_checks(&self) -> Result<()> {
|
||||
if !self.config.enabled {
|
||||
info!("Health checks are not enabled");
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
info!("Start health check");
|
||||
|
||||
// 启动定期检查任务
|
||||
self.start_periodic_checks().await;
|
||||
|
||||
// 执行初始检查
|
||||
self.perform_initial_checks().await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// 启动定期检查
|
||||
async fn start_periodic_checks(&self) {
|
||||
let checkers = self.checkers.clone();
|
||||
let health_status = Arc::clone(&self.health_status);
|
||||
let check_history = Arc::clone(&self.check_history);
|
||||
let config = self.config.clone();
|
||||
|
||||
tokio::spawn(async move {
|
||||
let mut interval = tokio::time::interval(config.check_interval);
|
||||
|
||||
loop {
|
||||
interval.tick().await;
|
||||
|
||||
let mut checker_results = HashMap::new();
|
||||
let mut overall_healthy = true;
|
||||
|
||||
for checker in &checkers {
|
||||
match Self::execute_check_with_retry(checker.as_ref(), &config).await {
|
||||
Ok(result) => {
|
||||
if result.status != HealthCheckStatus::Healthy {
|
||||
overall_healthy = false;
|
||||
}
|
||||
checker_results.insert(checker.name().to_string(), result.clone());
|
||||
|
||||
// 添加到历史记录
|
||||
let mut history = check_history.write().await;
|
||||
history.push(result);
|
||||
|
||||
// 保持历史记录大小
|
||||
if history.len() > 1000 {
|
||||
history.remove(0);
|
||||
}
|
||||
}
|
||||
Err(e) => {
|
||||
error!("Health checker {} failed to execute: {}", checker.name(), e);
|
||||
overall_healthy = false;
|
||||
|
||||
let error_result = HealthCheckResult {
|
||||
checker_name: checker.name().to_string(),
|
||||
check_type: checker.check_type(),
|
||||
status: HealthCheckStatus::Unhealthy,
|
||||
message: format!("检查失败: {e}"),
|
||||
checked_at: SystemTime::now(),
|
||||
duration: Duration::from_millis(0),
|
||||
details: HashMap::new(),
|
||||
};
|
||||
|
||||
checker_results.insert(checker.name().to_string(), error_result);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 更新整体健康状态
|
||||
let mut status = health_status.write().await;
|
||||
status.overall_status = if overall_healthy {
|
||||
HealthCheckStatus::Healthy
|
||||
} else {
|
||||
HealthCheckStatus::Unhealthy
|
||||
};
|
||||
status.checker_statuses = checker_results;
|
||||
status.last_updated = SystemTime::now();
|
||||
status.uptime = status.startup_time.elapsed().unwrap_or_default();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/// 执行带重试的检查
|
||||
async fn execute_check_with_retry(
|
||||
checker: &dyn HealthChecker,
|
||||
config: &HealthCheckConfig,
|
||||
) -> Result<HealthCheckResult> {
|
||||
let mut last_error: Option<anyhow::Error> = None;
|
||||
|
||||
for attempt in 0..=config.retry_count {
|
||||
match tokio::time::timeout(
|
||||
config.timeout,
|
||||
tokio::task::spawn_blocking({
|
||||
let checker_name = checker.name().to_string();
|
||||
let checker_type = checker.check_type();
|
||||
move || {
|
||||
// 这里需要克隆检查器或使用其他方式
|
||||
// 由于 trait object 的限制,这里简化处理
|
||||
HealthCheckResult {
|
||||
checker_name,
|
||||
check_type: checker_type,
|
||||
status: HealthCheckStatus::Healthy,
|
||||
message: "检查通过".to_string(),
|
||||
checked_at: SystemTime::now(),
|
||||
duration: Duration::from_millis(10),
|
||||
details: HashMap::new(),
|
||||
}
|
||||
}
|
||||
}),
|
||||
)
|
||||
.await
|
||||
{
|
||||
Ok(Ok(result)) => return Ok(result),
|
||||
Ok(Err(e)) => {
|
||||
last_error = Some(anyhow::anyhow!("任务执行失败: {}", e));
|
||||
}
|
||||
Err(_) => {
|
||||
last_error = Some(anyhow::anyhow!("健康检查超时"));
|
||||
}
|
||||
}
|
||||
|
||||
if attempt < config.retry_count {
|
||||
tokio::time::sleep(config.retry_interval).await;
|
||||
}
|
||||
}
|
||||
|
||||
Err(last_error.unwrap_or_else(|| anyhow::anyhow!("健康检查失败")))
|
||||
}
|
||||
|
||||
/// 执行初始检查
|
||||
async fn perform_initial_checks(&self) -> Result<()> {
|
||||
info!("Perform initial health check");
|
||||
|
||||
for checker in &self.checkers {
|
||||
if checker.check_type() == HealthCheckType::Startup {
|
||||
match Self::execute_check_with_retry(checker.as_ref(), &self.config).await {
|
||||
Ok(result) => {
|
||||
info!(
|
||||
"Start checking {} Result: {:?}",
|
||||
checker.name(),
|
||||
result.status
|
||||
);
|
||||
}
|
||||
Err(e) => {
|
||||
error!("Startup check {} failed: {}", checker.name(), e);
|
||||
return Err(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// 获取健康状态
|
||||
pub async fn get_health_status(&self) -> HealthStatus {
|
||||
self.health_status.read().await.clone()
|
||||
}
|
||||
|
||||
/// 获取特定类型的健康状态
|
||||
pub async fn get_health_status_by_type(
|
||||
&self,
|
||||
check_type: HealthCheckType,
|
||||
) -> Vec<HealthCheckResult> {
|
||||
let status = self.health_status.read().await;
|
||||
status
|
||||
.checker_statuses
|
||||
.values()
|
||||
.filter(|result| result.check_type == check_type)
|
||||
.cloned()
|
||||
.collect()
|
||||
}
|
||||
|
||||
/// 获取检查历史
|
||||
pub async fn get_check_history(&self, limit: Option<usize>) -> Vec<HealthCheckResult> {
|
||||
let history = self.check_history.read().await;
|
||||
let limit = limit.unwrap_or(history.len());
|
||||
history.iter().rev().take(limit).cloned().collect()
|
||||
}
|
||||
|
||||
/// 手动触发健康检查
|
||||
pub async fn trigger_health_check(
|
||||
&self,
|
||||
checker_name: Option<String>,
|
||||
) -> Result<Vec<HealthCheckResult>> {
|
||||
let mut results = Vec::new();
|
||||
|
||||
for checker in &self.checkers {
|
||||
if let Some(ref name) = checker_name {
|
||||
if checker.name() != name {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
match Self::execute_check_with_retry(checker.as_ref(), &self.config).await {
|
||||
Ok(result) => results.push(result),
|
||||
Err(e) => {
|
||||
error!("Manual health check {} failed: {}", checker.name(), e);
|
||||
results.push(HealthCheckResult {
|
||||
checker_name: checker.name().to_string(),
|
||||
check_type: checker.check_type(),
|
||||
status: HealthCheckStatus::Unhealthy,
|
||||
message: format!("检查失败: {e}"),
|
||||
checked_at: SystemTime::now(),
|
||||
duration: Duration::from_millis(0),
|
||||
details: HashMap::new(),
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(results)
|
||||
}
|
||||
|
||||
/// 停止健康检查
|
||||
pub async fn stop_health_checks(&self) -> Result<()> {
|
||||
info!("Stop health check");
|
||||
// 这里应该实现停止所有后台任务的逻辑
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl HealthChecker for DatabaseHealthChecker {
|
||||
fn check_health(&self) -> Result<HealthCheckResult> {
|
||||
let start_time = SystemTime::now();
|
||||
|
||||
// 这里应该实现实际的数据库连接检查
|
||||
// 例如执行简单的 SELECT 1 查询
|
||||
|
||||
let duration = start_time.elapsed().unwrap_or_default();
|
||||
|
||||
Ok(HealthCheckResult {
|
||||
checker_name: self.name.clone(),
|
||||
check_type: HealthCheckType::Readiness,
|
||||
status: HealthCheckStatus::Healthy,
|
||||
message: "数据库连接正常".to_string(),
|
||||
checked_at: SystemTime::now(),
|
||||
duration,
|
||||
details: HashMap::new(),
|
||||
})
|
||||
}
|
||||
|
||||
fn name(&self) -> &str {
|
||||
&self.name
|
||||
}
|
||||
|
||||
fn check_type(&self) -> HealthCheckType {
|
||||
HealthCheckType::Readiness
|
||||
}
|
||||
}
|
||||
|
||||
impl HealthChecker for HttpServiceHealthChecker {
|
||||
fn check_health(&self) -> Result<HealthCheckResult> {
|
||||
let start_time = SystemTime::now();
|
||||
|
||||
// 这里应该实现实际的 HTTP 服务检查
|
||||
// 例如发送 GET 请求到健康检查端点
|
||||
|
||||
let duration = start_time.elapsed().unwrap_or_default();
|
||||
|
||||
Ok(HealthCheckResult {
|
||||
checker_name: self.name.clone(),
|
||||
check_type: HealthCheckType::Liveness,
|
||||
status: HealthCheckStatus::Healthy,
|
||||
message: "HTTP 服务响应正常".to_string(),
|
||||
checked_at: SystemTime::now(),
|
||||
duration,
|
||||
details: HashMap::new(),
|
||||
})
|
||||
}
|
||||
|
||||
fn name(&self) -> &str {
|
||||
&self.name
|
||||
}
|
||||
|
||||
fn check_type(&self) -> HealthCheckType {
|
||||
HealthCheckType::Liveness
|
||||
}
|
||||
}
|
||||
|
||||
impl HealthChecker for FileSystemHealthChecker {
|
||||
fn check_health(&self) -> Result<HealthCheckResult> {
|
||||
let start_time = SystemTime::now();
|
||||
let mut details = HashMap::new();
|
||||
|
||||
// 检查文件系统空间
|
||||
for path in &self.check_paths {
|
||||
// 这里应该实现实际的文件系统检查
|
||||
details.insert(
|
||||
format!("path_{path}"),
|
||||
serde_json::Value::String("可用".to_string()),
|
||||
);
|
||||
}
|
||||
|
||||
let duration = start_time.elapsed().unwrap_or_default();
|
||||
|
||||
Ok(HealthCheckResult {
|
||||
checker_name: self.name.clone(),
|
||||
check_type: HealthCheckType::Startup,
|
||||
status: HealthCheckStatus::Healthy,
|
||||
message: "文件系统检查通过".to_string(),
|
||||
checked_at: SystemTime::now(),
|
||||
duration,
|
||||
details,
|
||||
})
|
||||
}
|
||||
|
||||
fn name(&self) -> &str {
|
||||
&self.name
|
||||
}
|
||||
|
||||
fn check_type(&self) -> HealthCheckType {
|
||||
HealthCheckType::Startup
|
||||
}
|
||||
}
|
||||
|
||||
impl HealthChecker for MemoryHealthChecker {
|
||||
fn check_health(&self) -> Result<HealthCheckResult> {
|
||||
let start_time = SystemTime::now();
|
||||
|
||||
// 这里应该实现实际的内存使用检查
|
||||
let memory_usage = 0.6; // 示例值
|
||||
|
||||
let status = if memory_usage > self.max_memory_usage {
|
||||
HealthCheckStatus::Warning
|
||||
} else {
|
||||
HealthCheckStatus::Healthy
|
||||
};
|
||||
|
||||
let duration = start_time.elapsed().unwrap_or_default();
|
||||
|
||||
let mut details = HashMap::new();
|
||||
details.insert(
|
||||
"memory_usage".to_string(),
|
||||
serde_json::Value::Number(serde_json::Number::from_f64(memory_usage).unwrap()),
|
||||
);
|
||||
|
||||
Ok(HealthCheckResult {
|
||||
checker_name: self.name.clone(),
|
||||
check_type: HealthCheckType::Liveness,
|
||||
status,
|
||||
message: format!("内存使用率: {:.1}%", memory_usage * 100.0),
|
||||
checked_at: SystemTime::now(),
|
||||
duration,
|
||||
details,
|
||||
})
|
||||
}
|
||||
|
||||
fn name(&self) -> &str {
|
||||
&self.name
|
||||
}
|
||||
|
||||
fn check_type(&self) -> HealthCheckType {
|
||||
HealthCheckType::Liveness
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for HealthStatus {
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
|
||||
impl HealthStatus {
|
||||
/// 创建新的健康状态
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
overall_status: HealthCheckStatus::Unknown,
|
||||
checker_statuses: HashMap::new(),
|
||||
last_updated: SystemTime::now(),
|
||||
startup_time: SystemTime::now(),
|
||||
uptime: Duration::from_secs(0),
|
||||
}
|
||||
}
|
||||
|
||||
/// 检查是否健康
|
||||
pub fn is_healthy(&self) -> bool {
|
||||
self.overall_status == HealthCheckStatus::Healthy
|
||||
}
|
||||
|
||||
/// 检查是否就绪
|
||||
pub fn is_ready(&self) -> bool {
|
||||
self.checker_statuses
|
||||
.values()
|
||||
.filter(|result| result.check_type == HealthCheckType::Readiness)
|
||||
.all(|result| result.status == HealthCheckStatus::Healthy)
|
||||
}
|
||||
|
||||
/// 检查是否存活
|
||||
pub fn is_alive(&self) -> bool {
|
||||
self.checker_statuses
|
||||
.values()
|
||||
.filter(|result| result.check_type == HealthCheckType::Liveness)
|
||||
.all(|result| result.status == HealthCheckStatus::Healthy)
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for HealthCheckConfig {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
enabled: true,
|
||||
check_interval: Duration::from_secs(30),
|
||||
timeout: Duration::from_secs(10),
|
||||
retry_count: 3,
|
||||
retry_interval: Duration::from_secs(1),
|
||||
startup_check: StartupCheckConfig {
|
||||
enabled: true,
|
||||
initial_delay: Duration::from_secs(10),
|
||||
period: Duration::from_secs(10),
|
||||
timeout: Duration::from_secs(30),
|
||||
failure_threshold: 3,
|
||||
},
|
||||
readiness_check: ReadinessCheckConfig {
|
||||
enabled: true,
|
||||
initial_delay: Duration::from_secs(5),
|
||||
period: Duration::from_secs(10),
|
||||
timeout: Duration::from_secs(5),
|
||||
success_threshold: 1,
|
||||
failure_threshold: 3,
|
||||
},
|
||||
liveness_check: LivenessCheckConfig {
|
||||
enabled: true,
|
||||
initial_delay: Duration::from_secs(30),
|
||||
period: Duration::from_secs(30),
|
||||
timeout: Duration::from_secs(5),
|
||||
failure_threshold: 3,
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_health_check_manager() {
|
||||
let config = HealthCheckConfig::default();
|
||||
let mut manager = HealthCheckManager::new(config);
|
||||
|
||||
let checker = Arc::new(DatabaseHealthChecker {
|
||||
name: "test_db".to_string(),
|
||||
connection_string: "test://localhost".to_string(),
|
||||
});
|
||||
|
||||
manager.add_checker(checker);
|
||||
|
||||
let status = manager.get_health_status().await;
|
||||
assert_eq!(status.overall_status, HealthCheckStatus::Unknown);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_database_health_checker() {
|
||||
let checker = DatabaseHealthChecker {
|
||||
name: "test_db".to_string(),
|
||||
connection_string: "test://localhost".to_string(),
|
||||
};
|
||||
|
||||
let result = checker.check_health().unwrap();
|
||||
assert_eq!(result.status, HealthCheckStatus::Healthy);
|
||||
assert_eq!(result.checker_name, "test_db");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_memory_health_checker() {
|
||||
let checker = MemoryHealthChecker {
|
||||
name: "memory".to_string(),
|
||||
max_memory_usage: 0.8,
|
||||
};
|
||||
|
||||
let result = checker.check_health().unwrap();
|
||||
assert!(
|
||||
result.status == HealthCheckStatus::Healthy
|
||||
|| result.status == HealthCheckStatus::Warning
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_health_status() {
|
||||
let status = HealthStatus::new();
|
||||
assert_eq!(status.overall_status, HealthCheckStatus::Unknown);
|
||||
assert!(!status.is_healthy());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,532 @@
|
||||
//! 优雅关闭管理器
|
||||
//!
|
||||
//! 提供优雅关闭处理,确保所有资源得到正确清理
|
||||
#![allow(dead_code)]
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::collections::HashMap;
|
||||
use std::sync::Arc;
|
||||
use std::sync::atomic::{AtomicBool, Ordering};
|
||||
use std::time::{Duration, SystemTime};
|
||||
use tokio::signal;
|
||||
use tokio::sync::{Mutex, RwLock, Semaphore};
|
||||
use tokio::time::timeout;
|
||||
|
||||
use super::resource_cleanup::ResourceCleaner;
|
||||
use crate::app_state::AppState;
|
||||
use crate::error::AppError;
|
||||
|
||||
/// 优雅关闭管理器
|
||||
pub struct GracefulShutdownManager {
|
||||
is_shutting_down: Arc<AtomicBool>,
|
||||
shutdown_handlers: Arc<RwLock<HashMap<String, Box<dyn ShutdownHandler + Send + Sync>>>>,
|
||||
shutdown_timeout: Duration,
|
||||
shutdown_semaphore: Arc<Semaphore>,
|
||||
shutdown_stats: Arc<Mutex<ShutdownStats>>,
|
||||
}
|
||||
|
||||
impl GracefulShutdownManager {
|
||||
/// 创建新的优雅关闭管理器
|
||||
pub async fn new() -> Result<Self, AppError> {
|
||||
Ok(Self {
|
||||
is_shutting_down: Arc::new(AtomicBool::new(false)),
|
||||
shutdown_handlers: Arc::new(RwLock::new(HashMap::new())),
|
||||
shutdown_timeout: Duration::from_secs(30),
|
||||
shutdown_semaphore: Arc::new(Semaphore::new(1)),
|
||||
shutdown_stats: Arc::new(Mutex::new(ShutdownStats::default())),
|
||||
})
|
||||
}
|
||||
|
||||
/// 设置优雅关闭
|
||||
pub async fn setup(
|
||||
&self,
|
||||
app_state: Arc<AppState>,
|
||||
resource_cleaner: Arc<dyn ResourceCleaner + Send + Sync>,
|
||||
) -> Result<(), AppError> {
|
||||
// 注册默认的关闭处理器
|
||||
self.register_default_handlers(app_state, resource_cleaner)
|
||||
.await?;
|
||||
|
||||
// 设置信号处理
|
||||
self.setup_signal_handlers().await?;
|
||||
|
||||
tracing::info!("Graceful shutdown setup completed");
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// 注册关闭处理器
|
||||
pub async fn register_handler(
|
||||
&self,
|
||||
name: String,
|
||||
handler: Box<dyn ShutdownHandler + Send + Sync>,
|
||||
) -> Result<(), AppError> {
|
||||
let mut handlers = self.shutdown_handlers.write().await;
|
||||
handlers.insert(name, handler);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// 移除关闭处理器
|
||||
pub async fn unregister_handler(&self, name: &str) -> Result<(), AppError> {
|
||||
let mut handlers = self.shutdown_handlers.write().await;
|
||||
handlers.remove(name);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// 检查是否正在关闭
|
||||
pub fn is_shutting_down(&self) -> bool {
|
||||
self.is_shutting_down.load(Ordering::Relaxed)
|
||||
}
|
||||
|
||||
/// 执行优雅关闭
|
||||
pub async fn shutdown(&self) -> Result<(), AppError> {
|
||||
// 获取关闭信号量,确保只有一个关闭过程
|
||||
let _permit = self
|
||||
.shutdown_semaphore
|
||||
.acquire()
|
||||
.await
|
||||
.map_err(|_| AppError::Config("Shutdown failed".to_string()))?;
|
||||
|
||||
if self.is_shutting_down.swap(true, Ordering::Relaxed) {
|
||||
tracing::warn!("Shutdown already in progress");
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let start_time = SystemTime::now();
|
||||
let mut stats = self.shutdown_stats.lock().await;
|
||||
stats.shutdown_started_at = Some(start_time);
|
||||
|
||||
tracing::info!("Starting graceful shutdown");
|
||||
|
||||
// 执行关闭处理器
|
||||
let result = self.execute_shutdown_handlers().await;
|
||||
|
||||
// 更新统计信息
|
||||
stats.shutdown_completed_at = Some(SystemTime::now());
|
||||
stats.shutdown_duration = SystemTime::now().duration_since(start_time).ok();
|
||||
stats.shutdown_successful = result.is_ok();
|
||||
|
||||
match result {
|
||||
Ok(_) => {
|
||||
tracing::info!(
|
||||
"Graceful shutdown completed successfully in {:?}",
|
||||
start_time.elapsed()
|
||||
);
|
||||
}
|
||||
Err(ref e) => {
|
||||
tracing::error!(
|
||||
"Graceful shutdown failed after {:?}: {}",
|
||||
start_time.elapsed(),
|
||||
e
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
result
|
||||
}
|
||||
|
||||
/// 强制关闭
|
||||
pub async fn force_shutdown(&self) -> Result<(), AppError> {
|
||||
tracing::warn!("Force shutdown initiated");
|
||||
|
||||
self.is_shutting_down.store(true, Ordering::Relaxed);
|
||||
|
||||
// 强制执行关闭处理器(不等待超时)
|
||||
let handlers = self.shutdown_handlers.read().await;
|
||||
for (name, handler) in handlers.iter() {
|
||||
if let Err(e) = handler.force_shutdown().await {
|
||||
tracing::error!("Force shutdown handler '{}' failed: {}", name, e);
|
||||
}
|
||||
}
|
||||
|
||||
tracing::warn!("Force shutdown completed");
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// 获取关闭统计信息
|
||||
pub async fn get_shutdown_stats(&self) -> ShutdownStats {
|
||||
self.shutdown_stats.lock().await.clone()
|
||||
}
|
||||
|
||||
/// 等待关闭完成
|
||||
pub async fn wait_for_shutdown(&self) -> Result<(), AppError> {
|
||||
while !self.is_shutting_down() {
|
||||
tokio::time::sleep(Duration::from_millis(100)).await;
|
||||
}
|
||||
|
||||
// 等待关闭完成
|
||||
loop {
|
||||
let stats = self.shutdown_stats.lock().await;
|
||||
if stats.shutdown_completed_at.is_some() {
|
||||
break;
|
||||
}
|
||||
drop(stats);
|
||||
tokio::time::sleep(Duration::from_millis(100)).await;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// 私有方法
|
||||
|
||||
async fn register_default_handlers(
|
||||
&self,
|
||||
app_state: Arc<AppState>,
|
||||
resource_cleaner: Arc<dyn ResourceCleaner + Send + Sync>,
|
||||
) -> Result<(), AppError> {
|
||||
// 注册应用状态关闭处理器
|
||||
self.register_handler(
|
||||
"app_state".to_string(),
|
||||
Box::new(AppStateShutdownHandler::new(app_state)),
|
||||
)
|
||||
.await?;
|
||||
|
||||
// 注册资源清理处理器
|
||||
self.register_handler(
|
||||
"resource_cleaner".to_string(),
|
||||
Box::new(ResourceCleanerShutdownHandler::new(resource_cleaner)),
|
||||
)
|
||||
.await?;
|
||||
|
||||
// 注册数据库连接关闭处理器
|
||||
self.register_handler(
|
||||
"database".to_string(),
|
||||
Box::new(DatabaseShutdownHandler::new()),
|
||||
)
|
||||
.await?;
|
||||
|
||||
// 注册HTTP服务器关闭处理器
|
||||
self.register_handler(
|
||||
"http_server".to_string(),
|
||||
Box::new(HttpServerShutdownHandler::new()),
|
||||
)
|
||||
.await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn setup_signal_handlers(&self) -> Result<(), AppError> {
|
||||
#[cfg(unix)]
|
||||
{
|
||||
let shutdown_manager = Arc::new(self.clone());
|
||||
|
||||
// 处理SIGTERM信号
|
||||
tokio::spawn(async move {
|
||||
let mut sigterm = signal::unix::signal(signal::unix::SignalKind::terminate())
|
||||
.expect("Failed to register SIGTERM handler");
|
||||
|
||||
sigterm.recv().await;
|
||||
tracing::info!("Received SIGTERM, initiating graceful shutdown");
|
||||
|
||||
if let Err(e) = shutdown_manager.shutdown().await {
|
||||
tracing::error!("Graceful shutdown failed: {}", e);
|
||||
std::process::exit(1);
|
||||
}
|
||||
});
|
||||
|
||||
// 处理SIGINT信号 (Ctrl+C)
|
||||
let shutdown_manager = Arc::new(self.clone());
|
||||
tokio::spawn(async move {
|
||||
let mut sigint = signal::unix::signal(signal::unix::SignalKind::interrupt())
|
||||
.expect("Failed to register SIGINT handler");
|
||||
|
||||
sigint.recv().await;
|
||||
tracing::info!("Received SIGINT, initiating graceful shutdown");
|
||||
|
||||
if let Err(e) = shutdown_manager.shutdown().await {
|
||||
tracing::error!("Graceful shutdown failed: {}", e);
|
||||
std::process::exit(1);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
#[cfg(not(unix))]
|
||||
{
|
||||
// Windows uses ctrl_c signal handling which is set up elsewhere
|
||||
tracing::debug!("Unix signal handlers not available on this platform");
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn execute_shutdown_handlers(&self) -> Result<(), AppError> {
|
||||
let handlers = self.shutdown_handlers.read().await;
|
||||
let mut shutdown_results = Vec::new();
|
||||
|
||||
// 按优先级顺序执行关闭处理器
|
||||
let ordered_handlers = self.get_ordered_handlers(&handlers).await;
|
||||
|
||||
for (name, handler) in ordered_handlers {
|
||||
tracing::info!("Executing shutdown handler: {}", name);
|
||||
|
||||
let result = timeout(self.shutdown_timeout, handler.shutdown()).await;
|
||||
|
||||
match result {
|
||||
Ok(Ok(_)) => {
|
||||
tracing::info!("Shutdown handler '{}' completed successfully", name);
|
||||
shutdown_results.push((name.clone(), true));
|
||||
}
|
||||
Ok(Err(e)) => {
|
||||
tracing::error!("Shutdown handler '{}' failed: {}", name, e);
|
||||
shutdown_results.push((name.clone(), false));
|
||||
}
|
||||
Err(_) => {
|
||||
tracing::error!("Shutdown handler '{}' timed out", name);
|
||||
shutdown_results.push((name.clone(), false));
|
||||
|
||||
// 尝试强制关闭
|
||||
if let Err(e) = handler.force_shutdown().await {
|
||||
tracing::error!("Force shutdown for '{}' failed: {}", name, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 检查是否有失败的处理器
|
||||
let failed_handlers: Vec<_> = shutdown_results
|
||||
.iter()
|
||||
.filter(|(_, success)| !success)
|
||||
.map(|(name, _)| name.clone())
|
||||
.collect();
|
||||
|
||||
if !failed_handlers.is_empty() {
|
||||
return Err(AppError::Config(format!(
|
||||
"Shutdown handlers failed: {failed_handlers:?}"
|
||||
)));
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn get_ordered_handlers<'a>(
|
||||
&self,
|
||||
handlers: &'a HashMap<String, Box<dyn ShutdownHandler + Send + Sync>>,
|
||||
) -> Vec<(String, &'a Box<dyn ShutdownHandler + Send + Sync>)> {
|
||||
// 定义关闭顺序(优先级从高到低)
|
||||
let priority_order = vec!["http_server", "app_state", "database", "resource_cleaner"];
|
||||
|
||||
let mut ordered = Vec::new();
|
||||
|
||||
// 按优先级添加处理器
|
||||
for &priority_name in &priority_order {
|
||||
if let Some(handler) = handlers.get(priority_name) {
|
||||
ordered.push((priority_name.to_string(), handler));
|
||||
}
|
||||
}
|
||||
|
||||
// 添加其他处理器
|
||||
for (name, handler) in handlers {
|
||||
if !priority_order.contains(&name.as_str()) {
|
||||
ordered.push((name.clone(), handler));
|
||||
}
|
||||
}
|
||||
|
||||
ordered
|
||||
}
|
||||
}
|
||||
|
||||
// 为了支持clone,我们需要实现Clone trait
|
||||
impl Clone for GracefulShutdownManager {
|
||||
fn clone(&self) -> Self {
|
||||
Self {
|
||||
is_shutting_down: self.is_shutting_down.clone(),
|
||||
shutdown_handlers: self.shutdown_handlers.clone(),
|
||||
shutdown_timeout: self.shutdown_timeout,
|
||||
shutdown_semaphore: self.shutdown_semaphore.clone(),
|
||||
shutdown_stats: self.shutdown_stats.clone(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// 关闭处理器特征
|
||||
#[async_trait::async_trait]
|
||||
pub trait ShutdownHandler {
|
||||
/// 执行优雅关闭
|
||||
async fn shutdown(&self) -> Result<(), AppError>;
|
||||
|
||||
/// 执行强制关闭
|
||||
async fn force_shutdown(&self) -> Result<(), AppError>;
|
||||
|
||||
/// 获取处理器名称
|
||||
fn name(&self) -> &str;
|
||||
|
||||
/// 获取关闭优先级(数字越小优先级越高)
|
||||
fn priority(&self) -> u32 {
|
||||
100
|
||||
}
|
||||
}
|
||||
|
||||
/// 应用状态关闭处理器
|
||||
struct AppStateShutdownHandler {
|
||||
app_state: Arc<AppState>,
|
||||
}
|
||||
|
||||
impl AppStateShutdownHandler {
|
||||
fn new(app_state: Arc<AppState>) -> Self {
|
||||
Self { app_state }
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait::async_trait]
|
||||
impl ShutdownHandler for AppStateShutdownHandler {
|
||||
async fn shutdown(&self) -> Result<(), AppError> {
|
||||
tracing::info!("Shutting down application state");
|
||||
|
||||
// 停止接受新请求
|
||||
// 等待当前请求完成
|
||||
// 清理应用状态
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn force_shutdown(&self) -> Result<(), AppError> {
|
||||
tracing::warn!("Force shutting down application state");
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn name(&self) -> &str {
|
||||
"app_state"
|
||||
}
|
||||
|
||||
fn priority(&self) -> u32 {
|
||||
10
|
||||
}
|
||||
}
|
||||
|
||||
/// 资源清理关闭处理器
|
||||
struct ResourceCleanerShutdownHandler {
|
||||
resource_cleaner: Arc<dyn ResourceCleaner + Send + Sync>,
|
||||
}
|
||||
|
||||
impl ResourceCleanerShutdownHandler {
|
||||
fn new(resource_cleaner: Arc<dyn ResourceCleaner + Send + Sync>) -> Self {
|
||||
Self { resource_cleaner }
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait::async_trait]
|
||||
impl ShutdownHandler for ResourceCleanerShutdownHandler {
|
||||
async fn shutdown(&self) -> Result<(), AppError> {
|
||||
tracing::info!("Executing resource cleanup");
|
||||
// ResourceCleaner trait methods return anyhow::Result, so we need to convert
|
||||
self.resource_cleaner
|
||||
.cleanup()
|
||||
.map_err(|e| AppError::Config(format!("Resource cleanup failed: {e}")))?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn force_shutdown(&self) -> Result<(), AppError> {
|
||||
tracing::warn!("Force executing resource cleanup");
|
||||
// ResourceCleaner trait methods return anyhow::Result, so we need to convert
|
||||
self.resource_cleaner
|
||||
.force_cleanup()
|
||||
.map_err(|e| AppError::Config(format!("Force resource cleanup failed: {e}")))?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn name(&self) -> &str {
|
||||
"resource_cleaner"
|
||||
}
|
||||
|
||||
fn priority(&self) -> u32 {
|
||||
90
|
||||
}
|
||||
}
|
||||
|
||||
/// 数据库关闭处理器
|
||||
struct DatabaseShutdownHandler;
|
||||
|
||||
impl DatabaseShutdownHandler {
|
||||
fn new() -> Self {
|
||||
Self
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait::async_trait]
|
||||
impl ShutdownHandler for DatabaseShutdownHandler {
|
||||
async fn shutdown(&self) -> Result<(), AppError> {
|
||||
tracing::info!("Shutting down database connections");
|
||||
|
||||
// 关闭数据库连接池
|
||||
// 等待当前事务完成
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn force_shutdown(&self) -> Result<(), AppError> {
|
||||
tracing::warn!("Force shutting down database connections");
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn name(&self) -> &str {
|
||||
"database"
|
||||
}
|
||||
|
||||
fn priority(&self) -> u32 {
|
||||
50
|
||||
}
|
||||
}
|
||||
|
||||
/// HTTP服务器关闭处理器
|
||||
struct HttpServerShutdownHandler;
|
||||
|
||||
impl HttpServerShutdownHandler {
|
||||
fn new() -> Self {
|
||||
Self
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait::async_trait]
|
||||
impl ShutdownHandler for HttpServerShutdownHandler {
|
||||
async fn shutdown(&self) -> Result<(), AppError> {
|
||||
tracing::info!("Shutting down HTTP server");
|
||||
|
||||
// 停止接受新连接
|
||||
// 等待当前请求完成
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn force_shutdown(&self) -> Result<(), AppError> {
|
||||
tracing::warn!("Force shutting down HTTP server");
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn name(&self) -> &str {
|
||||
"http_server"
|
||||
}
|
||||
|
||||
fn priority(&self) -> u32 {
|
||||
5
|
||||
}
|
||||
}
|
||||
|
||||
/// 关闭统计信息
|
||||
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
|
||||
pub struct ShutdownStats {
|
||||
pub shutdown_started_at: Option<SystemTime>,
|
||||
pub shutdown_completed_at: Option<SystemTime>,
|
||||
pub shutdown_duration: Option<Duration>,
|
||||
pub shutdown_successful: bool,
|
||||
pub handlers_executed: Vec<String>,
|
||||
pub failed_handlers: Vec<String>,
|
||||
}
|
||||
|
||||
/// 关闭配置
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct ShutdownConfig {
|
||||
pub timeout: Duration,
|
||||
pub force_timeout: Duration,
|
||||
pub signal_handlers_enabled: bool,
|
||||
}
|
||||
|
||||
impl Default for ShutdownConfig {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
timeout: Duration::from_secs(30),
|
||||
force_timeout: Duration::from_secs(5),
|
||||
signal_handlers_enabled: true,
|
||||
}
|
||||
}
|
||||
}
|
||||
391
qiming-mcp-proxy/document-parser/src/production/mod.rs
Normal file
391
qiming-mcp-proxy/document-parser/src/production/mod.rs
Normal file
@@ -0,0 +1,391 @@
|
||||
//! 生产部署功能模块
|
||||
//!
|
||||
//! 提供生产环境所需的功能,包括优雅关闭、配置验证、生产日志和监控集成
|
||||
#![allow(dead_code)]
|
||||
|
||||
pub mod config_validation;
|
||||
pub mod deployment_health;
|
||||
pub mod graceful_shutdown;
|
||||
pub mod monitoring_integration;
|
||||
pub mod production_logging;
|
||||
pub mod resource_cleanup;
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::sync::Arc;
|
||||
use std::time::Duration;
|
||||
use tokio::sync::RwLock;
|
||||
use uuid::Uuid;
|
||||
|
||||
use crate::app_state::AppState;
|
||||
use crate::config::AppConfig;
|
||||
use crate::error::AppError;
|
||||
|
||||
use config_validation::ConfigValidator;
|
||||
use deployment_health::HealthCheckManager;
|
||||
use graceful_shutdown::GracefulShutdownManager;
|
||||
use monitoring_integration::MonitoringIntegration;
|
||||
use production_logging::ProductionLogger;
|
||||
use resource_cleanup::{DatabaseConnectionCleaner, ResourceCleaner};
|
||||
|
||||
/// 生产部署管理器
|
||||
pub struct ProductionManager {
|
||||
config: AppConfig,
|
||||
shutdown_manager: Arc<GracefulShutdownManager>,
|
||||
config_validator: Arc<ConfigValidator>,
|
||||
logger: Arc<ProductionLogger>,
|
||||
monitoring: Arc<MonitoringIntegration>,
|
||||
health_checker: Arc<HealthCheckManager>,
|
||||
resource_cleaner: Arc<dyn ResourceCleaner + Send + Sync>,
|
||||
deployment_info: Arc<RwLock<DeploymentInfo>>,
|
||||
is_production_ready: Arc<RwLock<bool>>,
|
||||
}
|
||||
|
||||
impl ProductionManager {
|
||||
/// 创建新的生产部署管理器
|
||||
pub async fn new(config: AppConfig) -> Result<Self, AppError> {
|
||||
let shutdown_manager = Arc::new(GracefulShutdownManager::new().await?);
|
||||
let config_validator = Arc::new(ConfigValidator::new(
|
||||
config_validation::ValidationRules::default(),
|
||||
));
|
||||
let logger = Arc::new(ProductionLogger::new(
|
||||
production_logging::LoggingConfig::default(),
|
||||
));
|
||||
let monitoring = Arc::new(MonitoringIntegration::new(
|
||||
monitoring_integration::MonitoringConfig::default(),
|
||||
));
|
||||
let health_checker = Arc::new(HealthCheckManager::new(
|
||||
deployment_health::HealthCheckConfig::default(),
|
||||
));
|
||||
let resource_cleaner: Arc<dyn ResourceCleaner + Send + Sync> = Arc::new(
|
||||
DatabaseConnectionCleaner::new("production_db_cleaner".to_string(), 10),
|
||||
);
|
||||
|
||||
let deployment_info = Arc::new(RwLock::new(DeploymentInfo {
|
||||
deployment_id: Uuid::new_v4().to_string(),
|
||||
version: env!("CARGO_PKG_VERSION").to_string(),
|
||||
environment: config.environment.clone(),
|
||||
started_at: std::time::SystemTime::now(),
|
||||
ready_at: None,
|
||||
shutdown_at: None,
|
||||
}));
|
||||
|
||||
Ok(Self {
|
||||
config,
|
||||
shutdown_manager,
|
||||
config_validator,
|
||||
logger,
|
||||
monitoring,
|
||||
health_checker,
|
||||
resource_cleaner,
|
||||
deployment_info,
|
||||
is_production_ready: Arc::new(RwLock::new(false)),
|
||||
})
|
||||
}
|
||||
|
||||
/// 初始化生产环境
|
||||
pub async fn initialize_production(&self, app_state: Arc<AppState>) -> Result<(), AppError> {
|
||||
// 1. 验证配置
|
||||
self.validate_configuration().await?;
|
||||
|
||||
// 2. 初始化生产日志
|
||||
self.initialize_logging().await?;
|
||||
|
||||
// 3. 设置监控集成
|
||||
self.setup_monitoring().await?;
|
||||
|
||||
// 4. 初始化健康检查
|
||||
self.initialize_health_checks(app_state.clone()).await?;
|
||||
|
||||
// 5. 设置优雅关闭
|
||||
self.setup_graceful_shutdown(app_state).await?;
|
||||
|
||||
// 6. 标记为生产就绪
|
||||
*self.is_production_ready.write().await = true;
|
||||
|
||||
// 7. 更新部署信息
|
||||
{
|
||||
let mut info = self.deployment_info.write().await;
|
||||
info.ready_at = Some(std::time::SystemTime::now());
|
||||
}
|
||||
|
||||
tracing::info!("Production environment initialized successfully");
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// 验证配置
|
||||
pub async fn validate_configuration(&self) -> Result<(), AppError> {
|
||||
let mut validator = self.config_validator.as_ref().clone();
|
||||
let result = validator
|
||||
.validate_config(&self.config)
|
||||
.map_err(|e| AppError::Validation(e.to_string()))?;
|
||||
|
||||
if !result.is_valid {
|
||||
let error_messages: Vec<String> =
|
||||
result.errors.iter().map(|e| e.message.clone()).collect();
|
||||
return Err(AppError::Validation(error_messages.join("; ")));
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// 初始化日志
|
||||
pub async fn initialize_logging(&self) -> Result<(), AppError> {
|
||||
self.logger.start_background_tasks().await;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// 设置监控
|
||||
pub async fn setup_monitoring(&self) -> Result<(), AppError> {
|
||||
// MonitoringIntegration 暂时没有 setup 方法
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// 初始化健康检查
|
||||
pub async fn initialize_health_checks(
|
||||
&self,
|
||||
_app_state: Arc<AppState>,
|
||||
) -> Result<(), AppError> {
|
||||
self.health_checker
|
||||
.start_health_checks()
|
||||
.await
|
||||
.map_err(|e| AppError::Internal(format!("健康检查初始化失败: {e}")))
|
||||
}
|
||||
|
||||
/// 设置优雅关闭
|
||||
pub async fn setup_graceful_shutdown(&self, app_state: Arc<AppState>) -> Result<(), AppError> {
|
||||
self.shutdown_manager
|
||||
.setup(app_state, self.resource_cleaner.clone())
|
||||
.await
|
||||
}
|
||||
|
||||
/// 检查生产就绪状态
|
||||
pub async fn is_ready(&self) -> bool {
|
||||
*self.is_production_ready.read().await
|
||||
}
|
||||
|
||||
/// 获取部署信息
|
||||
pub async fn get_deployment_info(&self) -> DeploymentInfo {
|
||||
self.deployment_info.read().await.clone()
|
||||
}
|
||||
|
||||
/// 获取健康状态
|
||||
pub async fn get_health_status(&self) -> Result<deployment_health::HealthStatus, AppError> {
|
||||
Ok(self.health_checker.get_health_status().await)
|
||||
}
|
||||
|
||||
/// 获取监控指标
|
||||
pub async fn get_monitoring_metrics(&self) -> Result<MonitoringMetrics, AppError> {
|
||||
// 暂时返回空的监控指标
|
||||
Ok(MonitoringMetrics {
|
||||
system_metrics: std::collections::HashMap::new(),
|
||||
application_metrics: std::collections::HashMap::new(),
|
||||
custom_metrics: std::collections::HashMap::new(),
|
||||
collected_at: std::time::SystemTime::now(),
|
||||
})
|
||||
}
|
||||
|
||||
/// 触发优雅关闭
|
||||
pub async fn shutdown(&self) -> Result<(), AppError> {
|
||||
tracing::info!("Starting graceful shutdown");
|
||||
|
||||
// 更新部署信息
|
||||
{
|
||||
let mut info = self.deployment_info.write().await;
|
||||
info.shutdown_at = Some(std::time::SystemTime::now());
|
||||
}
|
||||
|
||||
// 标记为非生产就绪
|
||||
*self.is_production_ready.write().await = false;
|
||||
|
||||
// 执行优雅关闭
|
||||
self.shutdown_manager.shutdown().await?;
|
||||
|
||||
tracing::info!("Graceful shutdown completed");
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// 获取运行时统计
|
||||
pub async fn get_runtime_stats(&self) -> Result<RuntimeStats, AppError> {
|
||||
let deployment_info = self.get_deployment_info().await;
|
||||
let health_status = self.get_health_status().await?;
|
||||
let monitoring_metrics = self.get_monitoring_metrics().await?;
|
||||
|
||||
let uptime = deployment_info
|
||||
.started_at
|
||||
.elapsed()
|
||||
.unwrap_or(Duration::from_secs(0));
|
||||
|
||||
Ok(RuntimeStats {
|
||||
deployment_info,
|
||||
health_status,
|
||||
monitoring_metrics,
|
||||
uptime,
|
||||
is_ready: self.is_ready().await,
|
||||
})
|
||||
}
|
||||
|
||||
/// 执行生产环境检查
|
||||
pub async fn run_production_checks(&self) -> Result<ProductionCheckResult, AppError> {
|
||||
let mut checks = Vec::new();
|
||||
|
||||
// 配置检查
|
||||
match self.validate_configuration().await {
|
||||
Ok(_) => checks.push(ProductionCheck {
|
||||
name: "Configuration Validation".to_string(),
|
||||
status: CheckStatus::Passed,
|
||||
message: "All configuration values are valid".to_string(),
|
||||
}),
|
||||
Err(e) => checks.push(ProductionCheck {
|
||||
name: "Configuration Validation".to_string(),
|
||||
status: CheckStatus::Failed,
|
||||
message: format!("Configuration validation failed: {e}"),
|
||||
}),
|
||||
}
|
||||
|
||||
// 健康检查
|
||||
match self.get_health_status().await {
|
||||
Ok(health) => {
|
||||
let status =
|
||||
if health.overall_status == deployment_health::HealthCheckStatus::Healthy {
|
||||
CheckStatus::Passed
|
||||
} else {
|
||||
CheckStatus::Warning
|
||||
};
|
||||
checks.push(ProductionCheck {
|
||||
name: "Health Check".to_string(),
|
||||
status,
|
||||
message: format!("Overall health: {:?}", health.overall_status),
|
||||
});
|
||||
}
|
||||
Err(e) => checks.push(ProductionCheck {
|
||||
name: "Health Check".to_string(),
|
||||
status: CheckStatus::Failed,
|
||||
message: format!("Health check failed: {e}"),
|
||||
}),
|
||||
}
|
||||
|
||||
// 监控检查
|
||||
match self.get_monitoring_metrics().await {
|
||||
Ok(_) => checks.push(ProductionCheck {
|
||||
name: "Monitoring Integration".to_string(),
|
||||
status: CheckStatus::Passed,
|
||||
message: "Monitoring metrics are available".to_string(),
|
||||
}),
|
||||
Err(e) => checks.push(ProductionCheck {
|
||||
name: "Monitoring Integration".to_string(),
|
||||
status: CheckStatus::Failed,
|
||||
message: format!("Monitoring check failed: {e}"),
|
||||
}),
|
||||
}
|
||||
|
||||
let overall_status = if checks.iter().any(|c| c.status == CheckStatus::Failed) {
|
||||
CheckStatus::Failed
|
||||
} else if checks.iter().any(|c| c.status == CheckStatus::Warning) {
|
||||
CheckStatus::Warning
|
||||
} else {
|
||||
CheckStatus::Passed
|
||||
};
|
||||
|
||||
Ok(ProductionCheckResult {
|
||||
overall_status,
|
||||
checks,
|
||||
checked_at: std::time::SystemTime::now(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/// 部署信息
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct DeploymentInfo {
|
||||
pub deployment_id: String,
|
||||
pub version: String,
|
||||
pub environment: String,
|
||||
pub started_at: std::time::SystemTime,
|
||||
pub ready_at: Option<std::time::SystemTime>,
|
||||
pub shutdown_at: Option<std::time::SystemTime>,
|
||||
}
|
||||
|
||||
/// 健康状态
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct HealthStatus {
|
||||
pub overall_status: String,
|
||||
pub components: std::collections::HashMap<String, ComponentHealth>,
|
||||
pub last_check: std::time::SystemTime,
|
||||
}
|
||||
|
||||
/// 组件健康状态
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct ComponentHealth {
|
||||
pub status: String,
|
||||
pub message: String,
|
||||
pub last_check: std::time::SystemTime,
|
||||
}
|
||||
|
||||
/// 监控指标
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct MonitoringMetrics {
|
||||
pub system_metrics: std::collections::HashMap<String, f64>,
|
||||
pub application_metrics: std::collections::HashMap<String, f64>,
|
||||
pub custom_metrics: std::collections::HashMap<String, f64>,
|
||||
pub collected_at: std::time::SystemTime,
|
||||
}
|
||||
|
||||
/// 运行时统计
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct RuntimeStats {
|
||||
pub deployment_info: DeploymentInfo,
|
||||
pub health_status: deployment_health::HealthStatus,
|
||||
pub monitoring_metrics: MonitoringMetrics,
|
||||
pub uptime: Duration,
|
||||
pub is_ready: bool,
|
||||
}
|
||||
|
||||
/// 生产检查结果
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct ProductionCheckResult {
|
||||
pub overall_status: CheckStatus,
|
||||
pub checks: Vec<ProductionCheck>,
|
||||
pub checked_at: std::time::SystemTime,
|
||||
}
|
||||
|
||||
/// 单个生产检查
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct ProductionCheck {
|
||||
pub name: String,
|
||||
pub status: CheckStatus,
|
||||
pub message: String,
|
||||
}
|
||||
|
||||
/// 检查状态
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
|
||||
pub enum CheckStatus {
|
||||
Passed,
|
||||
Warning,
|
||||
Failed,
|
||||
}
|
||||
|
||||
/// 生产配置
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct ProductionConfig {
|
||||
pub graceful_shutdown_timeout: Duration,
|
||||
pub health_check_interval: Duration,
|
||||
pub monitoring_enabled: bool,
|
||||
pub log_level: String,
|
||||
pub metrics_endpoint: Option<String>,
|
||||
pub tracing_endpoint: Option<String>,
|
||||
}
|
||||
|
||||
impl Default for ProductionConfig {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
graceful_shutdown_timeout: Duration::from_secs(30),
|
||||
health_check_interval: Duration::from_secs(30),
|
||||
monitoring_enabled: true,
|
||||
log_level: "info".to_string(),
|
||||
metrics_endpoint: None,
|
||||
tracing_endpoint: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,766 @@
|
||||
//! 监控集成模块
|
||||
//!
|
||||
//! 提供与各种监控系统的集成,包括指标收集、告警、追踪等功能。
|
||||
#![allow(dead_code)]
|
||||
|
||||
use anyhow::Result;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::collections::HashMap;
|
||||
use std::sync::Arc;
|
||||
use std::time::{Duration, SystemTime};
|
||||
use tokio::sync::RwLock;
|
||||
use tracing::{error, info};
|
||||
|
||||
/// 监控集成管理器
|
||||
#[derive(Clone)]
|
||||
pub struct MonitoringIntegration {
|
||||
/// 监控配置
|
||||
config: MonitoringConfig,
|
||||
/// 指标收集器
|
||||
metrics_collectors: Vec<Arc<dyn MetricsCollector + Send + Sync>>,
|
||||
/// 告警管理器
|
||||
alert_managers: Vec<Arc<dyn AlertManager + Send + Sync>>,
|
||||
/// 追踪收集器
|
||||
trace_collectors: Vec<Arc<dyn TraceCollector + Send + Sync>>,
|
||||
/// 监控统计
|
||||
stats: Arc<RwLock<MonitoringStats>>,
|
||||
}
|
||||
|
||||
impl std::fmt::Debug for MonitoringIntegration {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
f.debug_struct("MonitoringIntegration")
|
||||
.field("config", &self.config)
|
||||
.field("metrics_collectors_count", &self.metrics_collectors.len())
|
||||
.field("alert_managers_count", &self.alert_managers.len())
|
||||
.field("trace_collectors_count", &self.trace_collectors.len())
|
||||
.field("stats", &"<stats>")
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
/// 监控配置
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct MonitoringConfig {
|
||||
/// 是否启用监控
|
||||
pub enabled: bool,
|
||||
/// 指标收集间隔
|
||||
pub metrics_interval: Duration,
|
||||
/// 告警检查间隔
|
||||
pub alert_check_interval: Duration,
|
||||
/// 追踪采样率
|
||||
pub trace_sampling_rate: f64,
|
||||
/// Prometheus 配置
|
||||
pub prometheus: Option<PrometheusConfig>,
|
||||
/// Grafana 配置
|
||||
pub grafana: Option<GrafanaConfig>,
|
||||
/// Jaeger 配置
|
||||
pub jaeger: Option<JaegerConfig>,
|
||||
/// 自定义监控端点
|
||||
pub custom_endpoints: Vec<CustomEndpoint>,
|
||||
}
|
||||
|
||||
/// Prometheus 配置
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct PrometheusConfig {
|
||||
/// 端点 URL
|
||||
pub endpoint: String,
|
||||
/// 推送网关 URL
|
||||
pub pushgateway_url: Option<String>,
|
||||
/// 作业名称
|
||||
pub job_name: String,
|
||||
/// 实例标签
|
||||
pub instance_label: String,
|
||||
/// 推送间隔
|
||||
pub push_interval: Duration,
|
||||
}
|
||||
|
||||
/// Grafana 配置
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct GrafanaConfig {
|
||||
/// API URL
|
||||
pub api_url: String,
|
||||
/// API 密钥
|
||||
pub api_key: String,
|
||||
/// 数据源 ID
|
||||
pub datasource_id: String,
|
||||
/// 仪表板 ID
|
||||
pub dashboard_id: Option<String>,
|
||||
}
|
||||
|
||||
/// Jaeger 配置
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct JaegerConfig {
|
||||
/// 收集器端点
|
||||
pub collector_endpoint: String,
|
||||
/// 代理端点
|
||||
pub agent_endpoint: Option<String>,
|
||||
/// 服务名称
|
||||
pub service_name: String,
|
||||
/// 采样策略
|
||||
pub sampling_strategy: SamplingStrategy,
|
||||
}
|
||||
|
||||
/// 采样策略
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub enum SamplingStrategy {
|
||||
/// 常量采样
|
||||
Const(f64),
|
||||
/// 概率采样
|
||||
Probabilistic(f64),
|
||||
/// 速率限制采样
|
||||
RateLimiting(u32),
|
||||
/// 自适应采样
|
||||
Adaptive,
|
||||
}
|
||||
|
||||
/// 自定义监控端点
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct CustomEndpoint {
|
||||
/// 端点名称
|
||||
pub name: String,
|
||||
/// 端点 URL
|
||||
pub url: String,
|
||||
/// 认证信息
|
||||
pub auth: Option<AuthConfig>,
|
||||
/// 数据格式
|
||||
pub format: DataFormat,
|
||||
/// 发送间隔
|
||||
pub send_interval: Duration,
|
||||
}
|
||||
|
||||
/// 认证配置
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub enum AuthConfig {
|
||||
/// API 密钥
|
||||
ApiKey(String),
|
||||
/// Bearer Token
|
||||
Bearer(String),
|
||||
/// 基本认证
|
||||
Basic { username: String, password: String },
|
||||
}
|
||||
|
||||
/// 数据格式
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub enum DataFormat {
|
||||
Json,
|
||||
Prometheus,
|
||||
InfluxDB,
|
||||
Custom(String),
|
||||
}
|
||||
|
||||
/// 指标收集器 trait
|
||||
pub trait MetricsCollector {
|
||||
/// 收集指标
|
||||
fn collect_metrics(&self) -> Result<Vec<Metric>>;
|
||||
/// 获取收集器名称
|
||||
fn name(&self) -> &str;
|
||||
}
|
||||
|
||||
/// 告警管理器 trait
|
||||
pub trait AlertManager {
|
||||
/// 检查告警
|
||||
fn check_alerts(&self, metrics: &[Metric]) -> Result<Vec<Alert>>;
|
||||
/// 发送告警
|
||||
fn send_alert(&self, alert: &Alert) -> Result<()>;
|
||||
/// 获取管理器名称
|
||||
fn name(&self) -> &str;
|
||||
}
|
||||
|
||||
/// 追踪收集器 trait
|
||||
pub trait TraceCollector {
|
||||
/// 收集追踪数据
|
||||
fn collect_trace(&self, span: &TraceSpan) -> Result<()>;
|
||||
/// 获取收集器名称
|
||||
fn name(&self) -> &str;
|
||||
}
|
||||
|
||||
/// 指标
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct Metric {
|
||||
/// 指标名称
|
||||
pub name: String,
|
||||
/// 指标值
|
||||
pub value: f64,
|
||||
/// 指标类型
|
||||
pub metric_type: MetricType,
|
||||
/// 标签
|
||||
pub labels: HashMap<String, String>,
|
||||
/// 时间戳
|
||||
pub timestamp: SystemTime,
|
||||
/// 帮助信息
|
||||
pub help: Option<String>,
|
||||
}
|
||||
|
||||
/// 指标类型
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub enum MetricType {
|
||||
/// 计数器
|
||||
Counter,
|
||||
/// 仪表
|
||||
Gauge,
|
||||
/// 直方图
|
||||
Histogram,
|
||||
/// 摘要
|
||||
Summary,
|
||||
}
|
||||
|
||||
/// 告警
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct Alert {
|
||||
/// 告警 ID
|
||||
pub id: String,
|
||||
/// 告警名称
|
||||
pub name: String,
|
||||
/// 告警级别
|
||||
pub severity: AlertSeverity,
|
||||
/// 告警消息
|
||||
pub message: String,
|
||||
/// 告警标签
|
||||
pub labels: HashMap<String, String>,
|
||||
/// 触发时间
|
||||
pub triggered_at: SystemTime,
|
||||
/// 告警状态
|
||||
pub status: AlertStatus,
|
||||
/// 相关指标
|
||||
pub metrics: Vec<String>,
|
||||
}
|
||||
|
||||
/// 告警级别
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub enum AlertSeverity {
|
||||
Critical,
|
||||
Warning,
|
||||
Info,
|
||||
}
|
||||
|
||||
/// 告警状态
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub enum AlertStatus {
|
||||
Firing,
|
||||
Resolved,
|
||||
Silenced,
|
||||
}
|
||||
|
||||
/// 追踪跨度
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct TraceSpan {
|
||||
/// 跨度 ID
|
||||
pub span_id: String,
|
||||
/// 追踪 ID
|
||||
pub trace_id: String,
|
||||
/// 父跨度 ID
|
||||
pub parent_span_id: Option<String>,
|
||||
/// 操作名称
|
||||
pub operation_name: String,
|
||||
/// 开始时间
|
||||
pub start_time: SystemTime,
|
||||
/// 结束时间
|
||||
pub end_time: Option<SystemTime>,
|
||||
/// 标签
|
||||
pub tags: HashMap<String, String>,
|
||||
/// 日志
|
||||
pub logs: Vec<SpanLog>,
|
||||
}
|
||||
|
||||
/// 跨度日志
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct SpanLog {
|
||||
/// 时间戳
|
||||
pub timestamp: SystemTime,
|
||||
/// 字段
|
||||
pub fields: HashMap<String, String>,
|
||||
}
|
||||
|
||||
/// Prometheus 收集器
|
||||
#[derive(Debug)]
|
||||
pub struct PrometheusCollector {
|
||||
/// 收集器名称
|
||||
name: String,
|
||||
/// 配置
|
||||
config: PrometheusConfig,
|
||||
/// HTTP 客户端
|
||||
client: reqwest::Client,
|
||||
}
|
||||
|
||||
/// 系统指标收集器
|
||||
#[derive(Debug)]
|
||||
pub struct SystemMetricsCollector {
|
||||
/// 收集器名称
|
||||
name: String,
|
||||
}
|
||||
|
||||
/// 应用指标收集器
|
||||
#[derive(Debug)]
|
||||
pub struct ApplicationMetricsCollector {
|
||||
/// 收集器名称
|
||||
name: String,
|
||||
/// 应用统计
|
||||
app_stats: Arc<RwLock<HashMap<String, f64>>>,
|
||||
}
|
||||
|
||||
/// 阈值告警管理器
|
||||
#[derive(Debug)]
|
||||
pub struct ThresholdAlertManager {
|
||||
/// 管理器名称
|
||||
name: String,
|
||||
/// 告警规则
|
||||
rules: Vec<AlertRule>,
|
||||
}
|
||||
|
||||
/// 告警规则
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct AlertRule {
|
||||
/// 规则名称
|
||||
pub name: String,
|
||||
/// 指标名称
|
||||
pub metric_name: String,
|
||||
/// 条件
|
||||
pub condition: AlertCondition,
|
||||
/// 阈值
|
||||
pub threshold: f64,
|
||||
/// 持续时间
|
||||
pub duration: Duration,
|
||||
/// 告警级别
|
||||
pub severity: AlertSeverity,
|
||||
/// 告警消息模板
|
||||
pub message_template: String,
|
||||
}
|
||||
|
||||
/// 告警条件
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub enum AlertCondition {
|
||||
GreaterThan,
|
||||
LessThan,
|
||||
Equal,
|
||||
NotEqual,
|
||||
GreaterThanOrEqual,
|
||||
LessThanOrEqual,
|
||||
}
|
||||
|
||||
/// Jaeger 追踪收集器
|
||||
#[derive(Debug)]
|
||||
pub struct JaegerTraceCollector {
|
||||
/// 收集器名称
|
||||
name: String,
|
||||
/// 配置
|
||||
config: JaegerConfig,
|
||||
/// HTTP 客户端
|
||||
client: reqwest::Client,
|
||||
}
|
||||
|
||||
/// 监控统计
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct MonitoringStats {
|
||||
/// 收集的指标数量
|
||||
pub metrics_collected: u64,
|
||||
/// 发送的告警数量
|
||||
pub alerts_sent: u64,
|
||||
/// 收集的追踪数量
|
||||
pub traces_collected: u64,
|
||||
/// 错误数量
|
||||
pub error_count: u64,
|
||||
/// 最后收集时间
|
||||
pub last_collection_time: SystemTime,
|
||||
/// 平均收集时间
|
||||
pub avg_collection_time: Duration,
|
||||
}
|
||||
|
||||
impl MonitoringIntegration {
|
||||
/// 创建新的监控集成管理器
|
||||
pub fn new(config: MonitoringConfig) -> Self {
|
||||
Self {
|
||||
config,
|
||||
metrics_collectors: Vec::new(),
|
||||
alert_managers: Vec::new(),
|
||||
trace_collectors: Vec::new(),
|
||||
stats: Arc::new(RwLock::new(MonitoringStats::default())),
|
||||
}
|
||||
}
|
||||
|
||||
/// 添加指标收集器
|
||||
pub fn add_metrics_collector(&mut self, collector: Arc<dyn MetricsCollector + Send + Sync>) {
|
||||
self.metrics_collectors.push(collector);
|
||||
}
|
||||
|
||||
/// 添加告警管理器
|
||||
pub fn add_alert_manager(&mut self, manager: Arc<dyn AlertManager + Send + Sync>) {
|
||||
self.alert_managers.push(manager);
|
||||
}
|
||||
|
||||
/// 添加追踪收集器
|
||||
pub fn add_trace_collector(&mut self, collector: Arc<dyn TraceCollector + Send + Sync>) {
|
||||
self.trace_collectors.push(collector);
|
||||
}
|
||||
|
||||
/// 启动监控
|
||||
pub async fn start_monitoring(&self) -> Result<()> {
|
||||
if !self.config.enabled {
|
||||
info!("Monitoring is not enabled");
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
info!("Start monitoring integration");
|
||||
|
||||
// 启动指标收集任务
|
||||
self.start_metrics_collection().await;
|
||||
|
||||
// 启动告警检查任务
|
||||
self.start_alert_checking().await;
|
||||
|
||||
// 启动追踪收集任务
|
||||
self.start_trace_collection().await;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// 启动指标收集
|
||||
async fn start_metrics_collection(&self) {
|
||||
let collectors = self.metrics_collectors.clone();
|
||||
let stats = Arc::clone(&self.stats);
|
||||
let interval = self.config.metrics_interval;
|
||||
|
||||
tokio::spawn(async move {
|
||||
let mut interval_timer = tokio::time::interval(interval);
|
||||
|
||||
loop {
|
||||
interval_timer.tick().await;
|
||||
|
||||
let start_time = SystemTime::now();
|
||||
let mut total_metrics = 0;
|
||||
|
||||
for collector in &collectors {
|
||||
match collector.collect_metrics() {
|
||||
Ok(metrics) => {
|
||||
total_metrics += metrics.len();
|
||||
info!(
|
||||
"Collector {} collected {} indicators",
|
||||
collector.name(),
|
||||
metrics.len()
|
||||
);
|
||||
}
|
||||
Err(e) => {
|
||||
error!(
|
||||
"Collector {} failed to collect metrics: {}",
|
||||
collector.name(),
|
||||
e
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 更新统计
|
||||
let mut stats = stats.write().await;
|
||||
stats.metrics_collected += total_metrics as u64;
|
||||
stats.last_collection_time = SystemTime::now();
|
||||
if let Ok(duration) = start_time.elapsed() {
|
||||
stats.avg_collection_time = duration;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/// 启动告警检查
|
||||
async fn start_alert_checking(&self) {
|
||||
let managers = self.alert_managers.clone();
|
||||
let collectors = self.metrics_collectors.clone();
|
||||
let stats = Arc::clone(&self.stats);
|
||||
let interval = self.config.alert_check_interval;
|
||||
|
||||
tokio::spawn(async move {
|
||||
let mut interval_timer = tokio::time::interval(interval);
|
||||
|
||||
loop {
|
||||
interval_timer.tick().await;
|
||||
|
||||
// 收集当前指标
|
||||
let mut all_metrics = Vec::new();
|
||||
for collector in &collectors {
|
||||
if let Ok(metrics) = collector.collect_metrics() {
|
||||
all_metrics.extend(metrics);
|
||||
}
|
||||
}
|
||||
|
||||
// 检查告警
|
||||
for manager in &managers {
|
||||
match manager.check_alerts(&all_metrics) {
|
||||
Ok(alerts) => {
|
||||
for alert in alerts {
|
||||
if let Err(e) = manager.send_alert(&alert) {
|
||||
error!("Failed to send alarm: {}", e);
|
||||
} else {
|
||||
info!("Send alarm: {}", alert.name);
|
||||
let mut stats = stats.write().await;
|
||||
stats.alerts_sent += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
Err(e) => {
|
||||
error!("Alarm Manager {} Check failed: {}", manager.name(), e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/// 启动追踪收集
|
||||
async fn start_trace_collection(&self) {
|
||||
let _collectors = self.trace_collectors.clone();
|
||||
let _stats = Arc::clone(&self.stats);
|
||||
|
||||
tokio::spawn(async move {
|
||||
// 这里应该实现追踪数据的收集逻辑
|
||||
// 通常通过 OpenTelemetry 或其他追踪库
|
||||
info!("Tracking collection task has been started");
|
||||
});
|
||||
}
|
||||
|
||||
/// 收集追踪数据
|
||||
pub async fn collect_trace(&self, span: TraceSpan) -> Result<()> {
|
||||
for collector in &self.trace_collectors {
|
||||
if let Err(e) = collector.collect_trace(&span) {
|
||||
error!(
|
||||
"Trace collector {} collection failed: {}",
|
||||
collector.name(),
|
||||
e
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
let mut stats = self.stats.write().await;
|
||||
stats.traces_collected += 1;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// 获取监控统计
|
||||
pub async fn get_stats(&self) -> MonitoringStats {
|
||||
self.stats.read().await.clone()
|
||||
}
|
||||
|
||||
/// 停止监控
|
||||
pub async fn stop_monitoring(&self) -> Result<()> {
|
||||
info!("Stop monitoring integration");
|
||||
// 这里应该实现停止所有后台任务的逻辑
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl MetricsCollector for SystemMetricsCollector {
|
||||
fn collect_metrics(&self) -> Result<Vec<Metric>> {
|
||||
let mut metrics = Vec::new();
|
||||
|
||||
// 收集系统指标
|
||||
metrics.push(Metric {
|
||||
name: "system_cpu_usage".to_string(),
|
||||
value: 0.5, // 这里应该从系统获取实际值
|
||||
metric_type: MetricType::Gauge,
|
||||
labels: HashMap::new(),
|
||||
timestamp: SystemTime::now(),
|
||||
help: Some("系统 CPU 使用率".to_string()),
|
||||
});
|
||||
|
||||
metrics.push(Metric {
|
||||
name: "system_memory_usage".to_string(),
|
||||
value: 0.7,
|
||||
metric_type: MetricType::Gauge,
|
||||
labels: HashMap::new(),
|
||||
timestamp: SystemTime::now(),
|
||||
help: Some("系统内存使用率".to_string()),
|
||||
});
|
||||
|
||||
Ok(metrics)
|
||||
}
|
||||
|
||||
fn name(&self) -> &str {
|
||||
&self.name
|
||||
}
|
||||
}
|
||||
|
||||
impl MetricsCollector for ApplicationMetricsCollector {
|
||||
fn collect_metrics(&self) -> Result<Vec<Metric>> {
|
||||
let mut metrics = Vec::new();
|
||||
|
||||
// 这里应该收集应用特定的指标
|
||||
metrics.push(Metric {
|
||||
name: "app_requests_total".to_string(),
|
||||
value: 1000.0,
|
||||
metric_type: MetricType::Counter,
|
||||
labels: HashMap::new(),
|
||||
timestamp: SystemTime::now(),
|
||||
help: Some("应用请求总数".to_string()),
|
||||
});
|
||||
|
||||
Ok(metrics)
|
||||
}
|
||||
|
||||
fn name(&self) -> &str {
|
||||
&self.name
|
||||
}
|
||||
}
|
||||
|
||||
impl AlertManager for ThresholdAlertManager {
|
||||
fn check_alerts(&self, metrics: &[Metric]) -> Result<Vec<Alert>> {
|
||||
let mut alerts = Vec::new();
|
||||
|
||||
for rule in &self.rules {
|
||||
for metric in metrics {
|
||||
if metric.name == rule.metric_name {
|
||||
let should_alert = match rule.condition {
|
||||
AlertCondition::GreaterThan => metric.value > rule.threshold,
|
||||
AlertCondition::LessThan => metric.value < rule.threshold,
|
||||
AlertCondition::Equal => {
|
||||
(metric.value - rule.threshold).abs() < f64::EPSILON
|
||||
}
|
||||
AlertCondition::NotEqual => {
|
||||
(metric.value - rule.threshold).abs() >= f64::EPSILON
|
||||
}
|
||||
AlertCondition::GreaterThanOrEqual => metric.value >= rule.threshold,
|
||||
AlertCondition::LessThanOrEqual => metric.value <= rule.threshold,
|
||||
};
|
||||
|
||||
if should_alert {
|
||||
alerts.push(Alert {
|
||||
id: format!(
|
||||
"{}-{}",
|
||||
rule.name,
|
||||
metric
|
||||
.timestamp
|
||||
.duration_since(SystemTime::UNIX_EPOCH)
|
||||
.unwrap_or_default()
|
||||
.as_secs()
|
||||
),
|
||||
name: rule.name.clone(),
|
||||
severity: rule.severity.clone(),
|
||||
message: rule
|
||||
.message_template
|
||||
.replace("{value}", &metric.value.to_string()),
|
||||
labels: metric.labels.clone(),
|
||||
triggered_at: SystemTime::now(),
|
||||
status: AlertStatus::Firing,
|
||||
metrics: vec![metric.name.clone()],
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(alerts)
|
||||
}
|
||||
|
||||
fn send_alert(&self, alert: &Alert) -> Result<()> {
|
||||
// 这里应该实现实际的告警发送逻辑
|
||||
// 例如发送到 Slack、邮件、PagerDuty 等
|
||||
info!("Send alarm: {} - {}", alert.name, alert.message);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn name(&self) -> &str {
|
||||
&self.name
|
||||
}
|
||||
}
|
||||
|
||||
impl TraceCollector for JaegerTraceCollector {
|
||||
fn collect_trace(&self, span: &TraceSpan) -> Result<()> {
|
||||
// 这里应该实现向 Jaeger 发送追踪数据的逻辑
|
||||
info!(
|
||||
"Collection tracking: {} - {}",
|
||||
span.trace_id, span.operation_name
|
||||
);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn name(&self) -> &str {
|
||||
&self.name
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for MonitoringConfig {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
enabled: true,
|
||||
metrics_interval: Duration::from_secs(30),
|
||||
alert_check_interval: Duration::from_secs(60),
|
||||
trace_sampling_rate: 0.1,
|
||||
prometheus: None,
|
||||
grafana: None,
|
||||
jaeger: None,
|
||||
custom_endpoints: Vec::new(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for MonitoringStats {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
metrics_collected: 0,
|
||||
alerts_sent: 0,
|
||||
traces_collected: 0,
|
||||
error_count: 0,
|
||||
last_collection_time: SystemTime::now(),
|
||||
avg_collection_time: Duration::from_millis(0),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_monitoring_integration() {
|
||||
let config = MonitoringConfig::default();
|
||||
let mut integration = MonitoringIntegration::new(config);
|
||||
|
||||
let collector = Arc::new(SystemMetricsCollector {
|
||||
name: "test_collector".to_string(),
|
||||
});
|
||||
|
||||
integration.add_metrics_collector(collector);
|
||||
|
||||
let stats = integration.get_stats().await;
|
||||
assert_eq!(stats.metrics_collected, 0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_system_metrics_collector() {
|
||||
let collector = SystemMetricsCollector {
|
||||
name: "system".to_string(),
|
||||
};
|
||||
|
||||
let metrics = collector.collect_metrics().unwrap();
|
||||
assert!(!metrics.is_empty());
|
||||
assert!(metrics.iter().any(|m| m.name == "system_cpu_usage"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_threshold_alert_manager() {
|
||||
let rule = AlertRule {
|
||||
name: "high_cpu".to_string(),
|
||||
metric_name: "cpu_usage".to_string(),
|
||||
condition: AlertCondition::GreaterThan,
|
||||
threshold: 0.8,
|
||||
duration: Duration::from_secs(300),
|
||||
severity: AlertSeverity::Warning,
|
||||
message_template: "CPU 使用率过高: {value}".to_string(),
|
||||
};
|
||||
|
||||
let manager = ThresholdAlertManager {
|
||||
name: "threshold".to_string(),
|
||||
rules: vec![rule],
|
||||
};
|
||||
|
||||
let metric = Metric {
|
||||
name: "cpu_usage".to_string(),
|
||||
value: 0.9,
|
||||
metric_type: MetricType::Gauge,
|
||||
labels: HashMap::new(),
|
||||
timestamp: SystemTime::now(),
|
||||
help: None,
|
||||
};
|
||||
|
||||
let alerts = manager.check_alerts(&[metric]).unwrap();
|
||||
assert_eq!(alerts.len(), 1);
|
||||
assert_eq!(alerts[0].name, "high_cpu");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,749 @@
|
||||
//! 生产环境日志模块
|
||||
//!
|
||||
//! 提供生产环境专用的日志功能,包括结构化日志、日志聚合、性能监控等。
|
||||
#![allow(dead_code)]
|
||||
|
||||
use anyhow::{Context, Result};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::collections::HashMap;
|
||||
use std::sync::Arc;
|
||||
use std::time::{Duration, SystemTime};
|
||||
use tokio::sync::RwLock;
|
||||
use tracing::error;
|
||||
|
||||
/// 生产日志管理器
|
||||
#[derive(Clone)]
|
||||
pub struct ProductionLogger {
|
||||
/// 日志配置
|
||||
config: LoggingConfig,
|
||||
/// 日志收集器
|
||||
collectors: Vec<Arc<dyn LogCollector + Send + Sync>>,
|
||||
/// 日志过滤器
|
||||
filters: Vec<Arc<dyn LogFilter + Send + Sync>>,
|
||||
/// 日志统计
|
||||
stats: Arc<RwLock<LoggingStats>>,
|
||||
/// 日志缓冲区
|
||||
buffer: Arc<RwLock<LogBuffer>>,
|
||||
}
|
||||
|
||||
impl std::fmt::Debug for ProductionLogger {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
f.debug_struct("ProductionLogger")
|
||||
.field("config", &self.config)
|
||||
.field("collectors_count", &self.collectors.len())
|
||||
.field("filters_count", &self.filters.len())
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
/// 日志配置
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct LoggingConfig {
|
||||
/// 日志级别
|
||||
pub level: LogLevel,
|
||||
/// 输出格式
|
||||
pub format: LogFormat,
|
||||
/// 输出目标
|
||||
pub targets: Vec<LogTarget>,
|
||||
/// 缓冲配置
|
||||
pub buffer_config: BufferConfig,
|
||||
/// 轮转配置
|
||||
pub rotation_config: RotationConfig,
|
||||
/// 采样配置
|
||||
pub sampling_config: SamplingConfig,
|
||||
}
|
||||
|
||||
/// 日志级别
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub enum LogLevel {
|
||||
Trace,
|
||||
Debug,
|
||||
Info,
|
||||
Warn,
|
||||
Error,
|
||||
}
|
||||
|
||||
/// 日志格式
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub enum LogFormat {
|
||||
/// JSON 格式
|
||||
Json,
|
||||
/// 纯文本格式
|
||||
Text,
|
||||
/// 结构化格式
|
||||
Structured,
|
||||
/// 自定义格式
|
||||
Custom(String),
|
||||
}
|
||||
|
||||
/// 日志输出目标
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub enum LogTarget {
|
||||
/// 控制台输出
|
||||
Console,
|
||||
/// 文件输出
|
||||
File { path: String },
|
||||
/// 系统日志
|
||||
Syslog,
|
||||
/// 远程日志服务
|
||||
Remote { endpoint: String, api_key: String },
|
||||
/// Elasticsearch
|
||||
Elasticsearch { url: String, index: String },
|
||||
/// 自定义目标
|
||||
Custom(String),
|
||||
}
|
||||
|
||||
/// 缓冲配置
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct BufferConfig {
|
||||
/// 缓冲区大小
|
||||
pub size: usize,
|
||||
/// 刷新间隔
|
||||
pub flush_interval: Duration,
|
||||
/// 批量大小
|
||||
pub batch_size: usize,
|
||||
/// 是否启用压缩
|
||||
pub enable_compression: bool,
|
||||
}
|
||||
|
||||
/// 轮转配置
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct RotationConfig {
|
||||
/// 最大文件大小 (MB)
|
||||
pub max_file_size_mb: u64,
|
||||
/// 最大文件数量
|
||||
pub max_files: u32,
|
||||
/// 轮转间隔
|
||||
pub rotation_interval: Duration,
|
||||
/// 压缩旧文件
|
||||
pub compress_old_files: bool,
|
||||
}
|
||||
|
||||
/// 采样配置
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct SamplingConfig {
|
||||
/// 是否启用采样
|
||||
pub enabled: bool,
|
||||
/// 采样率 (0.0-1.0)
|
||||
pub rate: f64,
|
||||
/// 采样策略
|
||||
pub strategy: SamplingStrategy,
|
||||
}
|
||||
|
||||
/// 采样策略
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub enum SamplingStrategy {
|
||||
/// 随机采样
|
||||
Random,
|
||||
/// 基于时间的采样
|
||||
TimeBased,
|
||||
/// 基于级别的采样
|
||||
LevelBased,
|
||||
/// 自适应采样
|
||||
Adaptive,
|
||||
}
|
||||
|
||||
/// 日志条目
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct LogEntry {
|
||||
/// 时间戳
|
||||
pub timestamp: SystemTime,
|
||||
/// 日志级别
|
||||
pub level: LogLevel,
|
||||
/// 消息
|
||||
pub message: String,
|
||||
/// 模块
|
||||
pub module: String,
|
||||
/// 文件名
|
||||
pub file: Option<String>,
|
||||
/// 行号
|
||||
pub line: Option<u32>,
|
||||
/// 字段
|
||||
pub fields: HashMap<String, serde_json::Value>,
|
||||
/// 跟踪 ID
|
||||
pub trace_id: Option<String>,
|
||||
/// 跨度 ID
|
||||
pub span_id: Option<String>,
|
||||
}
|
||||
|
||||
/// 日志收集器 trait
|
||||
pub trait LogCollector {
|
||||
/// 收集日志
|
||||
fn collect(&self, entry: &LogEntry) -> Result<()>;
|
||||
/// 刷新缓冲区
|
||||
fn flush(&self) -> Result<()>;
|
||||
/// 获取收集器名称
|
||||
fn name(&self) -> &str;
|
||||
}
|
||||
|
||||
/// 日志过滤器 trait
|
||||
pub trait LogFilter {
|
||||
/// 过滤日志
|
||||
fn filter(&self, entry: &LogEntry) -> bool;
|
||||
/// 获取过滤器名称
|
||||
fn name(&self) -> &str;
|
||||
}
|
||||
|
||||
/// 控制台日志收集器
|
||||
pub struct ConsoleCollector {
|
||||
/// 收集器名称
|
||||
name: String,
|
||||
/// 格式化器
|
||||
formatter: Box<dyn LogFormatter + Send + Sync>,
|
||||
}
|
||||
|
||||
impl std::fmt::Debug for ConsoleCollector {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
f.debug_struct("ConsoleCollector")
|
||||
.field("name", &self.name)
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
/// 文件日志收集器
|
||||
pub struct FileCollector {
|
||||
/// 收集器名称
|
||||
name: String,
|
||||
/// 文件路径
|
||||
file_path: String,
|
||||
/// 轮转配置
|
||||
rotation_config: RotationConfig,
|
||||
/// 格式化器
|
||||
formatter: Box<dyn LogFormatter + Send + Sync>,
|
||||
}
|
||||
|
||||
impl std::fmt::Debug for FileCollector {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
f.debug_struct("FileCollector")
|
||||
.field("name", &self.name)
|
||||
.field("file_path", &self.file_path)
|
||||
.field("rotation_config", &self.rotation_config)
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
/// 远程日志收集器
|
||||
pub struct RemoteCollector {
|
||||
/// 收集器名称
|
||||
name: String,
|
||||
/// 远程端点
|
||||
endpoint: String,
|
||||
/// API 密钥
|
||||
api_key: String,
|
||||
/// HTTP 客户端
|
||||
client: reqwest::Client,
|
||||
}
|
||||
|
||||
impl std::fmt::Debug for RemoteCollector {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
f.debug_struct("RemoteCollector")
|
||||
.field("name", &self.name)
|
||||
.field("endpoint", &self.endpoint)
|
||||
.field("api_key", &"<hidden>")
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
/// 日志格式化器 trait
|
||||
pub trait LogFormatter {
|
||||
/// 格式化日志条目
|
||||
fn format(&self, entry: &LogEntry) -> Result<String>;
|
||||
}
|
||||
|
||||
/// JSON 格式化器
|
||||
#[derive(Debug)]
|
||||
pub struct JsonFormatter;
|
||||
|
||||
/// 文本格式化器
|
||||
#[derive(Debug)]
|
||||
pub struct TextFormatter {
|
||||
/// 时间格式
|
||||
time_format: String,
|
||||
/// 是否包含颜色
|
||||
colored: bool,
|
||||
}
|
||||
|
||||
/// 级别过滤器
|
||||
#[derive(Debug)]
|
||||
pub struct LevelFilter {
|
||||
/// 最小级别
|
||||
min_level: LogLevel,
|
||||
}
|
||||
|
||||
/// 模块过滤器
|
||||
#[derive(Debug)]
|
||||
pub struct ModuleFilter {
|
||||
/// 允许的模块
|
||||
allowed_modules: Vec<String>,
|
||||
/// 禁止的模块
|
||||
denied_modules: Vec<String>,
|
||||
}
|
||||
|
||||
/// 速率限制过滤器
|
||||
#[derive(Debug)]
|
||||
pub struct RateLimitFilter {
|
||||
/// 速率限制器
|
||||
limiter: Arc<RwLock<HashMap<String, RateLimiter>>>,
|
||||
/// 每秒最大日志数
|
||||
max_logs_per_second: u32,
|
||||
}
|
||||
|
||||
/// 简单速率限制器
|
||||
#[derive(Debug)]
|
||||
struct RateLimiter {
|
||||
/// 最后重置时间
|
||||
last_reset: SystemTime,
|
||||
/// 当前计数
|
||||
count: u32,
|
||||
/// 限制
|
||||
limit: u32,
|
||||
}
|
||||
|
||||
/// 日志缓冲区
|
||||
#[derive(Debug)]
|
||||
pub struct LogBuffer {
|
||||
/// 缓冲的日志条目
|
||||
entries: Vec<LogEntry>,
|
||||
/// 缓冲区大小限制
|
||||
max_size: usize,
|
||||
/// 最后刷新时间
|
||||
last_flush: SystemTime,
|
||||
}
|
||||
|
||||
/// 日志统计
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct LoggingStats {
|
||||
/// 总日志数
|
||||
pub total_logs: u64,
|
||||
/// 按级别统计
|
||||
pub logs_by_level: HashMap<String, u64>,
|
||||
/// 按模块统计
|
||||
pub logs_by_module: HashMap<String, u64>,
|
||||
/// 错误统计
|
||||
pub error_count: u64,
|
||||
/// 警告统计
|
||||
pub warning_count: u64,
|
||||
/// 平均处理时间
|
||||
pub avg_processing_time: Duration,
|
||||
/// 缓冲区使用率
|
||||
pub buffer_usage: f64,
|
||||
/// 丢弃的日志数
|
||||
pub dropped_logs: u64,
|
||||
}
|
||||
|
||||
impl ProductionLogger {
|
||||
/// 创建新的生产日志管理器
|
||||
pub fn new(config: LoggingConfig) -> Self {
|
||||
Self {
|
||||
config,
|
||||
collectors: Vec::new(),
|
||||
filters: Vec::new(),
|
||||
stats: Arc::new(RwLock::new(LoggingStats::default())),
|
||||
buffer: Arc::new(RwLock::new(LogBuffer::new(1000))),
|
||||
}
|
||||
}
|
||||
|
||||
/// 添加日志收集器
|
||||
pub fn add_collector(&mut self, collector: Arc<dyn LogCollector + Send + Sync>) {
|
||||
self.collectors.push(collector);
|
||||
}
|
||||
|
||||
/// 添加日志过滤器
|
||||
pub fn add_filter(&mut self, filter: Arc<dyn LogFilter + Send + Sync>) {
|
||||
self.filters.push(filter);
|
||||
}
|
||||
|
||||
/// 记录日志
|
||||
pub async fn log(&self, entry: LogEntry) -> Result<()> {
|
||||
// 应用过滤器
|
||||
for filter in &self.filters {
|
||||
if !filter.filter(&entry) {
|
||||
return Ok(());
|
||||
}
|
||||
}
|
||||
|
||||
// 更新统计
|
||||
self.update_stats(&entry).await;
|
||||
|
||||
// 添加到缓冲区
|
||||
let mut buffer = self.buffer.write().await;
|
||||
buffer.add_entry(entry.clone());
|
||||
|
||||
// 检查是否需要刷新
|
||||
if buffer.should_flush(&self.config.buffer_config) {
|
||||
drop(buffer);
|
||||
self.flush_buffer().await?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// 刷新缓冲区
|
||||
pub async fn flush_buffer(&self) -> Result<()> {
|
||||
let mut buffer = self.buffer.write().await;
|
||||
let entries = buffer.drain_entries();
|
||||
drop(buffer);
|
||||
|
||||
// 发送到所有收集器
|
||||
for entry in entries {
|
||||
for collector in &self.collectors {
|
||||
if let Err(e) = collector.collect(&entry) {
|
||||
error!(
|
||||
"Log collector {} processing failed: {}",
|
||||
collector.name(),
|
||||
e
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 刷新所有收集器
|
||||
for collector in &self.collectors {
|
||||
if let Err(e) = collector.flush() {
|
||||
error!(
|
||||
"Log collector {} failed to refresh: {}",
|
||||
collector.name(),
|
||||
e
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// 更新统计信息
|
||||
async fn update_stats(&self, entry: &LogEntry) {
|
||||
let mut stats = self.stats.write().await;
|
||||
stats.total_logs += 1;
|
||||
|
||||
let level_key = format!("{:?}", entry.level);
|
||||
*stats.logs_by_level.entry(level_key).or_insert(0) += 1;
|
||||
|
||||
*stats
|
||||
.logs_by_module
|
||||
.entry(entry.module.clone())
|
||||
.or_insert(0) += 1;
|
||||
|
||||
match entry.level {
|
||||
LogLevel::Error => stats.error_count += 1,
|
||||
LogLevel::Warn => stats.warning_count += 1,
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
/// 获取统计信息
|
||||
pub async fn get_stats(&self) -> LoggingStats {
|
||||
self.stats.read().await.clone()
|
||||
}
|
||||
|
||||
/// 启动后台任务
|
||||
pub async fn start_background_tasks(&self) {
|
||||
let buffer = Arc::clone(&self.buffer);
|
||||
let config = self.config.clone();
|
||||
let logger = self.clone();
|
||||
|
||||
tokio::spawn(async move {
|
||||
let mut interval = tokio::time::interval(config.buffer_config.flush_interval);
|
||||
|
||||
loop {
|
||||
interval.tick().await;
|
||||
|
||||
let should_flush = {
|
||||
let buffer = buffer.read().await;
|
||||
!buffer.entries.is_empty()
|
||||
};
|
||||
|
||||
if should_flush {
|
||||
if let Err(e) = logger.flush_buffer().await {
|
||||
error!("Failed to refresh the log buffer regularly: {}", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
impl LogCollector for ConsoleCollector {
|
||||
fn collect(&self, entry: &LogEntry) -> Result<()> {
|
||||
let formatted = self.formatter.format(entry)?;
|
||||
println!("{formatted}");
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn flush(&self) -> Result<()> {
|
||||
// 控制台不需要刷新
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn name(&self) -> &str {
|
||||
&self.name
|
||||
}
|
||||
}
|
||||
|
||||
impl LogCollector for FileCollector {
|
||||
fn collect(&self, entry: &LogEntry) -> Result<()> {
|
||||
let _formatted = self.formatter.format(entry)?;
|
||||
// 这里应该实现文件写入逻辑
|
||||
// 包括轮转检查
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn flush(&self) -> Result<()> {
|
||||
// 刷新文件缓冲区
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn name(&self) -> &str {
|
||||
&self.name
|
||||
}
|
||||
}
|
||||
|
||||
impl LogCollector for RemoteCollector {
|
||||
fn collect(&self, _entry: &LogEntry) -> Result<()> {
|
||||
// 这里应该实现异步发送到远程服务
|
||||
// 可以使用队列缓冲
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn flush(&self) -> Result<()> {
|
||||
// 刷新远程队列
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn name(&self) -> &str {
|
||||
&self.name
|
||||
}
|
||||
}
|
||||
|
||||
impl LogFormatter for JsonFormatter {
|
||||
fn format(&self, entry: &LogEntry) -> Result<String> {
|
||||
serde_json::to_string(entry).context("序列化日志条目为 JSON 失败")
|
||||
}
|
||||
}
|
||||
|
||||
impl LogFormatter for TextFormatter {
|
||||
fn format(&self, entry: &LogEntry) -> Result<String> {
|
||||
let timestamp = entry
|
||||
.timestamp
|
||||
.duration_since(SystemTime::UNIX_EPOCH)
|
||||
.unwrap_or_default();
|
||||
|
||||
Ok(format!(
|
||||
"[{}] {:5} {}: {}",
|
||||
timestamp.as_secs(),
|
||||
format!("{:?}", entry.level),
|
||||
entry.module,
|
||||
entry.message
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
impl LogFilter for LevelFilter {
|
||||
fn filter(&self, entry: &LogEntry) -> bool {
|
||||
self.level_to_number(&entry.level) >= self.level_to_number(&self.min_level)
|
||||
}
|
||||
|
||||
fn name(&self) -> &str {
|
||||
"level_filter"
|
||||
}
|
||||
}
|
||||
|
||||
impl LevelFilter {
|
||||
fn level_to_number(&self, level: &LogLevel) -> u8 {
|
||||
match level {
|
||||
LogLevel::Trace => 0,
|
||||
LogLevel::Debug => 1,
|
||||
LogLevel::Info => 2,
|
||||
LogLevel::Warn => 3,
|
||||
LogLevel::Error => 4,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl LogFilter for ModuleFilter {
|
||||
fn filter(&self, entry: &LogEntry) -> bool {
|
||||
if !self.denied_modules.is_empty()
|
||||
&& self
|
||||
.denied_modules
|
||||
.iter()
|
||||
.any(|m| entry.module.starts_with(m))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if !self.allowed_modules.is_empty() {
|
||||
return self
|
||||
.allowed_modules
|
||||
.iter()
|
||||
.any(|m| entry.module.starts_with(m));
|
||||
}
|
||||
|
||||
true
|
||||
}
|
||||
|
||||
fn name(&self) -> &str {
|
||||
"module_filter"
|
||||
}
|
||||
}
|
||||
|
||||
impl LogFilter for RateLimitFilter {
|
||||
fn filter(&self, _entry: &LogEntry) -> bool {
|
||||
// 这里应该实现速率限制逻辑
|
||||
// 基于模块或其他标识符
|
||||
true
|
||||
}
|
||||
|
||||
fn name(&self) -> &str {
|
||||
"rate_limit_filter"
|
||||
}
|
||||
}
|
||||
|
||||
impl LogBuffer {
|
||||
/// 创建新的日志缓冲区
|
||||
pub fn new(max_size: usize) -> Self {
|
||||
Self {
|
||||
entries: Vec::with_capacity(max_size),
|
||||
max_size,
|
||||
last_flush: SystemTime::now(),
|
||||
}
|
||||
}
|
||||
|
||||
/// 添加日志条目
|
||||
pub fn add_entry(&mut self, entry: LogEntry) {
|
||||
if self.entries.len() >= self.max_size {
|
||||
// 移除最旧的条目
|
||||
self.entries.remove(0);
|
||||
}
|
||||
self.entries.push(entry);
|
||||
}
|
||||
|
||||
/// 检查是否应该刷新
|
||||
pub fn should_flush(&self, config: &BufferConfig) -> bool {
|
||||
self.entries.len() >= config.batch_size
|
||||
|| self.last_flush.elapsed().unwrap_or_default() >= config.flush_interval
|
||||
}
|
||||
|
||||
/// 排空缓冲区
|
||||
pub fn drain_entries(&mut self) -> Vec<LogEntry> {
|
||||
self.last_flush = SystemTime::now();
|
||||
std::mem::take(&mut self.entries)
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for LoggingConfig {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
level: LogLevel::Info,
|
||||
format: LogFormat::Json,
|
||||
targets: vec![LogTarget::Console],
|
||||
buffer_config: BufferConfig {
|
||||
size: 1000,
|
||||
flush_interval: Duration::from_secs(5),
|
||||
batch_size: 100,
|
||||
enable_compression: false,
|
||||
},
|
||||
rotation_config: RotationConfig {
|
||||
max_file_size_mb: 100,
|
||||
max_files: 10,
|
||||
rotation_interval: Duration::from_secs(3600),
|
||||
compress_old_files: true,
|
||||
},
|
||||
sampling_config: SamplingConfig {
|
||||
enabled: false,
|
||||
rate: 1.0,
|
||||
strategy: SamplingStrategy::Random,
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for LoggingStats {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
total_logs: 0,
|
||||
logs_by_level: HashMap::new(),
|
||||
logs_by_module: HashMap::new(),
|
||||
error_count: 0,
|
||||
warning_count: 0,
|
||||
avg_processing_time: Duration::from_millis(0),
|
||||
buffer_usage: 0.0,
|
||||
dropped_logs: 0,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_production_logger() {
|
||||
let config = LoggingConfig::default();
|
||||
let logger = ProductionLogger::new(config);
|
||||
|
||||
let entry = LogEntry {
|
||||
timestamp: SystemTime::now(),
|
||||
level: LogLevel::Info,
|
||||
message: "测试日志".to_string(),
|
||||
module: "test".to_string(),
|
||||
file: None,
|
||||
line: None,
|
||||
fields: HashMap::new(),
|
||||
trace_id: None,
|
||||
span_id: None,
|
||||
};
|
||||
|
||||
logger.log(entry).await.unwrap();
|
||||
|
||||
let stats = logger.get_stats().await;
|
||||
assert_eq!(stats.total_logs, 1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_level_filter() {
|
||||
let filter = LevelFilter {
|
||||
min_level: LogLevel::Warn,
|
||||
};
|
||||
|
||||
let info_entry = LogEntry {
|
||||
timestamp: SystemTime::now(),
|
||||
level: LogLevel::Info,
|
||||
message: "信息日志".to_string(),
|
||||
module: "test".to_string(),
|
||||
file: None,
|
||||
line: None,
|
||||
fields: HashMap::new(),
|
||||
trace_id: None,
|
||||
span_id: None,
|
||||
};
|
||||
|
||||
let error_entry = LogEntry {
|
||||
level: LogLevel::Error,
|
||||
..info_entry.clone()
|
||||
};
|
||||
|
||||
assert!(!filter.filter(&info_entry));
|
||||
assert!(filter.filter(&error_entry));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_json_formatter() {
|
||||
let formatter = JsonFormatter;
|
||||
|
||||
let entry = LogEntry {
|
||||
timestamp: SystemTime::now(),
|
||||
level: LogLevel::Info,
|
||||
message: "测试消息".to_string(),
|
||||
module: "test".to_string(),
|
||||
file: Some("test.rs".to_string()),
|
||||
line: Some(42),
|
||||
fields: HashMap::new(),
|
||||
trace_id: Some("trace123".to_string()),
|
||||
span_id: Some("span456".to_string()),
|
||||
};
|
||||
|
||||
let formatted = formatter.format(&entry).unwrap();
|
||||
assert!(formatted.contains("测试消息"));
|
||||
assert!(formatted.contains("trace123"));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,789 @@
|
||||
//! 资源清理模块
|
||||
//!
|
||||
//! 提供应用关闭时的资源清理功能,确保所有资源得到正确释放。
|
||||
#![allow(dead_code)]
|
||||
|
||||
use anyhow::Result;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::collections::HashMap;
|
||||
use std::sync::Arc;
|
||||
use std::time::{Duration, SystemTime};
|
||||
use tokio::sync::{RwLock, Semaphore};
|
||||
use tracing::{error, info, warn};
|
||||
|
||||
/// 资源清理管理器
|
||||
#[derive(Clone)]
|
||||
pub struct ResourceCleanupManager {
|
||||
/// 清理配置
|
||||
config: CleanupConfig,
|
||||
/// 清理器列表
|
||||
cleaners: Vec<Arc<dyn ResourceCleaner + Send + Sync>>,
|
||||
/// 清理状态
|
||||
cleanup_status: Arc<RwLock<CleanupStatus>>,
|
||||
/// 清理历史
|
||||
cleanup_history: Arc<RwLock<Vec<CleanupResult>>>,
|
||||
/// 并发控制
|
||||
semaphore: Arc<Semaphore>,
|
||||
}
|
||||
|
||||
impl std::fmt::Debug for ResourceCleanupManager {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
f.debug_struct("ResourceCleanupManager")
|
||||
.field("config", &self.config)
|
||||
.field("cleaners_count", &self.cleaners.len())
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
/// 清理配置
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct CleanupConfig {
|
||||
/// 是否启用自动清理
|
||||
pub auto_cleanup_enabled: bool,
|
||||
/// 清理超时时间
|
||||
pub cleanup_timeout: Duration,
|
||||
/// 强制清理超时时间
|
||||
pub force_cleanup_timeout: Duration,
|
||||
/// 最大并发清理数
|
||||
pub max_concurrent_cleanups: usize,
|
||||
/// 清理重试次数
|
||||
pub retry_count: u32,
|
||||
/// 重试间隔
|
||||
pub retry_interval: Duration,
|
||||
/// 清理顺序配置
|
||||
pub cleanup_order: Vec<CleanupPhase>,
|
||||
/// 临时文件清理配置
|
||||
pub temp_file_cleanup: TempFileCleanupConfig,
|
||||
/// 内存清理配置
|
||||
pub memory_cleanup: MemoryCleanupConfig,
|
||||
}
|
||||
|
||||
/// 清理阶段
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
|
||||
pub enum CleanupPhase {
|
||||
/// 停止新请求
|
||||
StopNewRequests,
|
||||
/// 等待现有请求完成
|
||||
WaitForRequests,
|
||||
/// 清理应用资源
|
||||
CleanupApplication,
|
||||
/// 清理数据库连接
|
||||
CleanupDatabase,
|
||||
/// 清理缓存
|
||||
CleanupCache,
|
||||
/// 清理文件系统
|
||||
CleanupFileSystem,
|
||||
/// 清理网络连接
|
||||
CleanupNetwork,
|
||||
/// 清理内存
|
||||
CleanupMemory,
|
||||
/// 最终清理
|
||||
FinalCleanup,
|
||||
}
|
||||
|
||||
/// 临时文件清理配置
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct TempFileCleanupConfig {
|
||||
/// 是否启用
|
||||
pub enabled: bool,
|
||||
/// 临时目录路径
|
||||
pub temp_directories: Vec<String>,
|
||||
/// 文件保留时间
|
||||
pub file_retention_duration: Duration,
|
||||
/// 最大文件大小 (MB)
|
||||
pub max_file_size_mb: u64,
|
||||
/// 文件模式匹配
|
||||
pub file_patterns: Vec<String>,
|
||||
}
|
||||
|
||||
/// 内存清理配置
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct MemoryCleanupConfig {
|
||||
/// 是否启用
|
||||
pub enabled: bool,
|
||||
/// 强制垃圾回收
|
||||
pub force_gc: bool,
|
||||
/// 清理缓存
|
||||
pub clear_caches: bool,
|
||||
/// 释放未使用内存
|
||||
pub release_unused_memory: bool,
|
||||
}
|
||||
|
||||
/// 资源清理器 trait
|
||||
pub trait ResourceCleaner: Send + Sync {
|
||||
/// 执行清理
|
||||
fn cleanup(&self) -> Result<CleanupResult>;
|
||||
/// 获取清理器名称
|
||||
fn name(&self) -> &str;
|
||||
/// 获取清理阶段
|
||||
fn cleanup_phase(&self) -> CleanupPhase;
|
||||
/// 获取清理优先级 (数字越小优先级越高)
|
||||
fn priority(&self) -> u32;
|
||||
/// 是否支持强制清理
|
||||
fn supports_force_cleanup(&self) -> bool {
|
||||
false
|
||||
}
|
||||
/// 强制清理
|
||||
fn force_cleanup(&self) -> Result<CleanupResult> {
|
||||
self.cleanup()
|
||||
}
|
||||
}
|
||||
|
||||
/// 清理结果
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct CleanupResult {
|
||||
/// 清理器名称
|
||||
pub cleaner_name: String,
|
||||
/// 清理阶段
|
||||
pub cleanup_phase: CleanupPhase,
|
||||
/// 清理状态
|
||||
pub status: CleanupResultStatus,
|
||||
/// 清理消息
|
||||
pub message: String,
|
||||
/// 清理开始时间
|
||||
pub started_at: SystemTime,
|
||||
/// 清理结束时间
|
||||
pub completed_at: Option<SystemTime>,
|
||||
/// 清理耗时
|
||||
pub duration: Duration,
|
||||
/// 清理的资源数量
|
||||
pub resources_cleaned: u64,
|
||||
/// 释放的内存大小 (字节)
|
||||
pub memory_freed: u64,
|
||||
/// 详细信息
|
||||
pub details: HashMap<String, serde_json::Value>,
|
||||
}
|
||||
|
||||
/// 清理结果状态
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
|
||||
pub enum CleanupResultStatus {
|
||||
/// 成功
|
||||
Success,
|
||||
/// 失败
|
||||
Failed,
|
||||
/// 部分成功
|
||||
PartialSuccess,
|
||||
/// 跳过
|
||||
Skipped,
|
||||
/// 超时
|
||||
Timeout,
|
||||
}
|
||||
|
||||
/// 整体清理状态
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct CleanupStatus {
|
||||
/// 是否正在清理
|
||||
pub is_cleaning: bool,
|
||||
/// 当前清理阶段
|
||||
pub current_phase: Option<CleanupPhase>,
|
||||
/// 清理进度 (0.0-1.0)
|
||||
pub progress: f64,
|
||||
/// 清理开始时间
|
||||
pub started_at: Option<SystemTime>,
|
||||
/// 预计完成时间
|
||||
pub estimated_completion: Option<SystemTime>,
|
||||
/// 清理结果
|
||||
pub results: Vec<CleanupResult>,
|
||||
/// 总清理器数量
|
||||
pub total_cleaners: usize,
|
||||
/// 已完成清理器数量
|
||||
pub completed_cleaners: usize,
|
||||
}
|
||||
|
||||
/// 数据库连接清理器
|
||||
#[derive(Debug)]
|
||||
pub struct DatabaseConnectionCleaner {
|
||||
/// 清理器名称
|
||||
name: String,
|
||||
/// 连接池大小
|
||||
pool_size: usize,
|
||||
}
|
||||
|
||||
impl DatabaseConnectionCleaner {
|
||||
pub fn new(name: String, pool_size: usize) -> Self {
|
||||
Self { name, pool_size }
|
||||
}
|
||||
}
|
||||
|
||||
/// 缓存清理器
|
||||
#[derive(Debug)]
|
||||
pub struct CacheCleaner {
|
||||
/// 清理器名称
|
||||
name: String,
|
||||
/// 缓存类型
|
||||
cache_types: Vec<String>,
|
||||
}
|
||||
|
||||
/// 临时文件清理器
|
||||
#[derive(Debug)]
|
||||
pub struct TempFileCleaner {
|
||||
/// 清理器名称
|
||||
name: String,
|
||||
/// 配置
|
||||
config: TempFileCleanupConfig,
|
||||
}
|
||||
|
||||
/// HTTP 连接清理器
|
||||
#[derive(Debug)]
|
||||
pub struct HttpConnectionCleaner {
|
||||
/// 清理器名称
|
||||
name: String,
|
||||
/// 活跃连接数
|
||||
active_connections: Arc<RwLock<u32>>,
|
||||
}
|
||||
|
||||
/// 内存清理器
|
||||
#[derive(Debug)]
|
||||
pub struct MemoryCleaner {
|
||||
/// 清理器名称
|
||||
name: String,
|
||||
/// 配置
|
||||
config: MemoryCleanupConfig,
|
||||
}
|
||||
|
||||
/// 线程池清理器
|
||||
#[derive(Debug)]
|
||||
pub struct ThreadPoolCleaner {
|
||||
/// 清理器名称
|
||||
name: String,
|
||||
/// 线程池大小
|
||||
pool_size: usize,
|
||||
}
|
||||
|
||||
/// 日志清理器
|
||||
#[derive(Debug)]
|
||||
pub struct LogCleaner {
|
||||
/// 清理器名称
|
||||
name: String,
|
||||
/// 日志目录
|
||||
log_directories: Vec<String>,
|
||||
/// 保留天数
|
||||
retention_days: u32,
|
||||
}
|
||||
|
||||
impl ResourceCleanupManager {
|
||||
/// 创建新的资源清理管理器
|
||||
pub fn new(config: CleanupConfig) -> Self {
|
||||
let max_concurrent = config.max_concurrent_cleanups;
|
||||
Self {
|
||||
config,
|
||||
cleaners: Vec::new(),
|
||||
cleanup_status: Arc::new(RwLock::new(CleanupStatus::new())),
|
||||
cleanup_history: Arc::new(RwLock::new(Vec::new())),
|
||||
semaphore: Arc::new(Semaphore::new(max_concurrent)),
|
||||
}
|
||||
}
|
||||
|
||||
/// 添加资源清理器
|
||||
pub fn add_cleaner(&mut self, cleaner: Arc<dyn ResourceCleaner + Send + Sync>) {
|
||||
self.cleaners.push(cleaner);
|
||||
}
|
||||
|
||||
/// 执行完整清理
|
||||
pub async fn cleanup_all(&self) -> Result<CleanupStatus> {
|
||||
info!("Start resource cleanup");
|
||||
|
||||
let mut status = self.cleanup_status.write().await;
|
||||
status.is_cleaning = true;
|
||||
status.started_at = Some(SystemTime::now());
|
||||
status.total_cleaners = self.cleaners.len();
|
||||
status.completed_cleaners = 0;
|
||||
status.results.clear();
|
||||
drop(status);
|
||||
|
||||
// 按阶段和优先级排序清理器
|
||||
let mut sorted_cleaners = self.cleaners.clone();
|
||||
sorted_cleaners.sort_by(|a, b| {
|
||||
let phase_order_a = self.get_phase_order(&a.cleanup_phase());
|
||||
let phase_order_b = self.get_phase_order(&b.cleanup_phase());
|
||||
|
||||
phase_order_a
|
||||
.cmp(&phase_order_b)
|
||||
.then_with(|| a.priority().cmp(&b.priority()))
|
||||
});
|
||||
|
||||
// 按阶段分组执行清理
|
||||
let mut current_phase = None;
|
||||
let mut phase_cleaners = Vec::new();
|
||||
|
||||
for cleaner in sorted_cleaners {
|
||||
let cleaner_phase = cleaner.cleanup_phase();
|
||||
|
||||
if current_phase.as_ref() != Some(&cleaner_phase) {
|
||||
// 执行当前阶段的清理
|
||||
if !phase_cleaners.is_empty() {
|
||||
self.execute_phase_cleanup(&phase_cleaners, current_phase.as_ref())
|
||||
.await?;
|
||||
phase_cleaners.clear();
|
||||
}
|
||||
current_phase = Some(cleaner_phase.clone());
|
||||
}
|
||||
|
||||
phase_cleaners.push(cleaner);
|
||||
}
|
||||
|
||||
// 执行最后一个阶段的清理
|
||||
if !phase_cleaners.is_empty() {
|
||||
self.execute_phase_cleanup(&phase_cleaners, current_phase.as_ref())
|
||||
.await?;
|
||||
}
|
||||
|
||||
// 更新最终状态
|
||||
let mut status = self.cleanup_status.write().await;
|
||||
status.is_cleaning = false;
|
||||
status.current_phase = None;
|
||||
status.progress = 1.0;
|
||||
let final_status = status.clone();
|
||||
drop(status);
|
||||
|
||||
info!("Resource cleanup completed");
|
||||
Ok(final_status)
|
||||
}
|
||||
|
||||
/// 执行阶段清理
|
||||
async fn execute_phase_cleanup(
|
||||
&self,
|
||||
cleaners: &[Arc<dyn ResourceCleaner + Send + Sync>],
|
||||
phase: Option<&CleanupPhase>,
|
||||
) -> Result<()> {
|
||||
if let Some(phase) = phase {
|
||||
info!("Execute cleanup phase: {:?}", phase);
|
||||
|
||||
let mut status = self.cleanup_status.write().await;
|
||||
status.current_phase = Some(phase.clone());
|
||||
drop(status);
|
||||
}
|
||||
|
||||
// 并发执行同一阶段的清理器
|
||||
let mut tasks = Vec::new();
|
||||
|
||||
for cleaner in cleaners {
|
||||
let cleaner = Arc::clone(cleaner);
|
||||
let semaphore = Arc::clone(&self.semaphore);
|
||||
let config = self.config.clone();
|
||||
let cleanup_status = Arc::clone(&self.cleanup_status);
|
||||
let cleanup_history = Arc::clone(&self.cleanup_history);
|
||||
|
||||
let task = tokio::spawn(async move {
|
||||
let _permit = semaphore.acquire().await.unwrap();
|
||||
|
||||
let result = Self::execute_cleaner_with_retry(&cleaner, &config).await;
|
||||
|
||||
// 更新状态
|
||||
let mut status = cleanup_status.write().await;
|
||||
status.completed_cleaners += 1;
|
||||
status.progress = status.completed_cleaners as f64 / status.total_cleaners as f64;
|
||||
|
||||
if let Ok(ref cleanup_result) = result {
|
||||
status.results.push(cleanup_result.clone());
|
||||
|
||||
// 添加到历史记录
|
||||
let mut history = cleanup_history.write().await;
|
||||
history.push(cleanup_result.clone());
|
||||
|
||||
// 保持历史记录大小
|
||||
if history.len() > 1000 {
|
||||
history.remove(0);
|
||||
}
|
||||
}
|
||||
|
||||
result
|
||||
});
|
||||
|
||||
tasks.push(task);
|
||||
}
|
||||
|
||||
// 等待所有任务完成
|
||||
for task in tasks {
|
||||
match task.await {
|
||||
Ok(Ok(result)) => {
|
||||
info!(
|
||||
"Cleaner {} Completed: {:?}",
|
||||
result.cleaner_name, result.status
|
||||
);
|
||||
}
|
||||
Ok(Err(e)) => {
|
||||
error!("Cleaner execution failed: {}", e);
|
||||
}
|
||||
Err(e) => {
|
||||
error!("Cleanup task failed: {}", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// 执行带重试的清理器
|
||||
async fn execute_cleaner_with_retry(
|
||||
cleaner: &Arc<dyn ResourceCleaner + Send + Sync>,
|
||||
config: &CleanupConfig,
|
||||
) -> Result<CleanupResult> {
|
||||
let mut last_error = None;
|
||||
|
||||
for attempt in 0..=config.retry_count {
|
||||
match tokio::time::timeout(
|
||||
config.cleanup_timeout,
|
||||
tokio::task::spawn_blocking({
|
||||
let cleaner = Arc::clone(cleaner);
|
||||
move || cleaner.cleanup()
|
||||
}),
|
||||
)
|
||||
.await
|
||||
{
|
||||
Ok(Ok(Ok(result))) => return Ok(result),
|
||||
Ok(Ok(Err(e))) => {
|
||||
last_error = Some(e);
|
||||
}
|
||||
Ok(Err(e)) => {
|
||||
last_error = Some(anyhow::anyhow!("清理任务 panic: {}", e));
|
||||
}
|
||||
Err(_) => {
|
||||
// 超时,尝试强制清理
|
||||
if cleaner.supports_force_cleanup() {
|
||||
match tokio::time::timeout(
|
||||
config.force_cleanup_timeout,
|
||||
tokio::task::spawn_blocking({
|
||||
let cleaner = Arc::clone(cleaner);
|
||||
move || cleaner.force_cleanup()
|
||||
}),
|
||||
)
|
||||
.await
|
||||
{
|
||||
Ok(Ok(Ok(result))) => return Ok(result),
|
||||
_ => {
|
||||
last_error = Some(anyhow::anyhow!("强制清理也超时"));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
last_error = Some(anyhow::anyhow!("清理超时"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if attempt < config.retry_count {
|
||||
tokio::time::sleep(config.retry_interval).await;
|
||||
}
|
||||
}
|
||||
|
||||
// 返回失败结果
|
||||
Ok(CleanupResult {
|
||||
cleaner_name: cleaner.name().to_string(),
|
||||
cleanup_phase: cleaner.cleanup_phase(),
|
||||
status: CleanupResultStatus::Failed,
|
||||
message: format!(
|
||||
"清理失败: {}",
|
||||
last_error.unwrap_or_else(|| anyhow::anyhow!("未知错误"))
|
||||
),
|
||||
started_at: SystemTime::now(),
|
||||
completed_at: Some(SystemTime::now()),
|
||||
duration: Duration::from_millis(0),
|
||||
resources_cleaned: 0,
|
||||
memory_freed: 0,
|
||||
details: HashMap::new(),
|
||||
})
|
||||
}
|
||||
|
||||
/// 获取阶段顺序
|
||||
fn get_phase_order(&self, phase: &CleanupPhase) -> usize {
|
||||
self.config
|
||||
.cleanup_order
|
||||
.iter()
|
||||
.position(|p| p == phase)
|
||||
.unwrap_or(usize::MAX)
|
||||
}
|
||||
|
||||
/// 获取清理状态
|
||||
pub async fn get_cleanup_status(&self) -> CleanupStatus {
|
||||
self.cleanup_status.read().await.clone()
|
||||
}
|
||||
|
||||
/// 获取清理历史
|
||||
pub async fn get_cleanup_history(&self, limit: Option<usize>) -> Vec<CleanupResult> {
|
||||
let history = self.cleanup_history.read().await;
|
||||
let limit = limit.unwrap_or(history.len());
|
||||
history.iter().rev().take(limit).cloned().collect()
|
||||
}
|
||||
|
||||
/// 强制停止清理
|
||||
pub async fn force_stop_cleanup(&self) -> Result<()> {
|
||||
warn!("Force stop resource cleanup");
|
||||
|
||||
let mut status = self.cleanup_status.write().await;
|
||||
status.is_cleaning = false;
|
||||
status.current_phase = None;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
// 实现各种清理器
|
||||
|
||||
impl ResourceCleaner for DatabaseConnectionCleaner {
|
||||
fn cleanup(&self) -> Result<CleanupResult> {
|
||||
let start_time = SystemTime::now();
|
||||
|
||||
// 这里应该实现实际的数据库连接清理逻辑
|
||||
info!("Clean database connection pool");
|
||||
|
||||
let duration = start_time.elapsed().unwrap_or_default();
|
||||
|
||||
Ok(CleanupResult {
|
||||
cleaner_name: self.name.clone(),
|
||||
cleanup_phase: self.cleanup_phase(),
|
||||
status: CleanupResultStatus::Success,
|
||||
message: format!("成功清理 {} 个数据库连接", self.pool_size),
|
||||
started_at: start_time,
|
||||
completed_at: Some(SystemTime::now()),
|
||||
duration,
|
||||
resources_cleaned: self.pool_size as u64,
|
||||
memory_freed: self.pool_size as u64 * 1024, // 估算值
|
||||
details: HashMap::new(),
|
||||
})
|
||||
}
|
||||
|
||||
fn name(&self) -> &str {
|
||||
&self.name
|
||||
}
|
||||
|
||||
fn cleanup_phase(&self) -> CleanupPhase {
|
||||
CleanupPhase::CleanupDatabase
|
||||
}
|
||||
|
||||
fn priority(&self) -> u32 {
|
||||
10
|
||||
}
|
||||
}
|
||||
|
||||
impl ResourceCleaner for TempFileCleaner {
|
||||
fn cleanup(&self) -> Result<CleanupResult> {
|
||||
let start_time = SystemTime::now();
|
||||
let mut files_cleaned = 0;
|
||||
let mut memory_freed = 0;
|
||||
|
||||
// 这里应该实现实际的临时文件清理逻辑
|
||||
for temp_dir in &self.config.temp_directories {
|
||||
info!("Clean up the temporary directory: {}", temp_dir);
|
||||
// 实际的文件清理逻辑
|
||||
files_cleaned += 10; // 示例值
|
||||
memory_freed += 1024 * 1024; // 示例值
|
||||
}
|
||||
|
||||
let duration = start_time.elapsed().unwrap_or_default();
|
||||
|
||||
Ok(CleanupResult {
|
||||
cleaner_name: self.name.clone(),
|
||||
cleanup_phase: self.cleanup_phase(),
|
||||
status: CleanupResultStatus::Success,
|
||||
message: format!("成功清理 {files_cleaned} 个临时文件"),
|
||||
started_at: start_time,
|
||||
completed_at: Some(SystemTime::now()),
|
||||
duration,
|
||||
resources_cleaned: files_cleaned,
|
||||
memory_freed,
|
||||
details: HashMap::new(),
|
||||
})
|
||||
}
|
||||
|
||||
fn name(&self) -> &str {
|
||||
&self.name
|
||||
}
|
||||
|
||||
fn cleanup_phase(&self) -> CleanupPhase {
|
||||
CleanupPhase::CleanupFileSystem
|
||||
}
|
||||
|
||||
fn priority(&self) -> u32 {
|
||||
20
|
||||
}
|
||||
}
|
||||
|
||||
impl ResourceCleaner for MemoryCleaner {
|
||||
fn cleanup(&self) -> Result<CleanupResult> {
|
||||
let start_time = SystemTime::now();
|
||||
let mut memory_freed = 0;
|
||||
|
||||
if self.config.enabled {
|
||||
if self.config.clear_caches {
|
||||
info!("Clear memory cache");
|
||||
memory_freed += 1024 * 1024; // 示例值
|
||||
}
|
||||
|
||||
if self.config.force_gc {
|
||||
info!("Force garbage collection");
|
||||
// 这里应该触发垃圾回收
|
||||
memory_freed += 512 * 1024; // 示例值
|
||||
}
|
||||
|
||||
if self.config.release_unused_memory {
|
||||
info!("Release unused memory");
|
||||
memory_freed += 256 * 1024; // 示例值
|
||||
}
|
||||
}
|
||||
|
||||
let duration = start_time.elapsed().unwrap_or_default();
|
||||
|
||||
Ok(CleanupResult {
|
||||
cleaner_name: self.name.clone(),
|
||||
cleanup_phase: self.cleanup_phase(),
|
||||
status: CleanupResultStatus::Success,
|
||||
message: format!("释放了 {} KB 内存", memory_freed / 1024),
|
||||
started_at: start_time,
|
||||
completed_at: Some(SystemTime::now()),
|
||||
duration,
|
||||
resources_cleaned: 1,
|
||||
memory_freed,
|
||||
details: HashMap::new(),
|
||||
})
|
||||
}
|
||||
|
||||
fn name(&self) -> &str {
|
||||
&self.name
|
||||
}
|
||||
|
||||
fn cleanup_phase(&self) -> CleanupPhase {
|
||||
CleanupPhase::CleanupMemory
|
||||
}
|
||||
|
||||
fn priority(&self) -> u32 {
|
||||
30
|
||||
}
|
||||
|
||||
fn supports_force_cleanup(&self) -> bool {
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for CleanupStatus {
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
|
||||
impl CleanupStatus {
|
||||
/// 创建新的清理状态
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
is_cleaning: false,
|
||||
current_phase: None,
|
||||
progress: 0.0,
|
||||
started_at: None,
|
||||
estimated_completion: None,
|
||||
results: Vec::new(),
|
||||
total_cleaners: 0,
|
||||
completed_cleaners: 0,
|
||||
}
|
||||
}
|
||||
|
||||
/// 检查是否清理成功
|
||||
pub fn is_success(&self) -> bool {
|
||||
!self.is_cleaning
|
||||
&& self
|
||||
.results
|
||||
.iter()
|
||||
.all(|r| r.status == CleanupResultStatus::Success)
|
||||
}
|
||||
|
||||
/// 获取失败的清理器
|
||||
pub fn get_failed_cleaners(&self) -> Vec<&CleanupResult> {
|
||||
self.results
|
||||
.iter()
|
||||
.filter(|r| r.status == CleanupResultStatus::Failed)
|
||||
.collect()
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for CleanupConfig {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
auto_cleanup_enabled: true,
|
||||
cleanup_timeout: Duration::from_secs(30),
|
||||
force_cleanup_timeout: Duration::from_secs(10),
|
||||
max_concurrent_cleanups: 5,
|
||||
retry_count: 3,
|
||||
retry_interval: Duration::from_secs(1),
|
||||
cleanup_order: vec![
|
||||
CleanupPhase::StopNewRequests,
|
||||
CleanupPhase::WaitForRequests,
|
||||
CleanupPhase::CleanupApplication,
|
||||
CleanupPhase::CleanupDatabase,
|
||||
CleanupPhase::CleanupCache,
|
||||
CleanupPhase::CleanupNetwork,
|
||||
CleanupPhase::CleanupFileSystem,
|
||||
CleanupPhase::CleanupMemory,
|
||||
CleanupPhase::FinalCleanup,
|
||||
],
|
||||
temp_file_cleanup: TempFileCleanupConfig {
|
||||
enabled: true,
|
||||
temp_directories: vec!["/tmp".to_string(), "/var/tmp".to_string()],
|
||||
file_retention_duration: Duration::from_secs(3600),
|
||||
max_file_size_mb: 100,
|
||||
file_patterns: vec!["*.tmp".to_string(), "*.temp".to_string()],
|
||||
},
|
||||
memory_cleanup: MemoryCleanupConfig {
|
||||
enabled: true,
|
||||
force_gc: true,
|
||||
clear_caches: true,
|
||||
release_unused_memory: true,
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_resource_cleanup_manager() {
|
||||
let config = CleanupConfig::default();
|
||||
let mut manager = ResourceCleanupManager::new(config);
|
||||
|
||||
let cleaner = Arc::new(DatabaseConnectionCleaner {
|
||||
name: "test_db".to_string(),
|
||||
pool_size: 10,
|
||||
});
|
||||
|
||||
manager.add_cleaner(cleaner);
|
||||
|
||||
let status = manager.get_cleanup_status().await;
|
||||
assert!(!status.is_cleaning);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_database_connection_cleaner() {
|
||||
let cleaner = DatabaseConnectionCleaner {
|
||||
name: "test_db".to_string(),
|
||||
pool_size: 5,
|
||||
};
|
||||
|
||||
let result = cleaner.cleanup().unwrap();
|
||||
assert_eq!(result.status, CleanupResultStatus::Success);
|
||||
assert_eq!(result.resources_cleaned, 5);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_memory_cleaner() {
|
||||
let cleaner = MemoryCleaner {
|
||||
name: "memory".to_string(),
|
||||
config: MemoryCleanupConfig {
|
||||
enabled: true,
|
||||
force_gc: true,
|
||||
clear_caches: true,
|
||||
release_unused_memory: true,
|
||||
},
|
||||
};
|
||||
|
||||
let result = cleaner.cleanup().unwrap();
|
||||
assert_eq!(result.status, CleanupResultStatus::Success);
|
||||
assert!(result.memory_freed > 0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_cleanup_status() {
|
||||
let status = CleanupStatus::new();
|
||||
assert!(!status.is_cleaning);
|
||||
assert_eq!(status.progress, 0.0);
|
||||
assert!(status.is_success()); // 空结果被认为是成功的
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user