审计日志需在catch中记录失败操作,保留异常传播、区分业务失败与系统异常,并包含用户ID、操作行为、对象标识、时间戳、失败原因及traceId;应避免仅记message、混用error日志、字符串拼接及丢失用户身份。

在 catch 块中记录用户操作失败的审计日志,核心是:**不干扰原有异常传播路径、保留完整上下文、区分“业务失败”与“系统异常”,且日志内容满足审计要求(谁、做了什么、何时、为何失败)**。
明确审计日志的关键字段
一次合规的失败审计日志至少应包含:
- 操作主体:当前登录用户 ID 或用户名(避免仅记 IP)
- 操作行为:如 "删除订单"、"提交退款申请"(建议用业务语义化字符串,非方法名)
- 操作对象:关键标识,如 orderId=12345、userId=67890(脱敏处理敏感字段)
- 发生时间:使用 ISO 格式或带时区的时间戳(如 2024-05-22T14:23:08.123+08:00)
- 失败原因:异常类型 + 精简可读的提示(如 IllegalArgumentException: 退款金额不能为负),不直接打印堆栈(堆栈走 error 日志,审计日志重在归因)
- 请求标识:traceId 或 requestId(用于关联链路日志)
避免在 catch 中直接写日志的常见陷阱
以下做法看似简单,实则不健壮:
- 只记 e.getMessage() → 丢失异常类型和上下文,无法分类分析
- 在 catch 里调用 log.error("xxx", e) → 混淆审计日志与系统错误日志,审计日志不应含堆栈
- 手动拼接字符串记录(如 "用户"+uid+"删除订单"+oid+"失败:"+e)→ 难以结构化解析,易出格式错误
- 未捕获用户身份(如从 ThreadLocal 取用户信息失败)→ 审计日志缺失主体,失去意义
推荐实践:封装 AuditLogger 工具类
定义一个轻量、线程安全、支持 MDC 的审计日志工具:
立即学习“Java免费学习笔记(深入)”;
public class AuditLogger {
private static final Logger auditLog = LoggerFactory.getLogger("AUDIT");
public static void logFailure(String action, String target, String reason) {
// 自动获取:userId(从 SecurityContext)、traceId(从 MDC)、timestamp
String userId = getCurrentUserId();
String traceId = MDC.get("traceId");
String time = Instant.now().toString();
auditLog.info("AUDIT_FAIL | user={} | action={} | target={} | reason={} | trace={} | time={}",
userId, action, target, reason, traceId, time);
}
private static String getCurrentUserId() {
return Optional.ofNullable(SecurityContextHolder.getContext().getAuthentication())
.map(Authentication::getPrincipal)
.map(Object::toString)
.orElse("ANONYMOUS");
}
}
在 service 层 catch 中调用:
try {
orderService.cancelOrder(orderId);
} catch (InvalidOrderStatusException e) {
AuditLogger.logFailure("取消订单", "orderId=" + orderId,
"订单状态不允许取消:" + e.getMessage());
throw e; // 异常继续上抛,不吞掉
}
进阶:结合 AOP 统一处理(更优雅)
对标准 CRUD 操作,可用 @Around 切面自动捕获 Controller/Service 方法失败并审计:
- 用自定义注解(如 @AuditOnFailure(action = "修改用户资料"))标记方法
- 切面在异常时提取参数(通过 JoinPoint 获取 targetId)、捕获用户、组装审计日志
- 无需每个 catch 块手写,减少重复,保证一致性
- 注意:需排除系统级异常(如 NullPointerException),只审计业务预期异常


















