自定义异常应显式声明业务字段并在__init__中赋值,避免滥用args;java需标记transient并重写序列化方法;go宜组合error接口与结构体;node.js须继承error类并正确处理stack。

Python 自定义异常类怎么安全添加业务字段
直接在异常类里加 __init__ 并赋值字段是最稳妥的做法,别试图往 args 里塞结构化数据——它只保证是 tuple,且部分框架(如 logging、pytest)会直接展开或截断,导致元数据丢失或格式错乱。
常见错误现象:抛出异常后,用 exc.my_order_id 访问字段报 AttributeError;或者日志里只看到一串数字,看不出是订单号还是用户 ID。
- 继承
Exception,显式声明业务字段(如order_id、trace_id),并在__init__中赋值 - 避免重写
__str__或__repr__时拼接敏感字段(比如把完整 token 打进日志) - 如果需兼容老代码依赖
str(exc)的逻辑,可在__str__中返回精简摘要,而非原始字段值
class PaymentFailedError(Exception):
def __init__(self, order_id: str, amount: float, trace_id: str = None):
super().__init__(f"Payment failed for order {order_id}")
self.order_id = order_id
self.amount = amount
self.trace_id = trace_id
Java Throwable 子类如何避免字段被序列化干扰
Java 里直接加 public 字段看似方便,但反序列化时可能因字段缺失、类型不匹配或 serialVersionUID 变更导致 InvalidClassException;更隐蔽的问题是某些 RPC 框架(如 Dubbo)默认只传 message 和 cause,业务字段根本到不了下游。
使用场景:微服务间异常透传、审计日志需要关联工单号、熔断降级依据错误码分类。
- 所有业务字段必须标记为
transient,防止被默认序列化机制污染 - 重写
writeObject和readObject,手动控制字段的序列化/反序列化逻辑 - 若用 JSON 通信(如 Spring REST),优先实现
Serializable+@JsonInclude(JsonInclude.Include.NON_NULL),避免空字段干扰
public class InventoryShortageException extends RuntimeException implements Serializable {
private static final long serialVersionUID = 1L;
private final transient String skuCode;
private final transient int requestedQty;
private void writeObject(ObjectOutputStream s) throws IOException {
s.defaultWriteObject();
s.writeUTF(skuCode);
s.writeInt(requestedQty);
}
private void readObject(ObjectInputStream s) throws IOException, ClassNotFoundException {
s.defaultReadObject();
this.skuCode = s.readUTF();
this.requestedQty = s.readInt();
}
}
Go error 类型怎样携带结构化上下文而不破坏接口兼容性
Go 的 error 是接口,强行加字段会破坏鸭子类型;用 fmt.Errorf 拼字符串又没法结构化解析。正确做法是组合 error 接口 + 自定义结构体,再通过类型断言提取元数据。
容易踩的坑:用 errors.Wrap 包装后,原错误类型信息丢失,断言失败;或者把元数据存在闭包里,导致 panic 时无法访问。
- 定义结构体同时实现
Error()方法和业务字段(如OrderID、ErrorCode) - 对外暴露类型断言辅助函数(如
AsInventoryError(err)),降低调用方判断成本 - 避免在
Error()返回值里嵌套 JSON 或复杂结构——日志系统通常只取字符串,可读性反而下降
type InventoryError struct {
OrderID string
SKU string
ErrorCode string
}
func (e *InventoryError) Error() string {
return fmt.Sprintf("inventory error %s for order %s", e.ErrorCode, e.OrderID)
}
func AsInventoryError(err error) (*InventoryError, bool) {
e, ok := err.(*InventoryError)
return e, ok
}
Node.js 错误对象扩展字段为什么不能只靠 Object.assign
JavaScript 的 Error 构造函数生成的对象有特殊内部属性(如 stack),用 Object.assign 或展开运算符复制只会拷贝可枚举属性,stack 会丢失,且新对象不再被 instanceof Error 识别,导致 catch 分支失效。
性能影响:每次抛异常都新建带原型链的实例比纯对象慢,但比起网络 I/O 或 DB 查询几乎可忽略;真正要防的是字段名冲突(比如业务字段叫 message,覆盖了原错误提示)。
- 必须用
Object.setPrototypeOf显式挂载原型,或继承Error类 - 字段命名避开
message、name、stack等内置属性 - 若需跨域传递(如 SSR 渲染时抛错),记得手动序列化
stack字段,否则客户端收不到调用栈
class ApiRequestError extends Error {
constructor(message, { requestId, statusCode } = {}) {
super(message);
this.name = 'ApiRequestError';
this.requestId = requestId;
this.statusCode = statusCode;
// 确保 stack 正确生成
if (Error.captureStackTrace) {
Error.captureStackTrace(this, ApiRequestError);
}
}
}
业务元数据不是越多越好,关键字段得能被监控系统自动提取、被告警规则匹配、被前端友好展示。字段命名要统一(比如全用 trace_id 而不是混用 traceId、x-trace-id),否则下游解析时还得写一堆映射逻辑。










