在本文中,我将简单介绍 slog 包、它的功能以及如何在 Go 应用程序中使用它。
slog 提供结构化日志记录,其中的日志记录包括一条消息、level 和其他各种以键值对表示的属性。
import "log/slog"这里写一个非常简单的例子:
func main() { slog.Info("hello, world!", "coding", "happy") }输出:
2023/09/15 13:42:27 INFO hello, world! coding=happy
func main() { // 堆代码 duidaima.com 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提供了指定属性的功能。有多种类型的属性可供选择:
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" } }
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时设置级别。
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), ) }这里有个比较好的实践:
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总结