slog 简介:用于 Go 的结构化日志

TimLiuDream · · 1366 次点击 · 开始浏览    置顶
这是一个创建于 的主题,其中的信息可能已经有所发展或是发生改变。

日志是任何软件的重要组成部分,Go 提供了一个内置日志包(slog)。然而,随着应用的复杂性不断增加,对结构化日志的需求也越来越明显。结构化日志允许开发人员以结构化格式记录数据,便于日志聚合工具进行解析和分析,目前业界使用比较多的是:zap。 在本文中,我将简单介绍 slog 包、它的功能以及如何在 Go 应用程序中使用它。 # 什么是 slog? `slog` 提供结构化日志记录,其中的日志记录包括一条消息、level 和其他各种以键值对表示的属性。 # 使用 最新的`1.21`版本引入了`slog`。要在 Go 项目中使用`slog`,可以通过以下方式导入它: ``` import "log/slog" ``` 这里写一个非常简单的例子: ``` func main() { slog.Info("hello, world!", "coding", "happy") } ``` 输出: ``` 2023/09/15 13:42:27 INFO hello, world! coding=happy ``` # 功能 `slog` 提供了许多有用的功能,使其成为 Go 的强大日志包。以下是其中的一些主要功能: - 结构化日志 - 严重级别日志 - 分组日志 - 自定义处理 ## 结构化日志 `slog` 默认提供两种方式: - TextHandler - JSONHandler 我们写一下使用这两个方式的例子: ``` func main() { textHandler := slog.NewTextHandler(os.Stdout, nil) textLogger := slog.New(textHandler) textLogger.Info("TextDemo", slog.String("app-version", "v0.0.1"), slog.Int("release-version", 1), ) jsonHandler := slog.NewJSONHandler(os.Stdout, nil) jsonLogger := slog.New(jsonHandler) jsonLogger.Info("JsonDemo", slog.String("app-version", "v0.0.1"), slog.Int("release-version", 1), ) } ``` 输出为: ``` time=2023-09-15T13:48:37.424+08:00 level=INFO msg=TextDemo app-version=v0.0.1 release-version=1 {"time":"2023-09-15T13:48:37.4647782+08:00","level":"INFO","msg":"JsonDemo","app-version":"v0.0.1","release-version":1} ``` 从上面我们可以看到,代码中使用了类似`slog.String`的方法,`slog`提供了指定属性的功能。有多种类型的属性可供选择: - slog.String:字符串属性。 - slog.Int:整数属性。 - slog.Float64:浮点属性。 - slog.Bool:布尔属性。 - slog.Duration:持续时间属性。 - slog.Time:时间属性。 - slog.Group:组属性,可用于将相关属性分组 ``` func main() { jsonHandler := slog.NewJSONHandler(os.Stdout, nil) jsonLogger := slog.New(jsonHandler) jsonLogger.Info( "attributes", slog.String("version", "1.0.0"), slog.Int("app-version", 1), slog.Float64("point-value", 1.2), slog.Bool("status", true), slog.Duration("duration", time.Hour*1), slog.Time("time", time.Now()), slog.Group( "request", slog.String("path", "<https://example.com>"), slog.String("method", "get"), ), ) } ``` 对应的输出,我已经将其格式化了: ``` { "time": "2023-09-15T13:53:43.8848272+08:00", "level": "INFO", "msg": "attributes", "version": "1.0.0", "app-version": 1, "point-value": 1.2, "status": true, "duration": 3600000000000, "time": "2023-09-15T13:53:43.8848272+08:00", "request": { "path": "<https://example.com>", "method": "get" } } ``` ## 严重级别日志 这允许我们记录不同严重程度的信息。`slog` 默认提供四个日志级别,每个级别都有一个整数值: - DEBUG(-4) - INFO(0) - WARN(4) - ERROR(8) 例子: ``` func main() { jsonHandler := slog.NewJSONHandler(os.Stdout, &slog.HandlerOptions{ Level: slog.LevelDebug, }) jsonLogger := slog.New(jsonHandler) jsonLogger.Debug("Hello, world!") jsonLogger.Info("Hello, world!") jsonLogger.Warn("Hello, world!") jsonLogger.Error("Hello, world!") } ``` NOTE: 记得在`NewHandler`时设置级别。 ## 分组日志 分组日志指的是将日志信息归类为逻辑组或类别的做法。`Slog` 通过使用与日志记录相关联的属性或键值对来支持分组日志记录。 例子: ``` func main() { jsonHandler := slog.NewJSONHandler(os.Stdout, &slog.HandlerOptions{ Level: slog.LevelDebug, }) jsonLogger := slog.New(jsonHandler).WithGroup("request") jsonLogger.Info("", slog.String("url", "<https://example.com>"), slog.String("method", "GET"), slog.Int("response-code", 200), ) } ``` 这里有个比较好的实践: - 在 http 服务的 middleware 中定义一个 request 组的 logger,会一些 4xx、5xx等请求的一些信息打印出来。 - 而普通业务的话,我们又可以使用一个 service 组的 logger ## 自定义处理 `slog` 能够创建自定义`handler`,将日志信息写入不同的目的地,如文件、数据库或外部服务。 `slog` 提供的处理程序接口定义了自定义处理程序必须实现的方法。处理程序接口有四个方法: ``` type Handler interface { Enabled(Level) bool Handle(Record) error WithAttrs([]Attr) Handler WithGroup(string) Handler } ``` 下面举例说明如何创建一个将日志信息写入文件的自定义`handler`: ``` type FileHandler struct { file *os.File } func NewFileHandler(filename string) (*FileHandler, error) { file, err := os.OpenFile(filename, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644) if err != nil { return nil, err } return &FileHandler{file}, nil } func (h *FileHandler) Enabled(_ context.Context, level slog.Level) bool { return true } func (h *FileHandler) Handle(_ context.Context, record slog.Record) error { _, err := h.file.WriteString(record.Message + "\n") return err } func (h *FileHandler) WithAttrs(attrs []slog.Attr) slog.Handler { return h } func (h *FileHandler) WithGroup(name string) slog.Handler { return h } func (h *FileHandler) Close() error { return h.file.Close() } func main() { fileHandler, err := NewFileHandler("log.log") if err != nil { panic(err) } defer fileHandler.Close() logger := slog.New(fileHandler) logger.Info("Hello, world!") logger.Debug("Debugging errors") } ``` 这个时候运行程序时,控制台就不会有对应的日志输出了,而是输出到对应文件上: ```  slogDemo  cat .\log.log Hello, world! Debugging errors ``` # 总结 `slog` 是一个功能强大的 Go 日志包,提供结构化日志功能。它易于使用,并提供了许多有用的功能,如级别日志和自定义处理程序。如果你正在寻找一个能满足你不断增长的应用程序需求的日志包,那么 `slog` 绝对值得你一试。

有疑问加站长微信联系(非本文作者)

入群交流(和以上内容无关):加入Go大咖交流群,或添加微信:liuxiaoyan-s 备注:入群;或加QQ群:692541889

1366 次点击  
加入收藏 微博
添加一条新回复 (您需要 登录 后才能回复 没有账号 ?)
  • 请尽量让自己的回复能够对别人有帮助
  • 支持 Markdown 格式, **粗体**、~~删除线~~、`单行代码`
  • 支持 @ 本站用户;支持表情(输入 : 提示),见 Emoji cheat sheet
  • 图片支持拖拽、截图粘贴等方式上传