From 43d42d18b1d6303a9533a6e2a808d5f2d1422754 Mon Sep 17 00:00:00 2001 From: bryan Date: Sun, 6 Apr 2025 16:45:11 +0800 Subject: [PATCH] add dep mod --- database.go | 10 +++ go.mod | 8 +++ logger.go | 200 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 218 insertions(+) create mode 100644 logger.go diff --git a/database.go b/database.go index 4bd22c4..bcc39ea 100644 --- a/database.go +++ b/database.go @@ -4,6 +4,8 @@ import ( "fmt" "strconv" "strings" + + gormlogger "gorm.io/gorm/logger" ) type Config struct { @@ -46,3 +48,11 @@ func GetDsn(dbcfg *Config) (dsn string) { } return dsn } + +func DefaultLogger() gormlogger.Interface { + return defaultLogger +} + +func DefaultRecorder() traceRecorder { + return defautRecorder +} diff --git a/go.mod b/go.mod index 609f7ca..0028f17 100644 --- a/go.mod +++ b/go.mod @@ -1,3 +1,11 @@ module qoobing.com/gomod/database go 1.19.2 + +require qoobing.com/gomod/log v1.4.0 + +require ( + github.com/tylerb/gls v0.0.0-20150407001822-e606233f194d // indirect + gorm.io/gorm v1.25.12 + qoobing.com/gomod/str v1.0.1 // indirect +) diff --git a/logger.go b/logger.go new file mode 100644 index 0000000..82900e2 --- /dev/null +++ b/logger.go @@ -0,0 +1,200 @@ +package database + +import ( + "context" + "errors" + "fmt" + "time" + + gormlogger "gorm.io/gorm/logger" + "qoobing.com/gomod/log" +) + +// ErrRecordNotFound record not found error +var ErrRecordNotFound = errors.New("record not found") + +// Colors +const ( + Reset = "\033[0m" + Red = "\033[31m" + Green = "\033[32m" + Yellow = "\033[33m" + Blue = "\033[34m" + Magenta = "\033[35m" + Cyan = "\033[36m" + White = "\033[37m" + BlueBold = "\033[34;1m" + MagentaBold = "\033[35;1m" + RedBold = "\033[31;1m" + YellowBold = "\033[33;1m" +) + +// LogLevel log level +type LogLevel = gormlogger.LogLevel + +const ( + // Silent silent log level + Silent LogLevel = iota + 1 + // Error error log level + Error + // Warn warn log level + Warn + // Info info log level + Info +) + +// LogConfig logger config +type LogConfig struct { + SlowThreshold time.Duration + Colorful bool + IgnoreRecordNotFoundError bool + ParameterizedQueries bool + LogLevel LogLevel +} + +var ( + // // Discard logger will print any log to io.Discard + // Discard = New(log.New(io.Discard, "", log.LstdFlags), Config{}) + // // defaultLogger defaultLogger logger + // + defaultLogger = New(LogConfig{ + SlowThreshold: 200 * time.Millisecond, + LogLevel: Info, + IgnoreRecordNotFoundError: false, + Colorful: true, + }) + + // defautRecorder logger records running SQL into a recorder instance + defautRecorder = traceRecorder{Interface: defaultLogger, BeginAt: time.Now()} +) + +// New initialize logger +func New(config LogConfig) gormlogger.Interface { + var ( + infoStr = "%s\n[info] " + warnStr = "%s\n[warn] " + errStr = "%s\n[error] " + traceStr = "%s\n[%.3fms] [rows:%v] %s" + traceWarnStr = "%s\n[%.3fms] [rows:%v] %s" + traceErrStr = "%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 = Yellow + "[%.3fms] " + BlueBold + "[rows:%v]" + Reset + " %s" + traceWarnStr = Yellow + "%s\n" + Reset + RedBold + "[%.3fms] " + Yellow + "[rows:%v]" + Magenta + " %s" + Reset + traceErrStr = MagentaBold + "%s\n" + Reset + Yellow + "[%.3fms] " + BlueBold + "[rows:%v]" + Reset + " %s" + } + baseLogger := *log.New("gorm") + baseLogger.SetCalldepth(3) + return &logger{ + baseLogger: baseLogger, + LogConfig: config, + infoStr: infoStr, + warnStr: warnStr, + errStr: errStr, + traceStr: traceStr, + traceWarnStr: traceWarnStr, + traceErrStr: traceErrStr, + } +} + +type logger struct { + LogConfig + baseLogger log.Logger + infoStr, warnStr, errStr string + traceStr, traceErrStr, traceWarnStr string +} + +// LogMode log mode +func (l *logger) LogMode(level LogLevel) gormlogger.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.baseLogger.Infof(l.infoStr+msg, data) + } +} + +// Warn print warn messages +func (l *logger) Warn(ctx context.Context, msg string, data ...interface{}) { + if l.LogLevel >= Warn { + l.baseLogger.Warningf(l.warnStr+msg, data) + } +} + +// Error print error messages +func (l *logger) Error(ctx context.Context, msg string, data ...interface{}) { + if l.LogLevel >= Error { + l.baseLogger.Errorf(l.errStr+msg, data) + } +} + +// Trace print sql message +// +//nolint:cyclop +func (l *logger) Trace(ctx context.Context, begin time.Time, fc func() (string, int64), err error) { + if l.LogLevel <= Silent { + return + } + + elapsed := time.Since(begin) + switch { + case err != nil && l.LogLevel >= Error && (!errors.Is(err, ErrRecordNotFound) || !l.IgnoreRecordNotFoundError): + sql, rows := fc() + if rows == -1 { + l.baseLogger.Debugf(l.traceErrStr, err, float64(elapsed.Nanoseconds())/1e6, "-", sql) + } else { + l.baseLogger.Debugf(l.traceErrStr, 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.baseLogger.Debugf(l.traceWarnStr, slowLog, float64(elapsed.Nanoseconds())/1e6, "-", sql) + } else { + l.baseLogger.Debugf(l.traceWarnStr, slowLog, float64(elapsed.Nanoseconds())/1e6, rows, sql) + } + case l.LogLevel == Info: + sql, rows := fc() + if rows == -1 { + l.baseLogger.Debugf(l.traceStr, float64(elapsed.Nanoseconds())/1e6, "-", sql) + } else { + l.baseLogger.Debugf(l.traceStr, float64(elapsed.Nanoseconds())/1e6, rows, sql) + } + } +} + +// ParamsFilter filter params +func (l *logger) ParamsFilter(ctx context.Context, sql string, params ...interface{}) (string, []interface{}) { + if l.LogConfig.ParameterizedQueries { + return sql, nil + } + return sql, params +} + +type traceRecorder struct { + gormlogger.Interface + BeginAt time.Time + SQL string + RowsAffected int64 + Err error +} + +// New trace recorder +func (l *traceRecorder) New() *traceRecorder { + return &traceRecorder{Interface: l.Interface, BeginAt: time.Now()} +} + +// Trace implement logger interface +func (l *traceRecorder) Trace(ctx context.Context, begin time.Time, fc func() (string, int64), err error) { + l.BeginAt = begin + l.SQL, l.RowsAffected = fc() + l.Err = err +}