137 lines
2.9 KiB
Go
137 lines
2.9 KiB
Go
// Package logger 提供结构化日志配置。
|
||
package logger
|
||
|
||
import (
|
||
"context"
|
||
"fmt"
|
||
"io"
|
||
"log/slog"
|
||
"os"
|
||
"strings"
|
||
"sync"
|
||
)
|
||
|
||
// Config 日志配置。
|
||
type Config struct {
|
||
Level slog.Level
|
||
AddSource bool
|
||
JSON bool // true = JSONHandler,false = 彩色 TextHandler
|
||
}
|
||
|
||
// New 创建配置好的 Logger。
|
||
// 开发环境(JSON=false)输出彩色终端文本,生产环境输出 JSON 供采集。
|
||
func New(cfg Config) *slog.Logger {
|
||
var h slog.Handler
|
||
opts := &slog.HandlerOptions{
|
||
Level: cfg.Level,
|
||
AddSource: cfg.AddSource,
|
||
}
|
||
if cfg.JSON {
|
||
h = slog.NewJSONHandler(os.Stdout, opts)
|
||
} else {
|
||
h = newColorHandler(os.Stdout, opts)
|
||
}
|
||
return slog.New(h)
|
||
}
|
||
|
||
// ANSI 颜色代码
|
||
const (
|
||
colorReset = "\033[0m"
|
||
colorRed = "\033[31m"
|
||
colorGreen = "\033[32m"
|
||
colorYellow = "\033[33m"
|
||
colorBlue = "\033[34m"
|
||
colorPurple = "\033[35m"
|
||
colorCyan = "\033[36m"
|
||
colorGray = "\033[37m"
|
||
)
|
||
|
||
// levelColor 日志级别 → ANSI 颜色映射
|
||
var levelColor = map[slog.Level]string{
|
||
slog.LevelDebug: colorGray,
|
||
slog.LevelInfo: colorGreen,
|
||
slog.LevelWarn: colorYellow,
|
||
slog.LevelError: colorRed,
|
||
}
|
||
|
||
// levelLabel 日志级别 → 短标签映射
|
||
var levelLabel = map[slog.Level]string{
|
||
slog.LevelDebug: "DEBUG",
|
||
slog.LevelInfo: "INFO",
|
||
slog.LevelWarn: "WARN",
|
||
slog.LevelError: "ERROR",
|
||
}
|
||
|
||
// colorHandler 带颜色的 slog.Handler 包装。
|
||
type colorHandler struct {
|
||
inner slog.Handler
|
||
mu sync.Mutex
|
||
writer io.Writer
|
||
}
|
||
|
||
func newColorHandler(w io.Writer, opts *slog.HandlerOptions) *colorHandler {
|
||
return &colorHandler{
|
||
inner: slog.NewTextHandler(w, opts),
|
||
writer: w,
|
||
}
|
||
}
|
||
|
||
func (h *colorHandler) Enabled(ctx context.Context, level slog.Level) bool {
|
||
return h.inner.Enabled(ctx, level)
|
||
}
|
||
|
||
func (h *colorHandler) Handle(ctx context.Context, r slog.Record) error {
|
||
h.mu.Lock()
|
||
defer h.mu.Unlock()
|
||
|
||
// 时间(使用 -07:00 让 Go 自动填充时区偏移)
|
||
timeStr := r.Time.Format("2006-01-02T15:04:05.000-07:00")
|
||
|
||
// Level 带颜色
|
||
lvl := r.Level
|
||
color := levelColor[lvl]
|
||
label := levelLabel[lvl]
|
||
|
||
// 消息
|
||
msg := r.Message
|
||
|
||
// 组装属性
|
||
var attrs []string
|
||
r.Attrs(func(a slog.Attr) bool {
|
||
attrs = append(attrs, fmt.Sprintf("%s=%v", a.Key, a.Value.Any()))
|
||
return true
|
||
})
|
||
|
||
// 输出格式:[时间] Level颜色(LEVEL) 消息 attr1=val1 attr2=val2
|
||
var buf strings.Builder
|
||
buf.WriteString(timeStr)
|
||
buf.WriteString(" ")
|
||
buf.WriteString(color)
|
||
buf.WriteString(fmt.Sprintf("%-5s", label))
|
||
buf.WriteString(colorReset)
|
||
buf.WriteString(" ")
|
||
buf.WriteString(msg)
|
||
for _, a := range attrs {
|
||
buf.WriteString(" ")
|
||
buf.WriteString(a)
|
||
}
|
||
buf.WriteString("\n")
|
||
|
||
_, err := fmt.Fprint(h.writer, buf.String())
|
||
return err
|
||
}
|
||
|
||
func (h *colorHandler) WithAttrs(attrs []slog.Attr) slog.Handler {
|
||
return &colorHandler{
|
||
inner: h.inner.WithAttrs(attrs),
|
||
writer: h.writer,
|
||
}
|
||
}
|
||
|
||
func (h *colorHandler) WithGroup(name string) slog.Handler {
|
||
return &colorHandler{
|
||
inner: h.inner.WithGroup(name),
|
||
writer: h.writer,
|
||
}
|
||
}
|