chore(logging): 完成后端日志审计与结构化迁移
- 将高密度服务与处理器日志迁移到新日志系统(LegacyPrintf/结构化日志) - 增加 stdlog bridge 与兼容测试,保留旧日志捕获能力 - 将 OpenAI 断流告警改为结构化 Warn 并改造对应测试为 sink 捕获 - 补齐后端相关文件 logger 引用并通过全量 go test
This commit is contained in:
@@ -3,6 +3,7 @@ package logger
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"io"
|
||||
"log"
|
||||
"log/slog"
|
||||
"os"
|
||||
@@ -77,8 +78,8 @@ func initLocked(options InitOptions) error {
|
||||
atomicLevel = al
|
||||
initOptions = normalized
|
||||
|
||||
bridgeStdLogLocked()
|
||||
bridgeSlogLocked()
|
||||
bridgeStdLogLocked()
|
||||
|
||||
if prev != nil {
|
||||
_ = prev.Sync()
|
||||
@@ -163,14 +164,19 @@ func bridgeStdLogLocked() {
|
||||
stdLogUndo = nil
|
||||
}
|
||||
|
||||
prevFlags := log.Flags()
|
||||
prevPrefix := log.Prefix()
|
||||
prevWriter := log.Writer()
|
||||
|
||||
log.SetFlags(0)
|
||||
log.SetPrefix("")
|
||||
undo, err := zap.RedirectStdLogAt(global.Named("stdlog"), zap.InfoLevel)
|
||||
if err != nil {
|
||||
_, _ = fmt.Fprintf(os.Stderr, "logger redirect stdlog failed: %v\n", err)
|
||||
return
|
||||
log.SetOutput(newStdLogBridge(global.Named("stdlog")))
|
||||
|
||||
stdLogUndo = func() {
|
||||
log.SetOutput(prevWriter)
|
||||
log.SetFlags(prevFlags)
|
||||
log.SetPrefix(prevPrefix)
|
||||
}
|
||||
stdLogUndo = undo
|
||||
}
|
||||
|
||||
func bridgeSlogLocked() {
|
||||
@@ -347,6 +353,106 @@ func (s *sinkCore) Sync() error {
|
||||
return s.core.Sync()
|
||||
}
|
||||
|
||||
type stdLogBridge struct {
|
||||
logger *zap.Logger
|
||||
}
|
||||
|
||||
func newStdLogBridge(l *zap.Logger) io.Writer {
|
||||
if l == nil {
|
||||
l = zap.NewNop()
|
||||
}
|
||||
return &stdLogBridge{logger: l}
|
||||
}
|
||||
|
||||
func (b *stdLogBridge) Write(p []byte) (int, error) {
|
||||
msg := normalizeStdLogMessage(string(p))
|
||||
if msg == "" {
|
||||
return len(p), nil
|
||||
}
|
||||
|
||||
level := inferStdLogLevel(msg)
|
||||
entry := b.logger.WithOptions(zap.AddCallerSkip(4))
|
||||
|
||||
switch level {
|
||||
case LevelDebug:
|
||||
entry.Debug(msg, zap.Bool("legacy_stdlog", true))
|
||||
case LevelWarn:
|
||||
entry.Warn(msg, zap.Bool("legacy_stdlog", true))
|
||||
case LevelError, LevelFatal:
|
||||
entry.Error(msg, zap.Bool("legacy_stdlog", true))
|
||||
default:
|
||||
entry.Info(msg, zap.Bool("legacy_stdlog", true))
|
||||
}
|
||||
return len(p), nil
|
||||
}
|
||||
|
||||
func normalizeStdLogMessage(raw string) string {
|
||||
msg := strings.TrimSpace(strings.ReplaceAll(raw, "\n", " "))
|
||||
if msg == "" {
|
||||
return ""
|
||||
}
|
||||
return strings.Join(strings.Fields(msg), " ")
|
||||
}
|
||||
|
||||
func inferStdLogLevel(msg string) Level {
|
||||
lower := strings.ToLower(strings.TrimSpace(msg))
|
||||
if lower == "" {
|
||||
return LevelInfo
|
||||
}
|
||||
|
||||
if strings.HasPrefix(lower, "[debug]") || strings.HasPrefix(lower, "debug:") {
|
||||
return LevelDebug
|
||||
}
|
||||
if strings.HasPrefix(lower, "[warn]") || strings.HasPrefix(lower, "[warning]") || strings.HasPrefix(lower, "warn:") || strings.HasPrefix(lower, "warning:") {
|
||||
return LevelWarn
|
||||
}
|
||||
if strings.HasPrefix(lower, "[error]") || strings.HasPrefix(lower, "error:") || strings.HasPrefix(lower, "fatal:") || strings.HasPrefix(lower, "panic:") {
|
||||
return LevelError
|
||||
}
|
||||
|
||||
if strings.Contains(lower, " failed") || strings.Contains(lower, "error") || strings.Contains(lower, "panic") || strings.Contains(lower, "fatal") {
|
||||
return LevelError
|
||||
}
|
||||
if strings.Contains(lower, "warning") || strings.Contains(lower, "warn") || strings.Contains(lower, " retry") || strings.Contains(lower, " queue full") || strings.Contains(lower, "fallback") {
|
||||
return LevelWarn
|
||||
}
|
||||
return LevelInfo
|
||||
}
|
||||
|
||||
// LegacyPrintf 用于平滑迁移历史的 printf 风格日志到结构化 logger。
|
||||
func LegacyPrintf(component, format string, args ...any) {
|
||||
msg := normalizeStdLogMessage(fmt.Sprintf(format, args...))
|
||||
if msg == "" {
|
||||
return
|
||||
}
|
||||
|
||||
mu.RLock()
|
||||
initialized := global != nil
|
||||
mu.RUnlock()
|
||||
if !initialized {
|
||||
// 在日志系统未初始化前,回退到标准库 log,避免测试/工具链丢日志。
|
||||
log.Print(msg)
|
||||
return
|
||||
}
|
||||
|
||||
l := L()
|
||||
if component != "" {
|
||||
l = l.With(zap.String("component", component))
|
||||
}
|
||||
l = l.WithOptions(zap.AddCallerSkip(1))
|
||||
|
||||
switch inferStdLogLevel(msg) {
|
||||
case LevelDebug:
|
||||
l.Debug(msg, zap.Bool("legacy_printf", true))
|
||||
case LevelWarn:
|
||||
l.Warn(msg, zap.Bool("legacy_printf", true))
|
||||
case LevelError, LevelFatal:
|
||||
l.Error(msg, zap.Bool("legacy_printf", true))
|
||||
default:
|
||||
l.Info(msg, zap.Bool("legacy_printf", true))
|
||||
}
|
||||
}
|
||||
|
||||
type contextKey string
|
||||
|
||||
const loggerContextKey contextKey = "ctx_logger"
|
||||
|
||||
Reference in New Issue
Block a user