Files
hunjing/mcp/web-search/internal/logger/logger.go
T
2026-08-21 16:00:13 +08:00

137 lines
2.9 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// 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,
}
}