Documentation ¶
Overview ¶
Package gorm0log provides a gorm logger using zerolog as backend.
Thanks to zerolog, we have goo performance since most features are skipped if the log message is not visible.
If you queries gorm with context (gorm.DB.WithContext), the context can be handled by a function in Config named "Customize".
Default value of Config should be fairly enough for small projects.
gorm.DB.Debug swtiches underlying zerolog.Logger to Debug level, which shows every message but Trace level ones. Some features, controled by Config, can be set to customized level. So you can disable those messages by changing its level to UseTrace.
Example ¶
// This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at https://mozilla.org/MPL/2.0/. package main import ( "os" "github.com/glebarez/sqlite" "github.com/rs/zerolog" "github.com/rs/zerolog/log" "gorm.io/gorm" ) type User struct { ID int Name string } func initLog() { w := zerolog.NewConsoleWriter() w.NoColor = true w.Out = os.Stdout log.Logger = zerolog.New(w).Level(zerolog.WarnLevel) } func initDB() (*gorm.DB, error) { db, err := gorm.Open(sqlite.Open(":memory:"), &gorm.Config{ TranslateError: true, Logger: &Logger{ Logger: log.Logger, }, }) if err != nil { return nil, err } err = db.AutoMigrate(&User{}) if err != nil { return nil, err } return db, nil } func SaveUser(db *gorm.DB, u *User) error { return db.Save(u).Error } func LoadUser(db *gorm.DB, uid int) (*User, error) { var u User err := db.Where("id", uid).First(&u).Error if err != nil { return nil, err } return &u, nil } func main() { initLog() db, err := initDB() if err != nil { log.Error().Err(err).Msg("cannot init db") return } // this line shows sql dump if err = SaveUser(db.Debug(), &User{Name: "John Doe"}); err != nil { log.Error().Err(err).Msg("cannot insert predefined record") return } // no log message u, err := LoadUser(db, 1) if err != nil { log.Error().Err(err).Msg("cannot load predefined record") return } if u.Name != "John Doe" { log.Error().Str("name", u.Name).Msg("unexpected record loaded") return } // record not found error is logged to error level by default if u, err = LoadUser(db, 2); err == nil { log.Error().Interface("user", u).Msg("unexpected user found for id#2") return } // change log level of record not found message to hide it tx := db.Session(&gorm.Session{ Logger: &Logger{ Logger: log.Logger, Config: Config{ // logs to Debug level ErrorLevel: DebugCommonErr, // it's shortcut to // ErrorLevel: LogErrorAt(UseDebug, CommonError), }, }, }) if u, err = LoadUser(tx, 3); err == nil { log.Error().Interface("user", u).Msg("unexpected user found for id#2") return } // change level setting of logger to log record not found error again if u, err = LoadUser(tx.Debug(), 4); err == nil { log.Error().Interface("user", u).Msg("unexpected user found for id#2") return } // disable record not found log, but dumps sql tx = db.Session(&gorm.Session{ Logger: &Logger{ Logger: log.Logger.Level(zerolog.DebugLevel), Config: Config{ErrorLevel: IgnoreCommonErr}, }, }) if u, err = LoadUser(tx, 5); err == nil { log.Error().Interface("user", u).Msg("unexpected user found for id#2") return } // dump sql with source file info // // example output: // <nil> DBG dump sql affected_rows=1 source_file=/path/to/this/example_test.go source_line=53 sql="SELECT * FROM `users` WHERE `id` = 1 ORDER BY `users`.`id` LIMIT 1" tx = db.Session(&gorm.Session{ Logger: &Logger{ Config: Config{Customize: LogSource("_test.go")}, // uncomment this to actually logs it // Logger: log.Logger.Level(zerolog.DebugLevel), }, }) if _, err = LoadUser(tx, 1); err != nil { log.Error().Err(err).Msg("cannot load predefined record") return } }
Output: <nil> DBG dump sql affected_rows=1 sql="INSERT INTO `users` (`name`) VALUES (\"John Doe\") RETURNING `id`" <nil> ERR a sql error occured error="record not found" affected_rows=0 sql="SELECT * FROM `users` WHERE `id` = 2 ORDER BY `users`.`id` LIMIT 1" <nil> DBG a sql error occured error="record not found" affected_rows=0 sql="SELECT * FROM `users` WHERE `id` = 4 ORDER BY `users`.`id` LIMIT 1" <nil> DBG dump sql affected_rows=0 sql="SELECT * FROM `users` WHERE `id` = 5 ORDER BY `users`.`id` LIMIT 1"
Index ¶
- func CommonError(err error) bool
- func DebugCommonErr(e error, l zerolog.Logger) *zerolog.Event
- func DefaultLogLevelMap(i logger.LogLevel) zerolog.Level
- func Ignore(l zerolog.Logger) *zerolog.Event
- func IgnoreCommonErr(e error, l zerolog.Logger) *zerolog.Event
- func LogErrorAt(level func(zerolog.Logger) *zerolog.Event, cmpErr func(error) bool) func(error, zerolog.Logger) *zerolog.Event
- func LogSource(keywords ...string) func(context.Context, *zerolog.Event)
- func UseDebug(l zerolog.Logger) *zerolog.Event
- func UseError(l zerolog.Logger) *zerolog.Event
- func UseFatal(l zerolog.Logger) *zerolog.Event
- func UseInfo(l zerolog.Logger) *zerolog.Event
- func UseTrace(l zerolog.Logger) *zerolog.Event
- func UseWarn(l zerolog.Logger) *zerolog.Event
- func WithTimeTracking(i logger.LogLevel) zerolog.Level
- type Config
- type Logger
- func (l *Logger) Error(ctx context.Context, msg string, args ...any)
- func (l *Logger) Info(ctx context.Context, msg string, args ...any)
- func (l *Logger) LogMode(lv logger.LogLevel) logger.Interface
- func (l *Logger) ParamsFilter(ctx context.Context, sql string, params ...interface{}) (string, []interface{})
- func (l *Logger) Trace(ctx context.Context, begin time.Time, f func() (string, int64), err error)
- func (l *Logger) Warn(ctx context.Context, msg string, args ...any)
Examples ¶
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func CommonError ¶
CommonError detects if err is gorm.ErrDuplicatedKey or gorm.ErrRecordNotFound.
func DebugCommonErr ¶
DebugCommonErr is shortcut of LogErrorAt(UseDebug, CommonError).
func DefaultLogLevelMap ¶
DefaultLogLevelMap enables sql dumping if you use logger.Info log mode.
It translates Gorm's logger.Info mode to zerolog.DebugLevel, so sql dumping is logged.
func IgnoreCommonErr ¶
IgnoreCommonErr is shortcut of LogErrorAt(UseTrace, CommonError).
func LogErrorAt ¶
func LogErrorAt(level func(zerolog.Logger) *zerolog.Event, cmpErr func(error) bool) func(error, zerolog.Logger) *zerolog.Event
LogErrorAt creates a function to be used at ErrorLevel of Config. It compares error using cmpErr, use specified level to log it if matched, Error level otherwise.
func LogSource ¶
LogSource creates a function to provide caller info.
It iterates the stack to find fist file that matches any of keywords, puts filename and line number to json field "source_file" and "source_line".
func WithTimeTracking ¶
WithTimeTracking enables time tracking info if you use logger.Info log mode.
It translates Gorm's logger.Info mode to zerolog.TraceLevel, so sql dumping and time tracking info are logged.
Types ¶
type Config ¶
type Config struct { // Duration threshold of slow log, 0 or less disables it. SlowThreshold time.Duration // Log level of slow sql messages, default to [UseWarn]. SlowLevel func(zerolog.Logger) *zerolog.Event // Key used to show time tracking info, default to "duration" Duration string // Log level for special error, default to log every error at Error level. // You might use it to change log level of non-critical errors like // [gorm.ErrRecordNotFound] or [gorm.ErrDuplicatedKey]. Helpers are // provided, see [IgnoreCommonErr] and [DebugCommonErr]. ErrorLevel func(error, zerolog.Logger) *zerolog.Event // Do not log value of parameters. ParameterizedQueries bool // Dump SQL // Log level of sql dumping messages, default to [UseDebug]. DumpLevel func(zerolog.Logger) *zerolog.Event // Adds execution time info to sql dumping message. DumpWithDuration bool // Key used to show sql dump, default to "sql". SQL string // Key used to show affected rows, default to "affected_rows". AffectedRows string // A function to log extra info, context value or call stacks for example. // This function is called only if the message is visible. Customize func(context.Context, *zerolog.Event) }
Config is a switch for extra features of Logger.
Default value is fairly enough for general use:
- log general error as Error level
- record not found error as Debug level
- dump every sql as Debug level
- slow log message as Warn level
- disable slow log, as you should have your own slow threshold
- common json key
- ignore context values
By default, gorm.DB.Debug shows sql dump. This can be changed by setting DumpLevel to UseTrace in Config.
type Logger ¶
Logger implements logger.Interface.
This implementation provides some customizable features, take a look at Config.
Using gorm.DB.Debug switches log level of zerolog.Logger to Debug level.
Zero value means a logger that:
- Log to empty zerolog.Logger, which logs nothing.
- Using default Config.
func (*Logger) Error ¶
Error implements logger.Interface, to show a message at Error level.
func (*Logger) Info ¶
Info implements logger.Interface, to show a message at Info level.
func (*Logger) LogMode ¶
LogMode implements logger.Interface, to control which message is visible.
func (*Logger) ParamsFilter ¶
func (l *Logger) ParamsFilter(ctx context.Context, sql string, params ...interface{}) (string, []interface{})
ParamsFilter implements gorm.ParamsFilter to check if parameters should be shown.
func (*Logger) Trace ¶
Trace implements logger.Ingerface. It is called every query by Gorm, so we can provide useful features like slow log or sql dump.