前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Go中日志库

Go中日志库

作者头像
码客说
发布2024-03-29 13:06:24
950
发布2024-03-29 13:06:24
举报
文章被收录于专栏:码客码客

自定义日志(推荐)

这里实现了控制台和文件日志

/utils/zlog/zlog.go

代码语言:javascript
复制
package zlog

import (
	"fmt"
	"os"
	"path/filepath"
	"runtime"
	"strconv"
	"strings"
	"time"
)

type LogType int

const (
	Console LogType = iota
	File
	ConsoleFile
)

var logType = Console
var projectPath = ""

// Init 在main.go中调用
func Init() {
	_, file, _, _ := runtime.Caller(1)
	dirPath := filepath.Dir(file)
	dirPath = strings.ReplaceAll(dirPath, "\\", "/")
	projectPath = dirPath + "/"
}

func SetLogType(mType LogType) {
	logType = mType
}

func LogDebug(msg string, a ...any) {
	logRecord(fmt.Sprintf(msg, a...), "DEBUG")
}

func LogError(msg string, a ...any) {
	logRecord(fmt.Sprintf(msg, a...), "ERROR")
}

func logRecord(msg string, level string) {
	now := time.Now().Format("2006-01-02 15:04:05")
	_, mFilepath, line, _ := runtime.Caller(2)
	if projectPath != "" {
		mFilepath = strings.Replace(mFilepath, projectPath, "", 1)
	}
	logStr := "[" + now + "]\t" + level + "\t" + msg + "\t" + mFilepath + ":" + strconv.Itoa(line)
	switch logType {
	case Console:
		writeConsole(logStr)
	case File:
		writeFile(logStr, level)
	case ConsoleFile:
		writeConsole(logStr)
		writeFile(logStr, level)
	}
}

func writeConsole(logStr string) {
	fmt.Println(logStr)
}

func writeFile(logStr string, level string) {
	ymd := time.Now().Format("2006-01-02")
	err := os.MkdirAll("logs", os.ModePerm)
	if err != nil {
		return
	}
	fileName := "logs/" + level + "-" + ymd + ".log"
	logFile, logFileErr := os.OpenFile(fileName, os.O_APPEND|os.O_WRONLY|os.O_CREATE, 0644)
	if logFileErr != nil {
		return
	}
	defer logFile.Close()
	_, err = logFile.WriteString(logStr)
	if err != nil {
		return
	}
	return
}

调用

代码语言:javascript
复制
//先调用这个方法 可以不调用
zlog.Init()
//设置日志的输出类型 可以不调用
zlog.SetLogType(zlog.Console)
//打印日志的地方这样调用
zlog.LogDebug("测试日志")

zlog.LogDebug("insert success, last id:%d\n", 123)

和Gin结合

基本封装

当我们使用Gin框架的时候,我们可以简单的封装一下

代码语言:javascript
复制
package zlog

import (
	"fmt"
	"github.com/gin-gonic/gin"
	"time"
)

func Debug(format string, values ...any) {
	now := time.Now().Format("2006/01/02 15:04:05")
	f := fmt.Sprintf("[Debug] %s %s\n", now, format)
	_, _ = fmt.Fprintf(gin.DefaultWriter, f, values...)
}

func Error(format string, values ...any) {
	now := time.Now().Format("2006/01/02 15:04:05")
	f := fmt.Sprintf("[Error] %s %s\n", now, format)
	_, _ = fmt.Fprintf(gin.DefaultWriter, f, values...)
}

调用

代码语言:javascript
复制
zlog.Debug(utils.ObjToJson(list))

打印行号

代码语言:javascript
复制
package zlog

import (
	"fmt"
	"github.com/gin-gonic/gin"
	"runtime"
	"time"
)

func mCaller(skip int) string {
	pc, file, line, ok := runtime.Caller(skip)
	if !ok {
		return ""
	}
	// 获取方法名
	funcName := runtime.FuncForPC(pc).Name()
	return fmt.Sprintf("%s:%d(%s)", file, line, funcName)
}

func Info(format string, values ...any) {
	fileStrAll := mCaller(2)
	now := time.Now().Format("2006-01-02 15:04:05")
	f := fmt.Sprintf("%s INFO %s ↓\n%s\n", now, fileStrAll, format)
	_, _ = fmt.Fprintf(gin.DefaultWriter, f, values...)
}

func Error(format string, values ...any) {
	fileStrAll := mCaller(2)
	now := time.Now().Format("2006-01-02 15:04:05")
	f := fmt.Sprintf("%s ERROR %s ↓\n%s\n", now, fileStrAll, format)
	_, _ = fmt.Fprintf(gin.DefaultWriter, f, values...)
}

