log包默认不支持彩色输出,因其设计为纯文本、无格式,仅将字符串写入io.Writer;实现彩色需手动拼接ANSI转义序列,并用golang.org/x/term.IsTerminal()判断终端支持性。

为什么 log 包默认不支持彩色输出
Go 标准库的 log 包是纯文本、无格式、无样式设计的,它只负责把 string 写到 io.Writer,连换行都要手动加。所以直接调用 log.Println() 或 log.Printf() 永远不会出现颜色——不是配置没开对,而是根本没这功能。
要实现控制台彩色日志,必须自己构造带 ANSI 转义序列的字符串,再写入 os.Stdout(或包装它的 io.Writer)。关键点在于:颜色不是“开启开关”,而是“拼接前缀字符串”。
用 golang.org/x/term 判断终端是否支持颜色
盲目输出 ANSI 序列会导致日志文件里出现乱码(比如重定向到 log.txt 时),所以得先检测当前输出目标是否为真终端。标准做法是用 golang.org/x/term 的 IsTerminal():
import "golang.org/x/term"
fd := int(os.Stdout.Fd())
if term.IsTerminal(fd) {
// 可以安全写 \033[32m 这类序列
}
注意:IsTerminal() 在 Windows 上依赖 golang.org/x/sys/windows,但 Go 1.21+ 已内置支持;若用旧版或交叉编译,需确保 x/sys 版本匹配。
立即学习“go语言免费学习笔记(深入)”;
- 别用
os.Getenv("TERM")判断——Docker 容器里常为空,但实际仍支持颜色 - 别在测试中硬编码
os.Stdout = &bytes.Buffer{}后还期望颜色生效——bytes.Buffer不是终端 - CI 环境(如 GitHub Actions)通常禁用颜色,靠
IsTerminal()自动 fallback 更可靠
中间件里怎么包装 http.Handler 并注入彩色日志
典型场景是 HTTP 请求日志:状态码绿色、4xx 黄色、5xx 红色,路径加粗,耗时按阈值变色。核心不是改 log,而是拦截 ResponseWriter 获取状态码,并在 WriteHeader() 和 Write() 之间记录。
使用 uber-go/dig 在 Golang 中实现依赖注入——基于反射容器,提供 Provide/Invoke、dig.In/dig.Out 参数与结果对象,支持命名依赖等
示例关键逻辑:
type colorResponseWriter struct {
http.ResponseWriter
statusCode int
}
func (w *colorResponseWriter) WriteHeader(code int) {
w.statusCode = code
w.ResponseWriter.WriteHeader(code)
}
func loggingMiddleware(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
start := time.Now()
cw := &colorResponseWriter{
ResponseWriter: w,
statusCode: http.StatusOK,
}
next.ServeHTTP(cw, r)
dur := time.Since(start)
statusColor := "\033[37m" // 默认灰
switch {
case cw.statusCode >= 500:
statusColor = "\033[31m" // 红
case cw.statusCode >= 400:
statusColor = "\033[33m" // 黄
case cw.statusCode >= 200:
statusColor = "\033[32m" // 绿
}
reset := "\033[0m"
fmt.Printf("%s%s%s %s%s%s %s%dms%s\n",
"\033[1m", r.Method, reset,
statusColor, cw.statusCode, reset,
"\033[36m", int64(dur.Microseconds())/1000, reset)
})
}
要点:
- 必须用
fmt.Printf或fmt.Fprint直接输出,log.Printf会额外加时间戳和换行,破坏颜色对齐 - ANSI 序列要用
\033(不是\e),Windows CMD 兼容性更好 - 每个颜色后必须跟
\033[0m重置,否则后续所有终端输出都带色 - 别在
net/http中间件里用log.SetOutput()全局替换——会影响其他组件(如pprof)
第三方库选 logrus 还是 zerolog?
如果项目已用 logrus,可加 logrus.TextFormatter{DisableColors: false}(默认启用颜色);zerolog 则需自行包装 zerolog.ConsoleWriter 并设 Out: os.Stdout ——但两者底层仍是拼 ANSI 字符串。
真正要注意的是:这些库的“彩色”只是开发机友好,上线后通常关掉。所以中间件里应留钩子,比如:
var enableColorLog = os.Getenv("ENABLE_COLOR_LOG") == "1"
func coloredStatus(status int) string {
if !enableColorLog {
return strconv.Itoa(status)
}
// ... 返回带 \033 的字符串
}
容易被忽略的点:
- Windows PowerShell 默认支持 ANSI,但旧版 CMD 需提前调用
SetConsoleMode(... ENABLE_VIRTUAL_TERMINAL_PROCESSING)(Go 1.19+os/exec已自动处理,但直接写os.Stdout仍需手动) - 某些 IDE(如 VS Code 的 Debug Console)解析 ANSI 不完整,颜色可能错位,建议优先在真实终端验证
- 结构化日志(JSON)和彩色日志互斥——别试图给 JSON 字段加颜色,那是终端渲染层的事

















