目 录CONTENT

文章目录

Golang 日志

Sakura
2023-12-18 / 0 评论 / 0 点赞 / 36 阅读 / 5741 字 / 正在检测是否收录...

Golang 日志

1. Logger

  • Logger 用来记录程序运行的中间状态,通常用于排查、定位问题和业务数据的统计

  • Logger 应该具备的功能:

    • 分级,DEBUG、INFO、WARN、ERROR,方便定位问题、屏蔽低级别日志

    • 日志里需要显示时间以及输出日志的文件名和行号

    • 并发调用,多协程写同一个日志文件

    • 日志分割,通常一天一个日志文件,日志量太大时也可一小时一个日志文件 太久的日志文件需要定期清理

2. Logrus

func InitLogrus(logFile string, logLevel logrus.Level) {
	logFile = "log/" + logFile
	LogRus = logrus.New()
	LogRus.SetLevel(logLevel)
	LogRus.SetFormatter(&logrus.TextFormatter{
		TimestampFormat: "2006-01-02 15:04:05.000", // 显示ms
	}) // 选择文本格式或者JSON格式
	fout, err := rotatelogs.New(
		logFile+".%Y%m%d%H",                      //指定日志文件的路径和名称
		rotatelogs.WithLinkName(logFile),         //为最新的一份日志创建软链接,在windows上就是一个快捷方式(并不是把日志写入了2个文件)
		rotatelogs.WithRotationTime(1*time.Hour), //每隔1小时生成一份新的日志文件
		rotatelogs.WithMaxAge(7*24*time.Hour),    //只留最近7天的日志,或使用WithRotationCount只保留最近的几份日志
	)
	if err != nil {
		panic(err)
	}
	LogRus.SetOutput(fout)       //设置日志文件
	LogRus.SetReportCaller(true) //输出是从哪里调起的日志打印,这样日志里会多出func和file
}

3. zap

4. zerolog

5. 标准库

var (
	myLogger *log.Logger
)

func init() {
	if logOut, err := os.OpenFile("Sakura.log", os.O_CREATE|os.O_APPEND|os.O_WRONLY, 0o664); err != nil { //注意是APPEND模式
		panic(err)
	} else {
		myLogger = log.New(logOut, "[Sakura] ", log.Ldate|log.Ltime|log.Lmicroseconds|log.Llongfile) //Llongfile会输出go文件的绝对路径和行号,但是是执行log.Logger.Printf的行号,不是调用Debug()或Info()的行号
	}
} 

func Debug(format string, v ...any) {
	myLogger.Printf("DEBUG "+format, v...) //如果不加...,会把v当成一个slice参数来处理,即只能对应到第一个%占位符
}

func Info(format string, v ...any) {
	myLogger.Printf("INFO "+format, v...)
}

func main() {
	Debug("%s", "TCP监听失败") //输出到日志文件
	Info("%s", "UDP连接失败")
}

缺陷:

  1. 日志文件中打印的行号有问题,需求的是调用 Debug 的位置的行号,而不是 Debug() 和 Info() 两个函数的位置

6. 自行实现高性能 Logger

package CustomerLogger

import (
	"log"
	"os"
	"runtime"
	"strconv"
	"strings"
	"time"
)

// 自行实现高性能日志需求:
// 1. 日志要能同时输出到文件和终端
// 2. 日志分级
// 3. 显示调用的位置

var (
	// log.Logger 支持并发调用
	// 这些负责打印到文件
	debugFile *log.Logger
	infoFile  *log.Logger
	warnFile  *log.Logger
	errorFile *log.Logger
	// 这些负责在终端打印
	debugStdout *log.Logger
	infoStdout  *log.Logger
	warnStdout  *log.Logger
	errorStdout *log.Logger

	logfile string
)

func InitLogger() {
	// 日志命名为当天时间
	logFileName := setLogFileName()

	if fileOut, err := os.OpenFile(logFileName, os.O_CREATE|os.O_APPEND|os.O_WRONLY, 0666); err != nil {
		panic(err)
	} else {
		debugFile = log.New(fileOut, "[Debug]", log.LstdFlags) //LstdFlags == log.time + log.data
		infoFile = log.New(fileOut, "[Indo]", log.LstdFlags)
		warnFile = log.New(fileOut, "[Warn]", log.LstdFlags)
		errorFile = log.New(fileOut, "[Error]", log.LstdFlags)
	}

	debugStdout = log.New(os.Stdout, "[Debug]", log.LstdFlags) //LstdFlags == log.time + log.data
	infoStdout = log.New(os.Stdout, "[Info]", log.LstdFlags)
	warnStdout = log.New(os.Stdout, "[Warn]", log.LstdFlags)
	errorStdout = log.New(os.Stdout, "[Error]", log.LstdFlags)
}

// 设置日志文件
func setLogFileName() string {
	// 自定义每小时为日志文件或者每天为日志文件
	filename := time.Now().Format("2006_01_02")
	filename = "log_" + filename + ".log"
	return filename
}

// 获取当前运行代码的文件名和行号,并且拼接
func getFileAndLineNo() string {
	_, file, line, _ := runtime.Caller(2) //返回函数名、文件名、行号。runtime.Caller(3)是第3层调用堆栈,getFileAndLineNo()第0层 -->  addPrefix()第1层 --> Info()第2层 --> 调用logger.Info()的地方第3层

	arr := strings.Split(file, "/")
	if len(arr) > 3 {
		arr = arr[len(arr)-3:] //不需要完整的绝对路径,只取最末3级路径
	}
	return strings.Join(arr, "/") + ":" + strconv.Itoa(line) //文件名加行号
}

// 计算

func Debug(format string, v ...any) {
	funcname := getFileAndLineNo()
	debugFile.Printf(funcname+" "+format, v...)
	//debugStdout.Printf(funcname+" "+format, v...)
}

func Info(format string, v ...any) {
	funcname := getFileAndLineNo()

	infoFile.Printf(funcname+" "+format, v...)
	infoStdout.Printf(funcname+" "+format, v...)

}

func Warn(format string, v ...any) {
	funcname := getFileAndLineNo()

	warnFile.Printf(funcname+" "+format, v...)
	warnStdout.Printf(funcname+" "+format, v...)
}

func Error(format string, v ...any) {
	funcname := getFileAndLineNo()

	errorFile.Printf(funcname+" "+format, v...)
	errorStdout.Printf(funcname+" "+format, v...)

}
// 进行性能测试
go test -bench=BenchmarkLogger -v -benchmem
  • 只开启文件写入日志

  • 文件写入和终端打印日志都开启

0

评论区