调用

代码语言:javascript
复制
zlog.Info("Hello World")

打印日志

2024-02-26 21:53:15 INFO D:/Project/go/z-wiki/main.go:33(main.main) ↓ Hello World

这样我们点击日志就直接跳转到对应的代码位置了。

在文件输出日志

Gin框架的请求日志默认在控制台输出,但更多的时候,尤其上线运行时,我们希望将用户的请求日志保存到日志文件中,以便更好的分析与备份。

在Gin框架中,通过gin.DefaultWriter变量可能控制日志的保存方式,gin.DefaultWriter在Gin框架中的定义如下:

代码语言:javascript
复制
var DefaultWriter io.Writer = os.Stdout

从上面的定义我们可以看出,gin.DefaultWriter的类型为io.Writer,默认值为os.Stdout,即控制台输出,

因此我们可以通过修改gin.DefaultWriter值来将请求日志保存到日志文件或其他地方(比如数据库)。

代码语言:javascript
复制
package main
import (
    "github.com/gin-gonic/gin"
    "io"
    "os"
)
func main() {
    gin.DisableConsoleColor()//保存到文件不需要颜色
    file, _ := os.Create("access.log")
    gin.DefaultWriter = file
    //gin.DefaultWriter = io.MultiWriter(file) 效果是一样的
    router := gin.Default()
    router.GET("/test", func(c *gin.Context) {
        c.String(200, "test")
    })
    _ = router.Run(":8080")
}

运行后上面的程序,会在程序所在目录创建access.log文件,当我们发起Web请求后,请求的日志会保存到access.log文件,而不会在控制台输出。

也可以让请求日志同行保存到文件和在控制台输出:

代码语言:javascript
复制
file, _ := os.Create("access.log")
gin.DefaultWriter = io.MultiWriter(file,os.Stdout) //同时保存到文件和在控制台中输出

zap(推荐)

需要go1.19版本及以上

安装

代码语言:javascript
复制
go get go.uber.org/zap

设置全局变量

如果您想在不先创建 Logger 实例的情况下写入日志,您可以在 init() 函数中使用 ReplaceGlobals() 方法:

代码语言:javascript
复制
package main

import (
    "go.uber.org/zap"
)

func init() {
    zap.ReplaceGlobals(zap.Must(zap.NewDevelopment()))
}

func main() {
    zap.L().Info("Hello from Zap!")
}

此方法将可通过 zap.L() 访问的全局记录器替换为功能性 Logger 实例,以便您只需将 zap 包导入文件即可直接使用它。

不同环境切换

您可以使用环境变量轻松地在开发和生产 Logger 之间切换:

代码语言:javascript
复制
logger := zap.Must(zap.NewProduction())
if os.Getenv("APP_ENV") == "development" {
    logger = zap.Must(zap.NewDevelopment())
}

在Gin中

代码语言:javascript
复制
func init() {
	if gin.Mode() == gin.DebugMode {
		zap.ReplaceGlobals(zap.Must(zap.NewDevelopment()))
	} else {
		zap.ReplaceGlobals(zap.Must(zap.NewProduction()))
	}
}

不同模式

示例

代码语言:javascript
复制
logger := zap.NewExample(zap.AddCaller())
defer logger.Sync()
logger.Info("Hello World", zap.String("name", "psvmc"))

logger2, _ := zap.NewProduction(zap.AddCaller())
defer logger2.Sync()
logger2.Info("Hello World", zap.String("name", "psvmc"))

logger3, _ := zap.NewDevelopment(zap.AddCaller())
defer logger3.Sync()
logger3.Info("Hello World", zap.String("name", "psvmc"))

打印输出结果:

{“level”:”info”,”msg”:”Hello World”,”name”:”psvmc”} {“level”:”info”,”ts”:1708956240.9281151,”caller”:”z-wiki/main.go:38”,”msg”:”Hello World”,”name”:”psvmc”} 2024-02-26T22:04:00.928+0800 INFO z-wiki/main.go:42 Hello World {“name”: “psvmc”}

log/slog

需要go1.21版本及以上

log/slog是Go 1.21中引入的一个新的结构化日志库,它与标准库的log包兼容,但提供了更多的功能和灵活性。

log/slog定义了一个类型,Logger,用于记录不同级别和格式的日志信息。每个Logger都关联一个Handler,用于处理日志记录。

log/slog还提供了一个默认的Logger,可以通过顶级函数(如Info和Error)来使用,它们会调用相应的Logger方法。

该默认Logger将日志信息写入标准错误,并在每条日志信息前添加日期和时间。

