首页 > 其他分享 >gorm: 自定义日志

gorm: 自定义日志

时间:2024-12-14 13:10:02浏览次数:3  
标签:err 自定义 gorm content go 日志 data logger

一,官方文档:

地址

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)

 

标签:err,自定义,gorm,content,go,日志,data,logger
From: https://www.cnblogs.com/architectforest/p/18591993

相关文章

  • 群控系统服务端开发模式-应用开发-获取登录者今天操作日志
    一、后端api开放路由    在根目录下route文件夹下app.php文件中,在perimission的group中添加如下代码:Route::get('member/personal_log','permission.Member/personalLog');//获取个人信息操作接口二、后端api添加方法    在根目录下app文件夹下controlle......
  • 使用FastGPT制做一个AI网站日志分析器
    越来越的多网站面临每天上千次的扫描和各类攻击,及时发现攻击IP,并有效的屏蔽不良访问成为网站安全的重要保障,这里我们使用AI来完成对网站日志的日常分析。我们来使用FastGPT来制做一个AI网站日志析器,下面就开始:一:打开FastGPT,并生成一个简单会话二:书写提示词:##Background:......
  • 基于Python的日志数据可视化分析系统
    收藏关注不迷路!!......
  • Vue3自定义组件实现图片预览下载
    示例代码ImgPreview.vue<template> <divclass="preview"@click="onClick"> <divclass="preview-img"> <divclass="opt-box"> <CloudDownloadOutlined:style="{fontSize:'44p......
  • 转载:【AI系统】自定义计算图 IR
    模型转换涉及对模型的结构和参数进行重新表示。在进行模型转换时,通常需要理解模型的计算图结构,并根据目标格式的要求对其进行调整和转换,可能包括添加、删除或修改节点、边等操作,以确保转换后的计算图能够正确地表示模型的计算流程。本文主要介绍自定义计算图的方法以及模型转换的......
  • SpringBoot - 自定义启动Banner(附:使用艺术字体)
    我们知道 SpringBoot 项目启动时会在控制台打印出一个 banner,下面演示如何定制这个 banner。1,修改banner文字 (1)首先在 resources 目录下创建一个 banner.txt 文件。2,使用艺术字体(1)如果想要将文本设置为类似默认 banner 那样的艺术字体,可以借助下面几个在线......
  • QT日志类SimpleQtLogger的简单记录
    在现代软件开发中,日志记录是必不可少的部分。它不仅帮助开发者在调试和维护软件时了解程序的运行状态,还能提供关键的错误信息。对于使用Qt框架开发应用程序的开发者来说,选择一个合适的日志库至关重要。本文将详细介绍Qt日志库SimpleQtLogger的特点、安装方法、使用示例以及它在实......
  • Spring Security6 实现数据库自定义验证和jwt校验
    SpringSecurity6数据库自定义验证和jwt校验的简单实现以及个人解读版本springboot3.4.0mybatis-plus3.5.7jjwt0.12.6在使用jjwt的时候需要导入三个依赖分别是jjwt-api,jjwt-impl和jjwt-jackson,导入三个有点麻烦,所以可以直接导入jjwt依赖,这个依赖包含前面三个<depen......
  • 自定义资源支持:K8s Device Plugin 从原理到实现
    本文主要分析k8s中的device-plugin机制工作原理,并通过实现一个简单的device-plugin来加深理解。1.背景默认情况下,k8s中的Pod只能申请CPU和Memory这两种资源,就像下面这样:resources:requests:memory:"1024Mi"cpu:"100m"limits:memory:"2......
  • ASP .NET Core 中的请求-响应日志记录
    参考源码:https://download.csdn.net/download/hefeng_aspnet/90084914         记录ASP.NETCorehttp请求和响应是几乎每个.NET开发人员迟早都会面临的常见任务。长期以来,开发团队选择的最流行的方法似乎是编写自定义中间件。但是,既然 .NET6 我们有一个Micr......