// 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, } }