Skip to content

日志记录

概述

Goyave 通过自定义封装 Go 标准的 *slog.Logger统一日志记录。来自框架、Gorm 和您应用程序的日志都由同一系统处理,使用相同的输出 io.Writer,并保持一致的格式。

结构化日志除了提供丰富且易于解析的内容外,还允许您使用日志级别DebugInfoWarnError

INFO

  • 默认情况下,使用的 slog.Handler 是 Go 标准的 *slog.JSONHandler
  • 开发模式下(配置 app.debug = true),日志记录器将使用自定义处理程序,以人类可读的方式格式化数据。
  • Goyave 包装器使用框架的错误系统来丰富错误日志。
  • 标准库的所有选项和设置都可以应用于此包装器。
  • 默认情况下,所有日志的输出都是 os.Stderr

自定义 slog 处理程序

自定义处理程序必须实现 slog.Handler 接口。以下是一个非常简单的自定义 slog 处理程序示例:

go
import (
	"bytes"
	"context"
	"io"
	"log/slog"
	"sync"

	"goyave.dev/goyave/v5/util/errors"
)

type CustomHandlerOptions struct {
	Level slog.Leveler
}

type CustomHandler struct {
	opts   *CustomHandlerOptions
	mu     *sync.Mutex
	w      io.Writer
	attrs  []slog.Attr
	groups []string
}

func NewCustomSlogHandler(w io.Writer, opts *CustomHandlerOptions) *CustomHandler {
	if opts == nil {
		opts = &CustomHandlerOptions{}
	}
	return &CustomHandler{
		w:    w,
		mu:   &sync.Mutex{},
		opts: opts,
	}
}

func (h *CustomHandler) Handle(_ context.Context, r slog.Record) error {

	buf := bytes.NewBuffer(make([]byte, 0, 1024))

	buf.WriteString(r.Level.String())
	buf.WriteRune(' ')
	buf.WriteString(r.Message)

	// 添加属性和组...

	h.mu.Lock()
	defer h.mu.Unlock()
	_, err := h.w.Write(buf.Bytes())
	return errors.New(err)
}

func (h *CustomHandler) Enabled(_ context.Context, level slog.Level) bool {
	minLevel := slog.LevelInfo
	if h.opts.Level != nil {
		minLevel = h.opts.Level.Level()
	}
	return level >= minLevel
}

func (h *CustomHandler) WithAttrs(attrs []slog.Attr) slog.Handler {
	newAttrs := make([]slog.Attr, 0, len(h.attrs)+len(attrs))
	newAttrs = append(newAttrs, h.attrs...)
	newAttrs = append(newAttrs, attrs...)
	return &CustomHandler{
		opts:   h.opts,
		w:      h.w,
		mu:     h.mu,
		attrs:  newAttrs,
		groups: h.groups,
	}
}

func (h *CustomHandler) WithGroup(name string) slog.Handler {
	return &CustomHandler{
		opts:   h.opts,
		w:      h.w,
		mu:     h.mu,
		attrs:  append(make([]slog.Attr, 0, len(h.attrs)), h.attrs...),
		groups: append(h.groups, name),
	}
}

然后,您可以通过在服务器的 Options 中设置自定义处理程序来使用它:

go
import (
	//...
	stdslog "log/slog"

	"goyave.dev/goyave/v5"
	"goyave.dev/goyave/v5/slog"
)

func main() {
	slogHandler := NewCustomSlogHandler(os.Stderr, &CustomHandlerOptions{Level: stdslog.LevelDebug})
	slogger := slog.New(slogHandler)

	opts := goyave.Options{
		Logger: slogger,
	}

	server, err := goyave.New(opts)
	if err != nil {
		slogger.Error(err)
		os.Exit(1)
	}
	//...
}