✨ Enhance logging configuration by introducing file output options and rotation settings in config.dev.toml. Update logger implementation to support multiple output streams, including file logging with rotation using lumberjack. Improve error handling and logging across various components, ensuring consistent logging practices. Update .gitignore to include log files and compressed logs. Add new Go module dependency for lumberjack.
This commit is contained in:
+117
-16
@@ -2,44 +2,145 @@ package log
|
||||
|
||||
import (
|
||||
"caatsm/internal/infra/config"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"time"
|
||||
|
||||
"go.uber.org/zap"
|
||||
"go.uber.org/zap/zapcore"
|
||||
"gopkg.in/natefinch/lumberjack.v2"
|
||||
)
|
||||
|
||||
// ProvideLogger creates a zap logger based on configuration
|
||||
func ProvideLogger(cfg *config.Config) (*zap.Logger, error) {
|
||||
var zapConfig zap.Config
|
||||
|
||||
if cfg.Log.Format == "console" {
|
||||
|
||||
if cfg.Log.Development || cfg.Log.Format == "console" {
|
||||
zapConfig = zap.NewDevelopmentConfig()
|
||||
} else {
|
||||
zapConfig = zap.NewProductionConfig()
|
||||
}
|
||||
|
||||
|
||||
// Set log level
|
||||
var level zapcore.Level
|
||||
switch cfg.Log.Level {
|
||||
case "debug":
|
||||
zapConfig.Level = zap.NewAtomicLevelAt(zapcore.DebugLevel)
|
||||
level = zapcore.DebugLevel
|
||||
case "info":
|
||||
zapConfig.Level = zap.NewAtomicLevelAt(zapcore.InfoLevel)
|
||||
level = zapcore.InfoLevel
|
||||
case "warn":
|
||||
zapConfig.Level = zap.NewAtomicLevelAt(zapcore.WarnLevel)
|
||||
level = zapcore.WarnLevel
|
||||
case "error":
|
||||
zapConfig.Level = zap.NewAtomicLevelAt(zapcore.ErrorLevel)
|
||||
level = zapcore.ErrorLevel
|
||||
default:
|
||||
zapConfig.Level = zap.NewAtomicLevelAt(zapcore.InfoLevel)
|
||||
level = zapcore.InfoLevel
|
||||
}
|
||||
|
||||
zapConfig.Level = zap.NewAtomicLevelAt(level)
|
||||
|
||||
// Determine outputs
|
||||
outputs := cfg.Log.Output
|
||||
if len(outputs) == 0 {
|
||||
outputs = []string{"stdout"} // Default to stdout
|
||||
}
|
||||
|
||||
// Create encoder
|
||||
var encoder zapcore.Encoder
|
||||
if cfg.Log.Format == "json" {
|
||||
encoder = zapcore.NewJSONEncoder(zapConfig.EncoderConfig)
|
||||
} else {
|
||||
encoder = zapcore.NewConsoleEncoder(zapConfig.EncoderConfig)
|
||||
}
|
||||
|
||||
// Build cores for different outputs
|
||||
var cores []zapcore.Core
|
||||
|
||||
// Create cores for each output
|
||||
for _, output := range outputs {
|
||||
var writeSyncer zapcore.WriteSyncer
|
||||
|
||||
switch output {
|
||||
case "stdout":
|
||||
writeSyncer = zapcore.AddSync(os.Stdout)
|
||||
case "stderr":
|
||||
writeSyncer = zapcore.AddSync(os.Stderr)
|
||||
case "file":
|
||||
filePath := cfg.Log.File
|
||||
if filePath == "" {
|
||||
// Use default path if file path is not specified
|
||||
filePath = "logs/caatsm.log"
|
||||
}
|
||||
// Ensure log directory exists
|
||||
dir := filepath.Dir(filePath)
|
||||
if dir != "" && dir != "." {
|
||||
if err := os.MkdirAll(dir, 0755); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
// Configure log rotation
|
||||
lj := &lumberjack.Logger{
|
||||
Filename: filePath,
|
||||
MaxSize: cfg.Log.MaxSize, // MB
|
||||
MaxBackups: cfg.Log.MaxBackups,
|
||||
MaxAge: cfg.Log.MaxAge, // days
|
||||
Compress: cfg.Log.Compress,
|
||||
}
|
||||
writeSyncer = zapcore.AddSync(lj)
|
||||
default:
|
||||
// Treat as file path
|
||||
dir := filepath.Dir(output)
|
||||
if dir != "" && dir != "." {
|
||||
if err := os.MkdirAll(dir, 0755); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
lj := &lumberjack.Logger{
|
||||
Filename: output,
|
||||
MaxSize: cfg.Log.MaxSize,
|
||||
MaxBackups: cfg.Log.MaxBackups,
|
||||
MaxAge: cfg.Log.MaxAge,
|
||||
Compress: cfg.Log.Compress,
|
||||
}
|
||||
writeSyncer = zapcore.AddSync(lj)
|
||||
}
|
||||
|
||||
cores = append(cores, zapcore.NewCore(encoder, writeSyncer, zapConfig.Level))
|
||||
}
|
||||
|
||||
// Combine cores
|
||||
core := zapcore.NewTee(cores...)
|
||||
|
||||
// Build options
|
||||
opts := []zap.Option{}
|
||||
if !cfg.Log.DisableCaller {
|
||||
opts = append(opts, zap.AddCaller())
|
||||
}
|
||||
if !cfg.Log.DisableStacktrace {
|
||||
opts = append(opts, zap.AddStacktrace(zapcore.ErrorLevel))
|
||||
}
|
||||
|
||||
// Add sampling if configured
|
||||
if cfg.Log.Sampling != nil && cfg.Log.Sampling.Initial > 0 {
|
||||
tick := cfg.Log.Sampling.Tick
|
||||
if tick == 0 {
|
||||
tick = time.Second // Default to 1 second
|
||||
}
|
||||
opts = append(opts, zap.WrapCore(func(core zapcore.Core) zapcore.Core {
|
||||
return zapcore.NewSamplerWithOptions(
|
||||
core,
|
||||
tick,
|
||||
cfg.Log.Sampling.Initial,
|
||||
cfg.Log.Sampling.Thereafter,
|
||||
)
|
||||
}))
|
||||
}
|
||||
|
||||
// Build logger
|
||||
logger, err := zapConfig.Build(zap.AddCaller(), zap.AddStacktrace(zapcore.ErrorLevel))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
logger := zap.New(core, opts...)
|
||||
|
||||
// Note: We still replace global logger for backward compatibility with parsers package
|
||||
// This will be removed once parsers are fully migrated to use dependency injection
|
||||
zap.ReplaceGlobals(logger)
|
||||
|
||||
|
||||
return logger, nil
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user