GO错误处理【3】返回err与日志的结合

我需要在每处返回err的地方加上日志吗?

日志需要在哪个地方使用,与err如何配套使用

我现在的目录结构是pkg与 internal

pkg下面有config\infra\utils

internal 下面有cron\handler\middleware\logic\repo\model
各地方都有可能报错,日志是不是也要加在报错返回之前?日志如何使用

针对"日志谁来打"的问题,有一个最清晰的判定准则:谁负责"终结"这个错误,谁就负责打日志。

1. 为什么 repomodel 绝对不打日志?

repo(数据库操作)层,它只负责执行 SQL。如果执行失败了,它并不知道这个失败对业务意味着什么。

  • 做法:直接 return fmt.Errorf("repo: 查询用户信息失败: %w", err)
  • 理由:如果你在 repo 打印了日志,而上层 logic 发现这个错误其实可以通过读缓存补偿,从而忽略了错误,那你的日志里就会多出一条"虚假报警"。

2. 谁该打日志?(终结者们)

A. Middleware(中间件)------ 必打!

它是所有请求的最后一道防线。

  • 职责:记录所有的 HTTP 访问日志,以及 Recovery 捕获到的程序崩溃。
  • 逻辑:它拿到了最终的结果,所以它记录的是"全局视角"。

B. Cron(定时任务)------ 必打!

定时任务没有前端页面,也没有 Response。

  • 职责:如果 logic 报错返回给 cron 了,cron 就是这个错误的终结者。
  • 逻辑:它必须把错误记下来,否则没人知道后台任务失败了。

C. Handler(控制器)------ 选打(视情况而定)

  • 职责:如果一个错误 logic 处理不了,传到了 handlerhandler 准备给前端回一个 ApiError
  • 建议:如果你已经在 middleware 里记录了所有带错误信息的访问日志,那么 handler 其实可以不打,省得重复。但如果是特殊的业务警告,可以在这里打。

D. Logic(业务逻辑)------ 关键逻辑打

  • 职责:这里是处理业务的地方。
  • 场景:比如"用户支付失败",这种事关金钱的重大业务异常,即使你还没把错误返回给上层,也建议在这里打一条结构化日志,方便以后对账。

3. "报错即链条"示意图

想象一个请求的传递:

  1. Repo: 发现连接断了,return %w
  2. Logic: 收到报错,由于是关键业务,infra.Log("pay").Error("支付中断", "err", err),然后继续 return %w
  3. Handler: 收到报错,调用 res.ApiError(w, 500, ...) 返回给前端。
  4. Middleware: 最后的 Access Log 记录下:method=POST status=500 err="logic: pay fail: repo: conn broken"

结果:你的日志文件里,一条是详细的业务告警,一条是总体的 HTTP 记录。没有废话,全是干货。

相关推荐
唐青枫1 小时前
别再把 struct 只当字段集合:Go 结构体从语法到项目实战
go
程序员老邢9 小时前
《技术底稿 46》AI 解构成果→知识库自动化同步管道 设计与落地总结
架构设计·异步任务·数据同步·后端开发·幂等性·技术底稿
码农飞哥9 小时前
Spring Boot 多角色权限隔离实战:接口层+路由层+UI层三层防御,杜绝生产数据泄露
spring boot·状态模式·架构设计·系统设计·权限控制
踏着七彩祥云的小丑12 小时前
Go学习第4天:条件、循环语句+函数
学习·golang·go
tyung1 天前
Go 手写 Wait-Free SPSC 无界队列:无 CAS、无锁、泛型节点池
数据结构·后端·go
踏着七彩祥云的小丑2 天前
Go学习第3天:变量+常量+运算符
开发语言·学习·golang·go
brycegao3213 天前
Android MVI进阶:纯原生实现Slot化可插拔架构
android·kotlin·架构设计·mvi·viewmodel
踏着七彩祥云的小丑3 天前
Go学习第2天:程序结构+基础语法+数据类型
开发语言·学习·golang·go
吴佳浩3 天前
AI Infra 的真相:Go 没输,rust也不是取代
后端·rust·go