java自定义异常应专注封装错误语义和上下文字段(如orderid、userid),避免在构造器中打日志;特定错误日志应在catch块中结合异常实例与执行上下文,用slf4j占位符+e.getlogcontext()+异常对象统一记录,并可配合mdc实现请求级追踪。

在 Java 中自定义异常时,记录特定错误日志的关键不是在异常类里直接写日志,而是让异常被抛出后,在 捕获位置 结合异常上下文做有针对性的日志记录。自定义异常本身应专注封装错误语义和必要信息,日志行为由调用方控制更灵活、更符合职责分离原则。
自定义异常类:携带关键上下文字段
异常类应提供构造方法,允许传入业务相关的参数(如订单号、用户 ID、操作类型),并在 getMessage() 或新增的 getter 方法中暴露这些信息,方便日志中引用。
- 避免在异常构造器里打日志(会干扰异常传播,且日志时机不可控)
- 推荐使用 final 字段存储上下文,保证不可变性
- 重写 toString() 或提供 toLogString() 方法,便于日志格式统一
public class InsufficientBalanceException extends RuntimeException {
private final String orderId;
private final String userId;
private final BigDecimal requiredAmount;
public InsufficientBalanceException(String userId, String orderId, BigDecimal requiredAmount) {
super("Insufficient balance for user " + userId + ", order " + orderId);
this.userId = userId;
this.orderId = orderId;
this.requiredAmount = requiredAmount;
}
// 提供结构化日志所需的方法
public String getLogContext() {
return String.format("userId=%s, orderId=%s, requiredAmount=%.2f",
userId, orderId, requiredAmount.doubleValue());
}
}
捕获时记录带上下文的错误日志
真正记录“特定错误日志”的地方是 try-catch 块。此时你可以结合异常实例、当前执行变量、请求参数等,拼出高可读、易排查的日志内容。
在 Java 中初始化和管理阿里云 SDK客户端。包括单例模式、线程安全、endpoint 与 region 配置、VPC 终端节点、同步与异步等。
- 使用 SLF4J(推荐)或 Log4j2,用占位符避免字符串拼接
- 用异常对象的结构化方法(如 getLogContext())注入上下文
- 保留原始异常堆栈(用 logger.error(msg, e) 形式),不只打 message
try {
paymentService.charge(orderId, amount);
} catch (InsufficientBalanceException e) {
log.error("Payment failed due to insufficient balance: {}",
e.getLogContext(), e); // 第二个参数传异常,自动打印堆栈
}
配合 MDC 实现请求级日志追踪
如果想让同一请求的所有日志(包括异常前后的普通日志)都带上 traceId、userId 等,可用 MDC(Mapped Diagnostic Context)。在进入请求时写入,在结束时清理。
- 在拦截器或过滤器中 put("traceId", UUID.randomUUID().toString())
- 在异常捕获处无需额外拼接,MDC 字段会自动附加到每条日志行
- logback.xml 中配置 %X{traceId} 即可输出
进阶建议:异常日志分级与告警联动
不是所有异常都要记 ERROR 级别。可根据业务影响区分处理:
- 用户输入错误(如参数校验失败)→ WARN 级别,不告警
- 系统依赖超时、数据库连接失败 → ERROR + 钉钉/邮件告警(通过 AOP 或日志 appender 过滤)
- 在日志中明确标注 “business_error” 或 “system_error” 标签,便于 ELK 聚类分析
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南










