2019-09-20 16:01:31 +02:00
|
|
|
package build
|
|
|
|
|
|
|
|
import (
|
2024-08-27 00:59:52 +02:00
|
|
|
"compress/gzip"
|
2019-09-20 16:01:31 +02:00
|
|
|
"fmt"
|
|
|
|
"io"
|
|
|
|
"os"
|
|
|
|
"path/filepath"
|
|
|
|
|
|
|
|
"github.com/jrick/logrotate/rotator"
|
2024-08-27 00:59:52 +02:00
|
|
|
"github.com/klauspost/compress/zstd"
|
2019-09-20 16:01:31 +02:00
|
|
|
)
|
|
|
|
|
|
|
|
// RotatingLogWriter is a wrapper around the LogWriter that supports log file
|
|
|
|
// rotation.
|
|
|
|
type RotatingLogWriter struct {
|
2024-10-15 14:34:24 +02:00
|
|
|
// pipe is the write-end pipe for writing to the log rotator.
|
|
|
|
pipe *io.PipeWriter
|
2019-09-20 16:01:31 +02:00
|
|
|
|
2024-09-10 18:21:35 +02:00
|
|
|
rotator *rotator.Rotator
|
2019-09-20 16:01:31 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
// NewRotatingLogWriter creates a new file rotating log writer.
|
|
|
|
//
|
|
|
|
// NOTE: `InitLogRotator` must be called to set up log rotation after creating
|
|
|
|
// the writer.
|
2024-10-15 14:34:24 +02:00
|
|
|
func NewRotatingLogWriter() *RotatingLogWriter {
|
|
|
|
return &RotatingLogWriter{}
|
2019-09-20 16:01:31 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
// InitLogRotator initializes the log file rotator to write logs to logFile and
|
|
|
|
// create roll files in the same directory. It should be called as early on
|
|
|
|
// startup and possible and must be closed on shutdown by calling `Close`.
|
2024-08-27 00:59:52 +02:00
|
|
|
func (r *RotatingLogWriter) InitLogRotator(logFile, logCompressor string,
|
|
|
|
maxLogFileSize int, maxLogFiles int) error {
|
2019-09-20 16:01:31 +02:00
|
|
|
|
|
|
|
logDir, _ := filepath.Split(logFile)
|
|
|
|
err := os.MkdirAll(logDir, 0700)
|
|
|
|
if err != nil {
|
2024-02-26 12:19:38 +01:00
|
|
|
return fmt.Errorf("failed to create log directory: %w", err)
|
2019-09-20 16:01:31 +02:00
|
|
|
}
|
2024-09-10 18:21:35 +02:00
|
|
|
|
|
|
|
r.rotator, err = rotator.New(
|
2019-09-20 16:01:31 +02:00
|
|
|
logFile, int64(maxLogFileSize*1024), false, maxLogFiles,
|
|
|
|
)
|
|
|
|
if err != nil {
|
2024-02-26 12:19:38 +01:00
|
|
|
return fmt.Errorf("failed to create file rotator: %w", err)
|
2019-09-20 16:01:31 +02:00
|
|
|
}
|
|
|
|
|
2024-08-27 00:59:52 +02:00
|
|
|
// Reject unknown compressors.
|
|
|
|
if !SuportedLogCompressor(logCompressor) {
|
|
|
|
return fmt.Errorf("unknown log compressor: %v", logCompressor)
|
|
|
|
}
|
|
|
|
|
|
|
|
var c rotator.Compressor
|
|
|
|
switch logCompressor {
|
|
|
|
case Gzip:
|
|
|
|
c = gzip.NewWriter(nil)
|
|
|
|
|
|
|
|
case Zstd:
|
|
|
|
c, err = zstd.NewWriter(nil)
|
|
|
|
if err != nil {
|
|
|
|
return fmt.Errorf("failed to create zstd compressor: "+
|
|
|
|
"%w", err)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Apply the compressor and its file suffix to the log rotator.
|
2024-09-10 18:21:35 +02:00
|
|
|
r.rotator.SetCompressor(c, logCompressors[logCompressor])
|
2024-08-27 00:59:52 +02:00
|
|
|
|
2019-09-20 16:01:31 +02:00
|
|
|
// Run rotator as a goroutine now but make sure we catch any errors
|
|
|
|
// that happen in case something with the rotation goes wrong during
|
|
|
|
// runtime (like running out of disk space or not being allowed to
|
|
|
|
// create a new logfile for whatever reason).
|
|
|
|
pr, pw := io.Pipe()
|
|
|
|
go func() {
|
2024-09-10 18:21:35 +02:00
|
|
|
err := r.rotator.Run(pr)
|
2019-09-20 16:01:31 +02:00
|
|
|
if err != nil {
|
|
|
|
_, _ = fmt.Fprintf(os.Stderr,
|
|
|
|
"failed to run file rotator: %v\n", err)
|
|
|
|
}
|
|
|
|
}()
|
|
|
|
|
2024-10-15 14:34:24 +02:00
|
|
|
r.pipe = pw
|
2024-09-10 18:21:35 +02:00
|
|
|
|
2019-09-20 16:01:31 +02:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2024-10-15 14:34:24 +02:00
|
|
|
// Write writes the byte slice to the log rotator, if present.
|
|
|
|
func (r *RotatingLogWriter) Write(b []byte) (int, error) {
|
|
|
|
if r.rotator != nil {
|
|
|
|
return r.rotator.Write(b)
|
|
|
|
}
|
|
|
|
|
|
|
|
return len(b), nil
|
|
|
|
}
|
|
|
|
|
2019-09-20 16:01:31 +02:00
|
|
|
// Close closes the underlying log rotator if it has already been created.
|
|
|
|
func (r *RotatingLogWriter) Close() error {
|
2024-09-10 18:21:35 +02:00
|
|
|
if r.rotator != nil {
|
|
|
|
return r.rotator.Close()
|
2019-09-20 16:01:31 +02:00
|
|
|
}
|
|
|
|
|
2024-09-10 18:21:35 +02:00
|
|
|
return nil
|
2019-09-20 16:01:31 +02:00
|
|
|
}
|