

新闻资讯
技术学院Go error不可跨服务传播,需用结构化ErrorResponse;勿用fmt.Errorf包装远程错误;日志须用.Err(err)保留链路;自定义错误必须实现Unwrap()和Error()以支持errors.Is/As判定。
error 类型不适合跨服务传播微服务间通信依赖序列化(如 JSON、Protobuf),而 Go 原生 error 是接口类型,无法直接编码。常见错误是把 err 直接塞进 HTTP 响应体或 gRPC 返回值,结果得到空对象或 panic。
json.Marshal(err) 总是返回 null,因为 error 接口没有导出字段proto 定义外硬塞 err.Error(),会丢失堆栈、码、上下文等关键信息ErrorResponse{Code: "INVALID_INPUT", Message: "...", Details: map[string]interface{}{}}
fmt.Errorf 包装远程调用错误对下游服务的 HTTP/gRPC 调用失败时,原错误往往已含状态码、超时标识、重试建议等语义。用 fmt.Errorf("failed to call X: %w", err) 会抹掉这些信息,只剩字符串描述。
errors
.Is(err, context.DeadlineExceeded) 在包装后失效 —— %w 不传递底层类型断言能力func ToServiceError(err error) *ServiceError {
if errors.Is(err, context.DeadlineExceeded) {
return &ServiceError{Code: "TIMEOUT", HTTPStatus: 408}
}
if httpErr, ok := err.(*url.Error); ok && httpErr.Err != nil {
return &ServiceError{Code: "CONNECTION_FAILED", Cause: httpErr.Err}
}
return &ServiceError{Code: "UNKNOWN", Raw: err}
}TIMEOUT 重试)err.Error()
微服务排查依赖链路追踪和集中日志。仅记录 err.Error() 会让问题定位退化成“猜谜”——没有堆栈、没有调用路径、没有原始错误类型。
log.With().Err(err).Msg("failed to process order")(如 zerolog/zap),它会自动展开 Unwrap() 链并保留字段log.Printf("error: %v", err) —— 它忽略所有额外字段,且不格式化嵌套错误logger.Error().Str("trace_id", traceID).Str("span_id", spanID).Err(err).Msg("payment declined")Unwrap() 和 Error()
Go 1.13 引入的错误链机制依赖 Unwrap(),但很多团队写的 ServiceError 只实现了 Error(),导致 errors.Is() 和 errors.As() 失效。
error:如果当前错误封装了底层错误,Unwrap() 应返回它;否则返回 nil
Error() 中拼接 Unwrap().Error() —— 这会导致重复打印,且破坏错误链遍历type ServiceError struct {
Code string
Message string
Cause error
}
func (e *ServiceError) Error() string { return e.Message }
func (e *ServiceError) Unwrap() error { return e.Cause }errors.Is(err, MyTimeout) 成立,否则熔断、重试、告警策略全都会失效。