GO错误处理【4】报错即链条

上文你提到了"报错即链条",能不能清楚描述一下什么意思,Go的return err和Java/PHP的try-catch为什么要搞这么大差别?

这个弯儿确实得绕一下。Java/PHP 的异常是"横冲直撞"的,而 Go 的错误是"接力棒"。

我用一个"楼下保安抓小偷"的故事,一次性给你讲透"包装"和"链条":

1. 场景:数据库连接断了(小偷进屋了)

第一步:Repo 层(发现异常的现场保安)

保安发现门锁被撬了,但他不知道小偷想干嘛(是偷钱还是偷资料?)。他不能自己在那喊,他得汇报。

  • 做法:把现场情况"包装"一下,往上传。

    // internal/repo/user.go
    func (r *UserRepo) GetByID(id int) (*User, error) {
    err := r.pool.QueryRow(...)
    if err != nil {
    // 【错误包装】:%w 就像是在证据袋里装了原件,外面贴了张纸条说明
    return nil, fmt.Errorf("数据库底层报错 (ID:%d): %w", id, err)
    }
    return user, nil
    }

第二步:Logic 层(保安队长)

队长接到消息,他知道这个 ID 是"大客户"。他需要增加业务信息,继续往上传。

  • 做法:不打日志,继续包装。

    // internal/logic/user.go
    func (l *UserLogic) UserProfile(id int) (*User, error) {
    user, err := l.repo.GetByID(id)
    if err != nil {
    // 【再次包装】:叠罗汉,加上业务层的描述
    return nil, fmt.Errorf("查询大客户资料失败: %w", err)
    }
    return user, nil
    }

第三步:Middleware/Handler(总指挥中心)

终于到了处理问题的地方了。这里是链条的终点。

  • 做法:记录日志,并告诉前端(报信)。

    // internal/handler/user.go
    func GetUserHandler(w http.ResponseWriter, r *http.Request) {
    user, err := logic.UserProfile(123)
    if err != nil {
    // 【终结错误】:在这里,我们才真正调用 infra.Log
    // 因为 err 里包含了刚才每一层包装的信息,打印出来长这样:
    // "查询大客户资料失败: 数据库底层报错 (ID:123): connection refused"
    infra.Log("api").Error("请求失败", "err", err.Error())

    复制代码
          res.ApiError(w, 500, 10001, "系统忙") // 给用户的只有一句话
          return
      }
      res.ApiSuccess(w, user)

    }


2. 为什么这样是"最优解"?

概念 形象比喻 实际作用
错误包装 (%w) 叠罗汉 让错误在每一层都带上"当时在干嘛"的信息,而不是只有一个干巴巴的 connection refused
报错即链条 接力棒 保证一个错误只在终点打一次日志。避免你的日志文件里,同一个错误在 Repo 出现一次、Logic 出现一次、Handler 又出现一次。

3. "包装"的终极威力

当你在日志里看到一串:
查询大客户资料失败: 数据库底层报错 (ID:123): connection refused

你会瞬间明白:

  1. 最底层:网络不通(connection refused)。
  2. 中间层:是在查 ID 为 123 的时候崩的。
  3. 业务层:这是大客户模块的问题,得赶紧修!

4. 总结你的疑惑

  • 问:日志那里不是白写了?
    • 答:没白写!日志函数 (infra.Log) 是"笔",包装 (%w) 是"墨"。你以前是在每张纸上写一个字(到处打日志),现在是把所有信息聚成一句话,最后在这张纸上写个大的(只在终点打一次)。
  • 问:我要再写一次这个?
    • 答:不需要。你只需要在 Repo 和 Logic 里写 fmt.Errorf,只在 Middleware 或 Handler 里写一次 Log().Error()
相关推荐
学习星球17 小时前
# 6G通感一体化(ISAC)技术深度解析——从原理到实战> <br />
go·信息与通信·媒体
小满zs20 小时前
Go语言第十章(指针)
后端·google·go
学习星球2 天前
6G核心网架构深度解析——AI Native时代的网络变革
网络·人工智能·5g·架构·go·信息与通信
ylj_dev3 天前
从 0 构建 AI Workload Platform(九):真实场景、最小控制台与开源发布
go·react·开源项目·工作流·ai agent
tyung3 天前
znet 数据编解码:字节流怎么变成消息
后端·网络协议·go
leeyi3 天前
数据库迁移不翻车:golang-migrate 实战,143 个 DDL 有序执行(第97篇-E83)
go·aigc·agent
用户330144867633 天前
mheap:全局堆管理器
go
用户330144867633 天前
10. Large 对象分配:直接路径
go
yinchnag3 天前
CRTP:奇异递归模板模式在 Go 模块系统中的实现
设计模式·go
ylj_dev4 天前
从 0 构建 AI Workload Platform(七):可观测性、故障注入与性能验证
go·prometheus·可观测性·opentelemetry·故障注入