feat: 添加 zap 日志工厂和 GORM 日志桥接
- NewLogger 工厂函数:支持 JSON/Console 编码、stdout/文件/多输出、lumberjack 轮转 - NewGormLogger 实现 gorm.Interface:Trace 区分错误/慢查询/正常查询 - output_stdout 用 *bool 三态处理(nil=true, true, false) - 默认值:level=info, encoding=json, max_size=100, max_backups=5, max_age=30 - 慢查询阈值 200ms,ErrRecordNotFound 不视为错误 - 编译时接口检查: var _ gormlogger.Interface = (*GormLogger)(nil) - 完整 TDD 测试覆盖 Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent) Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
This commit is contained in:
93
internal/logger/logger.go
Normal file
93
internal/logger/logger.go
Normal file
@@ -0,0 +1,93 @@
|
||||
package logger
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"gcy_hpc_server/internal/config"
|
||||
|
||||
"go.uber.org/zap"
|
||||
"go.uber.org/zap/zapcore"
|
||||
"gopkg.in/natefinch/lumberjack.v2"
|
||||
)
|
||||
|
||||
func NewLogger(cfg config.LogConfig) (*zap.Logger, error) {
|
||||
level := applyDefault(cfg.Level, "info")
|
||||
|
||||
var zapLevel zapcore.Level
|
||||
if err := zapLevel.UnmarshalText([]byte(level)); err != nil {
|
||||
return nil, fmt.Errorf("invalid log level %q: %w", level, err)
|
||||
}
|
||||
|
||||
encoding := applyDefault(cfg.Encoding, "json")
|
||||
encoderConfig := zap.NewProductionEncoderConfig()
|
||||
encoderConfig.TimeKey = "ts"
|
||||
encoderConfig.EncodeTime = zapcore.ISO8601TimeEncoder
|
||||
encoderConfig.EncodeLevel = zapcore.CapitalLevelEncoder
|
||||
|
||||
var encoder zapcore.Encoder
|
||||
switch encoding {
|
||||
case "console":
|
||||
encoder = zapcore.NewConsoleEncoder(encoderConfig)
|
||||
default:
|
||||
encoder = zapcore.NewJSONEncoder(encoderConfig)
|
||||
}
|
||||
|
||||
var syncers []zapcore.WriteSyncer
|
||||
|
||||
stdout := true
|
||||
if cfg.OutputStdout != nil {
|
||||
stdout = *cfg.OutputStdout
|
||||
}
|
||||
if stdout {
|
||||
syncers = append(syncers, zapcore.AddSync(os.Stdout))
|
||||
}
|
||||
|
||||
if cfg.FilePath != "" {
|
||||
maxSize := applyDefaultInt(cfg.MaxSize, 100)
|
||||
maxBackups := applyDefaultInt(cfg.MaxBackups, 5)
|
||||
maxAge := applyDefaultInt(cfg.MaxAge, 30)
|
||||
compress := cfg.Compress || cfg.MaxSize == 0 && cfg.MaxBackups == 0 && cfg.MaxAge == 0
|
||||
|
||||
lj := &lumberjack.Logger{
|
||||
Filename: cfg.FilePath,
|
||||
MaxSize: maxSize,
|
||||
MaxBackups: maxBackups,
|
||||
MaxAge: maxAge,
|
||||
Compress: compress,
|
||||
}
|
||||
syncers = append(syncers, zapcore.AddSync(lj))
|
||||
}
|
||||
|
||||
if len(syncers) == 0 {
|
||||
syncers = append(syncers, zapcore.AddSync(os.Stdout))
|
||||
}
|
||||
|
||||
writeSyncer := syncers[0]
|
||||
if len(syncers) > 1 {
|
||||
writeSyncer = zapcore.NewMultiWriteSyncer(syncers...)
|
||||
}
|
||||
|
||||
core := zapcore.NewCore(encoder, writeSyncer, zapLevel)
|
||||
|
||||
opts := []zap.Option{
|
||||
zap.AddCaller(),
|
||||
zap.AddStacktrace(zapcore.ErrorLevel),
|
||||
}
|
||||
|
||||
return zap.New(core, opts...), nil
|
||||
}
|
||||
|
||||
func applyDefault(val, def string) string {
|
||||
if val == "" {
|
||||
return def
|
||||
}
|
||||
return val
|
||||
}
|
||||
|
||||
func applyDefaultInt(val, def int) int {
|
||||
if val == 0 {
|
||||
return def
|
||||
}
|
||||
return val
|
||||
}
|
||||
Reference in New Issue
Block a user