github.com/goravel/framework@v1.13.9/database/gorm/logger.go (about)

     1  package gorm
     2  
     3  import (
     4  	"context"
     5  	"errors"
     6  	"fmt"
     7  	"net"
     8  	"regexp"
     9  	"runtime"
    10  	"strconv"
    11  	"strings"
    12  	"time"
    13  
    14  	"gorm.io/gorm/logger"
    15  )
    16  
    17  func NewLogger(writer logger.Writer, config logger.Config) logger.Interface {
    18  	var (
    19  		infoStr      = "%s\n[Orm] "
    20  		warnStr      = "%s\n[Orm] "
    21  		errStr       = "%s\n[Orm] "
    22  		traceStr     = "%s\n[%.3fms] [rows:%v] %s"
    23  		traceWarnStr = "%s %s\n[%.3fms] [rows:%v] %s"
    24  		traceErrStr  = "%s %s\n[%.3fms] [rows:%v] %s"
    25  	)
    26  
    27  	if config.Colorful {
    28  		infoStr = logger.Green + "%s\n" + logger.Reset + logger.Green + "[Orm] " + logger.Reset
    29  		warnStr = logger.BlueBold + "%s\n" + logger.Reset + logger.Magenta + "[Orm] " + logger.Reset
    30  		errStr = logger.Magenta + "%s\n" + logger.Reset + logger.Red + "[Orm] " + logger.Reset
    31  		traceStr = logger.Green + "%s\n" + logger.Reset + logger.Yellow + "[%.3fms] " + logger.BlueBold + "[rows:%v]" + logger.Reset + " %s"
    32  		traceWarnStr = logger.Green + "%s " + logger.Yellow + "%s\n" + logger.Reset + logger.RedBold + "[%.3fms] " + logger.Yellow + "[rows:%v]" + logger.Magenta + " %s" + logger.Reset
    33  		traceErrStr = logger.RedBold + "%s " + logger.MagentaBold + "%s\n" + logger.Reset + logger.Yellow + "[%.3fms] " + logger.BlueBold + "[rows:%v]" + logger.Reset + " %s"
    34  	}
    35  
    36  	return &Logger{
    37  		Writer:       writer,
    38  		Config:       config,
    39  		infoStr:      infoStr,
    40  		warnStr:      warnStr,
    41  		errStr:       errStr,
    42  		traceStr:     traceStr,
    43  		traceWarnStr: traceWarnStr,
    44  		traceErrStr:  traceErrStr,
    45  	}
    46  }
    47  
    48  type Logger struct {
    49  	logger.Writer
    50  	logger.Config
    51  	infoStr, warnStr, errStr            string
    52  	traceStr, traceErrStr, traceWarnStr string
    53  }
    54  
    55  // LogMode log mode
    56  func (l *Logger) LogMode(level logger.LogLevel) logger.Interface {
    57  	newlogger := *l
    58  	newlogger.LogLevel = level
    59  	return &newlogger
    60  }
    61  
    62  // Info print info
    63  func (l Logger) Info(ctx context.Context, msg string, data ...any) {
    64  	if l.LogLevel >= logger.Info {
    65  		l.Printf(l.infoStr+msg, append([]any{FileWithLineNum()}, data...)...)
    66  	}
    67  }
    68  
    69  // Warn print warn messages
    70  func (l Logger) Warn(ctx context.Context, msg string, data ...any) {
    71  	if l.LogLevel >= logger.Warn {
    72  		l.Printf(l.warnStr+msg, append([]any{FileWithLineNum()}, data...)...)
    73  	}
    74  }
    75  
    76  // Error print error messages
    77  func (l Logger) Error(ctx context.Context, msg string, data ...any) {
    78  	// Let upper layer function deals with connection refused error
    79  	var cancel bool
    80  	for _, item := range data {
    81  		if tempItem, ok := item.(*net.OpError); ok {
    82  			if strings.Contains(tempItem.Error(), "connection refused") {
    83  				return
    84  			}
    85  
    86  		}
    87  		if tempItem, ok := item.(error); ok {
    88  			// Avoid duplicate output
    89  			if strings.Contains(tempItem.Error(), "Access denied") {
    90  				cancel = true
    91  			}
    92  		}
    93  	}
    94  
    95  	if cancel {
    96  		return
    97  	}
    98  
    99  	if l.LogLevel >= logger.Error {
   100  		l.Printf(l.errStr+msg, append([]any{FileWithLineNum()}, data...)...)
   101  	}
   102  }
   103  
   104  // Trace print sql message
   105  func (l Logger) Trace(ctx context.Context, begin time.Time, fc func() (string, int64), err error) {
   106  	if l.LogLevel <= logger.Silent {
   107  		return
   108  	}
   109  
   110  	elapsed := time.Since(begin)
   111  	switch {
   112  	case err != nil && l.LogLevel >= logger.Error && (!errors.Is(err, logger.ErrRecordNotFound) || !l.IgnoreRecordNotFoundError):
   113  		sql, rows := fc()
   114  		if rows == -1 {
   115  			l.Printf(l.traceErrStr, FileWithLineNum(), err, float64(elapsed.Nanoseconds())/1e6, "-", sql)
   116  		} else {
   117  			l.Printf(l.traceErrStr, FileWithLineNum(), err, float64(elapsed.Nanoseconds())/1e6, rows, sql)
   118  		}
   119  	case elapsed > l.SlowThreshold && l.SlowThreshold != 0 && l.LogLevel >= logger.Warn:
   120  		sql, rows := fc()
   121  		slowLog := fmt.Sprintf("SLOW SQL >= %v", l.SlowThreshold)
   122  		if rows == -1 {
   123  			l.Printf(l.traceWarnStr, FileWithLineNum(), slowLog, float64(elapsed.Nanoseconds())/1e6, "-", sql)
   124  		} else {
   125  			l.Printf(l.traceWarnStr, FileWithLineNum(), slowLog, float64(elapsed.Nanoseconds())/1e6, rows, sql)
   126  		}
   127  	case l.LogLevel == logger.Info:
   128  		sql, rows := fc()
   129  		if rows == -1 {
   130  			l.Printf(l.traceStr, FileWithLineNum(), float64(elapsed.Nanoseconds())/1e6, "-", sql)
   131  		} else {
   132  			l.Printf(l.traceStr, FileWithLineNum(), float64(elapsed.Nanoseconds())/1e6, rows, sql)
   133  		}
   134  	}
   135  }
   136  
   137  // FileWithLineNum return the file name and line number of the current file
   138  func FileWithLineNum() string {
   139  	_, file, _, _ := runtime.Caller(0)
   140  	gormSourceDir := regexp.MustCompile(`utils.utils\.go`).ReplaceAllString(file, "")
   141  	goravelSourceDir := "database/gorm.go"
   142  
   143  	// the second caller usually from gorm internal, so set i start from 5
   144  	for i := 5; i < 15; i++ {
   145  		_, file, line, ok := runtime.Caller(i)
   146  		if ok && ((!strings.HasPrefix(file, gormSourceDir) && !strings.Contains(file, goravelSourceDir)) || strings.HasSuffix(file, "_test.go")) {
   147  			return file + ":" + strconv.FormatInt(int64(line), 10)
   148  		}
   149  	}
   150  
   151  	return ""
   152  }