Go中错误日志持久化核心是统一收集+可靠落盘/入库,推荐zap+lumberjack文件轮转、sqlx异步写库双写策略,并注意panic捕获、上下文传递与敏感信息过滤。
在 Go 中实现错误日志持久化,核心是把 error 或结构化日志(含时间、级别、堆栈、上下文等)可靠地写入文件或数据库。关键不在于“捕获错误”,而在于“统一收集 + 可靠落盘/入库”。下面分场景给出实用方案。
Zap 是目前最主流的高性能结构化日志库,搭配 lumberjack 可自动轮转、压缩、清理日志文件,适合生产环境。
go get -u go.uber.org/zap go.uber.org/zap/zapcore gopkg.in/natefinch/lumberjack.v2
示例代码:
import (
"go.uber.org/zap"
"go.uber.org/zap/zapcore"
"gopkg.in/natefinch/lumberjack.v2"
)
func newFileLogger() (*zap.Logger, error) {
writer := zapcore.AddSync(&lumberjack.Logger{
Filename: "logs/error.log",
MaxSize: 100, // MB
MaxBackups: 5,
MaxAge: 28, // 天
Compress: true,
})
encoderConfig := zap.NewProductionEncoderConfig()
encoderConfig.EncodeTime = zapcore.ISO8601TimeEncoder
encoderConfig.LevelKey = "level"
encoderConfig.MessageKey = "msg"
core := zapcore.NewCore(
zapcore.NewJSONEncoder(encoderConfig),
writer,
zapcore.ErrorLevel, // 仅记录 Error 及以上级别
)
return zap.New(core), nil
}
// 使用
logger, _ := newFileLogger()
defer logger.Sync()
if err := someOperation(); err != nil {
logger.Error("operation failed",
zap.String("component", "api"),
zap.Error(err),
zap.String("user_id", "u123"),
)
}
适合需关联业务数据、做统计分析或告警联动的场景。建议建专用错误日志表,而非混在业务表中。
sqlx 简洁插入(避免 ORM 开销):建表 SQL(PostgreSQL 示例):
CREATE TABLE error_logs ( id SERIAL PRIMARY KEY, created_at TIMESTAMPTZ DEFAULT NOW(), level VARCHAR(10), message TEXT, error_stack TEXT, module VARCHAR(50), trace_id VARCHAR(64), context JSONB );
Go 写入逻辑:
type ErrorLog struct {
Level string `db:"level"`
Message string `db:"message"`
ErrorStack string `db:"error_stack"`
Module string `db:"module"`
TraceID string `db:"trace_id"`
Context map[string]any `db:"context"`
}
func logErrorToDB(db *sqlx.DB, err error, module, traceID string, ctx map[string]any) error {
stack := debug.Stack()
log := ErrorLog{
Level: "ERROR",
Message: err.Error(),
ErrorStack: string(stack),
Module: module,
TraceID: traceID,
Context: ctx,
}
_, err = db.NamedExec(`
INSERT INTO error_logs (level, message, error_stack, module, trace_id, context)
VALUES (:level, :message, :error_stack, :module, :trace_id, :context)
`, log)
return err
}
注意:数据库写入不可阻塞主流程,建议用 goroutine + channel 异步提交,或接入消息队列(如 Kafka)解耦。
生产环境推荐双写(或异步写):文件保证不丢日志(本地磁盘最可靠),数据库提供查询和聚合能力。
multiwriter 同时写入多个 io.Writer(如文件 + HTTP 上报端点)Erro
rLogger 接口,内部同时调用 fileLogger.Error() 和 dbLogger.Log()
recover() + debug.PrintStack() 记录未处理 panic,避免进程静默退出requestID、用户 ID、路径等应随错误一起记录,方便排查logger.Sync() 在进程退出前必须调用,否则缓冲日志可能丢失