Files
udmin/docs/ERROR_HANDLING.md
ayou a3f2f99a68 docs: 添加项目文档包括总览、架构、流程引擎和服务层
新增以下文档文件:
- PROJECT_OVERVIEW.md 项目总览文档
- BACKEND_ARCHITECTURE.md 后端架构文档
- FRONTEND_ARCHITECTURE.md 前端架构文档
- FLOW_ENGINE.md 流程引擎文档
- SERVICES.md 服务层文档
- ERROR_HANDLING.md 错误处理模块文档

文档内容涵盖项目整体介绍、技术架构、核心模块设计和实现细节
2025-09-24 20:21:45 +08:00

878 lines
28 KiB
Markdown
Raw Permalink 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.

# UdminAI 错误处理模块文档
## 概述
UdminAI 项目的错误处理模块提供了统一的错误定义、处理和响应机制。该模块基于 Rust 的 `Result` 类型和 `thiserror` 库构建,确保错误信息的一致性、可追踪性和用户友好性。
## 设计原则
### 核心理念
- **统一性**: 所有模块使用统一的错误类型
- **可追踪性**: 错误包含足够的上下文信息
- **用户友好**: 面向用户的错误消息清晰易懂
- **开发友好**: 面向开发者的错误信息详细准确
- **类型安全**: 编译时错误类型检查
### 错误分层
1. **应用层错误**: 业务逻辑错误
2. **服务层错误**: 服务调用错误
3. **数据层错误**: 数据库和缓存错误
4. **网络层错误**: HTTP 和网络通信错误
5. **系统层错误**: 系统资源和配置错误
## 错误类型定义 (error.rs)
### 主要错误类型
```rust
use axum::{
http::StatusCode,
response::{IntoResponse, Response},
Json,
};
use serde::{Deserialize, Serialize};
use std::fmt;
use thiserror::Error;
use tracing::error;
/// 应用主错误类型
#[derive(Error, Debug, Clone, Serialize, Deserialize)]
pub enum AppError {
// 认证和授权错误
#[error("认证失败: {message}")]
AuthenticationFailed { message: String },
#[error("授权失败: {message}")]
AuthorizationFailed { message: String },
#[error("令牌无效: {message}")]
InvalidToken { message: String },
#[error("令牌已过期")]
TokenExpired,
// 验证错误
#[error("验证失败: {field} - {message}")]
ValidationFailed { field: String, message: String },
#[error("请求参数无效: {message}")]
InvalidRequest { message: String },
#[error("必需字段缺失: {field}")]
MissingField { field: String },
// 资源错误
#[error("资源未找到: {resource}")]
NotFound(String),
#[error("资源已存在: {resource}")]
AlreadyExists(String),
#[error("资源冲突: {message}")]
Conflict { message: String },
// 业务逻辑错误
#[error("业务规则违反: {message}")]
BusinessRuleViolation { message: String },
#[error("操作不被允许: {message}")]
OperationNotAllowed { message: String },
#[error("状态无效: 当前状态 {current}, 期望状态 {expected}")]
InvalidState { current: String, expected: String },
// 数据库错误
#[error("数据库错误: {0}")]
DatabaseError(String),
#[error("数据库连接失败: {message}")]
DatabaseConnectionFailed { message: String },
#[error("事务失败: {message}")]
TransactionFailed { message: String },
// 缓存错误
#[error("缓存错误: {0}")]
CacheError(String),
#[error("缓存连接失败: {message}")]
CacheConnectionFailed { message: String },
// 网络和外部服务错误
#[error("网络错误: {message}")]
NetworkError { message: String },
#[error("外部服务错误: {service} - {message}")]
ExternalServiceError { service: String, message: String },
#[error("HTTP 请求失败: {status} - {message}")]
HttpRequestFailed { status: u16, message: String },
// 文件和 I/O 错误
#[error("文件操作失败: {message}")]
FileOperationFailed { message: String },
#[error("文件未找到: {path}")]
FileNotFound { path: String },
#[error("文件权限不足: {path}")]
FilePermissionDenied { path: String },
// 配置和环境错误
#[error("配置错误: {message}")]
ConfigurationError { message: String },
#[error("环境变量缺失: {variable}")]
MissingEnvironmentVariable { variable: String },
// 序列化和反序列化错误
#[error("序列化失败: {message}")]
SerializationFailed { message: String },
#[error("反序列化失败: {message}")]
DeserializationFailed { message: String },
#[error("JSON 格式错误: {message}")]
JsonFormatError { message: String },
// 流程引擎错误
#[error("流程执行失败: {flow_id} - {message}")]
FlowExecutionFailed { flow_id: String, message: String },
#[error("流程解析失败: {message}")]
FlowParsingFailed { message: String },
#[error("节点执行失败: {node_id} - {message}")]
NodeExecutionFailed { node_id: String, message: String },
// 调度任务错误
#[error("任务调度失败: {job_id} - {message}")]
JobSchedulingFailed { job_id: String, message: String },
#[error("Cron 表达式无效: {expression}")]
InvalidCronExpression { expression: String },
#[error("任务执行超时: {job_id}")]
JobExecutionTimeout { job_id: String },
// 系统错误
#[error("内部服务器错误: {message}")]
InternalServerError { message: String },
#[error("服务不可用: {message}")]
ServiceUnavailable { message: String },
#[error("请求超时: {message}")]
RequestTimeout { message: String },
#[error("资源耗尽: {resource}")]
ResourceExhausted { resource: String },
// 限流和安全错误
#[error("请求频率过高: {message}")]
RateLimitExceeded { message: String },
#[error("请求体过大: 当前大小 {current}, 最大允许 {max}")]
PayloadTooLarge { current: usize, max: usize },
#[error("不支持的媒体类型: {media_type}")]
UnsupportedMediaType { media_type: String },
}
/// 错误响应结构
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ErrorResponse {
pub error: ErrorDetail,
pub timestamp: chrono::DateTime<chrono::Utc>,
pub request_id: Option<String>,
}
/// 错误详情
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ErrorDetail {
pub code: String,
pub message: String,
pub details: Option<serde_json::Value>,
pub field: Option<String>,
}
impl AppError {
/// 获取错误代码
pub fn error_code(&self) -> &'static str {
match self {
// 认证和授权
AppError::AuthenticationFailed { .. } => "AUTH_FAILED",
AppError::AuthorizationFailed { .. } => "AUTHORIZATION_FAILED",
AppError::InvalidToken { .. } => "INVALID_TOKEN",
AppError::TokenExpired => "TOKEN_EXPIRED",
// 验证
AppError::ValidationFailed { .. } => "VALIDATION_FAILED",
AppError::InvalidRequest { .. } => "INVALID_REQUEST",
AppError::MissingField { .. } => "MISSING_FIELD",
// 资源
AppError::NotFound(_) => "NOT_FOUND",
AppError::AlreadyExists(_) => "ALREADY_EXISTS",
AppError::Conflict { .. } => "CONFLICT",
// 业务逻辑
AppError::BusinessRuleViolation { .. } => "BUSINESS_RULE_VIOLATION",
AppError::OperationNotAllowed { .. } => "OPERATION_NOT_ALLOWED",
AppError::InvalidState { .. } => "INVALID_STATE",
// 数据库
AppError::DatabaseError(_) => "DATABASE_ERROR",
AppError::DatabaseConnectionFailed { .. } => "DATABASE_CONNECTION_FAILED",
AppError::TransactionFailed { .. } => "TRANSACTION_FAILED",
// 缓存
AppError::CacheError(_) => "CACHE_ERROR",
AppError::CacheConnectionFailed { .. } => "CACHE_CONNECTION_FAILED",
// 网络
AppError::NetworkError { .. } => "NETWORK_ERROR",
AppError::ExternalServiceError { .. } => "EXTERNAL_SERVICE_ERROR",
AppError::HttpRequestFailed { .. } => "HTTP_REQUEST_FAILED",
// 文件
AppError::FileOperationFailed { .. } => "FILE_OPERATION_FAILED",
AppError::FileNotFound { .. } => "FILE_NOT_FOUND",
AppError::FilePermissionDenied { .. } => "FILE_PERMISSION_DENIED",
// 配置
AppError::ConfigurationError { .. } => "CONFIGURATION_ERROR",
AppError::MissingEnvironmentVariable { .. } => "MISSING_ENV_VAR",
// 序列化
AppError::SerializationFailed { .. } => "SERIALIZATION_FAILED",
AppError::DeserializationFailed { .. } => "DESERIALIZATION_FAILED",
AppError::JsonFormatError { .. } => "JSON_FORMAT_ERROR",
// 流程引擎
AppError::FlowExecutionFailed { .. } => "FLOW_EXECUTION_FAILED",
AppError::FlowParsingFailed { .. } => "FLOW_PARSING_FAILED",
AppError::NodeExecutionFailed { .. } => "NODE_EXECUTION_FAILED",
// 调度任务
AppError::JobSchedulingFailed { .. } => "JOB_SCHEDULING_FAILED",
AppError::InvalidCronExpression { .. } => "INVALID_CRON_EXPRESSION",
AppError::JobExecutionTimeout { .. } => "JOB_EXECUTION_TIMEOUT",
// 系统
AppError::InternalServerError { .. } => "INTERNAL_SERVER_ERROR",
AppError::ServiceUnavailable { .. } => "SERVICE_UNAVAILABLE",
AppError::RequestTimeout { .. } => "REQUEST_TIMEOUT",
AppError::ResourceExhausted { .. } => "RESOURCE_EXHAUSTED",
// 限流和安全
AppError::RateLimitExceeded { .. } => "RATE_LIMIT_EXCEEDED",
AppError::PayloadTooLarge { .. } => "PAYLOAD_TOO_LARGE",
AppError::UnsupportedMediaType { .. } => "UNSUPPORTED_MEDIA_TYPE",
}
}
/// 获取 HTTP 状态码
pub fn status_code(&self) -> StatusCode {
match self {
// 4xx 客户端错误
AppError::AuthenticationFailed { .. } => StatusCode::UNAUTHORIZED,
AppError::AuthorizationFailed { .. } => StatusCode::FORBIDDEN,
AppError::InvalidToken { .. } => StatusCode::UNAUTHORIZED,
AppError::TokenExpired => StatusCode::UNAUTHORIZED,
AppError::ValidationFailed { .. } => StatusCode::BAD_REQUEST,
AppError::InvalidRequest { .. } => StatusCode::BAD_REQUEST,
AppError::MissingField { .. } => StatusCode::BAD_REQUEST,
AppError::NotFound(_) => StatusCode::NOT_FOUND,
AppError::AlreadyExists(_) => StatusCode::CONFLICT,
AppError::Conflict { .. } => StatusCode::CONFLICT,
AppError::BusinessRuleViolation { .. } => StatusCode::BAD_REQUEST,
AppError::OperationNotAllowed { .. } => StatusCode::FORBIDDEN,
AppError::InvalidState { .. } => StatusCode::BAD_REQUEST,
AppError::FileNotFound { .. } => StatusCode::NOT_FOUND,
AppError::FilePermissionDenied { .. } => StatusCode::FORBIDDEN,
AppError::JsonFormatError { .. } => StatusCode::BAD_REQUEST,
AppError::InvalidCronExpression { .. } => StatusCode::BAD_REQUEST,
AppError::RateLimitExceeded { .. } => StatusCode::TOO_MANY_REQUESTS,
AppError::PayloadTooLarge { .. } => StatusCode::PAYLOAD_TOO_LARGE,
AppError::UnsupportedMediaType { .. } => StatusCode::UNSUPPORTED_MEDIA_TYPE,
// 5xx 服务器错误
AppError::DatabaseError(_) => StatusCode::INTERNAL_SERVER_ERROR,
AppError::DatabaseConnectionFailed { .. } => StatusCode::SERVICE_UNAVAILABLE,
AppError::TransactionFailed { .. } => StatusCode::INTERNAL_SERVER_ERROR,
AppError::CacheError(_) => StatusCode::INTERNAL_SERVER_ERROR,
AppError::CacheConnectionFailed { .. } => StatusCode::SERVICE_UNAVAILABLE,
AppError::NetworkError { .. } => StatusCode::BAD_GATEWAY,
AppError::ExternalServiceError { .. } => StatusCode::BAD_GATEWAY,
AppError::HttpRequestFailed { .. } => StatusCode::BAD_GATEWAY,
AppError::FileOperationFailed { .. } => StatusCode::INTERNAL_SERVER_ERROR,
AppError::ConfigurationError { .. } => StatusCode::INTERNAL_SERVER_ERROR,
AppError::MissingEnvironmentVariable { .. } => StatusCode::INTERNAL_SERVER_ERROR,
AppError::SerializationFailed { .. } => StatusCode::INTERNAL_SERVER_ERROR,
AppError::DeserializationFailed { .. } => StatusCode::INTERNAL_SERVER_ERROR,
AppError::FlowExecutionFailed { .. } => StatusCode::INTERNAL_SERVER_ERROR,
AppError::FlowParsingFailed { .. } => StatusCode::BAD_REQUEST,
AppError::NodeExecutionFailed { .. } => StatusCode::INTERNAL_SERVER_ERROR,
AppError::JobSchedulingFailed { .. } => StatusCode::INTERNAL_SERVER_ERROR,
AppError::JobExecutionTimeout { .. } => StatusCode::REQUEST_TIMEOUT,
AppError::InternalServerError { .. } => StatusCode::INTERNAL_SERVER_ERROR,
AppError::ServiceUnavailable { .. } => StatusCode::SERVICE_UNAVAILABLE,
AppError::RequestTimeout { .. } => StatusCode::REQUEST_TIMEOUT,
AppError::ResourceExhausted { .. } => StatusCode::SERVICE_UNAVAILABLE,
}
}
/// 获取错误字段(如果适用)
pub fn error_field(&self) -> Option<String> {
match self {
AppError::ValidationFailed { field, .. } => Some(field.clone()),
AppError::MissingField { field } => Some(field.clone()),
_ => None,
}
}
/// 是否为客户端错误
pub fn is_client_error(&self) -> bool {
self.status_code().is_client_error()
}
/// 是否为服务器错误
pub fn is_server_error(&self) -> bool {
self.status_code().is_server_error()
}
/// 创建错误响应
pub fn to_error_response(&self, request_id: Option<String>) -> ErrorResponse {
ErrorResponse {
error: ErrorDetail {
code: self.error_code().to_string(),
message: self.to_string(),
details: None,
field: self.error_field(),
},
timestamp: chrono::Utc::now(),
request_id,
}
}
/// 记录错误日志
pub fn log_error(&self, request_id: Option<&str>) {
let level = if self.is_server_error() {
tracing::Level::ERROR
} else {
tracing::Level::WARN
};
match level {
tracing::Level::ERROR => {
error!(
target = "udmin",
error_code = %self.error_code(),
error_message = %self,
request_id = ?request_id,
"application.error.server"
);
}
_ => {
tracing::warn!(
target = "udmin",
error_code = %self.error_code(),
error_message = %self,
request_id = ?request_id,
"application.error.client"
);
}
}
}
}
/// 实现 IntoResponse使错误可以直接作为 HTTP 响应返回
impl IntoResponse for AppError {
fn into_response(self) -> Response {
// 从请求上下文获取 request_id实际实现中可能需要通过中间件传递
let request_id = None; // 这里应该从上下文获取
// 记录错误日志
self.log_error(request_id.as_deref());
// 创建错误响应
let error_response = self.to_error_response(request_id);
let status_code = self.status_code();
(status_code, Json(error_response)).into_response()
}
}
/// 应用结果类型别名
pub type AppResult<T> = Result<T, AppError>;
/// 错误转换实现
impl From<sea_orm::DbErr> for AppError {
fn from(err: sea_orm::DbErr) -> Self {
match err {
sea_orm::DbErr::RecordNotFound(_) => AppError::NotFound("记录不存在".to_string()),
sea_orm::DbErr::Custom(msg) => AppError::DatabaseError(msg),
sea_orm::DbErr::Conn(msg) => AppError::DatabaseConnectionFailed { message: msg },
sea_orm::DbErr::Exec(msg) => AppError::DatabaseError(msg),
sea_orm::DbErr::Query(msg) => AppError::DatabaseError(msg),
_ => AppError::DatabaseError("未知数据库错误".to_string()),
}
}
}
impl From<redis::RedisError> for AppError {
fn from(err: redis::RedisError) -> Self {
match err.kind() {
redis::ErrorKind::IoError => AppError::CacheConnectionFailed {
message: err.to_string(),
},
redis::ErrorKind::AuthenticationFailed => AppError::CacheConnectionFailed {
message: "Redis 认证失败".to_string(),
},
_ => AppError::CacheError(err.to_string()),
}
}
}
impl From<reqwest::Error> for AppError {
fn from(err: reqwest::Error) -> Self {
if err.is_timeout() {
AppError::RequestTimeout {
message: "HTTP 请求超时".to_string(),
}
} else if err.is_connect() {
AppError::NetworkError {
message: "网络连接失败".to_string(),
}
} else if let Some(status) = err.status() {
AppError::HttpRequestFailed {
status: status.as_u16(),
message: err.to_string(),
}
} else {
AppError::NetworkError {
message: err.to_string(),
}
}
}
}
impl From<serde_json::Error> for AppError {
fn from(err: serde_json::Error) -> Self {
if err.is_syntax() {
AppError::JsonFormatError {
message: "JSON 语法错误".to_string(),
}
} else if err.is_data() {
AppError::DeserializationFailed {
message: err.to_string(),
}
} else {
AppError::SerializationFailed {
message: err.to_string(),
}
}
}
}
impl From<std::io::Error> for AppError {
fn from(err: std::io::Error) -> Self {
match err.kind() {
std::io::ErrorKind::NotFound => AppError::FileNotFound {
path: "未知路径".to_string(),
},
std::io::ErrorKind::PermissionDenied => AppError::FilePermissionDenied {
path: "未知路径".to_string(),
},
_ => AppError::FileOperationFailed {
message: err.to_string(),
},
}
}
}
impl From<tokio::time::error::Elapsed> for AppError {
fn from(_: tokio::time::error::Elapsed) -> Self {
AppError::RequestTimeout {
message: "操作超时".to_string(),
}
}
}
/// 错误构建器
pub struct ErrorBuilder {
error: AppError,
}
impl ErrorBuilder {
pub fn new(error: AppError) -> Self {
Self { error }
}
pub fn with_details(mut self, details: serde_json::Value) -> Self {
// 这里可以扩展错误以包含更多详情
self
}
pub fn with_field(mut self, field: String) -> Self {
// 设置错误字段
self
}
pub fn build(self) -> AppError {
self.error
}
}
/// 错误宏
#[macro_export]
macro_rules! app_error {
($error_type:ident, $($field:ident = $value:expr),*) => {
AppError::$error_type {
$($field: $value.into()),*
}
};
}
/// 结果扩展 trait
pub trait ResultExt<T> {
/// 将错误转换为 AppError
fn map_app_error<F>(self, f: F) -> AppResult<T>
where
F: FnOnce() -> AppError;
/// 添加错误上下文
fn with_context<F>(self, f: F) -> AppResult<T>
where
F: FnOnce() -> String;
}
impl<T, E> ResultExt<T> for Result<T, E>
where
E: Into<AppError>,
{
fn map_app_error<F>(self, f: F) -> AppResult<T>
where
F: FnOnce() -> AppError,
{
self.map_err(|_| f())
}
fn with_context<F>(self, f: F) -> AppResult<T>
where
F: FnOnce() -> String,
{
self.map_err(|e| {
let context = f();
match e.into() {
AppError::InternalServerError { message } => AppError::InternalServerError {
message: format!("{}: {}", context, message),
},
other => other,
}
})
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_error_codes() {
let error = AppError::NotFound("用户".to_string());
assert_eq!(error.error_code(), "NOT_FOUND");
assert_eq!(error.status_code(), StatusCode::NOT_FOUND);
}
#[test]
fn test_error_response() {
let error = AppError::ValidationFailed {
field: "email".to_string(),
message: "格式无效".to_string(),
};
let response = error.to_error_response(Some("req-123".to_string()));
assert_eq!(response.error.code, "VALIDATION_FAILED");
assert_eq!(response.error.field, Some("email".to_string()));
assert_eq!(response.request_id, Some("req-123".to_string()));
}
#[test]
fn test_error_conversion() {
let db_error = sea_orm::DbErr::RecordNotFound("test".to_string());
let app_error: AppError = db_error.into();
match app_error {
AppError::NotFound(_) => assert!(true),
_ => assert!(false, "Expected NotFound error"),
}
}
#[test]
fn test_error_macro() {
let error = app_error!(ValidationFailed,
field = "username",
message = "用户名已存在"
);
match error {
AppError::ValidationFailed { field, message } => {
assert_eq!(field, "username");
assert_eq!(message, "用户名已存在");
}
_ => assert!(false, "Expected ValidationFailed error"),
}
}
#[test]
fn test_result_ext() {
let result: Result<i32, &str> = Err("test error");
let app_result = result.map_app_error(|| AppError::InternalServerError {
message: "转换错误".to_string(),
});
assert!(app_result.is_err());
}
}
```
## 错误处理策略
### 错误传播
```rust
/// 错误传播示例
pub async fn create_user(req: CreateUserReq) -> AppResult<UserDoc> {
// 验证请求
validate_create_user_request(&req)?;
// 检查用户是否已存在
if user_exists(&req.email).await? {
return Err(AppError::AlreadyExists("用户邮箱已存在".to_string()));
}
// 创建用户
let user = User::create(req).await
.with_context(|| "创建用户失败")?;
Ok(user.into())
}
```
### 错误恢复
```rust
/// 错误恢复示例
pub async fn get_user_with_fallback(id: &str) -> AppResult<UserDoc> {
// 首先尝试从缓存获取
match get_user_from_cache(id).await {
Ok(user) => return Ok(user),
Err(AppError::CacheError(_)) => {
// 缓存错误,尝试从数据库获取
tracing::warn!("缓存获取用户失败,尝试数据库");
}
Err(e) => return Err(e),
}
// 从数据库获取
let user = get_user_from_db(id).await?;
// 尝试更新缓存(忽略错误)
if let Err(e) = set_user_cache(id, &user).await {
tracing::warn!(error = %e, "更新用户缓存失败");
}
Ok(user)
}
```
### 错误聚合
```rust
/// 错误聚合示例
pub struct ValidationErrors {
pub errors: Vec<AppError>,
}
impl ValidationErrors {
pub fn new() -> Self {
Self { errors: Vec::new() }
}
pub fn add(&mut self, error: AppError) {
self.errors.push(error);
}
pub fn is_empty(&self) -> bool {
self.errors.is_empty()
}
pub fn into_result(self) -> AppResult<()> {
if self.errors.is_empty() {
Ok(())
} else {
// 返回第一个错误,或者可以创建一个聚合错误类型
Err(self.errors.into_iter().next().unwrap())
}
}
}
pub fn validate_user_data(data: &CreateUserReq) -> AppResult<()> {
let mut errors = ValidationErrors::new();
if data.email.is_empty() {
errors.add(AppError::MissingField { field: "email".to_string() });
}
if data.password.len() < 8 {
errors.add(AppError::ValidationFailed {
field: "password".to_string(),
message: "密码长度至少8位".to_string(),
});
}
errors.into_result()
}
```
## 中间件集成
### 错误处理中间件
```rust
use axum::{
extract::Request,
middleware::Next,
response::Response,
};
use uuid::Uuid;
/// 错误处理中间件
pub async fn error_handler_middleware(
mut request: Request,
next: Next,
) -> Response {
// 生成请求 ID
let request_id = Uuid::new_v4().to_string();
request.extensions_mut().insert(request_id.clone());
// 执行请求
let response = next.run(request).await;
// 如果响应是错误,添加请求 ID
if response.status().is_client_error() || response.status().is_server_error() {
// 这里可以修改响应头,添加请求 ID
tracing::info!(
target = "udmin",
request_id = %request_id,
status = %response.status(),
"request.completed.error"
);
}
response
}
```
## 监控和告警
### 错误指标收集
```rust
use prometheus::{Counter, Histogram, Registry};
use std::sync::Arc;
/// 错误指标收集器
#[derive(Clone)]
pub struct ErrorMetrics {
error_counter: Counter,
error_duration: Histogram,
}
impl ErrorMetrics {
pub fn new(registry: &Registry) -> Self {
let error_counter = Counter::new(
"app_errors_total",
"Total number of application errors"
).unwrap();
let error_duration = Histogram::new(
"error_handling_duration_seconds",
"Time spent handling errors"
).unwrap();
registry.register(Box::new(error_counter.clone())).unwrap();
registry.register(Box::new(error_duration.clone())).unwrap();
Self {
error_counter,
error_duration,
}
}
pub fn record_error(&self, error: &AppError) {
self.error_counter.inc();
// 可以根据错误类型添加标签
tracing::info!(
target = "udmin",
error_code = %error.error_code(),
error_type = %std::any::type_name::<AppError>(),
"metrics.error.recorded"
);
}
}
```
## 最佳实践
### 错误设计原则
1. **明确性**: 错误消息应该清楚地说明发生了什么
2. **可操作性**: 错误消息应该告诉用户如何解决问题
3. **一致性**: 相同类型的错误应该有一致的格式和处理方式
4. **安全性**: 不要在错误消息中泄露敏感信息
### 错误处理模式
1. **快速失败**: 尽早检测和报告错误
2. **优雅降级**: 在可能的情况下提供备选方案
3. **错误隔离**: 防止错误在系统中传播
4. **错误恢复**: 在适当的时候尝试从错误中恢复
### 日志记录
1. **结构化日志**: 使用结构化格式记录错误信息
2. **上下文信息**: 包含足够的上下文信息用于调试
3. **敏感信息**: 避免在日志中记录敏感信息
4. **日志级别**: 根据错误严重程度选择合适的日志级别
## 总结
UdminAI 的错误处理模块提供了完整的错误管理解决方案,具有以下特点:
- **类型安全**: 编译时错误类型检查
- **统一处理**: 所有错误使用统一的类型和格式
- **用户友好**: 清晰的错误消息和适当的 HTTP 状态码
- **可观测性**: 完整的错误日志和指标收集
- **可扩展性**: 易于添加新的错误类型和处理逻辑
通过合理的错误处理设计,确保了系统的稳定性、可维护性和用户体验。