Go的标准库日志Slog

背景

在Go1.21中,在Go的标准库函数中引入了Slog

据说使用的内存比Zap还要小

基础用法

slog.Info("This is info log")
slog.Warn("This is warning log")
slog.Error("This is error log")

日志输出:
2024/07/23 15:26:01 INFO This is info log
2024/07/23 15:26:01 WARN This is warning log
2024/07/23 15:26:01 ERROR This is error log

输出参数的日志

	name := "sss"

slog.Info("msg", slog.String("name", name))
slog.Error("ERROR: value is empty", slog.Any("name", name))

日志输出:
2024/07/23 15:27:31 INFO msg name=sss
2024/07/23 15:27:31 ERROR ERROR: value is empty name=sss

设置打印的日志

	// 设置日志格式为JSON
slog.SetDefault(slog.New(slog.NewJSONHandler(os.Stderr, &slog.HandlerOptions{})))
slog.Error("ERROR: value is empty", slog.Any("name", name))
// 设置日志为Text
slog.SetDefault(slog.New(slog.NewTextHandler(os.Stderr, &slog.HandlerOptions{})))
slog.Error("ERROR: value is empty", slog.Any("name", name))

日志输出:
{"time":"2024-07-23T15:27:31.890811+08:00","level":"ERROR","msg":"ERROR: value is empty","name":"sss"}
time=2024-07-23T15:27:31.890+08:00 level=ERROR msg="ERROR: value is empty" name=sss

设置打印代码源

	// 设置日志里面有代码行
slog.SetDefault(slog.New(slog.NewTextHandler(os.Stderr, &slog.HandlerOptions{AddSource: true})))
slog.Error("ERROR: value is empty", slog.Any("name", name))

日志输出:
time=2024-07-23T15:27:31.890+08:00 level=ERROR source=/Users/joohwan/GolandProjects/go-tools/basic/slog/slog.go:24 msg="ERROR: value is empty" name=sss

设置日志写入文件

// 设置日志写入本地文件
f, err := os.Create("foo.log")
if err != nil {
panic(err)
}
defer f.Close()
slog.SetDefault(slog.New(slog.NewJSONHandler(f, nil)))
slog.Error("ERROR: value is empty", slog.Any("name", name))

设置Group

	// Group 日志
g1 := slog.Group("g1", slog.String("module", "authentication"), slog.String("method", "login"))
g2 := slog.Group("g2", slog.String("module", "authentication"), slog.String("method", "login"))

slog.Info("User login attempt",
slog.String("service", "login-service"),
g1,
)
slog.Info("User login attempt",
slog.String("service", "login-service"),
g2,
)

日志输出:
2024/07/23 15:38:08 INFO User login attempt service=login-service g1.module=authentication g1.method=login
2024/07/23 15:38:08 INFO User login attempt service=login-service g2.module=authentication g2.method=login

最佳实践-轮转日志

package main

import (
"gopkg.in/natefinch/lumberjack.v2"
"io"
"log/slog"
"os"
"strings"
)

func main() {
logger := InitLogger("app.log", "warn", 1, 1, 1, true)
defer logger.Close()
slog.Info("LOG int arr", slog.Any("intarr", "sss"))
slog.Info("This is info log")
slog.Warn("This is warning log")
slog.Error("This is error log", "err", "x", "ss", "xx")
}

func InitLogger(filePath string, logLevel string, maxSize int, maxBackups int, maxAge int, isCompress bool) *lumberjack.Logger {
// 设置日志文件轮转
logFile := &lumberjack.Logger{
Filename: filePath, // 日志文件路径
MaxSize: maxSize, // 每个日志文件的最大尺寸(MB)
MaxBackups: maxBackups, // 保留的旧日志文件数
MaxAge: maxAge, // 日志文件保存的天数
Compress: isCompress, // 是否压缩/归档旧日志文件
}

// 设置控制台和文件输出
multiWriter := io.MultiWriter(os.Stdout, logFile)

var level slog.Level
switch logLevel {
case slog.LevelInfo.String(), strings.ToLower(slog.LevelInfo.String()):
level = slog.LevelInfo
case slog.LevelDebug.String(), strings.ToLower(slog.LevelDebug.String()):
level = slog.LevelDebug
case slog.LevelWarn.String(), strings.ToLower(slog.LevelWarn.String()):
level = slog.LevelWarn
case slog.LevelError.String(), strings.ToLower(slog.LevelError.String()):
level = slog.LevelError
default:
level = slog.LevelInfo
}

// 创建自定义的 `slog.Handler`,将日志输出到 multiWriter
handler := slog.NewTextHandler(multiWriter, &slog.HandlerOptions{AddSource: true, Level: level})

// 创建 logger 实例
logger := slog.New(handler)
slog.SetDefault(logger)
// 在程序结束时关闭日志文件
return logFile
}