package logger import ( "cargo-erp-backend/internal/config" "fmt" "log" "os" "path/filepath" "strconv" "go.uber.org/zap" "go.uber.org/zap/zapcore" "gopkg.in/natefinch/lumberjack.v2" ) type LoggerInterface interface { Init() Get(key string) *zap.Logger } type Logger struct { Config config.ConfigInterface Logger map[string]*zap.Logger } func NewLogger(config config.ConfigInterface) LoggerInterface { return &Logger{ Config: config, } } func (l *Logger) Init() { log.Println("initial database logger") l.Logger = make(map[string]*zap.Logger) l.Logger["DB"] = nil l.Logger["APP"] = nil if v, ok := l.Config.Get("database.logger").(map[string]interface{}); ok { if val, ok := v["filename"]; ok { if filename, ok := val.(string); ok { file_location, err := filepath.Abs(filename) MaxSize := 10 MaxBackups := 5 MaxAge := 30 Compress := true if err == nil { if s, ok := v["MaxSize"]; ok { size, err := strconv.Atoi(fmt.Sprintf("%v", s)) if err != nil { MaxSize = size } } if s, ok := v["max_backups"]; ok { size, err := strconv.Atoi(fmt.Sprintf("%v", s)) if err != nil { MaxBackups = size } } if s, ok := v["max_age"]; ok { size, err := strconv.Atoi(fmt.Sprintf("%v", s)) if err != nil { MaxAge = size } } if s, ok := v["compress"]; ok { if sv, ok := s.(bool); ok { Compress = sv } } } l.Logger["DB"] = l.NewLog(file_location, MaxSize, MaxBackups, MaxAge, Compress) } } } if v, ok := l.Config.Get("logger").(map[string]interface{}); ok { if val, ok := v["filename"]; ok { if filename, ok := val.(string); ok { file_location, err := filepath.Abs(filename) MaxSize := 10 MaxBackups := 5 MaxAge := 30 Compress := true if err == nil { if s, ok := v["MaxSize"]; ok { size, err := strconv.Atoi(fmt.Sprintf("%v", s)) if err != nil { MaxSize = size } } if s, ok := v["max_backups"]; ok { size, err := strconv.Atoi(fmt.Sprintf("%v", s)) if err != nil { MaxBackups = size } } if s, ok := v["max_age"]; ok { size, err := strconv.Atoi(fmt.Sprintf("%v", s)) if err != nil { MaxAge = size } } if s, ok := v["compress"]; ok { if sv, ok := s.(bool); ok { Compress = sv } } } l.Logger["APP"] = l.NewLog(file_location, MaxSize, MaxBackups, MaxAge, Compress) if l.Logger["DB"] == nil { l.Logger["DB"] = l.Logger["APP"] } } } } } func (l *Logger) Get(key string) *zap.Logger { if v, ok := l.Logger[key]; ok { return v } return nil } func (l *Logger) NewLog(filename string, max_size int, max_backup int, max_age int, compress bool) *zap.Logger { lumberjackLogger := &lumberjack.Logger{ Filename: filename, // Lokasi file MaxSize: max_size, // Megabytes sebelum rotasi MaxBackups: max_backup, // Jumlah file cadangan MaxAge: max_age, // Retention dalam HARI Compress: compress, // Otomatis kompres ke file .gz } encoderConfig := zap.NewProductionEncoderConfig() encoderConfig.EncodeTime = zapcore.ISO8601TimeEncoder // Format waktu: 2026-08-13T... jsonEncoder := zapcore.NewJSONEncoder(encoderConfig) consoleEncoder := zapcore.NewConsoleEncoder(zap.NewDevelopmentEncoderConfig()) // Hanya meloloskan level DEBUG dan INFO isInfoOrDebug := zap.LevelEnablerFunc(func(lvl zapcore.Level) bool { return lvl == zap.DebugLevel || lvl == zap.InfoLevel }) // Hanya meloloskan level WARN, ERROR, PANIC, FATAL isErrorOrHigher := zap.LevelEnablerFunc(func(lvl zapcore.Level) bool { return lvl >= zap.WarnLevel }) core := zapcore.NewTee( zapcore.NewCore(jsonEncoder, zapcore.AddSync(lumberjackLogger), isInfoOrDebug), // Masuk ke file error (Ganti os.Stderr dengan errorFileLogger Anda) zapcore.NewCore(jsonEncoder, zapcore.AddSync(lumberjackLogger), isErrorOrHigher), // CONSOLE LOGS (Terminal): Biasanya ingin melihat semua level dari DEBUG sampai FATAL zapcore.NewCore(consoleEncoder, zapcore.AddSync(os.Stdout), zap.DebugLevel), ) logger := zap.New(core, zap.AddCaller()) defer logger.Sync() return logger }