docs: 添加项目文档包括总览、架构、流程引擎和服务层

新增以下文档文件:
- PROJECT_OVERVIEW.md 项目总览文档
- BACKEND_ARCHITECTURE.md 后端架构文档
- FRONTEND_ARCHITECTURE.md 前端架构文档
- FLOW_ENGINE.md 流程引擎文档
- SERVICES.md 服务层文档
- ERROR_HANDLING.md 错误处理模块文档

文档内容涵盖项目整体介绍、技术架构、核心模块设计和实现细节
This commit is contained in:
2025-09-24 20:21:45 +08:00
parent 6ff587dc23
commit a3f2f99a68
13 changed files with 11803 additions and 0 deletions

878
docs/ERROR_HANDLING.md Normal file
View File

@ -0,0 +1,878 @@
# 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 状态码
- **可观测性**: 完整的错误日志和指标收集
- **可扩展性**: 易于添加新的错误类型和处理逻辑
通过合理的错误处理设计,确保了系统的稳定性、可维护性和用户体验。