在 Go(Golang)项目中“定位日志错误”通常分为 三步:
1️⃣ 从日志中定位错误发生位置
2️⃣ 在代码中还原调用链
3️⃣ 用工具辅助快速排查
下面按实战方式来说明。
如果日志只有:
error: something failed
基本无法定位。
推荐日志至少包含:
✅ 示例(良好日志):
2026-01-15 10:22:31 ERROR [req=abc123] main.go:45 handler failed: redis timeout
runtime.Caller(不推荐但基础)import (
"runtime"
"fmt"
)
func logError(err error) {
_, file, line, _ := runtime.Caller(1)
fmt.Printf("ERROR %s:%d %v\n", file, line, err)
}
缺点:手动、不灵活。
errors.WithStack(强烈推荐)import "github.com/pkg/errors"
func doSomething() error {
return errors.WithStack(fmt.Errorf("db query failed"))
}
日志输出:
db query failed
main.doSomething
/app/service/user.go:34
✅ 能直接看到 文件 + 行号
fmt.Errorf + %w(Go 1.13+)if err != nil {
return fmt.Errorf("query user: %w", err)
}
配合:
errors.Is / errors.As
适合 错误类型判断,但看不到堆栈。
logger, _ := zap.NewProduction()
defer logger.Sync()
logger.Error("handler failed",
zap.String("req_id", "abc123"),
zap.Error(err),
)
开启 Caller:
zap.AddCaller()
输出:
{
"level":"error",
"caller":"handler.go:58",
"msg":"handler failed",
"error":"redis timeout"
}
log := logrus.WithField("req_id", "abc123")
log.WithError(err).Error("handler failed")
配合 github.com/sirupsen/logrus 的 ReportCaller
分布式 or 并发场景必备:
ctx = context.WithValue(ctx, "trace_id", "abc123")
日志中始终带:
[trace_id=abc123]
然后:
grep abc123 app.log
即可还原 完整调用链
日志:
panic: runtime error: invalid memory address
定位:
pprofimport _ "net/http/pprof"
go tool pprof http://localhost:6060/debug/pprof/goroutine
trace_idretry + logfunc wrapErr(msg string, err error) error {
return errors.WithMessage(err, msg)
}
if err != nil {
log.Error("user service failed",
zap.String("trace_id", traceID),
zap.Error(wrapErr("get user", err)),
)
}
定位 Go 日志错误的核心 = 错误带堆栈 + 日志带位置 + 请求有 ID
如果你愿意,可以:
log / zap / logrus我可以直接帮你给一套最适合你项目的日志方案。