tint.newhandler 是 slog 彩色日志首选,因其纯 handler 实现、零运行时开销、逐级颜色控制及终端/重定向安全;需显式设 level、正确配置 timeformat 与 writer(如 colorable 包装),并统一日志链路。

直接用 slog 配 tint 是目前 Go 生态中最轻量、最可靠、最贴合标准库演进方向的彩色日志方案。别再手动拼 \x1b[31m,也别在 log.Printf 上套 fatih/color —— 那不是日志,是样式污染。
为什么 tint.NewHandler 是 slog 彩色日志的首选
tint 是一个纯 slog.Handler 实现,不劫持 io.Writer 链路,不包装 os.Stdout,也不依赖任何终端能力判断逻辑。它只做一件事:把 slog.Record 转成带 ANSI 色码的字符串,再写入你给的 io.Writer。
- Windows 下无需手动调
SetConsoleMode:tint 不处理终端启用逻辑,它信任你传进来的 writer 已就绪(比如你已用colorable.NewColorableStdout()包装过) - 重定向安全:颜色只在终端有效;写入文件时,ANSI 序列仍会输出,但这是预期行为 —— 若需完全过滤,得靠上层 writer 控制(如
colorable会在非终端时自动跳过色码) - 零运行时开销:没有反射、无 goroutine、无缓冲池竞争,
tint.Options全部在初始化时编译进 handler - 级别颜色可逐级覆盖:
LevelColors映射支持slog.LevelDebug到tint.FgCyan这类精确控制,不是粗粒度的“开/关颜色”
tint.Options 常见配置陷阱
很多人以为配了 LevelColors 就万事大吉,结果 Debug 日志还是灰的 —— 因为没设 Level 或没传给 slog.New()。
-
Level字段必须显式设置:tint 默认只处理slog.LevelInfo及以上,Debug和Trace被静默丢弃。要启用 Debug,请传&tint.Options{Level: slog.LevelDebug} -
TimeFormat必须是 Go 时间格式字符串:"2006-01-02 15:04:05",不是 RFC3339 或 "YYYY-MM-DD HH:mm:ss" -
LevelPrefix开启后会在级别名前加[INFO]这类括号,但不会自动加空格 —— 如果你自定义了LevelColors,括号和文字颜色一致,否则可能看起来像没生效 -
Colors是全局色表,LevelColors是级别覆盖,二者叠加生效;若只改LevelColors,时间、key、value 的颜色仍走Colors默认值
如何让 tint 在 CI/重定向/Windows cmd 中不崩
关键不在 tint,而在你传给它的 io.Writer。tint 本身不检测终端、不读环境变量、不降级 —— 它假设你已准备好一个“能正确处理 ANSI 的 writer”。
- Linux/macOS 终端:直接传
os.Stdout即可,现代终端都支持 ANSI - Windows cmd.exe:必须先 wrap:
os.Stdout = colorable.NewColorableStdout(),放在main()最开头 - 重定向到文件或管道(
go run main.go > out.log):用colorable.NewColorableStdout()后,它会自动检测并跳过所有 ANSI 序列,输出纯文本 - CI 环境(GitHub Actions、GitLab CI):优先检查
os.Getenv("NO_COLOR") != "",若成立,传colorable.NewColorableStdout()后再设colorable.NoColor = true;不要设fatih/color.NoColor = true,那是另一个库
别把 tint 和 log.Printf 混用
常见错误:先用 slog.SetDefault(slog.New(tint.NewHandler(...))),又在某处写 log.Printf("\x1b[33mWARN\x1b[0m: %s", msg) —— 结果 WARN 是黄色,但后面所有 slog.Info 日志全变成黄色,甚至错位。
-
log.Printf和slog是两条独立日志链路,它们共用os.Stdout,但着色逻辑互不感知 - ANSI 序列未闭合(漏
\x1b[0m)会导致后续所有输出被染色,而 tint 输出的色码无法抵消它 - 真正统一方案:全切到
slog,用tint处理全部日志;或保留log,但彻底禁用所有手写 ANSI,改用colorable包装后的fmt.Fprintf(colorable.NewColorableStdout(), ...) - 调试时临时加色?用
fmt.Fprintln(colorable.NewColorableStdout(), "\x1b[36mDEBUG\x1b[0m", val),而不是fmt.Println—— 确保 writer 一致
最易被忽略的一点:tint 的颜色是“语义化”的,不是“装饰性”的。你改 slog.LevelError 的颜色,是为了让错误立刻被眼睛捕获,不是为了好看。一旦开始调色,就要同步更新团队文档和监控告警的颜色识别规则 —— 否则某天运维在灰度环境看到青色 Error,会以为是 tint bug。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











