From 07ba3df0f4af4d8223af99f6df1130c364b36e28 Mon Sep 17 00:00:00 2001 From: YoVinchen Date: Sun, 11 Jan 2026 00:44:54 +0800 Subject: [PATCH] feat(proxy): add structured log codes for i18n support Add error code system to proxy module logs for multi-language support: - CB-001~006: Circuit breaker state transitions and triggers - SRV-001~004: Proxy server lifecycle events - FWD-001~002: Request forwarding and failover - FO-001~005: Failover switch operations - USG-001~002: Usage logging errors Log format: [CODE] Chinese message Frontend/log tools can map codes to any language. New file: src/proxy/log_codes.rs - centralized code definitions --- src-tauri/src/proxy/circuit_breaker.rs | 71 +++++--------------------- src-tauri/src/proxy/failover_switch.rs | 12 ++--- src-tauri/src/proxy/forwarder.rs | 8 ++- src-tauri/src/proxy/log_codes.rs | 59 +++++++++++++++++++++ src-tauri/src/proxy/mod.rs | 1 + src-tauri/src/proxy/server.rs | 8 +-- 6 files changed, 85 insertions(+), 74 deletions(-) create mode 100644 src-tauri/src/proxy/log_codes.rs diff --git a/src-tauri/src/proxy/circuit_breaker.rs b/src-tauri/src/proxy/circuit_breaker.rs index 521ced868..def493442 100644 --- a/src-tauri/src/proxy/circuit_breaker.rs +++ b/src-tauri/src/proxy/circuit_breaker.rs @@ -106,7 +106,6 @@ impl CircuitBreaker { /// 更新熔断器配置(热更新,不重置状态) pub async fn update_config(&self, new_config: CircuitBreakerConfig) { *self.config.write().await = new_config; - log::debug!("Circuit breaker config updated"); } /// 判断当前 Provider 是否“可被纳入候选链路” @@ -127,9 +126,7 @@ impl CircuitBreaker { if let Some(opened_at) = *self.last_opened_at.read().await { if opened_at.elapsed().as_secs() >= config.timeout_seconds { drop(config); // 释放读锁再转换状态 - log::info!( - "Circuit breaker transitioning from Open to HalfOpen (timeout reached)" - ); + log::info!("[CB-001] 熔断器 Open → HalfOpen (超时恢复)"); self.transition_to_half_open().await; return true; } @@ -154,9 +151,7 @@ impl CircuitBreaker { if let Some(opened_at) = *self.last_opened_at.read().await { if opened_at.elapsed().as_secs() >= config.timeout_seconds { drop(config); // 释放读锁再转换状态 - log::info!( - "Circuit breaker transitioning from Open to HalfOpen (timeout reached)" - ); + log::info!("熔断器 Open → HalfOpen (超时恢复)"); self.transition_to_half_open().await; // 转换后按当前状态决定是否需要获取 HalfOpen 探测名额 @@ -197,25 +192,14 @@ impl CircuitBreaker { self.consecutive_failures.store(0, Ordering::SeqCst); self.total_requests.fetch_add(1, Ordering::SeqCst); - match state { - CircuitState::HalfOpen => { - let successes = self.consecutive_successes.fetch_add(1, Ordering::SeqCst) + 1; - log::debug!( - "Circuit breaker HalfOpen: {} consecutive successes (threshold: {})", - successes, - config.success_threshold - ); + if state == CircuitState::HalfOpen { + let successes = self.consecutive_successes.fetch_add(1, Ordering::SeqCst) + 1; - if successes >= config.success_threshold { - drop(config); // 释放读锁再转换状态 - log::info!("Circuit breaker transitioning from HalfOpen to Closed (success threshold reached)"); - self.transition_to_closed().await; - } + if successes >= config.success_threshold { + drop(config); // 释放读锁再转换状态 + log::info!("[CB-002] 熔断器 HalfOpen → Closed (恢复正常)"); + self.transition_to_closed().await; } - CircuitState::Closed => { - log::debug!("Circuit breaker Closed: request succeeded"); - } - _ => {} } } @@ -236,29 +220,18 @@ impl CircuitBreaker { // 重置成功计数 self.consecutive_successes.store(0, Ordering::SeqCst); - log::debug!( - "Circuit breaker {:?}: {} consecutive failures (threshold: {})", - state, - failures, - config.failure_threshold - ); - // 检查是否应该打开熔断器 match state { CircuitState::HalfOpen => { // HalfOpen 状态下失败,立即转为 Open - log::warn!("Circuit breaker HalfOpen probe failed, transitioning to Open"); + log::warn!("[CB-003] 熔断器 HalfOpen 探测失败 → Open"); drop(config); self.transition_to_open().await; } CircuitState::Closed => { // 检查连续失败次数 if failures >= config.failure_threshold { - log::warn!( - "Circuit breaker opening due to {} consecutive failures (threshold: {})", - failures, - config.failure_threshold - ); + log::warn!("[CB-004] 熔断器触发: 连续失败 {failures} 次 → Open"); drop(config); // 释放读锁再转换状态 self.transition_to_open().await; } else { @@ -268,18 +241,11 @@ impl CircuitBreaker { if total >= config.min_requests { let error_rate = failed as f64 / total as f64; - log::debug!( - "Circuit breaker error rate: {:.2}% ({}/{} requests)", - error_rate * 100.0, - failed, - total - ); if error_rate >= config.error_rate_threshold { log::warn!( - "Circuit breaker opening due to high error rate: {:.2}% (threshold: {:.2}%)", - error_rate * 100.0, - config.error_rate_threshold * 100.0 + "[CB-005] 熔断器触发: 错误率 {:.1}% → Open", + error_rate * 100.0 ); drop(config); // 释放读锁再转换状态 self.transition_to_open().await; @@ -312,22 +278,16 @@ impl CircuitBreaker { /// 重置熔断器(手动恢复) #[allow(dead_code)] pub async fn reset(&self) { - log::info!("Circuit breaker manually reset to Closed state"); + log::info!("[CB-006] 熔断器手动重置 → Closed"); self.transition_to_closed().await; } fn allow_half_open_probe(&self) -> AllowResult { // 半开状态限流:只允许有限请求通过进行探测 - // 默认最多允许 1 个请求(可在配置中扩展) let max_half_open_requests = 1u32; let current = self.half_open_requests.fetch_add(1, Ordering::SeqCst); if current < max_half_open_requests { - log::debug!( - "Circuit breaker HalfOpen: allowing probe request ({}/{})", - current + 1, - max_half_open_requests - ); AllowResult { allowed: true, used_half_open_permit: true, @@ -335,9 +295,6 @@ impl CircuitBreaker { } else { // 超过限额,回退计数,拒绝请求 self.half_open_requests.fetch_sub(1, Ordering::SeqCst); - log::debug!( - "Circuit breaker HalfOpen: rejecting request (limit reached: {max_half_open_requests})" - ); AllowResult { allowed: false, used_half_open_permit: false, @@ -349,8 +306,6 @@ impl CircuitBreaker { let mut current = self.half_open_requests.load(Ordering::SeqCst); loop { if current == 0 { - // 理论上不应该发生:说明调用方传入的 used_half_open_permit 与实际占用不一致 - log::debug!("Circuit breaker HalfOpen permit already released (counter=0)"); return; } diff --git a/src-tauri/src/proxy/failover_switch.rs b/src-tauri/src/proxy/failover_switch.rs index e0bb8a00d..cc4846eb7 100644 --- a/src-tauri/src/proxy/failover_switch.rs +++ b/src-tauri/src/proxy/failover_switch.rs @@ -86,17 +86,17 @@ impl FailoverSwitchManager { let app_enabled = match self.db.get_proxy_config_for_app(app_type).await { Ok(config) => config.enabled, Err(e) => { - log::warn!("[Failover] 无法读取 {app_type} 配置: {e},跳过切换"); + log::warn!("[FO-002] 无法读取 {app_type} 配置: {e},跳过切换"); return Ok(false); } }; if !app_enabled { - log::info!("[Failover] {app_type} 未被代理接管(enabled=false),跳过切换"); + log::debug!("[Failover] {app_type} 未启用代理,跳过切换"); return Ok(false); } - log::info!("[Failover] 开始切换供应商: {app_type} -> {provider_name} ({provider_id})"); + log::info!("[FO-001] 切换: {app_type} → {provider_name}"); // 1. 更新数据库 is_current self.db.set_current_provider(app_type, provider_id)?; @@ -117,7 +117,7 @@ impl FailoverSwitchManager { .update_live_backup_from_provider(app_type, &provider) .await { - log::warn!("[Failover] 更新 Live 备份失败: {e}"); + log::warn!("[FO-003] Live 备份更新失败: {e}"); } } @@ -138,12 +138,10 @@ impl FailoverSwitchManager { "source": "failover" // 标识来源是故障转移 }); if let Err(e) = app.emit("provider-switched", event_data) { - log::error!("[Failover] 发射供应商切换事件失败: {e}"); + log::error!("[Failover] 发射事件失败: {e}"); } } - log::info!("[Failover] 供应商切换完成: {app_type} -> {provider_name} ({provider_id})"); - Ok(true) } } diff --git a/src-tauri/src/proxy/forwarder.rs b/src-tauri/src/proxy/forwarder.rs index ff2ec358c..b3d47fcf3 100644 --- a/src-tauri/src/proxy/forwarder.rs +++ b/src-tauri/src/proxy/forwarder.rs @@ -305,8 +305,8 @@ impl RequestForwarder { Some(format!("Provider {} 失败: {}", provider.name, e)); } - log::info!( - "[{}] Provider {} 请求失败,切换到下一个 (已尝试 {}/{})", + log::warn!( + "[{}] [FWD-001] Provider {} 失败,切换下一个 ({}/{})", app_type_str, provider.name, attempted_providers, @@ -368,9 +368,7 @@ impl RequestForwarder { } } - log::info!( - "[{app_type_str}] 所有 {attempted_providers} 个 Provider 均已尝试失败" - ); + log::warn!("[{app_type_str}] [FWD-002] 所有 Provider 均失败"); Err(ForwardError { error: last_error.unwrap_or(ProxyError::MaxRetriesExceeded), diff --git a/src-tauri/src/proxy/log_codes.rs b/src-tauri/src/proxy/log_codes.rs new file mode 100644 index 000000000..48e16d332 --- /dev/null +++ b/src-tauri/src/proxy/log_codes.rs @@ -0,0 +1,59 @@ +//! 代理模块日志错误码定义 +//! +//! 格式: [模块-编号] 消息 +//! - CB: Circuit Breaker (熔断器) +//! - SRV: Server (服务器) +//! - FWD: Forwarder (转发器) +//! - FO: Failover (故障转移) +//! - RSP: Response (响应处理) +//! - USG: Usage (使用量) + +#![allow(dead_code)] + +/// 熔断器日志码 +pub mod cb { + pub const OPEN_TO_HALF_OPEN: &str = "CB-001"; + pub const HALF_OPEN_TO_CLOSED: &str = "CB-002"; + pub const HALF_OPEN_PROBE_FAILED: &str = "CB-003"; + pub const TRIGGERED_FAILURES: &str = "CB-004"; + pub const TRIGGERED_ERROR_RATE: &str = "CB-005"; + pub const MANUAL_RESET: &str = "CB-006"; +} + +/// 服务器日志码 +pub mod srv { + pub const STARTED: &str = "SRV-001"; + pub const STOPPED: &str = "SRV-002"; + pub const STOP_TIMEOUT: &str = "SRV-003"; + pub const TASK_ERROR: &str = "SRV-004"; +} + +/// 转发器日志码 +pub mod fwd { + pub const PROVIDER_FAILED_RETRY: &str = "FWD-001"; + pub const ALL_PROVIDERS_FAILED: &str = "FWD-002"; +} + +/// 故障转移日志码 +pub mod fo { + pub const SWITCH_SUCCESS: &str = "FO-001"; + pub const CONFIG_READ_ERROR: &str = "FO-002"; + pub const LIVE_BACKUP_ERROR: &str = "FO-003"; + pub const ALL_CIRCUIT_OPEN: &str = "FO-004"; + pub const NO_PROVIDERS: &str = "FO-005"; +} + +/// 响应处理日志码 +pub mod rsp { + pub const BUILD_STREAM_ERROR: &str = "RSP-001"; + pub const READ_BODY_ERROR: &str = "RSP-002"; + pub const BUILD_RESPONSE_ERROR: &str = "RSP-003"; + pub const STREAM_TIMEOUT: &str = "RSP-004"; + pub const STREAM_ERROR: &str = "RSP-005"; +} + +/// 使用量日志码 +pub mod usg { + pub const LOG_FAILED: &str = "USG-001"; + pub const PRICING_NOT_FOUND: &str = "USG-002"; +} diff --git a/src-tauri/src/proxy/mod.rs b/src-tauri/src/proxy/mod.rs index 5b0ac0216..bcd946a77 100644 --- a/src-tauri/src/proxy/mod.rs +++ b/src-tauri/src/proxy/mod.rs @@ -12,6 +12,7 @@ pub mod handler_config; pub mod handler_context; mod handlers; mod health; +pub mod log_codes; pub mod model_mapper; pub mod provider_router; pub mod providers; diff --git a/src-tauri/src/proxy/server.rs b/src-tauri/src/proxy/server.rs index 5da90c86f..2a4b2867f 100644 --- a/src-tauri/src/proxy/server.rs +++ b/src-tauri/src/proxy/server.rs @@ -95,7 +95,7 @@ impl ProxyServer { .await .map_err(|e| ProxyError::BindFailed(e.to_string()))?; - log::info!("代理服务器启动于 {addr}"); + log::info!("[SRV-001] 代理服务器启动于 {addr}"); // 保存关闭句柄 *self.shutdown_tx.write().await = Some(shutdown_tx); @@ -146,9 +146,9 @@ impl ProxyServer { // 2. 等待服务器任务结束(带 5 秒超时保护) if let Some(handle) = self.server_handle.write().await.take() { match tokio::time::timeout(std::time::Duration::from_secs(5), handle).await { - Ok(Ok(())) => log::info!("代理服务器已完全停止"), - Ok(Err(e)) => log::warn!("代理服务器任务异常终止: {e}"), - Err(_) => log::warn!("代理服务器停止超时(5秒),强制继续"), + Ok(Ok(())) => log::info!("[SRV-002] 代理服务器已完全停止"), + Ok(Err(e)) => log::warn!("[SRV-003] 代理服务器任务异常终止: {e}"), + Err(_) => log::warn!("[SRV-004] 代理服务器停止超时(5秒),强制继续"), } }