使用 zap 等结构化日志库,定义 AppError 统一错误结构,封装 LogError 函数记录上下文,并通过中间件自动捕获 HTTP 异常,实现标准化、可解析的日志输出,提升问题排查效率。

在 Golang 项目中,统一的错误日志格式有助于快速定位问题、提升排查效率。实现结构化、可读性强的日志输出,是构建稳定服务的重要一环。关键在于使用结构化日志库、定义统一错误结构,并结合上下文信息进行记录。
使用结构化日志库(如 zap 或 logrus)
Go 标准库的 log 包功能有限,推荐使用支持结构化输出的日志库:
- uber-go/zap:高性能、结构化日志库,适合生产环境
- spf13/logrus:功能丰富,API 友好,支持 JSON 格式输出
以 zap 为例,初始化一个全局 logger:
func init() {logger, _ := zap.NewProduction()
zap.ReplaceGlobals(logger)
}
记录错误时自动包含时间、级别、调用位置等字段。
立即学习“go语言免费学习笔记(深入)”;
定义统一的错误结构体
为便于日志解析,建议自定义错误类型,携带必要上下文:
type AppError struct {Code string `json:"code"`
Message string `json:"message"`
Cause error `json:"cause,omitempty"`
TraceID string `json:"trace_id,omitempty"`
}
func (e *AppError) Error() string {
return e.Message
}
这样可以在日志中输出结构化的错误码、消息和追踪 ID。
封装日志记录函数
避免散落的日志调用,封装通用错误记录方法:
func LogError(err error, fields ...zap.Field) {if appErr, ok := err.(*AppError); ok {
zap.L().Error(appErr.Message,
zap.String("code", appErr.Code),
zap.String("trace_id", appErr.TraceID),
zap.Error(appErr.Cause),
fields...,
)
} else {
zap.L().Error("unexpected error", zap.Error(err), fields...)
}
}
调用时传入附加字段,如用户 ID、请求路径等:
LogError(appErr, zap.String("user_id", "123"), zap.String("path", "/api/v1/login"))结合中间件自动记录 HTTP 错误
在 Web 服务中,可通过中间件捕获处理异常并统一写日志:
func ErrorLoggingMiddleware(next http.Handler) http.Handler {return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
defer func() {
if rec := recover(); rec != nil {
zap.L().Error("panic recovered",
zap.Any("panic", rec),
zap.String("path", r.URL.Path),
zap.String("method", r.Method),
)
http.Error(w, "Internal Server Error", 500)
}
}()
next.ServeHTTP(w, r)
})
}
确保所有接口层错误都被捕获并格式化输出。
基本上就这些。通过结构化日志库 + 自定义错误类型 + 统一封装 + 中间件集成,可以实现清晰、一致的错误日志输出,极大提升运维效率。










