github.com/benz9527/xboot@v0.0.0-20240504061247-c23f15593274/xlog/gorm.go (about)

     1  package xlog
     2  
     3  import (
     4  	"context"
     5  	"errors"
     6  	"fmt"
     7  	"strconv"
     8  	"sync/atomic"
     9  	"time"
    10  
    11  	"go.uber.org/zap"
    12  	"go.uber.org/zap/zapcore"
    13  	glogger "gorm.io/gorm/logger"
    14  	gutils "gorm.io/gorm/utils"
    15  )
    16  
    17  var _ glogger.Interface = (*GormXLogger)(nil)
    18  
    19  type GormXLogger struct {
    20  	logger              XLogger
    21  	cfg                 *glogger.Config
    22  	dynamicLevelEnabler zap.AtomicLevel
    23  	gormLevel           int32
    24  }
    25  
    26  func (l *GormXLogger) LogMode(lvl glogger.LogLevel) glogger.Interface {
    27  	atomic.StoreInt32(&l.gormLevel, int32(lvl))
    28  	l.dynamicLevelEnabler.SetLevel(getLogLevelOrDefaultForGorm(lvl))
    29  	return l
    30  }
    31  
    32  func (l *GormXLogger) Info(ctx context.Context, msg string, data ...any) {
    33  	if glogger.LogLevel(atomic.LoadInt32(&l.gormLevel)) >= glogger.Info {
    34  		l.logger.InfoContext(ctx, fmt.Sprintf(msg, data...), zap.String("fileAndLine", gutils.FileWithLineNum()))
    35  	}
    36  }
    37  
    38  func (l *GormXLogger) Warn(ctx context.Context, msg string, data ...any) {
    39  	if glogger.LogLevel(atomic.LoadInt32(&l.gormLevel)) >= glogger.Warn {
    40  		l.logger.WarnContext(ctx, fmt.Sprintf(msg, data...), zap.String("fileAndLine", gutils.FileWithLineNum()))
    41  	}
    42  }
    43  
    44  func (l *GormXLogger) Error(ctx context.Context, msg string, data ...any) {
    45  	if glogger.LogLevel(atomic.LoadInt32(&l.gormLevel)) >= glogger.Error {
    46  		l.logger.ErrorContext(ctx, nil, fmt.Sprintf(msg, data...), zap.String("fileAndLine", gutils.FileWithLineNum()))
    47  	}
    48  }
    49  
    50  func (l *GormXLogger) Trace(ctx context.Context, begin time.Time, fc func() (sql string, rowsAffected int64), err error) {
    51  	if glogger.LogLevel(atomic.LoadInt32(&l.gormLevel)) <= glogger.Silent {
    52  		return
    53  	}
    54  
    55  	elapsed := time.Since(begin)
    56  	switch {
    57  	case err != nil && glogger.LogLevel(atomic.LoadInt32(&l.gormLevel)) >= glogger.Error && (!errors.Is(err, glogger.ErrRecordNotFound) || l.cfg.IgnoreRecordNotFoundError):
    58  		sql, rows := fc()
    59  		if rows <= -1 {
    60  			l.logger.ErrorContext(ctx, err, "error trace",
    61  				zap.String("fileAndLine", gutils.FileWithLineNum()),
    62  				zap.String("rows", "-"),
    63  				zap.Int64("elapsedMs", elapsed.Milliseconds()),
    64  				zap.String("sql", sql),
    65  			)
    66  		} else {
    67  			l.logger.ErrorContext(ctx, err, "error trace",
    68  				zap.String("fileAndLine", gutils.FileWithLineNum()),
    69  				zap.String("rows", strconv.FormatInt(rows, 10)),
    70  				zap.Int64("elapsedMs", elapsed.Milliseconds()),
    71  				zap.String("sql", sql),
    72  			)
    73  		}
    74  	case elapsed > l.cfg.SlowThreshold && l.cfg.SlowThreshold != 0 && glogger.LogLevel(atomic.LoadInt32(&l.gormLevel)) >= glogger.Warn:
    75  		sql, rows := fc()
    76  		if rows <= -1 {
    77  			l.logger.WarnContext(ctx, "slow sql",
    78  				zap.Int64("thresholdMs", l.cfg.SlowThreshold.Milliseconds()),
    79  				zap.String("fileAndLine", gutils.FileWithLineNum()),
    80  				zap.String("rows", "-"),
    81  				zap.Int64("elapsedMs", elapsed.Milliseconds()),
    82  				zap.String("sql", sql),
    83  			)
    84  		} else {
    85  			l.logger.WarnContext(ctx, "slow sql",
    86  				zap.Int64("thresholdMs", l.cfg.SlowThreshold.Milliseconds()),
    87  				zap.String("fileAndLine", gutils.FileWithLineNum()),
    88  				zap.String("rows", strconv.FormatInt(rows, 10)),
    89  				zap.Int64("elapsedMs", elapsed.Milliseconds()),
    90  				zap.String("sql", sql),
    91  			)
    92  		}
    93  	case glogger.LogLevel(atomic.LoadInt32(&l.gormLevel)) == glogger.Info:
    94  		sql, rows := fc()
    95  		if rows <= -1 {
    96  			l.logger.InfoContext(ctx, "common sql info",
    97  				zap.String("fileAndLine", gutils.FileWithLineNum()),
    98  				zap.String("rows", "-"),
    99  				zap.Int64("elapsedMs", elapsed.Milliseconds()),
   100  				zap.String("sql", sql),
   101  			)
   102  		} else {
   103  			l.logger.InfoContext(ctx, "common sql info",
   104  				zap.String("fileAndLine", gutils.FileWithLineNum()),
   105  				zap.String("rows", strconv.FormatInt(rows, 10)),
   106  				zap.Int64("elapsedMs", elapsed.Milliseconds()),
   107  				zap.String("sql", sql),
   108  			)
   109  		}
   110  	}
   111  }
   112  
   113  func NewGormXLogger(logger XLogger, opts ...GormXLoggerOption) *GormXLogger {
   114  	gl := &GormXLogger{
   115  		cfg: &glogger.Config{},
   116  	}
   117  	for _, o := range opts {
   118  		o(gl.cfg)
   119  	}
   120  	if gl.cfg.SlowThreshold <= 0 {
   121  		gl.cfg.SlowThreshold = 500 * time.Millisecond
   122  	}
   123  	gl.gormLevel = int32(gl.cfg.LogLevel)
   124  	gl.dynamicLevelEnabler = zap.NewAtomicLevelAt(getLogLevelOrDefaultForGorm(gl.cfg.LogLevel))
   125  
   126  	l := &xLogger{}
   127  	l.logger.Store(logger.
   128  		zap().
   129  		Named("Gorm").
   130  		WithOptions(zap.WrapCore(func(core zapcore.Core) zapcore.Core {
   131  			if core == nil {
   132  				panic("[XLogger] core is nil")
   133  			}
   134  			cc, ok := core.(xLogCore)
   135  			if !ok {
   136  				panic("[XLogger] core is not XLogCore")
   137  			}
   138  			var err error
   139  			if mc, ok := cc.(xLogMultiCore); ok && mc != nil {
   140  				if cc, err = WrapCoresNewLevelEnabler(mc,
   141  					gl.dynamicLevelEnabler,
   142  					componentCoreEncoderCfg(),
   143  				); err != nil {
   144  					panic(err)
   145  				}
   146  			} else {
   147  				if cc, err = WrapCoreNewLevelEnabler(cc,
   148  					gl.dynamicLevelEnabler,
   149  					componentCoreEncoderCfg(),
   150  				); err != nil {
   151  					panic(err)
   152  				}
   153  			}
   154  			return cc
   155  		})),
   156  	)
   157  	gl.logger = l
   158  	return gl
   159  }
   160  
   161  func getLogLevelOrDefaultForGorm(lvl glogger.LogLevel) zapcore.Level {
   162  	switch lvl {
   163  	case glogger.Info:
   164  		return zapcore.InfoLevel
   165  	case glogger.Warn:
   166  		return zapcore.WarnLevel
   167  	case glogger.Error:
   168  		return zapcore.ErrorLevel
   169  	case glogger.Silent:
   170  		fallthrough
   171  	default:
   172  		return zapcore.DebugLevel
   173  	}
   174  }
   175  
   176  type GormXLoggerOption func(*glogger.Config)
   177  
   178  func WithGormXLoggerSlowThreshold(threshold time.Duration) GormXLoggerOption {
   179  	return func(cfg *glogger.Config) {
   180  		cfg.SlowThreshold = threshold
   181  	}
   182  }
   183  
   184  func WithGormXLoggerLogLevel(lvl glogger.LogLevel) GormXLoggerOption {
   185  	return func(cfg *glogger.Config) {
   186  		cfg.LogLevel = lvl
   187  	}
   188  }
   189  
   190  func WithGormXLoggerIgnoreRecord404Err() GormXLoggerOption {
   191  	return func(cfg *glogger.Config) {
   192  		cfg.IgnoreRecordNotFoundError = true
   193  	}
   194  }
   195  
   196  func WithGormXLoggerParameterizedQueries() GormXLoggerOption {
   197  	return func(cfg *glogger.Config) {
   198  		cfg.ParameterizedQueries = true
   199  	}
   200  }