修复bug
This commit is contained in:
@@ -1,8 +1,6 @@
|
|||||||
package logs
|
package logs
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
|
||||||
|
|
||||||
"go.uber.org/zap/buffer"
|
"go.uber.org/zap/buffer"
|
||||||
"go.uber.org/zap/zapcore"
|
"go.uber.org/zap/zapcore"
|
||||||
)
|
)
|
||||||
@@ -24,43 +22,16 @@ func NewColoredConsoleEncoder(cfg zapcore.EncoderConfig) zapcore.Encoder {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// EncodeEntry 重写了核心的编码方法。
|
||||||
// EncodeEntry 重写了核心的编码方法。
|
// EncodeEntry 重写了核心的编码方法。
|
||||||
func (c *coloredConsoleEncoder) EncodeEntry(entry zapcore.Entry, fields []zapcore.Field) (*buffer.Buffer, error) {
|
func (c *coloredConsoleEncoder) EncodeEntry(entry zapcore.Entry, fields []zapcore.Field) (*buffer.Buffer, error) {
|
||||||
// 1. 从所有字段中分离出“调用链”字段和其他字段
|
// 1. 直接使用内嵌的 Encoder 编码日志条目和所有字段。
|
||||||
var traceField zapcore.Field
|
line, err := c.Encoder.EncodeEntry(entry, fields)
|
||||||
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 {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// 3. 如果存在“调用链”字段,则将其手动编码并追加到末尾
|
// 2. 为最终的日志行添加颜色。
|
||||||
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
|
var color string
|
||||||
switch entry.Level {
|
switch entry.Level {
|
||||||
case zapcore.DebugLevel:
|
case zapcore.DebugLevel:
|
||||||
@@ -77,14 +48,14 @@ func (c *coloredConsoleEncoder) EncodeEntry(entry zapcore.Entry, fields []zapcor
|
|||||||
color = reset
|
color = reset
|
||||||
}
|
}
|
||||||
|
|
||||||
// 创建一个新的 buffer,将颜色、日志内容和重置代码包裹起来
|
// 3. 创建一个新的 buffer,将颜色、日志内容和重置代码包裹起来。
|
||||||
finalBuf := buffer.NewPool().Get()
|
finalBuf := buffer.NewPool().Get()
|
||||||
finalBuf.AppendString(color)
|
finalBuf.AppendString(color)
|
||||||
finalBuf.Write(line.Bytes()) // 写入已包含调用链的日志行
|
finalBuf.Write(line.Bytes())
|
||||||
finalBuf.AppendString(reset)
|
finalBuf.AppendString(reset)
|
||||||
|
|
||||||
// 如果原始日志行末尾没有换行符,则添加一个
|
// 4. 如果原始日志行末尾没有换行符,则添加一个。
|
||||||
if line.Bytes()[line.Len()-1] != '\n' {
|
if line.Len() > 0 && line.Bytes()[line.Len()-1] != '\n' {
|
||||||
finalBuf.AppendByte('\n')
|
finalBuf.AppendByte('\n')
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
153
internal/infra/logs/logger_methods.go
Normal file
153
internal/infra/logs/logger_methods.go
Normal file
@@ -0,0 +1,153 @@
|
|||||||
|
package logs
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"go.uber.org/zap/zapcore"
|
||||||
|
)
|
||||||
|
|
||||||
|
// logWithTrace 是一个私有的辅助函数,用于统一处理日志记录。
|
||||||
|
// 它会检查 logger 实例中是否包含 traceValue,如果存在,则将其作为最后一个结构化字段追加。
|
||||||
|
// 然后,它根据传入的日志级别,调用底层 SugaredLogger 对应级别的 '...w' 方法。
|
||||||
|
func (l *Logger) logWithTrace(level zapcore.Level, msg string, keysAndValues ...interface{}) {
|
||||||
|
// 如果存在调用链信息,则将其追加到键值对列表的末尾
|
||||||
|
if l.traceValue != "" {
|
||||||
|
keysAndValues = append(keysAndValues, traceKey, l.traceValue)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 根据级别调用相应的 '...w' 方法
|
||||||
|
switch level {
|
||||||
|
case zapcore.DebugLevel:
|
||||||
|
l.sl.Debugw(msg, keysAndValues...)
|
||||||
|
case zapcore.InfoLevel:
|
||||||
|
l.sl.Infow(msg, keysAndValues...)
|
||||||
|
case zapcore.WarnLevel:
|
||||||
|
l.sl.Warnw(msg, keysAndValues...)
|
||||||
|
case zapcore.ErrorLevel:
|
||||||
|
l.sl.Errorw(msg, keysAndValues...)
|
||||||
|
case zapcore.DPanicLevel:
|
||||||
|
l.sl.DPanicw(msg, keysAndValues...)
|
||||||
|
case zapcore.PanicLevel:
|
||||||
|
l.sl.Panicw(msg, keysAndValues...)
|
||||||
|
case zapcore.FatalLevel:
|
||||||
|
l.sl.Fatalw(msg, keysAndValues...)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- 重写所有日志记录方法 ---
|
||||||
|
|
||||||
|
// Debug 使用 fmt.Sprint 格式化参数并记录 Debug 级别的日志。
|
||||||
|
func (l *Logger) Debug(args ...interface{}) {
|
||||||
|
l.logWithTrace(zapcore.DebugLevel, fmt.Sprint(args...))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Info 使用 fmt.Sprint 格式化参数并记录 Info 级别的日志。
|
||||||
|
func (l *Logger) Info(args ...interface{}) {
|
||||||
|
l.logWithTrace(zapcore.InfoLevel, fmt.Sprint(args...))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Warn 使用 fmt.Sprint 格式化参数并记录 Warn 级别的日志。
|
||||||
|
func (l *Logger) Warn(args ...interface{}) {
|
||||||
|
l.logWithTrace(zapcore.WarnLevel, fmt.Sprint(args...))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Error 使用 fmt.Sprint 格式化参数并记录 Error 级别的日志。
|
||||||
|
func (l *Logger) Error(args ...interface{}) {
|
||||||
|
l.logWithTrace(zapcore.ErrorLevel, fmt.Sprint(args...))
|
||||||
|
}
|
||||||
|
|
||||||
|
// DPanic 使用 fmt.Sprint 格式化参数并记录 DPanic 级别的日志。
|
||||||
|
func (l *Logger) DPanic(args ...interface{}) {
|
||||||
|
l.logWithTrace(zapcore.DPanicLevel, fmt.Sprint(args...))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Panic 使用 fmt.Sprint 格式化参数并记录 Panic 级别的日志。
|
||||||
|
func (l *Logger) Panic(args ...interface{}) {
|
||||||
|
l.logWithTrace(zapcore.PanicLevel, fmt.Sprint(args...))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fatal 使用 fmt.Sprint 格式化参数并记录 Fatal 级别的日志。
|
||||||
|
func (l *Logger) Fatal(args ...interface{}) {
|
||||||
|
l.logWithTrace(zapcore.FatalLevel, fmt.Sprint(args...))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Debugf 使用 fmt.Sprintf 格式化模板和参数,并记录 Debug 级别的日志。
|
||||||
|
func (l *Logger) Debugf(template string, args ...interface{}) {
|
||||||
|
l.logWithTrace(zapcore.DebugLevel, fmt.Sprintf(template, args...))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Infof 使用 fmt.Sprintf 格式化模板和参数,并记录 Info 级别的日志。
|
||||||
|
func (l *Logger) Infof(template string, args ...interface{}) {
|
||||||
|
l.logWithTrace(zapcore.InfoLevel, fmt.Sprintf(template, args...))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Warnf 使用 fmt.Sprintf 格式化模板和参数,并记录 Warn 级别的日志。
|
||||||
|
func (l *Logger) Warnf(template string, args ...interface{}) {
|
||||||
|
l.logWithTrace(zapcore.WarnLevel, fmt.Sprintf(template, args...))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Errorf 使用 fmt.Sprintf 格式化模板和参数,并记录 Error 级别的日志。
|
||||||
|
func (l *Logger) Errorf(template string, args ...interface{}) {
|
||||||
|
l.logWithTrace(zapcore.ErrorLevel, fmt.Sprintf(template, args...))
|
||||||
|
}
|
||||||
|
|
||||||
|
// DPanicf 使用 fmt.Sprintf 格式化模板和参数,并记录 DPanic 级别的日志。
|
||||||
|
func (l *Logger) DPanicf(template string, args ...interface{}) {
|
||||||
|
l.logWithTrace(zapcore.DPanicLevel, fmt.Sprintf(template, args...))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Panicf 使用 fmt.Sprintf 格式化模板和参数,并记录 Panic 级别的日志。
|
||||||
|
func (l *Logger) Panicf(template string, args ...interface{}) {
|
||||||
|
l.logWithTrace(zapcore.PanicLevel, fmt.Sprintf(template, args...))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fatalf 使用 fmt.Sprintf 格式化模板和参数,并记录 Fatal 级别的日志。
|
||||||
|
func (l *Logger) Fatalf(template string, args ...interface{}) {
|
||||||
|
l.logWithTrace(zapcore.FatalLevel, fmt.Sprintf(template, args...))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Debugw 记录 Debug 级别的结构化日志。
|
||||||
|
func (l *Logger) Debugw(msg string, keysAndValues ...interface{}) {
|
||||||
|
l.logWithTrace(zapcore.DebugLevel, msg, keysAndValues...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Infow 记录 Info 级别的结构化日志。
|
||||||
|
func (l *Logger) Infow(msg string, keysAndValues ...interface{}) {
|
||||||
|
l.logWithTrace(zapcore.InfoLevel, msg, keysAndValues...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Warnw 记录 Warn 级别的结构化日志。
|
||||||
|
func (l *Logger) Warnw(msg string, keysAndValues ...interface{}) {
|
||||||
|
l.logWithTrace(zapcore.WarnLevel, msg, keysAndValues...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Errorw 记录 Error 级别的结构化日志。
|
||||||
|
func (l *Logger) Errorw(msg string, keysAndValues ...interface{}) {
|
||||||
|
l.logWithTrace(zapcore.ErrorLevel, msg, keysAndValues...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// DPanicw 记录 DPanic 级别的结构化日志。
|
||||||
|
func (l *Logger) DPanicw(msg string, keysAndValues ...interface{}) {
|
||||||
|
l.logWithTrace(zapcore.DPanicLevel, msg, keysAndValues...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Panicw 记录 Panic 级别的结构化日志。
|
||||||
|
func (l *Logger) Panicw(msg string, keysAndValues ...interface{}) {
|
||||||
|
l.logWithTrace(zapcore.PanicLevel, msg, keysAndValues...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fatalw 记录 Fatal 级别的结构化日志。
|
||||||
|
func (l *Logger) Fatalw(msg string, keysAndValues ...interface{}) {
|
||||||
|
l.logWithTrace(zapcore.FatalLevel, msg, keysAndValues...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// With 在 zap 中,With 方法返回的是一个新的 SugaredLogger。
|
||||||
|
// 为了保持兼容性并继续传递 traceValue,我们需要在这里也返回一个新的 Logger 实例。
|
||||||
|
func (l *Logger) With(args ...interface{}) *Logger {
|
||||||
|
newSl := l.sl.With(args...)
|
||||||
|
return &Logger{sl: newSl, traceValue: l.traceValue}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *Logger) Sync() error {
|
||||||
|
return l.sl.Sync()
|
||||||
|
}
|
||||||
@@ -41,7 +41,8 @@ var (
|
|||||||
// Logger 是一个封装了 zap.SugaredLogger 的日志记录器。
|
// Logger 是一个封装了 zap.SugaredLogger 的日志记录器。
|
||||||
// 它提供了结构化日志记录的各种方法,并实现了 io.Writer 接口以兼容 Gin。
|
// 它提供了结构化日志记录的各种方法,并实现了 io.Writer 接口以兼容 Gin。
|
||||||
type Logger struct {
|
type Logger struct {
|
||||||
*zap.SugaredLogger
|
sl *zap.SugaredLogger
|
||||||
|
traceValue string
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewLogger 根据提供的配置创建一个新的 Logger 实例。
|
// NewLogger 根据提供的配置创建一个新的 Logger 实例。
|
||||||
@@ -67,7 +68,7 @@ func NewLogger(cfg config.LogConfig) *Logger {
|
|||||||
// zap.AddCallerSkip(1) 可以向上跳一层调用栈,如果我们将 logger.Info 等方法再封装一层,这个选项会很有用
|
// zap.AddCallerSkip(1) 可以向上跳一层调用栈,如果我们将 logger.Info 等方法再封装一层,这个选项会很有用
|
||||||
zapLogger := zap.New(core, zap.AddCaller(), zap.AddCallerSkip(1))
|
zapLogger := zap.New(core, zap.AddCaller(), zap.AddCallerSkip(1))
|
||||||
|
|
||||||
return &Logger{zapLogger.Sugar()}
|
return &Logger{sl: zapLogger.Sugar()}
|
||||||
}
|
}
|
||||||
|
|
||||||
// InitDefaultLogger 初始化包级单例的 defaultLogger。
|
// InitDefaultLogger 初始化包级单例的 defaultLogger。
|
||||||
@@ -93,19 +94,13 @@ func GetLogger(ctx context.Context) *Logger {
|
|||||||
return defaultLogger
|
return defaultLogger
|
||||||
}
|
}
|
||||||
|
|
||||||
val := ctx.Value(chainKey)
|
|
||||||
if val == nil {
|
|
||||||
return defaultLogger
|
|
||||||
}
|
|
||||||
|
|
||||||
chain := GetTraceStr(ctx)
|
chain := GetTraceStr(ctx)
|
||||||
if chain == "" {
|
if chain == "" {
|
||||||
return defaultLogger
|
return defaultLogger
|
||||||
}
|
}
|
||||||
|
|
||||||
// 使用 With 方法创建带有 traceKey 字段的 Logger 副本
|
// 返回一个新的 Logger 实例,它携带了调用链信息
|
||||||
newSugaredLogger := defaultLogger.With(traceKey, chain)
|
return &Logger{sl: defaultLogger.sl, traceValue: chain}
|
||||||
return &Logger{newSugaredLogger}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Trace 是构建和记录调用链的核心函数。
|
// Trace 是构建和记录调用链的核心函数。
|
||||||
@@ -218,32 +213,24 @@ func (g *GormLogger) Trace(ctx context.Context, begin time.Time, fc func() (sql
|
|||||||
"elapsed", fmt.Sprintf("%.3fms", float32(elapsed.Nanoseconds())/1e6),
|
"elapsed", fmt.Sprintf("%.3fms", float32(elapsed.Nanoseconds())/1e6),
|
||||||
}
|
}
|
||||||
|
|
||||||
// 仅在启用调用链时附加信息
|
|
||||||
if logConfig.EnableTrace {
|
|
||||||
chain := GetTraceStr(ctx)
|
|
||||||
if chain != "" {
|
|
||||||
fields = append(fields, traceKey, chain)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
// 如果是 "record not found" 错误且我们配置了跳过,则直接返回
|
// 如果是 "record not found" 错误且我们配置了跳过,则直接返回
|
||||||
if g.SkipErrRecordNotFound && strings.Contains(err.Error(), "record not found") {
|
if g.SkipErrRecordNotFound && strings.Contains(err.Error(), "record not found") {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
// 否则,记录为错误日志
|
// 否则,记录为错误日志
|
||||||
defaultLogger.With(fields...).Errorf("[GORM] error: %s", err)
|
GetLogger(ctx).Errorw(fmt.Sprintf("[GORM] error: %s", err), fields...)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// 如果查询时间超过慢查询阈值,则记录警告
|
// 如果查询时间超过慢查询阈值,则记录警告
|
||||||
if g.SlowThreshold != 0 && elapsed > g.SlowThreshold {
|
if g.SlowThreshold != 0 && elapsed > g.SlowThreshold {
|
||||||
defaultLogger.With(fields...).Warnf("[GORM] slow query")
|
GetLogger(ctx).Warnw("[GORM] slow query", fields...)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// 正常情况,记录 Debug 级别的 SQL 查询
|
// 正常情况,记录 Debug 级别的 SQL 查询
|
||||||
defaultLogger.With(fields...).Debugf("[GORM] trace")
|
GetLogger(ctx).Debugw("[GORM] trace", fields...)
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewSilentLogger 创建一个不输出任何日志的 Logger 实例, 用于测试中屏蔽日志
|
// NewSilentLogger 创建一个不输出任何日志的 Logger 实例, 用于测试中屏蔽日志
|
||||||
@@ -255,5 +242,5 @@ func NewSilentLogger() *Logger {
|
|||||||
core := zapcore.NewCore(encoder, discardSyncer, zap.DebugLevel) // 设置为 DebugLevel 以确保所有日志都被处理(并丢弃)
|
core := zapcore.NewCore(encoder, discardSyncer, zap.DebugLevel) // 设置为 DebugLevel 以确保所有日志都被处理(并丢弃)
|
||||||
zapLogger := zap.New(core)
|
zapLogger := zap.New(core)
|
||||||
sugaredLogger := zapLogger.Sugar()
|
sugaredLogger := zapLogger.Sugar()
|
||||||
return &Logger{SugaredLogger: sugaredLogger}
|
return &Logger{sl: sugaredLogger}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user