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  }