package rotating_logger import ( "execguard/core/configure" "compress/gzip" "os" "fmt" "io" "strings" "path/filepath" "sync" ) type RotatingLogger struct { config configure.LoggingConfig currentFile *os.File mu sync.Mutex } func NewRotatingLogger(config configure.LoggingConfig) (*RotatingLogger, error) { rl := &RotatingLogger{config: config} if err := rl.openFile(); err != nil { return nil, err } return rl, nil } func (rl *RotatingLogger) openFile() error { if err := os.MkdirAll(filepath.Dir(rl.config.FilePath), 0755); err != nil { return fmt.Errorf("failed to create log directory: %v", err) } file, err := os.OpenFile(rl.config.FilePath, os.O_WRONLY|os.O_CREATE|os.O_APPEND, 0640) if err != nil { return fmt.Errorf("failed to open log file: %v", err) } rl.currentFile = file return nil } func (rl *RotatingLogger) rotate() error { rl.mu.Lock() defer rl.mu.Unlock() if err := rl.currentFile.Close(); err != nil { return err } for i := rl.config.Backups - 1; i >= 0; i-- { src := rl.getBackupName(i) if _, err := os.Stat(src); err == nil { dst := rl.getBackupName(i + 1) if i+1 >= rl.config.Backups { os.Remove(dst) } else { if rl.config.CompressBackups && !strings.HasSuffix(src, ".gz") { if err := rl.compressFile(src); err != nil { return err } src += ".gz" dst += ".gz" } os.Rename(src, dst) } } } if err := os.Rename(rl.config.FilePath, rl.getBackupName(0)); err != nil { return err } return rl.openFile() } func (rl *RotatingLogger) compressFile(src string) error { in, err := os.Open(src) if err != nil { return err } defer in.Close() out, err := os.Create(src + ".gz") if err != nil { return err } defer out.Close() gz := gzip.NewWriter(out) defer gz.Close() if _, err = io.Copy(gz, in); err != nil { return err } return os.Remove(src) } func (rl *RotatingLogger) getBackupName(index int) string { if index == 0 { return rl.config.FilePath + ".1" } return fmt.Sprintf("%s.%d", rl.config.FilePath, index+1) } func (rl *RotatingLogger) needsRotation() (bool, error) { info, err := rl.currentFile.Stat() if err != nil { return false, err } return info.Size() >= int64(rl.config.MaxSizeMB*1024*1024), nil } func (rl *RotatingLogger) Write(p []byte) (n int, err error) { if rotate, err := rl.needsRotation(); rotate && err == nil { if err := rl.rotate(); err != nil { return 0, err } } rl.mu.Lock() defer rl.mu.Unlock() return rl.currentFile.Write(p) } func (rl *RotatingLogger) Close() error { rl.mu.Lock() defer rl.mu.Unlock() return rl.currentFile.Close() }