日志与错误在大型项目中的规范

面试问法:大型 GoFrame 项目里,日志和错误怎么打才不乱?有没有分层规范? 一句话结论:日志按“请求入口/业务/错误”分层,统一中间件记访问日志与错误堆栈,业务日志全程带 ctx(自动挂 TraceId);错误一律错误码化(gcode + gerror)抛给上层,展示文案与内部信息分离。规范的核心是:谁打、在哪打、打到什么级别、什么能落盘。

日志分层约定

内容落点
访问日志method/path/status/耗时/IP/uid全局后置中间件,一行一条
业务日志关键业务动作与上下文service 内 g.Log().Ctx(ctx).Infof
错误日志err + gerror 堆栈 + code统一响应中间件记一次,业务不重复打
启动/任务日志初始化、定时任务执行cmd 与任务入口
  • 级别纪律:Debug 只在开发;Info 记业务主流程;Warn 记可降级异常;Error 记需要人看的错误——每类日志都别刷屏。
  • 敏感信息:密码、Token、身份证等一律脱敏或禁止落盘;不要原样打整个请求体。
  • 存储:按模块/服务拆文件并轮转(glog 提供文件输出与轮转配置,能力以官方文档为准),或直接对接采集系统。

错误规范

// 1. 只抛业务码,不吞错误、不打印裸错误字符串
if user == nil {
    return nil, gerror.NewCode(code.UserNotExist)
}

// 2. 需要追加上下文时用 gerror.Wrap 保留原始错误与堆栈
if err := dao.Xxx.Ctx(ctx).Update(); err != nil {
    return nil, gerror.Wrap(err, "update order failed")
}

// 3. 统一出口:响应中间件取码输出,错误堆栈只进日志
if err := r.GetError(); err != nil {
    c := gerror.Code(err)
    g.Log().Errorf(r.Context(), "%+v", err)
    r.Response.WriteJson(...)
}
  • 不要在循环里打错误日志(最终统一出口记一次即可);panic 用恢复中间件兜底并记录堆栈。
  • 给用户看的 message 要友好稳定,内部 detail/code 用于排查;文案变化不破坏接口契约。

常见追问

  • 追问:TraceId 怎么进日志?——保证 ctx 贯穿,链路开启后日志组件自动输出 TraceId 片段(见链路追踪章节),查询系统按它捞全链路日志。
  • 追问:日志性能怎么办?——热路径避免大对象序列化进日志、控制级别开关、异步写盘能力视版本而定(以官方文档为准),超大规模再上采样与结构化采集。

记忆点

  • 一层请求一层错误一层业务;日志带 ctx、错误带 code;展示与排查信息分离。
笔记加载中…