214 lines
		
	
	
		
			5.8 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			214 lines
		
	
	
		
			5.8 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
package logger
 | 
						|
 | 
						|
import (
 | 
						|
	"context"
 | 
						|
	"errors"
 | 
						|
	"fmt"
 | 
						|
	"io"
 | 
						|
	"log"
 | 
						|
	"os"
 | 
						|
	"time"
 | 
						|
 | 
						|
	"gorm.io/gorm/utils"
 | 
						|
)
 | 
						|
 | 
						|
// 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 int
 | 
						|
 | 
						|
const (
 | 
						|
	// Silent silent log level
 | 
						|
	Silent LogLevel = iota + 1
 | 
						|
	// Error error log level
 | 
						|
	Error
 | 
						|
	// Warn warn log level
 | 
						|
	Warn
 | 
						|
	// Info info log level
 | 
						|
	Info
 | 
						|
)
 | 
						|
 | 
						|
// Writer log writer interface
 | 
						|
type Writer interface {
 | 
						|
	Printf(string, ...interface{})
 | 
						|
}
 | 
						|
 | 
						|
// Config logger config
 | 
						|
type Config struct {
 | 
						|
	SlowThreshold             time.Duration
 | 
						|
	Colorful                  bool
 | 
						|
	IgnoreRecordNotFoundError bool
 | 
						|
	ParameterizedQueries      bool
 | 
						|
	LogLevel                  LogLevel
 | 
						|
}
 | 
						|
 | 
						|
// Interface logger interface
 | 
						|
type Interface interface {
 | 
						|
	LogMode(LogLevel) Interface
 | 
						|
	Info(context.Context, string, ...interface{})
 | 
						|
	Warn(context.Context, string, ...interface{})
 | 
						|
	Error(context.Context, string, ...interface{})
 | 
						|
	Trace(ctx context.Context, begin time.Time, fc func() (sql string, rowsAffected int64), err error)
 | 
						|
}
 | 
						|
 | 
						|
var (
 | 
						|
	// Discard logger will print any log to io.Discard
 | 
						|
	Discard = New(log.New(io.Discard, "", log.LstdFlags), Config{})
 | 
						|
	// Default Default logger
 | 
						|
	Default = New(log.New(os.Stdout, "\r\n", log.LstdFlags), Config{
 | 
						|
		SlowThreshold:             200 * time.Millisecond,
 | 
						|
		LogLevel:                  Warn,
 | 
						|
		IgnoreRecordNotFoundError: false,
 | 
						|
		Colorful:                  true,
 | 
						|
	})
 | 
						|
	// Recorder logger records running SQL into a recorder instance
 | 
						|
	Recorder = traceRecorder{Interface: Default, BeginAt: time.Now()}
 | 
						|
)
 | 
						|
 | 
						|
// New initialize logger
 | 
						|
func New(writer Writer, config Config) Interface {
 | 
						|
	var (
 | 
						|
		infoStr      = "%s\n[info] "
 | 
						|
		warnStr      = "%s\n[warn] "
 | 
						|
		errStr       = "%s\n[error] "
 | 
						|
		traceStr     = "%s\n[%.3fms] [rows:%v] %s"
 | 
						|
		traceWarnStr = "%s %s\n[%.3fms] [rows:%v] %s"
 | 
						|
		traceErrStr  = "%s %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 = Green + "%s\n" + Reset + Yellow + "[%.3fms] " + BlueBold + "[rows:%v]" + Reset + " %s"
 | 
						|
		traceWarnStr = Green + "%s " + Yellow + "%s\n" + Reset + RedBold + "[%.3fms] " + Yellow + "[rows:%v]" + Magenta + " %s" + Reset
 | 
						|
		traceErrStr = RedBold + "%s " + MagentaBold + "%s\n" + Reset + Yellow + "[%.3fms] " + BlueBold + "[rows:%v]" + Reset + " %s"
 | 
						|
	}
 | 
						|
 | 
						|
	return &logger{
 | 
						|
		Writer:       writer,
 | 
						|
		Config:       config,
 | 
						|
		infoStr:      infoStr,
 | 
						|
		warnStr:      warnStr,
 | 
						|
		errStr:       errStr,
 | 
						|
		traceStr:     traceStr,
 | 
						|
		traceWarnStr: traceWarnStr,
 | 
						|
		traceErrStr:  traceErrStr,
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
type logger struct {
 | 
						|
	Writer
 | 
						|
	Config
 | 
						|
	infoStr, warnStr, errStr            string
 | 
						|
	traceStr, traceErrStr, traceWarnStr string
 | 
						|
}
 | 
						|
 | 
						|
// LogMode log mode
 | 
						|
func (l *logger) LogMode(level LogLevel) 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.Printf(l.infoStr+msg, append([]interface{}{utils.FileWithLineNum()}, data...)...)
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
// Warn print warn messages
 | 
						|
func (l *logger) Warn(ctx context.Context, msg string, data ...interface{}) {
 | 
						|
	if l.LogLevel >= Warn {
 | 
						|
		l.Printf(l.warnStr+msg, append([]interface{}{utils.FileWithLineNum()}, data...)...)
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
// Error print error messages
 | 
						|
func (l *logger) Error(ctx context.Context, msg string, data ...interface{}) {
 | 
						|
	if l.LogLevel >= Error {
 | 
						|
		l.Printf(l.errStr+msg, append([]interface{}{utils.FileWithLineNum()}, 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.Printf(l.traceErrStr, utils.FileWithLineNum(), err, float64(elapsed.Nanoseconds())/1e6, "-", sql)
 | 
						|
		} else {
 | 
						|
			l.Printf(l.traceErrStr, utils.FileWithLineNum(), 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.Printf(l.traceWarnStr, utils.FileWithLineNum(), slowLog, float64(elapsed.Nanoseconds())/1e6, "-", sql)
 | 
						|
		} else {
 | 
						|
			l.Printf(l.traceWarnStr, utils.FileWithLineNum(), slowLog, float64(elapsed.Nanoseconds())/1e6, rows, sql)
 | 
						|
		}
 | 
						|
	case l.LogLevel == Info:
 | 
						|
		sql, rows := fc()
 | 
						|
		if rows == -1 {
 | 
						|
			l.Printf(l.traceStr, utils.FileWithLineNum(), float64(elapsed.Nanoseconds())/1e6, "-", sql)
 | 
						|
		} else {
 | 
						|
			l.Printf(l.traceStr, utils.FileWithLineNum(), float64(elapsed.Nanoseconds())/1e6, rows, sql)
 | 
						|
		}
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
// ParamsFilter filter params
 | 
						|
func (l *logger) ParamsFilter(ctx context.Context, sql string, params ...interface{}) (string, []interface{}) {
 | 
						|
	if l.Config.ParameterizedQueries {
 | 
						|
		return sql, nil
 | 
						|
	}
 | 
						|
	return sql, params
 | 
						|
}
 | 
						|
 | 
						|
type traceRecorder struct {
 | 
						|
	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
 | 
						|
}
 |