Sync and async sinks, redaction, and a queryable HTTP service keep concurrent logs tied to a trace id. Co-authored-by: Cursor <cursoragent@cursor.com>
160 lines
3.5 KiB
Go
160 lines
3.5 KiB
Go
// Package slogadapter 在 logsys 与标准库 log/slog 之间双向转发。
|
|
package slogadapter
|
|
|
|
import (
|
|
"context"
|
|
"log/slog"
|
|
|
|
"logging-system/logsys"
|
|
)
|
|
|
|
// Handler 把 slog 记录写入 logsys。
|
|
type Handler struct {
|
|
logger *logsys.Logger
|
|
group string
|
|
attrs []slog.Attr
|
|
}
|
|
|
|
// NewHandler 包装一个 logsys 日志器。
|
|
func NewHandler(l *logsys.Logger) *Handler {
|
|
if l == nil {
|
|
l = logsys.Nop()
|
|
}
|
|
return &Handler{logger: l}
|
|
}
|
|
|
|
// Slog 返回写入 logsys 的 *slog.Logger。
|
|
func Slog(l *logsys.Logger) *slog.Logger { return slog.New(NewHandler(l)) }
|
|
|
|
// Enabled 按 logsys 的级别过滤。
|
|
func (h *Handler) Enabled(_ context.Context, level slog.Level) bool {
|
|
return h.logger.Enabled(fromSlogLevel(level))
|
|
}
|
|
|
|
// Handle 转发一条记录。
|
|
func (h *Handler) Handle(ctx context.Context, rec slog.Record) error {
|
|
kv := make([]any, 0, (len(h.attrs)+rec.NumAttrs())*2)
|
|
for _, attr := range h.attrs {
|
|
kv = append(kv, h.name(attr.Key), attr.Value.Any())
|
|
}
|
|
rec.Attrs(func(attr slog.Attr) bool {
|
|
kv = append(kv, h.name(attr.Key), attr.Value.Any())
|
|
return true
|
|
})
|
|
l := h.logger.WithContext(ctx)
|
|
switch {
|
|
case rec.Level >= slog.LevelError+4:
|
|
l.Fatal(rec.Message, nil, kv...)
|
|
case rec.Level >= slog.LevelError:
|
|
l.Error(rec.Message, nil, kv...)
|
|
case rec.Level >= slog.LevelWarn:
|
|
l.Warn(rec.Message, kv...)
|
|
case rec.Level >= slog.LevelInfo:
|
|
l.Info(rec.Message, kv...)
|
|
case rec.Level >= slog.LevelDebug:
|
|
l.Debug(rec.Message, kv...)
|
|
default:
|
|
l.Trace(rec.Message, kv...)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// WithAttrs 追加属性。
|
|
func (h *Handler) WithAttrs(attrs []slog.Attr) slog.Handler {
|
|
n := *h
|
|
n.attrs = append(append([]slog.Attr{}, h.attrs...), attrs...)
|
|
return &n
|
|
}
|
|
|
|
// WithGroup 追加分组前缀。
|
|
func (h *Handler) WithGroup(name string) slog.Handler {
|
|
n := *h
|
|
if name == "" {
|
|
return &n
|
|
}
|
|
if n.group != "" {
|
|
n.group += "."
|
|
}
|
|
n.group += name
|
|
return &n
|
|
}
|
|
|
|
func (h *Handler) name(key string) string {
|
|
if h.group == "" {
|
|
return key
|
|
}
|
|
return h.group + "." + key
|
|
}
|
|
|
|
// Sink 把 logsys 条目写进 slog.Handler。
|
|
type Sink struct {
|
|
Handler slog.Handler
|
|
}
|
|
|
|
// Write 转发一条日志。
|
|
func (s Sink) Write(e logsys.Entry) error {
|
|
if s.Handler == nil {
|
|
return nil
|
|
}
|
|
rec := slog.NewRecord(e.Time, toSlogLevel(e.Level), e.Message, 0)
|
|
if e.Service != "" {
|
|
rec.AddAttrs(slog.String("service", e.Service))
|
|
}
|
|
if e.Module != "" {
|
|
rec.AddAttrs(slog.String("module", e.Module))
|
|
}
|
|
if e.TraceID != "" {
|
|
rec.AddAttrs(slog.String("trace_id", e.TraceID))
|
|
}
|
|
if e.Code != "" {
|
|
rec.AddAttrs(slog.String("code", e.Code))
|
|
}
|
|
if e.Error != "" {
|
|
rec.AddAttrs(slog.String("error", e.Error))
|
|
}
|
|
for k, v := range e.Fields {
|
|
rec.AddAttrs(slog.Any(k, v))
|
|
}
|
|
return s.Handler.Handle(context.Background(), rec)
|
|
}
|
|
|
|
// Sync 无独立缓冲。
|
|
func (s Sink) Sync() error { return nil }
|
|
|
|
// Close 不关闭 slog.Handler。
|
|
func (s Sink) Close() error { return nil }
|
|
|
|
func fromSlogLevel(level slog.Level) logsys.Level {
|
|
switch {
|
|
case level >= slog.LevelError+4:
|
|
return logsys.LevelFatal
|
|
case level >= slog.LevelError:
|
|
return logsys.LevelError
|
|
case level >= slog.LevelWarn:
|
|
return logsys.LevelWarn
|
|
case level >= slog.LevelInfo:
|
|
return logsys.LevelInfo
|
|
case level >= slog.LevelDebug:
|
|
return logsys.LevelDebug
|
|
default:
|
|
return logsys.LevelTrace
|
|
}
|
|
}
|
|
|
|
func toSlogLevel(level string) slog.Level {
|
|
switch level {
|
|
case "trace":
|
|
return slog.LevelDebug - 4
|
|
case "debug":
|
|
return slog.LevelDebug
|
|
case "warn":
|
|
return slog.LevelWarn
|
|
case "error":
|
|
return slog.LevelError
|
|
case "fatal":
|
|
return slog.LevelError + 4
|
|
default:
|
|
return slog.LevelInfo
|
|
}
|
|
}
|