Go slog 日志包尝鲜

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 语言文章也非常值得一读。