Repository navigation
Replies: 1 comment
|
You can implement the gormLogger.Interface and use your own logger package instead of gorm's default logger. Inside your custom logger package you can extract request_id from context and write it to the log. Always ensure that you are passing the context to the package database
import (
"context"
"errors"
"fmt"
"runtime"
"strconv"
"strings"
"time"
"customer/internal/config"
"customer/internal/utility"
"customer/pkg/logger"
"github.com/sirupsen/logrus"
"gorm.io/driver/mysql"
"gorm.io/gorm"
gormLogger "gorm.io/gorm/logger"
)
func ConnectDB() *gorm.DB {
dbConfig := config.GetDBConfig()
// Build the DSN (Data Source Name)
dsn := fmt.Sprintf("%s:%s@tcp(%s:%d)/%s?charset=utf8mb4&parseTime=True&loc=Local", dbConfig.User, dbConfig.Password, dbConfig.Host, dbConfig.Port, dbConfig.DatabaseName)
db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{
Logger: newCustomGormLogger(logger.Log, gormLogger.Config{
LogLevel: gormLogger.Info,
IgnoreRecordNotFoundError: false,
Colorful: config.GetAppConfig().EnableGormColor,
}),
TranslateError: true,
})
if err != nil {
logger.Log.Fatal("Failed to connect to database:", err)
}
return db
}
// Below code implements the gormLogger.Interface to add request context in gorm logs
// Implementation taken from here: https://gist.github.com/mickamy/f2155cb4a78efa89ed7da9753fe813fc
var loggerFile string
func init() {
_, file, _, _ := runtime.Caller(0)
loggerFile = file
}
// newCustomGormLogger initialize logger
func newCustomGormLogger(logger *logrus.Logger, config gormLogger.Config) gormLogger.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 = gormLogger.Green + "%s\n" + gormLogger.Reset + gormLogger.Green + "[info] " + gormLogger.Reset
warnStr = gormLogger.BlueBold + "%s\n" + gormLogger.Reset + gormLogger.Magenta + "[warn] " + gormLogger.Reset
errStr = gormLogger.Magenta + "%s\n" + gormLogger.Reset + gormLogger.Red + "[error] " + gormLogger.Reset
traceStr = gormLogger.Green + "%s\n" + gormLogger.Reset + gormLogger.Yellow + "[%.3fms] " + gormLogger.BlueBold + "[rows:%v]" + gormLogger.Reset + " %s"
traceWarnStr = gormLogger.Green + "%s " + gormLogger.Yellow + "%s\n" + gormLogger.Reset + gormLogger.RedBold + "[%.3fms] " + gormLogger.Yellow + "[rows:%v]" + gormLogger.Magenta + " %s" + gormLogger.Reset
traceErrStr = gormLogger.RedBold + "%s " + gormLogger.MagentaBold + "%s\n" + gormLogger.Reset + gormLogger.Yellow + "[%.3fms] " + gormLogger.BlueBold + "[rows:%v]" + gormLogger.Reset + " %s"
}
return &customGormLogger{
logger: logger,
Config: config,
infoStr: infoStr,
warnStr: warnStr,
errStr: errStr,
traceStr: traceStr,
traceWarnStr: traceWarnStr,
traceErrStr: traceErrStr,
}
}
type customGormLogger struct {
logger *logrus.Logger
gormLogger.Config
infoStr, warnStr, errStr string
traceStr, traceErrStr, traceWarnStr string
}
// LogMode log mode
func (l *customGormLogger) LogMode(level gormLogger.LogLevel) gormLogger.Interface {
newlogger := *l
newlogger.LogLevel = level
return &newlogger
}
// Info print info
func (l *customGormLogger) Info(ctx context.Context, msg string, data ...interface{}) {
if l.LogLevel >= gormLogger.Info {
l.logger.WithContext(ctx).Printf(l.infoStr+msg, append([]interface{}{fileWithLineNum()}, data...)...)
}
}
// Warn print warn messages
func (l *customGormLogger) Warn(ctx context.Context, msg string, data ...interface{}) {
if l.LogLevel >= gormLogger.Warn {
l.logger.WithContext(ctx).Printf(l.warnStr+msg, append([]interface{}{fileWithLineNum()}, data...)...)
}
}
// Error print error messages
func (l *customGormLogger) Error(ctx context.Context, msg string, data ...interface{}) {
if l.LogLevel >= gormLogger.Error {
l.logger.WithContext(ctx).Printf(l.errStr+msg, append([]interface{}{fileWithLineNum()}, data...)...)
}
}
// Trace print sql message
//
//nolint:cyclop
func (l *customGormLogger) Trace(ctx context.Context, begin time.Time, fc func() (string, int64), err error) {
if l.LogLevel <= gormLogger.Silent {
return
}
elapsed := time.Since(begin)
switch {
case err != nil && l.LogLevel >= gormLogger.Error && (!errors.Is(err, gormLogger.ErrRecordNotFound) || !l.IgnoreRecordNotFoundError):
sql, rows := fc()
sql = utility.CleanRawSQL(sql)
if rows == -1 {
l.logger.WithContext(ctx).Printf(l.traceErrStr, fileWithLineNum(), err, float64(elapsed.Nanoseconds())/1e6, "-", sql)
} else {
l.logger.WithContext(ctx).Printf(l.traceErrStr, fileWithLineNum(), err, float64(elapsed.Nanoseconds())/1e6, rows, sql)
}
case elapsed > l.SlowThreshold && l.SlowThreshold != 0 && l.LogLevel >= gormLogger.Warn:
sql, rows := fc()
sql = utility.CleanRawSQL(sql)
slowLog := fmt.Sprintf("SLOW SQL >= %v", l.SlowThreshold)
if rows == -1 {
l.logger.WithContext(ctx).Printf(l.traceWarnStr, fileWithLineNum(), slowLog, float64(elapsed.Nanoseconds())/1e6, "-", sql)
} else {
l.logger.WithContext(ctx).Printf(l.traceWarnStr, fileWithLineNum(), slowLog, float64(elapsed.Nanoseconds())/1e6, rows, sql)
}
case l.LogLevel == gormLogger.Info:
sql, rows := fc()
sql = utility.CleanRawSQL(sql)
if rows == -1 {
l.logger.WithContext(ctx).Printf(l.traceStr, fileWithLineNum(), float64(elapsed.Nanoseconds())/1e6, "-", sql)
} else {
l.logger.WithContext(ctx).Printf(l.traceStr, fileWithLineNum(), float64(elapsed.Nanoseconds())/1e6, rows, sql)
}
}
}
// ParamsFilter filter params
func (l *customGormLogger) ParamsFilter(ctx context.Context, sql string, params ...interface{}) (string, []interface{}) {
if l.Config.ParameterizedQueries {
return sql, nil
}
return sql, params
}
func fileWithLineNum() string {
pcs := [13]uintptr{}
// the third caller usually from gorm internal
length := runtime.Callers(3, pcs[:])
frames := runtime.CallersFrames(pcs[:length])
for i := 0; i < length; i++ {
// second return value is "more", not "ok"
frame, _ := frames.Next()
if !isIgnoreFile(frame.File) && !strings.HasSuffix(frame.File, ".gen.go") {
return string(strconv.AppendInt(append([]byte(frame.File), ':'), int64(frame.Line), 10))
}
}
return ""
}
func isIgnoreFile(file string) bool {
return isGormDir(file) || isLoggerFile(file) || isRepositoryFile(file)
}
func isGormDir(file string) bool {
return strings.Contains(file, "gorm.io")
}
func isLoggerFile(file string) bool {
return strings.HasPrefix(file, loggerFile)
}
func isRepositoryFile(file string) bool {
return strings.HasSuffix(file, "repository.go")
} |
0 replies
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Uh oh!
There was an error while loading. Please reload this page.
There is a requirement to retrieve the requestId from the gin.Context and include it in the database operation logs when printing them, under the GORM framework. How can this be achieved?
All reactions