slog 是 Go 团队开发的一个实验性日志包,它提供了结构化日志记录的功能。 注意:当前该包还未在Go基础库中包含。
安装
创建新的 Go 项目或者使用现有的项目通过以下命令进行安装。
go get golang.org/x/exp/slog
使用
package main
import "golang.org/x/exp/slog"
func main() {
slog.Info("Go is best language!")
}
输出:
$ go run main.go
2022/12/21 11:16:00 INFO Go is best language!
默认情况下,输出包括时间、日志级别和消息。
以下是可用的日志级别。
Debug
Info
Warn
Error
结构化日志记录
slog 是一个结构化日志记录工具,支持两种记录格式:text 和 json。
Text Handler
首先创建一个 text handler 和 一个 new logger。
package main
import (
"os"
"golang.org/x/exp/slog")
func main() {
textHandler := slog.NewTextHandler(os.Stdout)
logger := slog.New(textHandler)
logger.Info("Go is the best language!")
}
输出:
$ go run main.go
time=2022-12-21T11:20:52.919+08:00 level=INFO msg="Go is the best language!"
我们可以看出日志是以键值对的形式输出。这通常称为 logfmt 格式。
许多日志分析工具都可以处理logfmt格式的日志。Logfmt
也是人类可读的一种日志格式。
JSON Handler
你还可以输出 JSON 格式的日志。
package main
import (
"os"
"golang.org/x/exp/slog")
func main() {
jsonHandler := slog.NewJSONHandler(os.Stdout) // 👈
logger := slog.New(jsonHandler)
logger.Info("Go is the best language!")
}
输出:
$ go run main.go
{"time":"2022-12-21T11:26:08.355317+08:00","level":"INFO","msg":"Go is the best language!"}
每个日志都记录为一个 json 对象,其中包含属性。
属性
slog 是一个结构化的记录器,提供了指定属性的能力。
package main
import (
"os"
"golang.org/x/exp/slog")
func main() {
textHandler := slog.NewTextHandler(os.Stdout)
logger := slog.New(textHandler)
logger.Info("Usage Statistics", slog.Int("current-memory", 50))
}
输出:
$ go run main.go
time=2022-12-21T11:28:27.209+08:00 level=INFO msg="Usage Statistics" current-memory=50
在上面的示例中,使用 .
添加了一个整数属性slog.Int
。
以下是各类支持的属性:
String
Int64
Int
Uint64
Float64
Bool
Time
Duration
你可以根据需要添加任意数量的属性。
package main
import (
"os"
"golang.org/x/exp/slog")
func main() {
textHandler := slog.NewTextHandler(os.Stdout)
logger := slog.New(textHandler)
logger.Info("Usage Statistics",
slog.Int("current-memory", 50),
slog.Int("min-memory", 20),
slog.Int("max-memory", 80),
slog.Int("cpu", 10),
slog.String("app-version", "v0.0.1-beta"),
)
}
输出:
$ go run main.go
time=2022-12-21T11:31:12.469+08:00 level=INFO msg="Usage Statistics" current-memory=50 min-memory=20 max-memory=80 cpu=10 app-version=v0.0.1-beta
分组属性
你可以将属性分组在一个键下。例如,所有内存属性都可以分组在 memory
键下。
package main
import (
"os"
"golang.org/x/exp/slog")
func main() {
textHandler := slog.NewTextHandler(os.Stdout)
logger := slog.New(textHandler)
logger.Info("Usage Statistics",
slog.Group("memory",
slog.Int("current", 50),
slog.Int("min", 20),
slog.Int("max", 80)),
slog.Int("cpu", 10),
slog.String("app-version", "v0.0.1-beta"),
)
}
输出:
$ go run main.go
time=2022-12-21T11:33:38.016+08:00 level=INFO msg="Usage Statistics" memory.current=50 memory.min=20 memory.max=80 cpu=10 app-version=v0.0.1-beta
使用 JsonHandler
输出将如下所示。
$ go run main.go | jq
{
"time": "2022-12-21T11:35:24.648829+08:00",
"level": "INFO",
"msg": "Usage Statistics",
"memory": {
"current": 50,
"min": 20,
"max": 80
},
"cpu": 10,
"app-version": "v0.0.1-beta"
}
公共属性
假设你希望拥有一个应包含在所有正在生成的日志中的属性,此类属性的示例包括服务名称、应用程序版本。
package main
import (
"os"
"golang.org/x/exp/slog")
func main() {
textHandler := slog.NewTextHandler(os.Stdout).
WithAttrs([]slog.Attr{slog.String("app-version", "v0.0.1-beta")}) // 👈 为所有日志添加属性
logger := slog.New(textHandler)
logger.Info("Generating statistics")
logger.Info("Usage Statistics",
slog.Group("memory",
slog.Int("current", 50),
slog.Int("min", 20),
slog.Int("max", 80)),
slog.Int("cpu", 10),
)
}
输出:
$ go run main.go
time=2022-12-21T11:37:26.363+08:00 level=INFO msg="Generating statistics" app-version=v0.0.1-beta
time=2022-12-21T11:37:26.363+08:00 level=INFO msg="Usage Statistics" app-version=v0.0.1-beta memory.current=50 memory.min=20 memory.max=80 cpu=10
你可以看到 app-version
两个日志中都包含该属性。在处理程序上使用函数指定的属性WithAttrs
将包含在所有日志中。
在 context 中传递 logger
理想情况下,你希望创建一个具有特定配置和属性的记录器,并在整个应用程序中使用它。
slog具有内置函数,可让让你在context
中使用。
package main
import (
"context"
"os"
"golang.org/x/exp/slog")
func main() {
textHandler := slog.NewTextHandler(os.Stdout).
WithAttrs([]slog.Attr{slog.String("app-version", "v0.0.1-beta")})
logger := slog.New(textHandler)
ctx := slog.NewContext(context.Background(), logger) // 👈 context containing logger sendUsageStatus(ctx)
}
func sendUsageStatus(ctx context.Context) {
logger := slog.FromContext(ctx) // 👈 grab logger from context
logger.Info("Generating statistics")
logger.Info("Usage Statistics",
slog.Group("memory",
slog.Int("current", 50),
slog.Int("min", 20),
slog.Int("max", 80)),
slog.Int("cpu", 10),
)
}
输出:
$ go run main.go
time=2022-12-21T11:41:28.333+08:00 level=INFO msg="Generating statistics" app-version=v0.0.1-beta
time=2022-12-21T11:41:28.334+08:00 level=INFO msg="Usage Statistics" app-version=v0.0.1-beta memory.current=50 memory.min=20 memory.max=80 cpu=10
NewContext
创建一个包含 logger 的 新的 context。
FromContext
从 contex t中获取 logger。如果 context 不包含 logger,它会返回default logger。
日志级别
如果你使用的是默认 logger,它不会记录调试日志,因为默认日志级别为Info
.
你可以创建一个新的 logger,并将默认日志级别设置为Debug
以显示调试日志。
package main
import (
"os"
"golang.org/x/exp/slog")
func main() {
opts := slog.HandlerOptions{
Level: slog.LevelDebug,
}
textHandler := opts.NewTextHandler(os.Stdout)
logger := slog.New(textHandler)
logger.Debug("Debug")
logger.Info("Info")
logger.Warn("Warn")
}
输出:
$ go run main.go
time=2022-12-21T11:45:23.466+08:00 level=DEBUG msg=Debug
time=2022-12-21T11:45:23.466+08:00 level=INFO msg=Info
time=2022-12-21T11:45:23.466+08:00 level=WARN msg=Warn
本文来源
Logging in Go with slog - https://thedevelopercafe.com/articles/logging-in-go-with-slog-a7bb489755c2
本文作者的其他 Go 语言文章也非常值得一读。