Files
pig-farm-controller/internal/infra/logs/encoder.go
2025-11-05 23:10:51 +08:00

103 lines
3.5 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 logs
import (
"bytes"
"go.uber.org/zap/buffer"
"go.uber.org/zap/zapcore"
)
// traceKey 是日志中用于调用链的字段名,必须与 GetLogger 中使用的保持一致。
const traceKey = "Trace"
// coloredConsoleEncoder 是一个自定义的 zapcore.Encoder它实现了两个核心功能
// 1. 为控制台输出的日志行根据级别添加 ANSI 颜色。
// 2. 确保代表“调用链”的结构化字段总是出现在日志行的末尾。
type coloredConsoleEncoder struct {
zapcore.Encoder // 嵌入一个标准的 ConsoleEncoder复用其大部分能力。
}
// NewColoredConsoleEncoder 创建一个新的 coloredConsoleEncoder 实例。
func NewColoredConsoleEncoder(cfg zapcore.EncoderConfig) zapcore.Encoder {
return &coloredConsoleEncoder{
Encoder: zapcore.NewConsoleEncoder(cfg),
}
}
// EncodeEntry 重写了核心的编码方法。
func (c *coloredConsoleEncoder) EncodeEntry(entry zapcore.Entry, fields []zapcore.Field) (*buffer.Buffer, error) {
// 1. 从所有字段中分离出“调用链”字段和其他字段
var traceField zapcore.Field
otherFields := make([]zapcore.Field, 0, len(fields))
for _, field := range fields {
if field.Key == traceKey {
traceField = field
} else {
otherFields = append(otherFields, field)
}
}
// 2. 使用内嵌的 ConsoleEncoder 先编码日志条目和“其他”字段
// 这会生成不包含调用链的日志主体部分。
line, err := c.Encoder.EncodeEntry(entry, otherFields)
if err != nil {
return nil, err
}
// 3. 如果存在“调用链”字段,则将其手动编码并追加到末尾
if traceField.Key != "" {
// 为了保持格式一致,我们创建一个临时的 Encoder 来只编码这一个字段
// 注意:这里我们不能直接使用 c.Encoder因为它会添加不必要的前缀如时间、级别等
tempEncoder := zapcore.NewConsoleEncoder(zapcore.EncoderConfig{})
traceLine, err := tempEncoder.EncodeEntry(zapcore.Entry{}, []zapcore.Field{traceField})
if err != nil {
return nil, err // 理论上不应失败
}
// 将编码后的调用链字段(例如 "逻辑调用链=xxx->yyy")追加到日志主体后
// TrimSpace 用于移除 tempEncoder 可能产生的额外换行符
line.AppendString("\t") // 使用制表符分隔
line.AppendString(string(bytes.TrimSpace(traceLine.Bytes())))
}
// 4. 为最终的日志行(不包括调用链)添加颜色
var color string
switch entry.Level {
case zapcore.DebugLevel:
color = blue
case zapcore.InfoLevel:
color = green
case zapcore.WarnLevel:
color = yellow
case zapcore.ErrorLevel:
color = red
case zapcore.DPanicLevel, zapcore.PanicLevel, zapcore.FatalLevel:
color = bold + red
default:
color = reset
}
// 创建一个新的 buffer将颜色、日志内容和重置代码包裹起来
finalBuf := buffer.NewPool().Get()
finalBuf.AppendString(color)
finalBuf.Write(line.Bytes()) // 写入已包含调用链的日志行
finalBuf.AppendString(reset)
// 如果原始日志行末尾没有换行符,则添加一个
if line.Bytes()[line.Len()-1] != '\n' {
finalBuf.AppendByte('\n')
}
line.Free() // 释放由 c.Encoder.EncodeEntry 创建的 buffer
return finalBuf, nil
}
// Clone 必须被重写,以确保克隆时返回的是我们自定义的 encoder 类型。
func (c *coloredConsoleEncoder) Clone() zapcore.Encoder {
// 克隆内嵌的 Encoder并用它来创建一个新的 coloredConsoleEncoder
return &coloredConsoleEncoder{
Encoder: c.Encoder.Clone(),
}
}