Go 返回错误时,多补一层上下文比多打一行日志更值
刚开始写 Go 的时候,很多人会觉得错误处理很重复:每层都要判断 err != nil,看起来很机械。可真到了线上排查问题时,最怕的不是判断多,而是最后拿到的错误只有一句 “open failed” 或 “query error”,根本不知道是哪一步出的事。
错误值真正的价值,是把失败路径串起来
Go 没有异常栈帮你自动把上下文全带出来,所以每次把错误往上返回时,最好顺手补上当前操作的信息。读配置失败、解析请求失败、查询数据库失败,这些都是“错了”,但排查入口完全不同。
如果每层都只原样把底层错误扔上去,最后在接口层看到的往往还是一个非常短的句子。问题不是不能继续查,而是每次都要重新进代码里猜调用路径,效率会很差。
日志不要到处打,关键路径要把语义补够
很多项目喜欢在每一层都先打日志,再把错误继续返回。这样短期似乎很安心,长期却容易把日志打成重复瀑布,一次失败要看五六条相似信息。相比之下,我更认同在错误值上补足语义,在真正需要落盘或打点的边界层再统一记录。
比如“读取配置失败”“初始化数据库连接失败”“保存订单失败”,这种描述比简单打印一堆变量更有用。因为它直接说明当前步骤做什么、失败点在哪里,调用方拿到以后也更容易决定该怎么处理。
业务错误和系统错误要尽量分开想
不是所有错误都应该一路包到最上层。像参数校验失败、余额不足、重复提交,这类更接近业务分支的情况,往往需要在接口层转换成明确的用户反馈。数据库超时、文件不存在、第三方接口不可用,则更适合保留技术上下文,方便排查。
如果这两类错误都只用一句字符串随便表示,调用方很难区分“应该提示用户重试”还是“应该报警并排查依赖”。错误虽然只是一个返回值,但它背后其实承载着程序的决策信息。
写错误信息时,先想排查的人下一步要看哪里
我现在写错误信息,会优先问自己一个问题:别人看到这句话以后,下一步应该去查哪一层。如果答案仍然是不知道,那这条错误信息大概率还不够用。
Go 的错误处理不追求花哨,追求的是可追踪。多补一层上下文,往往就能少翻很多无谓日志。对协作开发来说,这种价值比“代码看起来简洁一点”更实际。
