Go中错误日志持久化核心是统一收集+可靠落盘/入库,推荐zap+lumberjack文件轮转、sqlx异步写库双写策略,并注意panic捕获、上下文传递与敏感信息过滤。

在 Go 中实现错误日志持久化,核心是把 error 或结构化日志(含时间、级别、堆栈、上下文等)可靠地写入文件或数据库。关键不在于“捕获错误”,而在于“统一收集 + 可靠落盘/入库”。下面分场景给出实用方案。
用 zap + lumberjack 实现高性能文件持久化
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 或 gorm 写入数据库(结构化错误表)
适合需关联业务数据、做统计分析或告警联动的场景。建议建专用错误日志表,而非混在业务表中。
立即学习“go语言免费学习笔记(深入)”;
- 推荐字段:id, created_at, level, message, error_stack, module, trace_id, context_json(JSONB 或 TEXT)
- 使用
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 上报端点) - 或封装一个
ErrorLogger接口,内部同时调用fileLogger.Error()和dbLogger.Log() - 关键错误(如 DB 连接失败、鉴权崩溃)优先确保写入本地文件,再尝试上报
别忽略的细节
-
panic 捕获:用
recover()+debug.PrintStack()记录未处理 panic,避免进程静默退出 -
上下文传递:HTTP 请求中的
requestID、用户 ID、路径等应随错误一起记录,方便排查 - 敏感信息过滤:日志中自动脱敏密码、token、手机号等字段(zap 支持自定义 encoder 做 redaction)
-
资源释放:
logger.Sync()在进程退出前必须调用,否则缓冲日志可能丢失










