log.Panic会打印堆栈且可recover,log.Fatal不打印堆栈且不可拦截;前者触发panic机制并执行defer,后者调用os.Exit直接退出、丢弃所有defer。

log.Panic 会打印堆栈,log.Fatal 不会
这是最直观、也最容易被忽略的差异。当你调用 log.Panic("msg"),它底层先调用 panic("msg");而 log.Fatal("msg") 底层是 fmt.Print + os.Exit(1)。前者触发 Go 的 panic 机制,后者绕过整个运行时异常流程。
结果就是:log.Panic 输出里一定包含完整的 goroutine 堆栈(从 panic 调用点开始向上追溯),而 log.Fatal 只输出一行日志,后面直接退出,没有任何调用链信息。
log.Panic 可被 recover 捕获,log.Fatal 完全不可拦截
log.Panic 是对 panic 的封装,所以它遵循 Go 的 panic/recover 语义;log.Fatal 是对 os.Exit 的封装,一旦执行,程序瞬间终止,连 defer 都不跑,更别说 recover。
这意味着:
立即学习“go语言免费学习笔记(深入)”;
- 如果你在测试中 mock 或拦截错误出口,
log.Panic可以用recover捕获并验证行为;log.Fatal会让测试直接挂掉,无法断言 - 在 CLI 工具或服务初始化阶段,误用
log.Fatal可能导致无法优雅降级(比如配置加载失败时本想 fallback,却直接退出) -
log.Panic的 recover 能力只在当前 goroutine 有效;跨 goroutine 的 panic 无法被外部 recover 捕获
两者都跳过 defer,但原因完全不同
表面上看,log.Fatal 和 log.Panic 都会让后续代码不执行,但 defer 的命运截然不同:
-
log.Fatal→ 调用os.Exit(1)→ 程序立即终止 → 所有未执行的defer全部丢弃(包括当前函数和调用链上的) -
log.Panic→ 触发panic→ 当前函数的defer会按逆序执行 → 然后向上传播,直到顶层未 recover → 此时才真正退出
所以如果你写了 defer close(fd),又用了 log.Panic,文件描述符大概率会被正常关闭;换成 log.Fatal,就真漏了。
实际选型:看要不要堆栈 + 能不能容错
大多数情况下,你应该避免在业务逻辑里用这两个函数。但如果必须非正常退出:
- 用
log.Panic:需要定位问题根源(比如 config 解析失败、数据库 schema 校验失败),且希望保留 panic 堆栈供排查;或者你明确会在某一层recover并转成用户友好的错误提示 - 用
log.Fatal:仅限程序启动期的致命错误,比如监听端口失败、环境变量缺失、证书文件读取失败——这些错误没有恢复价值,也不该被上层捕获,要让进程干净退出 - 别用
log.Panic处理可预期错误(如 HTTP 请求超时),那属于业务错误,该返回 error,不该 panic
真正容易被忽略的是:log.Panic 的堆栈默认输出到 stderr,但不会自动 flush;如果程序退出极快(比如在 init 函数里 panic),部分堆栈可能还没刷出就终止了——这时得手动加 os.Stderr.Sync() 或改用自定义 logger。
文章来自机圈观察员网,发布者:,转载请注明出处:https://www.jqgcy.com/jiquanzatan/126699.html