关于golang:聊聊gorm的logger

64次阅读

共计 5363 个字符,预计需要花费 14 分钟才能阅读完成。

本文次要钻研一下 gorm 的 logger

logger

gorm.io/gorm@v1.20.10/logger/logger.go

type logger struct {
    Writer
    Config
    infoStr, warnStr, errStr            string
    traceStr, traceErrStr, traceWarnStr string
}

type Writer interface {Printf(string, ...interface{})
}

type Config struct {
    SlowThreshold time.Duration
    Colorful      bool
    LogLevel      LogLevel
}

logger 内嵌了 Writer、Config、定义了 info、warn、err、trace、traceErr、traceWarn 属性

logger.New

gorm.io/gorm@v1.20.10/logger/logger.go

func New(writer Writer, config Config) Interface {
    var (infoStr      = "%s\n[info]"
        warnStr      = "%s\n[warn]"
        errStr       = "%s\n[error]"
        traceStr     = "%s\n[%.3fms] [rows:%v] %s"
        traceWarnStr = "%s %s\n[%.3fms] [rows:%v] %s"
        traceErrStr  = "%s %s\n[%.3fms] [rows:%v] %s"
    )

    if config.Colorful {infoStr = Green + "%s\n" + Reset + Green + "[info]" + Reset
        warnStr = BlueBold + "%s\n" + Reset + Magenta + "[warn]" + Reset
        errStr = Magenta + "%s\n" + Reset + Red + "[error]" + Reset
        traceStr = Green + "%s\n" + Reset + Yellow + "[%.3fms]" + BlueBold + "[rows:%v]" + Reset + "%s"
        traceWarnStr = Green + "%s" + Yellow + "%s\n" + Reset + RedBold + "[%.3fms]" + Yellow + "[rows:%v]" + Magenta + "%s" + Reset
        traceErrStr = RedBold + "%s" + MagentaBold + "%s\n" + Reset + Yellow + "[%.3fms]" + BlueBold + "[rows:%v]" + Reset + "%s"
    }

    return &logger{
        Writer:       writer,
        Config:       config,
        infoStr:      infoStr,
        warnStr:      warnStr,
        errStr:       errStr,
        traceStr:     traceStr,
        traceWarnStr: traceWarnStr,
        traceErrStr:  traceErrStr,
    }
}

logger.New 依据 config 来创立 logger

Interface

// Interface logger interface
type Interface interface {LogMode(LogLevel) Interface
    Info(context.Context, string, ...interface{})
    Warn(context.Context, string, ...interface{})
    Error(context.Context, string, ...interface{})
    Trace(ctx context.Context, begin time.Time, fc func() (string, int64), err error)
}

logger.Interface 接口定义了 LogMode、Info、Warn、Error、Trace 办法

LogMode

// LogMode log mode
func (l *logger) LogMode(level LogLevel) Interface {
    newlogger := *l
    newlogger.LogLevel = level
    return &newlogger
}

// Info print info
func (l logger) Info(ctx context.Context, msg string, data ...interface{}) {
    if l.LogLevel >= Info {l.Printf(l.infoStr+msg, append([]interface{}{utils.FileWithLineNum()}, data...)...)
    }
}

// Warn print warn messages
func (l logger) Warn(ctx context.Context, msg string, data ...interface{}) {
    if l.LogLevel >= Warn {l.Printf(l.warnStr+msg, append([]interface{}{utils.FileWithLineNum()}, data...)...)
    }
}

// Error print error messages
func (l logger) Error(ctx context.Context, msg string, data ...interface{}) {
    if l.LogLevel >= Error {l.Printf(l.errStr+msg, append([]interface{}{utils.FileWithLineNum()}, data...)...)
    }
}

