本规范基于项目现有代码实践,规定了日志记录和错误处理的统一标准。所有日志和错误处理必须遵循现有代码的实现方式,确保一致性和可维护性。
在类中声明 Logger 实例:
private static final Logger logger = LoggerFactory.getLogger(ClassName.class);
src/main/resources/logback-spring.xml。${LOG_PATH}/app.log,默认 ./logs/app.log。日志格式:%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n。
在 application.yml 中配置日志级别:
logging:
level:
root: info
work.baiyun.chronicdiseaseapp: debug
logger.debug("validateToken: token={}, userToken={}", token, ut)。logger.error("Error while calling Weixin API", e)。@RestControllerAdvice 注解的 CustomExceptionHandler 类统一处理异常。R<T>,包含错误码和消息。CustomException 在代码中继承自 RuntimeException(运行时异常),全局异常处理器会捕获并转换为统一 R.fail() 响应。构造函数接受消息字符串:
public class CustomException extends RuntimeException {
public CustomException(String message) {
super(message);
}
}
错误码采用数字编码方式,每个错误码由5位数字组成:
| 错误码 | 错误信息 | 说明 |
|---|---|---|
| 1000 | 系统内部错误 | 未预期的系统错误 |
| 1001 | 参数错误 | 请求参数不合法或缺失 |
| 1002 | 未授权访问 | 缺少有效的身份认证信息 |
| 1003 | 权限不足 | 当前用户无权执行该操作 |
| 1004 | 资源不存在 | 请求的资源未找到 |
| 1005 | 请求方法不被允许 | 使用了不支持的HTTP方法 |
| 错误码 | 错误信息 | 说明 |
|---|---|---|
| 2000 | 用户不存在 | 指定的用户ID找不到对应用户 |
| 2001 | 用户密码错误 | 提供的用户密码不正确 |
| 2002 | 用户未登录 | 用户会话已过期或未登录 |
| 2003 | 用户已存在 | 尝试创建已存在的用户 |
| 2004 | 用户账户被锁定 | 用户账户因安全原因被锁定 |
| 错误码 | 错误信息 | 说明 |
|---|---|---|
| 3000 | 微信授权码无效 | 提供的微信code参数无效或已过期 |
| 3001 | 获取微信openid失败 | 调用微信接口获取openid时失败 |
| 3002 | 用户角色不能为空 | 创建用户时未指定角色类型 |
| 3003 | 用户角色无效 | 提供的用户角色代码不合法 |
| 错误码 | 错误信息 | 说明 |
|---|---|---|
| 4000 | 数据不存在 | 指定的数据记录未找到 |
| 4001 | 无权访问该数据 | 当前用户无权访问指定的数据 |
| 4002 | 数据删除失败 | 执行数据删除操作时发生错误 |
| 4003 | 数据保存失败 | 执行数据保存操作时发生错误 |
| 错误码 | 错误信息 | 说明 |
|---|---|---|
| 5000 | 地理位置请求失败 | 调用地理位置服务时发生错误 |
| 5001 | 地理位置服务不可用 | 地理位置服务暂时不可用 |
项目中使用ErrorCode枚举类来定义和管理所有错误码,位于work.baiyun.chronicdiseaseapp.enums.ErrorCode。
所有控制器在返回错误响应时,应使用统一的R.fail()方法,并传入相应的错误码和错误信息:
// 正确用法
return R.fail(ErrorCode.PARAMETER_ERROR.getCode(),
ErrorCode.PARAMETER_ERROR.getMessage() + ": Missing code parameter");
// 错误用法(不推荐)
return R.fail(400, "Missing code parameter");
在全局异常处理器或控制器的try-catch块中,应根据具体的异常类型返回相应的错误码:
try {
// 业务逻辑
} catch (IllegalArgumentException e) {
return R.fail(ErrorCode.PARAMETER_ERROR.getCode(),
ErrorCode.PARAMETER_ERROR.getMessage() + ": " + e.getMessage());
} catch (Exception e) {
return R.fail(ErrorCode.SYSTEM_ERROR.getCode(),
ErrorCode.SYSTEM_ERROR.getMessage() + ": " + e.getMessage());
}
CustomException 或记录日志后处理。建议:
{} 直接记录字符串值)。示例:
logger.info("处理记录完成,recordId={}", String.valueOf(record.getId()));
注意:日志中以字符串记录 ID 并不改变数据库或实体类型,仅是记录/展示层的一致性约定。
为便于调用方与日志系统做关联,API 的统一返回结构 R<T> 已新增 timestamp、requestId、traceId 三个字段:
X-Request-Id,如果客户端未设置,服务端会自动生成。该 ID 也会保存在日志的 MDC 中(日志格式中使用 %X{requestId}),便于将一条响应与该请求在日志中关联起来。X-Trace-Id;若未传入,服务端会生成一个 UUID。该字段也写入 MDC(%X{traceId}),便于配合 Zipkin/Jaeger 等链路追踪系统排查问题。示例响应:
{
"code": 1000,
"message": "ok",
"timestamp": 1697028512345,
"requestId": "123e4567-e89b-12d3-a456-426614174000",
"traceId": "abcd1234efgh5678",
"data": { ... }
}
日志例子(logback pattern 建议包含 %X{requestId} 与 %X{traceId}):
2025-11-14 10:00:00.000 [http-nio-8080-exec-1] [123e4567-e89b-12d3-a456-426614174000] [abcd1234efgh5678] INFO work.baiyun.chronicdiseaseapp.service.UserService - user created id=100
注意:
requestId 或 traceId 中携带敏感信息(如 token、身份证号等)。这些字段只用于追踪和关联日志,非鉴权或隐私字段。traceId 与第三方追踪工具应尽量保持一致(例如使用 X-B3-TraceId 或 traceparent)。