| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121 |
- package logger
- import (
- "context"
- "errors"
- "path/filepath"
- "runtime"
- "strings"
- "time"
- "github.com/runningwater/gohub/pkg/helpers"
- "go.uber.org/zap"
- "gorm.io/gorm"
- gormlogger "gorm.io/gorm/logger"
- )
- // GormLogger 操作对象,实现 gormlogger.Interface 接口,用于自定义 GORM 的日志输出
- type GormLogger struct {
- ZapLogger *zap.Logger
- SlowThreshold time.Duration
- }
- // NewGormLogger 创建一个新的 GormLogger 实例
- // 该实例使用全局的 logger.Logger 对象作为日志记录器
- // 慢查询阈值为 200ms
- // 返回值: GormLogger 实例
- // 注意: 该函数不会启动 GORM 的日志记录功能,需要在 GORM 配置中手动启用
- // 例如: db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{Logger: logger.NewGormLogger()})
- func NewGormLogger() GormLogger {
- return GormLogger{
- ZapLogger: Logger, // 使用全局的 logger.Logger 对象
- SlowThreshold: 200 * time.Millisecond, // 慢查询阈值,200ms
- }
- }
- // LogMode 实现 gormlogger.Interface 接口的 LogMode 方法
- func (l GormLogger) LogMode(level gormlogger.LogLevel) gormlogger.Interface {
- return GormLogger{
- ZapLogger: l.ZapLogger,
- SlowThreshold: l.SlowThreshold,
- }
- }
- // Info 实现 gormlogger.Interface 接口的 Info 方法
- func (l GormLogger) Info(_ context.Context, str string, args ...any) {
- l.logger().Sugar().Debugf(str, args...)
- }
- // Warn 实现 gormlogger.Interface 接口的 Warn 方法
- func (l GormLogger) Warn(_ context.Context, str string, args ...any) {
- l.logger().Sugar().Warnf(str, args...)
- }
- // Error 实现 gormlogger.Interface 接口的 Error 方法
- func (l GormLogger) Error(_ context.Context, str string, args ...any) {
- l.logger().Sugar().Errorf(str, args...)
- }
- // Trace 实现 gormlogger.Interface 接口的 Trace 方法
- func (l GormLogger) Trace(ctx context.Context, begin time.Time, fc func() (sql string, rowsAffected int64), err error) {
- // 计算执行时间
- elapsed := time.Since(begin)
- // 获取 SQL 和影响行数
- sql, rows := fc()
- // 通用字段
- logFields := []zap.Field{
- zap.String("sql", sql),
- zap.Int64("rows", rows),
- zap.String("time", helpers.MicrosecondsStr(elapsed)),
- }
- // Gorm 错误
- if err != nil {
- // 记录未找到的错误使用 warning 等级
- if errors.Is(err, gorm.ErrRecordNotFound) {
- l.logger().Warn("Database ErrRecordNotFound", logFields...)
- } else {
- // 其他错误使用 error 等级
- logFields = append(logFields, zap.Error(err))
- l.logger().Error("Database Error", logFields...)
- }
- }
- // 慢查询日志
- if l.SlowThreshold != 0 && elapsed > l.SlowThreshold {
- l.logger().Warn("Database Slow Log", logFields...)
- }
- // 记录所有 SQL 请求 ,使用 debug 等级
- l.logger().Debug("Database Query", logFields...)
- }
- // logger 内部方法,确保 Zap 内置信息 Caller 的准确性
- // 返回值: *zap.Logger 对象
- func (l GormLogger) logger() *zap.Logger {
- var (
- gormPackage = filepath.Join("gorm.io", "gorm")
- zapGormPackage = filepath.Join("moul.io", "zapgorm2")
- )
- // 减去一次封装,以及一次在 logger 初始化中添加 zap.AddCallerSkip(1)
- clone := l.ZapLogger.WithOptions(zap.AddCallerSkip(-2))
- for i := 2; i < 15; i++ {
- _, file, _, ok := runtime.Caller(i)
- switch {
- case !ok:
- case strings.HasSuffix(file, "_test.go"):
- case strings.Contains(file, gormPackage):
- case strings.Contains(file, zapGormPackage):
- default:
- // 返回一个附带跳过行号的 zap.Logger
- return clone.WithOptions(zap.AddCallerSkip(i))
- }
- }
- return l.ZapLogger
- }
|