Files
CC-Switch/src-tauri/src/proxy/error_mapper.rs
T
YoVinchen ec20ff4d8c Feature/error request logging (#401)
* feat(proxy): add error mapper for HTTP status code mapping

- Add error_mapper.rs module to map ProxyError to HTTP status codes
- Implement map_proxy_error_to_status() for error classification
- Implement get_error_message() for user-friendly error messages
- Support all error types: upstream, timeout, connection, provider failures
- Include comprehensive unit tests for all mappings

* feat(proxy): enhance error logging with context support

- Add log_error_with_context() method for detailed error recording
- Support streaming flag, session_id, and provider_type fields
- Remove dead_code warning from log_error() method
- Enable comprehensive error request tracking in database

* feat(proxy): implement error capture and logging in all handlers

- Capture and log all failed requests in handle_messages (Claude)
- Capture and log all failed requests in handle_gemini (Gemini)
- Capture and log all failed requests in handle_responses (Codex)
- Capture and log all failed requests in handle_chat_completions (Codex)
- Record error status codes, messages, and latency for all failures
- Generate unique session_id for each request
- Support both streaming and non-streaming error scenarios

* style: fix clippy warnings and typescript errors

- Add allow(dead_code) for CircuitBreaker::get_state (reserved for future)
- Fix all uninlined format string warnings (27 instances)
- Use inline format syntax for better readability
- Fix unused import and parameter warnings in ProviderActions.tsx
- Achieve zero warnings in both Rust and TypeScript

* style: apply code formatting

- Remove trailing whitespace in misc.rs
- Add trailing comma in App.tsx
- Format multi-line className in ProviderCard.tsx

* feat(proxy): add settings button to proxy panel

Add configuration buttons in both running and stopped states to
provide easy access to proxy settings dialog.

* fix(speedtest): skip client build for invalid inputs

* chore(clippy): fix uninlined format args

* Merge branch 'main' into feature/error-request-logging
2025-12-16 21:02:08 +08:00

111 lines
3.6 KiB
Rust
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
//! 错误类型到 HTTP 状态码的映射
//!
//! 将 ProxyError 映射到合适的 HTTP 状态码,用于日志记录
use super::ProxyError;
/// 将 ProxyError 映射到 HTTP 状态码
///
/// 映射规则:
/// - 上游错误:直接使用上游返回的状态码
/// - 超时:504 Gateway Timeout
/// - 连接失败:502 Bad Gateway
/// - 无可用 Provider503 Service Unavailable
/// - 重试耗尽:503 Service Unavailable
/// - 其他错误:500 Internal Server Error
pub fn map_proxy_error_to_status(error: &ProxyError) -> u16 {
match error {
// 上游错误:使用实际状态码
ProxyError::UpstreamError { status, .. } => *status,
// 超时错误:504 Gateway Timeout
ProxyError::Timeout(_) => 504,
// 转发失败/连接失败:502 Bad Gateway
ProxyError::ForwardFailed(_) => 502,
// 无可用 Provider503 Service Unavailable
ProxyError::NoAvailableProvider => 503,
// 重试耗尽:503 Service Unavailable
ProxyError::MaxRetriesExceeded => 503,
// Provider 不健康:503 Service Unavailable
ProxyError::ProviderUnhealthy(_) => 503,
// 数据库错误:500 Internal Server Error
ProxyError::DatabaseError(_) => 500,
// 转换错误:500 Internal Server Error
ProxyError::TransformError(_) => 500,
// 其他未知错误:500 Internal Server Error
_ => 500,
}
}
/// 将 ProxyError 转换为用户友好的错误消息
pub fn get_error_message(error: &ProxyError) -> String {
match error {
ProxyError::UpstreamError { status, body } => {
if let Some(body) = body {
format!("上游错误 ({status}): {body}")
} else {
format!("上游错误 ({status})")
}
}
ProxyError::Timeout(msg) => format!("请求超时: {msg}"),
ProxyError::ForwardFailed(msg) => format!("转发失败: {msg}"),
ProxyError::NoAvailableProvider => "无可用 Provider".to_string(),
ProxyError::MaxRetriesExceeded => "所有 Provider 都失败,重试耗尽".to_string(),
ProxyError::ProviderUnhealthy(msg) => format!("Provider 不健康: {msg}"),
ProxyError::DatabaseError(msg) => format!("数据库错误: {msg}"),
ProxyError::TransformError(msg) => format!("请求/响应转换错误: {msg}"),
_ => error.to_string(),
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_map_upstream_error() {
let error = ProxyError::UpstreamError {
status: 401,
body: Some("Unauthorized".to_string()),
};
assert_eq!(map_proxy_error_to_status(&error), 401);
}
#[test]
fn test_map_timeout_error() {
let error = ProxyError::Timeout("Request timeout".to_string());
assert_eq!(map_proxy_error_to_status(&error), 504);
}
#[test]
fn test_map_connection_error() {
let error = ProxyError::ForwardFailed("Connection refused".to_string());
assert_eq!(map_proxy_error_to_status(&error), 502);
}
#[test]
fn test_map_no_provider_error() {
let error = ProxyError::NoAvailableProvider;
assert_eq!(map_proxy_error_to_status(&error), 503);
}
#[test]
fn test_get_error_message() {
let error = ProxyError::UpstreamError {
status: 500,
body: Some("Internal Server Error".to_string()),
};
let msg = get_error_message(&error);
assert!(msg.contains("上游错误"));
assert!(msg.contains("500"));
assert!(msg.contains("Internal Server Error"));
}
}