// Trace print sql message
func (l logger) Trace(ctx context.Context, begin time.Time, fc func() (string, int64), err error) {
    if l.LogLevel > Silent {elapsed := time.Since(begin)
        switch {
        case err != nil && l.LogLevel >= Error:
            sql, rows := fc()
            if rows == -1 {l.Printf(l.traceErrStr, utils.FileWithLineNum(), err, float64(elapsed.Nanoseconds())/1e6, "-", sql)
            } else {l.Printf(l.traceErrStr, utils.FileWithLineNum(), err, float64(elapsed.Nanoseconds())/1e6, rows, sql)
            }
        case elapsed > l.SlowThreshold && l.SlowThreshold != 0 && l.LogLevel >= Warn:
            sql, rows := fc()
            slowLog := fmt.Sprintf("SLOW SQL >= %v", l.SlowThreshold)
            if rows == -1 {l.Printf(l.traceWarnStr, utils.FileWithLineNum(), slowLog, float64(elapsed.Nanoseconds())/1e6, "-", sql)
            } else {l.Printf(l.traceWarnStr, utils.FileWithLineNum(), slowLog, float64(elapsed.Nanoseconds())/1e6, rows, sql)
            }
        default:
            sql, rows := fc()
            if rows == -1 {l.Printf(l.traceStr, utils.FileWithLineNum(), float64(elapsed.Nanoseconds())/1e6, "-", sql)
            } else {l.Printf(l.traceStr, utils.FileWithLineNum(), float64(elapsed.Nanoseconds())/1e6, rows, sql)
            }
        }
    }
}

logger 实现了 logger.Interface 接口定义的 LogMode、Info、Warn、Error、Trace 办法

Session.Logger

gorm.io/gorm@v1.20.10/gorm.go

type Session struct {
    DryRun                   bool
    PrepareStmt              bool
    NewDB                    bool
    SkipHooks                bool
    SkipDefaultTransaction   bool
    DisableNestedTransaction bool
    AllowGlobalUpdate        bool
    FullSaveAssociations     bool
    QueryFields              bool
    Context                  context.Context
    Logger                   logger.Interface
    NowFunc                  func() time.Time
    CreateBatchSize          int
}

Logger 定义了 Logger 属性,最初设置到 DB.Logger

callback

gorm.io/gorm@v1.20.10/callbacks.go

func (c *callback) Remove(name string) error {c.processor.db.Logger.Warn(context.Background(), "removing callback `%v` from %v\n", name, utils.FileWithLineNum())
    c.name = name
    c.remove = true
    c.processor.callbacks = append(c.processor.callbacks, c)
    return c.processor.compile()}

func (c *callback) Replace(name string, fn func(*DB)) error {c.processor.db.Logger.Info(context.Background(), "replacing callback `%v` from %v\n", name, utils.FileWithLineNum())
    c.name = name
    c.handler = fn
    c.replace = true
    c.processor.callbacks = append(c.processor.callbacks, c)
    return c.processor.compile()}

callback 的 Remove、Replace 等办法会应用 db.Logger 进行打印

实例

func loggerDemo() {
    newLogger := logger.New(log.New(os.Stdout, "\r\n", log.LstdFlags), // io writer
        logger.Config{
            SlowThreshold: time.Second,   // 慢 SQL 阈值
            LogLevel:      logger.Silent, // Log level
            Colorful:      true,          // 黑白打印
        },
    )

    // 全局模式
    db, err := gorm.Open(sqlite.Open("test.db"), &gorm.Config{Logger: newLogger,})
    if err != nil {panic(err)
    }

    if err := db.AutoMigrate(&User{}); err != nil {panic(err)
    }

    // 新建会话模式
    tx := db.Session(&gorm.Session{Logger: newLogger})
    user := User{Name: "Tom", Age: 18, Birthday: time.Now()}
    result := db.Create(&user) // pass pointer of data to Create
    log.Println("userId:", user.ID)
    log.Println("result.RowsAffected:", result.RowsAffected, "result.Error:", result.Error)
    tx.First(&user)
    log.Printf("%+v", user)
    tx.Model(&user).Update("Age", 18)
}

输入

2021/01/10 23:32:42 userId: 6
2021/01/10 23:32:42 result.RowsAffected: 1 result.Error: <nil>
2021/01/10 23:32:42 {ID:6 Name:Tom Age:18 Birthday:2021-01-10 23:32:42.818057 +0800 +0800 DeletedAt:<nil> CreatedAt:2021-01-10 23:32:42.818107 +0800 +0800 UpdatedAt:2021-01-10 23:32:42.818107 +0800 +0800}

小结

gorm 的 logger 提供了 Interface 接口,能够本人实现并全局设置或者在 session 级别设置;gorm 默认的 logger 实现了 logger.Interface 接口定义的 LogMode、Info、Warn、Error、Trace 办法。

doc

  • gorm

正文完
 0