gorm: 自定义日志

一,官方文档:

地址

https://gorm.io/zh_CN/docs/logger.html

参考的gorm logger代码实现:

https://github.com/go-gorm/gorm/blob/master/logger/logger.go

自定义日志需要实现的接口:

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)
}

二,代码例子:

1,自定义日志类:

package gormfilelogger

import (
	"bytes"
	"context"
	"errors"
	"fmt"
	gormlogger "gorm.io/gorm/logger"
	"gorm.io/gorm/utils"
	"log"
	"os"
	"runtime"
	"strings"
	"time"
)


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"
)

const (
	// Silent silent log level
	Silent gormlogger.LogLevel = iota + 1
	// Error error log level
	Error
	// Warn warn log level
	Warn
	// Info info log level
	Info
)

//gorm原日志的配置
type FileLogConfig struct {
	gormlogger.Config
}

//定义日志类
type StdFileLogger struct {
	FileLogConfig
	infoStr, warnStr, errStr            string
	traceStr, traceErrStr, traceWarnStr string
	path,name string
}

//初始化,区分有颜色和无颜色
func NewStdFileLogger(config FileLogConfig,path,name string) *StdFileLogger {
	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 &StdFileLogger{
		FileLogConfig:    config,
		path:path,
		name:name,
		//Loggers:      loggers,
		infoStr:      infoStr,
		warnStr:      warnStr,
		errStr:       errStr,
		traceStr:     traceStr,
		traceWarnStr: traceWarnStr,
		traceErrStr:  traceErrStr,
	}
}

func (logger *StdFileLogger) printf(msg string, data ...interface{}) {
    //得到要打印的内容
	content:=fmt.Sprintf(msg, data...)
	//打印到控制台
	log.Printf(content)
	//得到文件名:
	now := time.Now()
	dateStr:=now.Format("2006-01-02")
	filePath := logger.path+"/"+logger.name+"_"+dateStr+".log"

	// 替换掉彩色打印符号
	content = strings.ReplaceAll(content, Reset, "")
	content = strings.ReplaceAll(content, Red, "")
	content = strings.ReplaceAll(content, Green, "")
	content = strings.ReplaceAll(content, Yellow, "")
	content = strings.ReplaceAll(content, Blue, "")
	content = strings.ReplaceAll(content, Magenta, "")
	content = strings.ReplaceAll(content, Cyan, "")
	content = strings.ReplaceAll(content, White, "")
	content = strings.ReplaceAll(content, BlueBold, "")
	content = strings.ReplaceAll(content, MagentaBold, "")
	content = strings.ReplaceAll(content, RedBold, "")
	content = strings.ReplaceAll(content, YellowBold, "")

	// 格式化时间
	formatted := now.Format("2006-01-02 15:04:05")
	content = formatted+" "+content
	//保存到文件
	logger.LogToFile(filePath,content+"\n")
}

//日志的级别
func (logger *StdFileLogger) LogMode(lv gormlogger.LogLevel) gormlogger.Interface {
	logger.LogLevel = lv
	return logger
}

//info
func (logger *StdFileLogger) Info(ctx context.Context, msg string, data ...interface{}) {
	if logger.LogLevel >= Info {
		logger.printf(logger.infoStr+msg, append([]interface{}{utils.FileWithLineNum()}, data...)...)
	}
}

//warn
func (logger *StdFileLogger) Warn(ctx context.Context, msg string, data ...interface{}) {
	if logger.LogLevel >= Warn {
		logger.printf(logger.infoStr+msg, append([]interface{}{utils.FileWithLineNum()}, data...)...)
	}
}

//error
func (logger *StdFileLogger) Error(ctx context.Context, msg string, data ...interface{}) {
	if logger.LogLevel >= Error {
		//fmt.Println("当前是错误日志:")
		logger.printf("当前是错误日志:\n")
		logger.printf(logger.infoStr+msg, append([]interface{}{utils.FileWithLineNum()}, data...)...)
	}
}

// Trace 打印sql语句
func (logger *StdFileLogger) Trace(ctx context.Context, begin time.Time, fc func() (sql string, rowsAffected int64), err error) {
	if logger.LogLevel <= Silent {
		return
	}
	
	elapsed := time.Since(begin)
	switch {
	case err != nil && logger.LogLevel >= Error && (!errors.Is(err, gormlogger.ErrRecordNotFound) || !logger.IgnoreRecordNotFoundError):
		sql, rows := fc()
        //得到stack
		stack:= logger.PrintStackTrace(err)
		if rows == -1 {
			logger.printf(logger.traceErrStr+"\n"+stack, utils.FileWithLineNum(), err, float64(elapsed.Nanoseconds())/1e6, "-", sql)
		} else {
			logger.printf(logger.traceErrStr+"\n"+stack, utils.FileWithLineNum(), err, float64(elapsed.Nanoseconds())/1e6, rows, sql)
		}
	case elapsed > logger.SlowThreshold && logger.SlowThreshold != 0 && logger.LogLevel >= Warn:
		sql, rows := fc()
		slowLog := fmt.Sprintf("SLOW SQL >= %v", logger.SlowThreshold)
		if rows == -1 {
			logger.printf(logger.traceWarnStr, utils.FileWithLineNum(), slowLog, float64(elapsed.Nanoseconds())/1e6, "-", sql)
		} else {
			logger.printf(logger.traceWarnStr, utils.FileWithLineNum(), slowLog, float64(elapsed.Nanoseconds())/1e6, rows, sql)
		}
	case logger.LogLevel == Info:
		sql, rows := fc()
		if rows == -1 {
			logger.printf(logger.traceStr, utils.FileWithLineNum(), float64(elapsed.Nanoseconds())/1e6, "-", sql)
		} else {
			logger.printf(logger.traceStr, utils.FileWithLineNum(), float64(elapsed.Nanoseconds())/1e6, rows, sql)
		}
	}
}

