

新闻资讯
行业动态log.Printf 不显示调用栈因不自动捕获堆栈,需用 runtime.Caller 或 zap.Error();zap.Error() 支持错误展开与 nil 安全,优于直接 err.Error();log.Fatal 会终止进程,HTTP handler 中禁用;敏感字段必须脱敏,日志重在可追溯而非全量输出。
log.Printf 记录错误时,为什么看不到调用栈?因为 log.Printf 只做格式化输出,不自动捕获堆栈。你传入的 err 本身(比如 errors.New("xxx"))通常不含调用信息。要看到哪一行出的错,得手动加 runtime.Caller,或者换支持 trace 的日志库。
临时调试可以这样补:
import "runtime" funclogError(err error) { _, file, line, _ := runtime.Caller(1) log.Printf("ERROR [%s:%d] %v", file, line, err) }
runtime.Caller,有性能开销Caller(1) 表示跳过当前函数,取调用方的位置log 没有内置 error 字段结构化能力,err.Error() 是唯一能安全取的字符串zap 记录错误时,zap.Error() 和直接 zap.String("error", err.Error()) 有什么区别?zap.Error() 不只是把 err.Error() 存成字符串——它会尝试展开实现了 fmt.Formatter 或 stackTracer 接口的错误(比如 github.com/pkg/errors 包 wrap 的错误),并保留原始类型信息,方便后期结构化过滤或高亮显示。
推荐写法:
logger.Error("db query failed",
zap.String("query", sql),
zap.Error(err), // ← 这里传 err 本体,不是 err.Error()
zap.String("user_id", userID),
)
err 是 nil,zap.Error() 会自动写成 "error": null,不会 paniczap.String("error", err.Error()) 会丢失堆栈、丢掉类型语义,且 err 为 nil 时会 panicerrors.New 和 fmt.Errorf(Go 1.13+)不带堆栈,需用 fmt.Errorf("xxx: %w", err) 配合 errors.Is/As 才能链式追踪log.Fatal 不适合记录 HTTP handler 中的错误?log.Fatal 底层调用 os.Exit(1),会导致整个进程退出。在 HTTP server 里一旦某个请求出错就 kill 掉服务,显然不可接受。
log.Printf 或结构化 logger 记录,然后返回 http.Error 或自定义响应log.Panic 同样危险:触发 panic 后若没被 recover,照样崩进程log.Fatal 的场景极少,一般是 main 函数里初始化失败(如无法监听端口、加载配置失败)不要。哪怕是在 debug 环境,也不该让明文敏感数据落到磁盘日志文件里。zap 提供 zap.String("password", "[redacted]") 这种显式脱敏,但更稳妥的是从源头控制:
strings.ReplaceAll 或正则擦除已知敏感键值(如 "auth_token"、"card_number")zap.Object + 自定义 LogObjectMarshaler 接口,对 struct 字段做选择性序列化日志不是 dump,是线索。留够定位信息就行,多一条密码反而增加泄露风险。