github.com/wfusion/gofusion@v1.1.14/log/customlogger/gorm.go (about) 1 package customlogger 2 3 import ( 4 "context" 5 "fmt" 6 "reflect" 7 "strings" 8 "time" 9 10 "github.com/pkg/errors" 11 "github.com/spf13/cast" 12 "github.com/wfusion/gofusion/common/utils" 13 "gorm.io/gorm" 14 "gorm.io/gorm/logger" 15 16 "github.com/wfusion/gofusion/config" 17 "github.com/wfusion/gofusion/log" 18 ) 19 20 var ( 21 // GormLoggerType FIXME: should not be deleted to avoid compiler optimized 22 GormLoggerType = reflect.TypeOf(gormLogger{}) 23 dbFields = log.Fields{"component": strings.ToLower(config.ComponentDB)} 24 ) 25 26 func DefaultMySQLLogger() logger.Interface { 27 return &gormLogger{ 28 enabled: true, 29 slowThreshold: 200 * time.Millisecond, 30 logLevel: logger.Silent, 31 ignoreRecordNotFoundError: true, 32 } 33 } 34 35 type gormLogger struct { 36 log log.Loggable 37 appName string 38 confName string 39 enabled bool 40 logLevel logger.LogLevel 41 slowThreshold time.Duration 42 ignoreRecordNotFoundError bool 43 } 44 45 // LogMode log mode 46 func (g *gormLogger) LogMode(level logger.LogLevel) logger.Interface { 47 g.logLevel = level 48 return g 49 } 50 51 // Info print info 52 func (g *gormLogger) Info(ctx context.Context, msg string, data ...any) { 53 if g.isLoggable() && g.logLevel >= logger.Info { 54 g.logger().Info(ctx, msg, append(data, dbFields)...) 55 } 56 } 57 58 // Warn print warn messages 59 func (g *gormLogger) Warn(ctx context.Context, msg string, data ...any) { 60 if g.isLoggable() && g.logLevel >= logger.Warn { 61 g.logger().Warn(ctx, msg, append(data, dbFields)...) 62 } 63 } 64 65 // Error print error messages 66 func (g *gormLogger) Error(ctx context.Context, msg string, data ...any) { 67 if g.isLoggable() && g.logLevel >= logger.Error { 68 g.logger().Error(ctx, msg, append(data, dbFields)...) 69 } 70 } 71 72 // Trace print sql message 73 func (g *gormLogger) Trace(ctx context.Context, begin time.Time, fc func() (string, int64), err error) { 74 if !g.isLoggable() { 75 return 76 } 77 if g.logLevel <= logger.Silent { 78 return 79 } 80 81 elapsed := time.Since(begin) 82 switch { 83 case err != nil && g.logLevel >= logger.Error && 84 (!errors.Is(err, gorm.ErrRecordNotFound) || !g.ignoreRecordNotFoundError): 85 sql, rows := fc() 86 sql = fmt.Sprintf("err[%%s] %s", sql) 87 if rows == -1 { 88 g.logger().Info(ctx, sql, err.Error(), g.fields(log.Fields{"latency": elapsed.Milliseconds()})) 89 } else { 90 g.logger().Info(ctx, sql, err.Error(), 91 g.fields(log.Fields{"rows": rows, "latency": elapsed.Milliseconds()})) 92 } 93 case elapsed > g.slowThreshold && g.slowThreshold != 0 && g.logLevel >= logger.Warn: 94 sql, rows := fc() 95 slowLog := fmt.Sprintf("SLOW SQL >= %v %s", g.slowThreshold, g.format(sql)) 96 if rows == -1 { 97 g.logger().Info(ctx, slowLog, g.fields(log.Fields{"latency": elapsed.Milliseconds()})) 98 } else { 99 g.logger().Info(ctx, slowLog, g.fields(log.Fields{"rows": rows, "latency": elapsed.Milliseconds()})) 100 } 101 case g.logLevel == logger.Info: 102 sql, rows := fc() 103 sql = g.format(sql) 104 if rows == -1 { 105 g.logger().Info(ctx, sql, g.fields(log.Fields{"latency": elapsed.Milliseconds()})) 106 } else { 107 g.logger().Info(ctx, sql, g.fields(log.Fields{"rows": rows, "latency": elapsed.Milliseconds()})) 108 } 109 } 110 } 111 112 func (g *gormLogger) Init(log log.Loggable, appName, name string) { 113 g.log = log 114 g.appName = appName 115 g.confName = name 116 g.ignoreRecordNotFoundError = true 117 g.reloadConfig() 118 } 119 120 func (g *gormLogger) logger() log.Loggable { 121 if g.log != nil { 122 return g.log 123 } 124 return log.Use(config.DefaultInstanceKey, log.AppName(g.appName)) 125 } 126 127 func (g *gormLogger) format(sql string) string { 128 return strings.ReplaceAll(sql, "%", "%%") 129 } 130 131 func (g *gormLogger) fields(fields log.Fields) log.Fields { 132 return utils.MapMerge(fields, dbFields) 133 } 134 135 func (g *gormLogger) getLogLevel(level string) logger.LogLevel { 136 switch strings.ToLower(level) { 137 case "debug": 138 return logger.Info 139 case "info": 140 return logger.Info 141 case "warn": 142 return logger.Warn 143 case "error": 144 return logger.Error 145 default: 146 return g.logLevel 147 } 148 } 149 150 func (g *gormLogger) isLoggable() bool { 151 if g.confName == "" { 152 return true 153 } 154 g.reloadConfig() 155 return g.enabled 156 } 157 158 func (g *gormLogger) reloadConfig() { 159 var cfgs map[string]map[string]any 160 _ = config.Use(g.appName).LoadComponentConfig(config.ComponentDB, &cfgs) 161 if len(cfgs) == 0 { 162 return 163 } 164 165 cfg, ok := cfgs[g.confName] 166 if !ok { 167 return 168 } 169 g.enabled = cast.ToBool(cfg["enable_logger"]) 170 logConfigObj, ok1 := cfg["logger_config"] 171 logCfg, ok2 := logConfigObj.(map[string]any) 172 if !ok1 || !ok2 { 173 return 174 } 175 g.logLevel = g.getLogLevel(cast.ToString(logCfg["log_level"])) 176 g.slowThreshold, _ = time.ParseDuration(cast.ToString(logCfg["slow_threshold"])) 177 }