//出错时打印错误的堆栈信息
func (logger *StdFileLogger) PrintStackTrace(err error) string {
	// 创建一个缓冲区用于存储堆栈信息
	buf := bytes.NewBuffer(nil)

	// 获取当前goroutine的堆栈信息
	for i := 0; ; i++ {
		pc, file, line, ok := runtime.Caller(i)
		if !ok {
			break
		}

		fmt.Fprintf(buf, "%d: %s:%d (0x%x)\n", i, file, line, pc)
	}

	// 打印堆栈信息
	//fmt.Println(buf.String())
	return buf.String()
}

//写内容到文件
func (logger *StdFileLogger) LogToFile(filename,msg string) {
	// 输出到文件
	file, err := os.OpenFile(filename, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
	if err != nil {
		fmt.Println("日志文件的打开错误 :", err)
	}
	defer file.Close()
	if _, err := file.WriteString(msg); err != nil {
		fmt.Println("写入日志文件错误 :", err)
	}
}

2,在创建到数据的连接时调用自定义的日志类:

func SetupDBLink() (error) {
	var err error
	
    //创建日志类
	path:="/data/goapp/logs/"    //日志保存目录
    name:="gormfile"             //日志名字前缀
	mylogger := gormfilelogger.NewStdFileLogger(gormfilelogger.FileLogConfig{
		Config: logger.Config{  // gorm日志原始配置项
			SlowThreshold:             200 * time.Millisecond,
			IgnoreRecordNotFoundError: false,
			Colorful:                  true,
		},
	},path,name).LogMode(gormfilelogger.Info)
	
    //生成dsn
	dsn := fmt.Sprintf("%s:%s@tcp(%s)/%s?charset=%s&parseTime=%t&loc=Local",
		"username",
		"password",
		"127.0.0.1:3306",
		"databasename",
		"utf8",
		true, )

    //标准输出,替换掉
	//DBLink, err = gorm.Open(mysql.Open(dsn), &gorm.Config{Logger: logger.Default.LogMode(logger.Info),})
	DBLink, err = gorm.Open(mysql.Open(dsn), &gorm.Config{Logger: mylogger,})
	//DBLink.Logger.LogMode(logger.Info)
	if err != nil {
		return err
	}
	
	sqlDB, _ := DBLink.DB()
	// SetMaxIdleConns 设置空闲连接池中连接的最大数量
	sqlDB.SetMaxIdleConns(10)
	// SetMaxOpenConns 设置打开数据库连接的最大数量
	sqlDB.SetMaxOpenConns(30)
	// SetConnMaxLifetime 设置了连接可复用的最大时间
	sqlDB.SetConnMaxLifetime(time.Hour)

	return nil
}

 

三,测试效果:

日志内容例子:

2024-12-09 20:39:47 /data/test/service/userService.go:91 Error 1054 (42S22): Unknown column 'test' in 'field list'
[0.522ms] [rows:-] SELECT id,title,test FROM `image` WHERE uid=1851 and status in (0,1) LIMIT 20
0: /data/test/pkg/gormfilelogger/gormlogger2.go:221 (0x86340b)
1: /data/goapp/industry/pkg/gormfilelogger/gormlogger2.go:191 (0x862f57)
2: /data/gopath/pkg/mod/gorm.io/gorm@v1.25.12/callbacks.go:134 (0x7fd72a)
3: /data/gopath/pkg/mod/gorm.io/gorm@v1.25.12/finisher_api.go:516 (0x8082b1)
4: /data/test/service/userService.go:91 (0x9f0656)
5: /data/test/controller/userController.go:457 (0xa825fa)
6: /data/gopath/pkg/mod/github.com/gofiber/fiber/v2@v2.52.5/router.go:145 (0x9ae1fd)
7: /data/gopath/pkg/mod/github.com/gofiber/fiber/v2@v2.52.5/ctx.go:1034 (0x997e8c)
8: /data/gopath/pkg/mod/github.com/gofiber/fiber/v2@v2.52.5/router.go:425 (0x9afe30)
9: /data/gopath/pkg/mod/github.com/gofiber/fiber/v2@v2.52.5/ctx.go:1031 (0x997e7c)
10: /data/gopath/pkg/mod/github.com/gofiber/fiber/v2@v2.52.5/middleware/recover/recover.go:43 (0xa8681a)
11: /data/gopath/pkg/mod/github.com/gofiber/fiber/v2@v2.52.5/router.go:145 (0x9ae1fd)
12: /data/gopath/pkg/mod/github.com/gofiber/fiber/v2@v2.52.5/router.go:172 (0x9ae428)
13: /data/gopath/pkg/mod/github.com/valyala/fasthttp@v1.57.0/server.go:2385 (0x968c30)
14: /data/gopath/pkg/mod/github.com/valyala/fasthttp@v1.57.0/workerpool.go:225 (0x974851)
15: /data/gopath/pkg/mod/github.com/valyala/fasthttp@v1.57.0/workerpool.go:197 (0x9745f1)
16: /usr/local/soft/go/src/runtime/asm_amd64.s:1700 (0x478160)

 

posted @ 2024-12-14 13:05  刘宏缔的架构森林  阅读(40)  评论(0编辑  收藏  举报