zhangqian
2024-12-12 e012f2c5e7c6bc79277ff3317efae7d952a18d22
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
package mysqlx
 
import (
    "context"
    "errors"
    "path/filepath"
    "runtime"
    "strings"
    "time"
 
    "go.uber.org/zap"
    "go.uber.org/zap/zapcore"
    "gorm.io/gorm"
    gormlogger "gorm.io/gorm/logger"
)
 
type ContextFn func(ctx context.Context) []zapcore.Field
 
type Logger struct {
    ZapLogger                 *zap.Logger
    LogLevel                  gormlogger.LogLevel
    SlowThreshold             time.Duration
    SkipCallerLookup          bool
    IgnoreRecordNotFoundError bool
    Context                   ContextFn
}
 
func New(zapLogger *zap.Logger) Logger {
    return Logger{
        ZapLogger:                 zapLogger,
        LogLevel:                  gormlogger.Warn,
        SlowThreshold:             100 * time.Millisecond,
        SkipCallerLookup:          false,
        IgnoreRecordNotFoundError: true,
        Context:                   nil,
    }
}
 
func (l Logger) SetAsDefault() {
    gormlogger.Default = l
}
 
func (l Logger) LogMode(level gormlogger.LogLevel) gormlogger.Interface {
    return Logger{
        ZapLogger:                 l.ZapLogger,
        SlowThreshold:             l.SlowThreshold,
        LogLevel:                  level,
        SkipCallerLookup:          l.SkipCallerLookup,
        IgnoreRecordNotFoundError: l.IgnoreRecordNotFoundError,
        Context:                   l.Context,
    }
}
 
func (l Logger) Info(ctx context.Context, str string, args ...interface{}) {
    if l.LogLevel < gormlogger.Info {
        return
    }
    l.logger(ctx).Sugar().Debugf(str, args...)
}
 
func (l Logger) Warn(ctx context.Context, str string, args ...interface{}) {
    if l.LogLevel < gormlogger.Warn {
        return
    }
    l.logger(ctx).Sugar().Warnf(str, args...)
}
 
func (l Logger) Error(ctx context.Context, str string, args ...interface{}) {
    if l.LogLevel < gormlogger.Error {
        return
    }
    l.logger(ctx).Sugar().Errorf(str, args...)
}
 
func (l Logger) Trace(ctx context.Context, begin time.Time, fc func() (string, int64), err error) {
    if l.LogLevel <= 0 {
        return
    }
    elapsed := time.Since(begin)
    logger := l.logger(ctx)
    switch {
    case err != nil && l.LogLevel >= gormlogger.Error && (!l.IgnoreRecordNotFoundError || !errors.Is(err, gorm.ErrRecordNotFound)):
        sql, rows := fc()
        logger.Error("trace", zap.Error(err), zap.Duration("elapsed", elapsed), zap.Int64("rows", rows), zap.String("sql", sql))
    case l.SlowThreshold != 0 && elapsed > l.SlowThreshold && l.LogLevel >= gormlogger.Warn:
        sql, rows := fc()
        logger.Warn("trace", zap.Duration("elapsed", elapsed), zap.Int64("rows", rows), zap.String("sql", sql))
    case l.LogLevel >= gormlogger.Info:
        sql, rows := fc()
        logger.Debug("trace", zap.Duration("elapsed", elapsed), zap.Int64("rows", rows), zap.String("sql", sql))
    }
}
 
var (
    gormPackage    = filepath.Join("gorm.io", "gorm")
    zapgormPackage = filepath.Join("moul.io", "zapgorm2")
)
 
func (l Logger) logger(ctx context.Context) *zap.Logger {
    logger := l.ZapLogger
    if l.Context != nil {
        fields := l.Context(ctx)
        logger = logger.With(fields...)
    }
 
    if l.SkipCallerLookup {
        return logger
    }
 
    for i := 2; i < 15; i++ {
        _, file, _, ok := runtime.Caller(i)
        switch {
        case !ok:
        case strings.HasSuffix(file, "_test.go"):
        case strings.Contains(file, gormPackage):
        case strings.Contains(file, zapgormPackage):
        default:
            return logger.WithOptions(zap.AddCallerSkip(i))
        }
    }
    return logger
}