// Copyright (c) 2024 Proton AG // // This file is part of Proton Mail Bridge. // // Proton Mail Bridge is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // // Proton Mail Bridge is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with Proton Mail Bridge. If not, see . package logging import ( "fmt" "io" "os" "path/filepath" "github.com/ProtonMail/proton-bridge/v3/internal/constants" ) type Rotator struct { getFile FileProvider prune Pruner wc io.WriteCloser size int64 maxFileSize int64 nextIndex int } type FileProvider func(index int) (io.WriteCloser, error) func defaultFileProvider(logsPath string, sessionID SessionID, appName AppName) FileProvider { return func(index int) (io.WriteCloser, error) { return os.Create(filepath.Join(logsPath, //nolint:gosec // G304 fmt.Sprintf("%v_%v_%03d_v%v_%v.log", sessionID, appName, index, constants.Version, constants.Tag), )) } } func NewRotator(maxFileSize int64, getFile FileProvider, prune Pruner) (*Rotator, error) { r := &Rotator{ getFile: getFile, prune: prune, maxFileSize: maxFileSize, } if err := r.rotate(); err != nil { return nil, err } return r, nil } func NewDefaultRotator(logsPath string, sessionID SessionID, appName AppName, maxLogFileSize, pruningSize int64) (*Rotator, error) { var pruner Pruner if pruningSize < 0 { pruner = nullPruner } else { pruner = defaultPruner(logsPath, sessionID, pruningSize) } return NewRotator(maxLogFileSize, defaultFileProvider(logsPath, sessionID, appName), pruner) } func (r *Rotator) Write(p []byte) (int, error) { if r.size+int64(len(p)) > r.maxFileSize { if err := r.rotate(); err != nil { return 0, err } } n, err := r.wc.Write(p) if err != nil { return n, err } r.size += int64(n) return n, nil } func (r *Rotator) Close() error { if r.wc != nil { return r.wc.Close() } return nil } func (r *Rotator) rotate() error { if r.wc != nil { _ = r.wc.Close() } if _, err := r.prune(); err != nil { return err } wc, err := r.getFile(r.nextIndex) if err != nil { return err } r.nextIndex++ r.wc = wc r.size = 0 return nil }