log/slog的日志记录由以下几个部分组成:

  • 时间:日志记录发生的时间,可以是本地时间或UTC时间。
  • 级别:日志记录的严重程度,可以是预定义的四个级别之一(Debug、Info、Warn、Error),也可以是自定义的整数值。
  • 消息:日志记录的主要内容,通常是一个简短的描述性字符串。
  • 属性:日志记录的额外信息,以键值对的形式表示,键是字符串,值可以是任意类型。

基本示例

例如,以下代码:

代码语言:javascript
复制
package main
 
import (
 "log/slog"
 "os"
)
 
func main() {
 slog.Info("helloworld", "user", os.Getenv("USER"))
}

会产生以下输出:

代码语言:javascript
复制
2024/02/26 18:58:17 INFO hello-world user=""

其中,2024/02/26 18:58:17是时间,INFO是级别,hello-world是消息,user=""是属性。

log/slog还提供了一些有用的特性,如:

  • 可以自定义日志级别、输出目标、格式器(JSON或文本)、时间戳等。
  • 可以使用字段(Fields)来添加结构化的上下文信息,如键值对。
  • 可以使用处理器(Handler)来处理不同级别或条件的日志信息,如过滤、分割、彩色等。
  • 可以使用条目(Entry)来记录带有字段的日志信息,或者使用WithFields、WithTime、WithError等方法来创建带有字段的条目。
  • 可以使用日志级别函数(如Info、Warn、Error等)来记录不同级别的日志信息,或者使用Log或Print等方法来记录默认级别的日志信息。

标准错误和标准输出

以下是一个使用log/slog的示例代码:

代码语言:javascript
复制
package main
 
import (
 "log/slog"
 "os"
)
 
func main() {
	// 创建一个文本处理器
	textHandler := slog.NewTextHandler(os.Stderr, nil)
	// 创建一个文本 Logger
	textLogger := slog.New(textHandler)
	// 使用Logger记录结构化的日志信息
	textLogger.Info("hello-world", "user", os.Getenv("USER"))

	// 创建一个JSON处理器
	jsonHandler := slog.NewJSONHandler(os.Stdout, nil)
	// 创建一个 JSON Logger
	jsonLogger := slog.New(jsonHandler)
	// 使用Logger记录结构化的日志信息
	jsonLogger.Info("hello-world", "user", os.Getenv("USER"))
}

该程序会在标准错误上输出文本格式的日志信息:

代码语言:javascript
复制
time=2024-02-26T22:35:53.533+08:00 level=INFO msg=hello-world user=""

然后在标准输出上输出JSON格式的日志信息:

代码语言:javascript
复制
{"time":"2024-02-26T22:35:53.5512541+08:00","level":"INFO","msg":"hello-world","user":""}

logrus

代码语言:javascript
复制
go get github.com/sirupsen/logrus

引用

代码语言:javascript
复制
import "github.com/sirupsen/logrus"

示例

代码语言:javascript
复制
package main
 
import (
 "os"
 
 "github.com/sirupsen/logrus"
)
 
func main() {
 // 创建一个Logrus实例
 log := logrus.New()
 
 // 设置日志级别为Debug
 log.SetLevel(logrus.DebugLevel)
 
 // 设置输出目标为标准输出
 log.SetOutput(os.Stdout)
 
 // 设置格式器为JSON
 log.SetFormatter(&logrus.JSONFormatter{})
 
 // 使用Fields添加结构化的上下文信息
 log.WithFields(logrus.Fields{
  "key1": "value1",
  "key2": "value2",
 }).Info("This is a structured log")
 
 // 使用Entry记录带有字段的日志信息
 entry := log.WithFields(logrus.Fields{
  "key3": "value3",
  "key4": "value4",
 })
 entry.Warn("This is another structured log")
 
 // 使用日志级别函数记录不同级别的日志信息
 log.Debug("This is a debug log")
 log.Info("This is an info log")
 log.Warn("This is a warn log")
 log.Error("This is an error log")
 log.Fatal("This is a fatal log")
 log.Panic("This is a panic log")
 
 // 使用Log或Print等方法记录默认级别的日志信息
 log.Log(logrus.InfoLevel, "This is a log with level")
 log.Print("This is a print log")
}

IDEA日志高亮

File => Settings => Plugins 搜索 Grep Console安装即可,安装后会根据日志中日志级别显示不同颜色。

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2024-02-20,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 自定义日志(推荐)
  • 和Gin结合
    • 基本封装
      • 打印行号
        • 在文件输出日志
        • zap(推荐)
          • 设置全局变量
            • 不同环境切换
              • 不同模式
              • log/slog
                • 基本示例
                  • 标准错误和标准输出
                  • logrus
                  • IDEA日志高亮
                  领券
                  问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档