异常需区分cause(根本原因,用于异常链和栈追踪)与context(执行上下文,用于诊断日志);cause须通过构造函数显式设置,context应作为只读字段存于自定义异常中并结构化输出。
异常对象携带额外上下文信息,关键在于区分 cause(根本原因) 和 context(执行上下文):前者用于构建异常链、支持栈追踪回溯;后者用于辅助诊断、日志记录或调试,但不参与异常传播逻辑。
当一个异常由另一个异常引发时,应通过构造函数的 cause 参数(如 Java 的 Throwable(Throwable cause),Python 的 raise NewError() from original_exc)显式关联。这会让运行时保留原始异常的完整栈,调用 getCause() 或 __cause__ 可获取它,打印异常时也会自动显示 “Caused by”。
throw new IllegalArgumentException("Invalid user ID", originalException);
raise ValueError("Processing failed") from exc
cause 解决“为什么发生”,context 解决“当时发生了什么”。推荐在自定义异常类中添加只读字段(如 userId, requestId, inputData),初始化时传入并存为实例属性。
public final String requestId;,在构造器中赋值__init__ 中设 self.request_id = request_id
toString() 或 __str__ 时包含这些字段,方便日志直接输出上下文把本该是 context 的信息(如用户ID、时间戳)当作 cause 抛出,会导致异常链失真;反过来,把底层 I/O 异常仅作为 context 记录而不设为 cause,则丢失关键根因线索。
IOException 后创建新异常时,用 message += "user=123" 而不设 causeIOException 为 cause 构造新异常,并单独保存 userId 字段new RuntimeException(original, requestId)——把 con
cause(父异常)和 requestId(context 字段)异常对象中的 context 字段本身不自动出现在日志里,需主动提取。在全局异常处理器或日志 AOP 中,检查异常是否含自定义 context 属性,并将其作为结构化字段(如 MDC、log attributes)注入日志事件。
MDC.put("requestId", e.getRequestId())