forked from ebhomengo/niki
1
0
Fork 0
niki/vendor/gopkg.in/natefinch/lumberjack.v2/lumberjack.go

925 lines
16 KiB
Go
Raw Normal View History

2024-02-18 10:42:21 +00:00
// Package lumberjack provides a rolling logger.
2024-02-18 10:42:21 +00:00
//
2024-02-18 10:42:21 +00:00
// Note that this is v2.0 of lumberjack, and should be imported using gopkg.in
2024-02-18 10:42:21 +00:00
// thusly:
2024-02-18 10:42:21 +00:00
//
2024-06-14 08:41:36 +00:00
// import "gopkg.in/natefinch/lumberjack.v2"
2024-02-18 10:42:21 +00:00
//
2024-02-18 10:42:21 +00:00
// The package name remains simply lumberjack, and the code resides at
2024-02-18 10:42:21 +00:00
// https://github.com/natefinch/lumberjack under the v2.0 branch.
2024-02-18 10:42:21 +00:00
//
2024-02-18 10:42:21 +00:00
// Lumberjack is intended to be one part of a logging infrastructure.
2024-02-18 10:42:21 +00:00
// It is not an all-in-one solution, but instead is a pluggable
2024-02-18 10:42:21 +00:00
// component at the bottom of the logging stack that simply controls the files
2024-02-18 10:42:21 +00:00
// to which logs are written.
2024-02-18 10:42:21 +00:00
//
2024-02-18 10:42:21 +00:00
// Lumberjack plays well with any logging package that can write to an
2024-02-18 10:42:21 +00:00
// io.Writer, including the standard library's log package.
2024-02-18 10:42:21 +00:00
//
2024-02-18 10:42:21 +00:00
// Lumberjack assumes that only one process is writing to the output files.
2024-02-18 10:42:21 +00:00
// Using the same lumberjack configuration from multiple processes on the same
2024-02-18 10:42:21 +00:00
// machine will result in improper behavior.
2024-02-18 10:42:21 +00:00
package lumberjack
import (
"compress/gzip"
"errors"
"fmt"
"io"
"io/ioutil"
"os"
"path/filepath"
"sort"
"strings"
"sync"
"time"
)
const (
backupTimeFormat = "2006-01-02T15-04-05.000"
compressSuffix = ".gz"
defaultMaxSize = 100
2024-02-18 10:42:21 +00:00
)
// ensure we always implement io.WriteCloser
2024-02-18 10:42:21 +00:00
var _ io.WriteCloser = (*Logger)(nil)
// Logger is an io.WriteCloser that writes to the specified filename.
2024-02-18 10:42:21 +00:00
//
2024-02-18 10:42:21 +00:00
// Logger opens or creates the logfile on first Write. If the file exists and
2024-02-18 10:42:21 +00:00
// is less than MaxSize megabytes, lumberjack will open and append to that file.
2024-02-18 10:42:21 +00:00
// If the file exists and its size is >= MaxSize megabytes, the file is renamed
2024-02-18 10:42:21 +00:00
// by putting the current time in a timestamp in the name immediately before the
2024-02-18 10:42:21 +00:00
// file's extension (or the end of the filename if there's no extension). A new
2024-02-18 10:42:21 +00:00
// log file is then created using original filename.
2024-02-18 10:42:21 +00:00
//
2024-02-18 10:42:21 +00:00
// Whenever a write would cause the current log file exceed MaxSize megabytes,
2024-02-18 10:42:21 +00:00
// the current file is closed, renamed, and a new log file created with the
2024-02-18 10:42:21 +00:00
// original name. Thus, the filename you give Logger is always the "current" log
2024-02-18 10:42:21 +00:00
// file.
2024-02-18 10:42:21 +00:00
//
2024-02-18 10:42:21 +00:00
// Backups use the log file name given to Logger, in the form
2024-02-18 10:42:21 +00:00
// `name-timestamp.ext` where name is the filename without the extension,
2024-02-18 10:42:21 +00:00
// timestamp is the time at which the log was rotated formatted with the
2024-02-18 10:42:21 +00:00
// time.Time format of `2006-01-02T15-04-05.000` and the extension is the
2024-02-18 10:42:21 +00:00
// original extension. For example, if your Logger.Filename is
2024-02-18 10:42:21 +00:00
// `/var/log/foo/server.log`, a backup created at 6:30pm on Nov 11 2016 would
2024-02-18 10:42:21 +00:00
// use the filename `/var/log/foo/server-2016-11-04T18-30-00.000.log`
2024-02-18 10:42:21 +00:00
//
2024-06-14 08:41:36 +00:00
// # Cleaning Up Old Log Files
2024-02-18 10:42:21 +00:00
//
2024-02-18 10:42:21 +00:00
// Whenever a new logfile gets created, old log files may be deleted. The most
2024-02-18 10:42:21 +00:00
// recent files according to the encoded timestamp will be retained, up to a
2024-02-18 10:42:21 +00:00
// number equal to MaxBackups (or all of them if MaxBackups is 0). Any files
2024-02-18 10:42:21 +00:00
// with an encoded timestamp older than MaxAge days are deleted, regardless of
2024-02-18 10:42:21 +00:00
// MaxBackups. Note that the time encoded in the timestamp is the rotation
2024-02-18 10:42:21 +00:00
// time, which may differ from the last time that file was written to.
2024-02-18 10:42:21 +00:00
//
2024-02-18 10:42:21 +00:00
// If MaxBackups and MaxAge are both 0, no old log files will be deleted.
2024-02-18 10:42:21 +00:00
type Logger struct {
2024-02-18 10:42:21 +00:00
// Filename is the file to write logs to. Backup log files will be retained
2024-02-18 10:42:21 +00:00
// in the same directory. It uses <processname>-lumberjack.log in
2024-02-18 10:42:21 +00:00
// os.TempDir() if empty.
2024-02-18 10:42:21 +00:00
Filename string `json:"filename" yaml:"filename"`
// MaxSize is the maximum size in megabytes of the log file before it gets
2024-02-18 10:42:21 +00:00
// rotated. It defaults to 100 megabytes.
2024-02-18 10:42:21 +00:00
MaxSize int `json:"maxsize" yaml:"maxsize"`
// MaxAge is the maximum number of days to retain old log files based on the
2024-02-18 10:42:21 +00:00
// timestamp encoded in their filename. Note that a day is defined as 24
2024-02-18 10:42:21 +00:00
// hours and may not exactly correspond to calendar days due to daylight
2024-02-18 10:42:21 +00:00
// savings, leap seconds, etc. The default is not to remove old log files
2024-02-18 10:42:21 +00:00
// based on age.
2024-02-18 10:42:21 +00:00
MaxAge int `json:"maxage" yaml:"maxage"`
// MaxBackups is the maximum number of old log files to retain. The default
2024-02-18 10:42:21 +00:00
// is to retain all old log files (though MaxAge may still cause them to get
2024-02-18 10:42:21 +00:00
// deleted.)
2024-02-18 10:42:21 +00:00
MaxBackups int `json:"maxbackups" yaml:"maxbackups"`
// LocalTime determines if the time used for formatting the timestamps in
2024-02-18 10:42:21 +00:00
// backup files is the computer's local time. The default is to use UTC
2024-02-18 10:42:21 +00:00
// time.
2024-02-18 10:42:21 +00:00
LocalTime bool `json:"localtime" yaml:"localtime"`
// Compress determines if the rotated log files should be compressed
2024-02-18 10:42:21 +00:00
// using gzip. The default is not to perform compression.
2024-02-18 10:42:21 +00:00
Compress bool `json:"compress" yaml:"compress"`
size int64
2024-02-18 10:42:21 +00:00
file *os.File
mu sync.Mutex
millCh chan bool
2024-02-18 10:42:21 +00:00
startMill sync.Once
}
var (
2024-02-18 10:42:21 +00:00
// currentTime exists so it can be mocked out by tests.
2024-02-18 10:42:21 +00:00
currentTime = time.Now
// os_Stat exists so it can be mocked out by tests.
2024-02-18 10:42:21 +00:00
osStat = os.Stat
// megabyte is the conversion factor between MaxSize and bytes. It is a
2024-02-18 10:42:21 +00:00
// variable so tests can mock it out and not need to write megabytes of data
2024-02-18 10:42:21 +00:00
// to disk.
2024-02-18 10:42:21 +00:00
megabyte = 1024 * 1024
)
// Write implements io.Writer. If a write would cause the log file to be larger
2024-02-18 10:42:21 +00:00
// than MaxSize, the file is closed, renamed to include a timestamp of the
2024-02-18 10:42:21 +00:00
// current time, and a new log file is created using the original log file name.
2024-02-18 10:42:21 +00:00
// If the length of the write is greater than MaxSize, an error is returned.
2024-02-18 10:42:21 +00:00
func (l *Logger) Write(p []byte) (n int, err error) {
2024-02-18 10:42:21 +00:00
l.mu.Lock()
2024-02-18 10:42:21 +00:00
defer l.mu.Unlock()
writeLen := int64(len(p))
2024-02-18 10:42:21 +00:00
if writeLen > l.max() {
2024-02-18 10:42:21 +00:00
return 0, fmt.Errorf(
2024-02-18 10:42:21 +00:00
"write length %d exceeds maximum file size %d", writeLen, l.max(),
)
2024-02-18 10:42:21 +00:00
}
if l.file == nil {
2024-02-18 10:42:21 +00:00
if err = l.openExistingOrNew(len(p)); err != nil {
2024-02-18 10:42:21 +00:00
return 0, err
2024-02-18 10:42:21 +00:00
}
2024-02-18 10:42:21 +00:00
}
if l.size+writeLen > l.max() {
2024-02-18 10:42:21 +00:00
if err := l.rotate(); err != nil {
2024-02-18 10:42:21 +00:00
return 0, err
2024-02-18 10:42:21 +00:00
}
2024-02-18 10:42:21 +00:00
}
n, err = l.file.Write(p)
2024-02-18 10:42:21 +00:00
l.size += int64(n)
return n, err
2024-02-18 10:42:21 +00:00
}
// Close implements io.Closer, and closes the current logfile.
2024-02-18 10:42:21 +00:00
func (l *Logger) Close() error {
2024-02-18 10:42:21 +00:00
l.mu.Lock()
2024-02-18 10:42:21 +00:00
defer l.mu.Unlock()
2024-02-18 10:42:21 +00:00
return l.close()
2024-02-18 10:42:21 +00:00
}
// close closes the file if it is open.
2024-02-18 10:42:21 +00:00
func (l *Logger) close() error {
2024-02-18 10:42:21 +00:00
if l.file == nil {
2024-02-18 10:42:21 +00:00
return nil
2024-02-18 10:42:21 +00:00
}
2024-02-18 10:42:21 +00:00
err := l.file.Close()
2024-02-18 10:42:21 +00:00
l.file = nil
2024-02-18 10:42:21 +00:00
return err
2024-02-18 10:42:21 +00:00
}
// Rotate causes Logger to close the existing log file and immediately create a
2024-02-18 10:42:21 +00:00
// new one. This is a helper function for applications that want to initiate
2024-02-18 10:42:21 +00:00
// rotations outside of the normal rotation rules, such as in response to
2024-02-18 10:42:21 +00:00
// SIGHUP. After rotating, this initiates compression and removal of old log
2024-02-18 10:42:21 +00:00
// files according to the configuration.
2024-02-18 10:42:21 +00:00
func (l *Logger) Rotate() error {
2024-02-18 10:42:21 +00:00
l.mu.Lock()
2024-02-18 10:42:21 +00:00
defer l.mu.Unlock()
2024-02-18 10:42:21 +00:00
return l.rotate()
2024-02-18 10:42:21 +00:00
}
// rotate closes the current file, moves it aside with a timestamp in the name,
2024-02-18 10:42:21 +00:00
// (if it exists), opens a new file with the original filename, and then runs
2024-02-18 10:42:21 +00:00
// post-rotation processing and removal.
2024-02-18 10:42:21 +00:00
func (l *Logger) rotate() error {
2024-02-18 10:42:21 +00:00
if err := l.close(); err != nil {
2024-02-18 10:42:21 +00:00
return err
2024-02-18 10:42:21 +00:00
}
2024-02-18 10:42:21 +00:00
if err := l.openNew(); err != nil {
2024-02-18 10:42:21 +00:00
return err
2024-02-18 10:42:21 +00:00
}
2024-02-18 10:42:21 +00:00
l.mill()
2024-02-18 10:42:21 +00:00
return nil
2024-02-18 10:42:21 +00:00
}
// openNew opens a new log file for writing, moving any old log file out of the
2024-02-18 10:42:21 +00:00
// way. This methods assumes the file has already been closed.
2024-02-18 10:42:21 +00:00
func (l *Logger) openNew() error {
2024-02-18 10:42:21 +00:00
err := os.MkdirAll(l.dir(), 0755)
2024-02-18 10:42:21 +00:00
if err != nil {
2024-02-18 10:42:21 +00:00
return fmt.Errorf("can't make directories for new logfile: %s", err)
2024-02-18 10:42:21 +00:00
}
name := l.filename()
2024-02-18 10:42:21 +00:00
mode := os.FileMode(0600)
2024-02-18 10:42:21 +00:00
info, err := osStat(name)
2024-02-18 10:42:21 +00:00
if err == nil {
2024-02-18 10:42:21 +00:00
// Copy the mode off the old logfile.
2024-02-18 10:42:21 +00:00
mode = info.Mode()
2024-02-18 10:42:21 +00:00
// move the existing file
2024-02-18 10:42:21 +00:00
newname := backupName(name, l.LocalTime)
2024-02-18 10:42:21 +00:00
if err := os.Rename(name, newname); err != nil {
2024-02-18 10:42:21 +00:00
return fmt.Errorf("can't rename log file: %s", err)
2024-02-18 10:42:21 +00:00
}
// this is a no-op anywhere but linux
2024-02-18 10:42:21 +00:00
if err := chown(name, info); err != nil {
2024-02-18 10:42:21 +00:00
return err
2024-02-18 10:42:21 +00:00
}
2024-02-18 10:42:21 +00:00
}
// we use truncate here because this should only get called when we've moved
2024-02-18 10:42:21 +00:00
// the file ourselves. if someone else creates the file in the meantime,
2024-02-18 10:42:21 +00:00
// just wipe out the contents.
2024-02-18 10:42:21 +00:00
f, err := os.OpenFile(name, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, mode)
2024-02-18 10:42:21 +00:00
if err != nil {
2024-02-18 10:42:21 +00:00
return fmt.Errorf("can't open new logfile: %s", err)
2024-02-18 10:42:21 +00:00
}
2024-02-18 10:42:21 +00:00
l.file = f
2024-02-18 10:42:21 +00:00
l.size = 0
2024-02-18 10:42:21 +00:00
return nil
2024-02-18 10:42:21 +00:00
}
// backupName creates a new filename from the given name, inserting a timestamp
2024-02-18 10:42:21 +00:00
// between the filename and the extension, using the local time if requested
2024-02-18 10:42:21 +00:00
// (otherwise UTC).
2024-02-18 10:42:21 +00:00
func backupName(name string, local bool) string {
2024-02-18 10:42:21 +00:00
dir := filepath.Dir(name)
2024-02-18 10:42:21 +00:00
filename := filepath.Base(name)
2024-02-18 10:42:21 +00:00
ext := filepath.Ext(filename)
2024-02-18 10:42:21 +00:00
prefix := filename[:len(filename)-len(ext)]
2024-02-18 10:42:21 +00:00
t := currentTime()
2024-02-18 10:42:21 +00:00
if !local {
2024-02-18 10:42:21 +00:00
t = t.UTC()
2024-02-18 10:42:21 +00:00
}
timestamp := t.Format(backupTimeFormat)
2024-02-18 10:42:21 +00:00
return filepath.Join(dir, fmt.Sprintf("%s-%s%s", prefix, timestamp, ext))
2024-02-18 10:42:21 +00:00
}
// openExistingOrNew opens the logfile if it exists and if the current write
2024-02-18 10:42:21 +00:00
// would not put it over MaxSize. If there is no such file or the write would
2024-02-18 10:42:21 +00:00
// put it over the MaxSize, a new file is created.
2024-02-18 10:42:21 +00:00
func (l *Logger) openExistingOrNew(writeLen int) error {
2024-02-18 10:42:21 +00:00
l.mill()
filename := l.filename()
2024-02-18 10:42:21 +00:00
info, err := osStat(filename)
2024-02-18 10:42:21 +00:00
if os.IsNotExist(err) {
2024-02-18 10:42:21 +00:00
return l.openNew()
2024-02-18 10:42:21 +00:00
}
2024-02-18 10:42:21 +00:00
if err != nil {
2024-02-18 10:42:21 +00:00
return fmt.Errorf("error getting log file info: %s", err)
2024-02-18 10:42:21 +00:00
}
if info.Size()+int64(writeLen) >= l.max() {
2024-02-18 10:42:21 +00:00
return l.rotate()
2024-02-18 10:42:21 +00:00
}
file, err := os.OpenFile(filename, os.O_APPEND|os.O_WRONLY, 0644)
2024-02-18 10:42:21 +00:00
if err != nil {
2024-02-18 10:42:21 +00:00
// if we fail to open the old log file for some reason, just ignore
2024-02-18 10:42:21 +00:00
// it and open a new log file.
2024-02-18 10:42:21 +00:00
return l.openNew()
2024-02-18 10:42:21 +00:00
}
2024-02-18 10:42:21 +00:00
l.file = file
2024-02-18 10:42:21 +00:00
l.size = info.Size()
2024-02-18 10:42:21 +00:00
return nil
2024-02-18 10:42:21 +00:00
}
// filename generates the name of the logfile from the current time.
2024-02-18 10:42:21 +00:00
func (l *Logger) filename() string {
2024-02-18 10:42:21 +00:00
if l.Filename != "" {
2024-02-18 10:42:21 +00:00
return l.Filename
2024-02-18 10:42:21 +00:00
}
2024-02-18 10:42:21 +00:00
name := filepath.Base(os.Args[0]) + "-lumberjack.log"
2024-02-18 10:42:21 +00:00
return filepath.Join(os.TempDir(), name)
2024-02-18 10:42:21 +00:00
}
// millRunOnce performs compression and removal of stale log files.
2024-02-18 10:42:21 +00:00
// Log files are compressed if enabled via configuration and old log
2024-02-18 10:42:21 +00:00
// files are removed, keeping at most l.MaxBackups files, as long as
2024-02-18 10:42:21 +00:00
// none of them are older than MaxAge.
2024-02-18 10:42:21 +00:00
func (l *Logger) millRunOnce() error {
2024-02-18 10:42:21 +00:00
if l.MaxBackups == 0 && l.MaxAge == 0 && !l.Compress {
2024-02-18 10:42:21 +00:00
return nil
2024-02-18 10:42:21 +00:00
}
files, err := l.oldLogFiles()
2024-02-18 10:42:21 +00:00
if err != nil {
2024-02-18 10:42:21 +00:00
return err
2024-02-18 10:42:21 +00:00
}
var compress, remove []logInfo
if l.MaxBackups > 0 && l.MaxBackups < len(files) {
2024-02-18 10:42:21 +00:00
preserved := make(map[string]bool)
2024-02-18 10:42:21 +00:00
var remaining []logInfo
2024-02-18 10:42:21 +00:00
for _, f := range files {
2024-02-18 10:42:21 +00:00
// Only count the uncompressed log file or the
2024-02-18 10:42:21 +00:00
// compressed log file, not both.
2024-02-18 10:42:21 +00:00
fn := f.Name()
2024-02-18 10:42:21 +00:00
if strings.HasSuffix(fn, compressSuffix) {
2024-02-18 10:42:21 +00:00
fn = fn[:len(fn)-len(compressSuffix)]
2024-02-18 10:42:21 +00:00
}
2024-02-18 10:42:21 +00:00
preserved[fn] = true
if len(preserved) > l.MaxBackups {
2024-02-18 10:42:21 +00:00
remove = append(remove, f)
2024-02-18 10:42:21 +00:00
} else {
2024-02-18 10:42:21 +00:00
remaining = append(remaining, f)
2024-02-18 10:42:21 +00:00
}
2024-02-18 10:42:21 +00:00
}
2024-02-18 10:42:21 +00:00
files = remaining
2024-02-18 10:42:21 +00:00
}
2024-02-18 10:42:21 +00:00
if l.MaxAge > 0 {
2024-02-18 10:42:21 +00:00
diff := time.Duration(int64(24*time.Hour) * int64(l.MaxAge))
2024-02-18 10:42:21 +00:00
cutoff := currentTime().Add(-1 * diff)
var remaining []logInfo
2024-02-18 10:42:21 +00:00
for _, f := range files {
2024-02-18 10:42:21 +00:00
if f.timestamp.Before(cutoff) {
2024-02-18 10:42:21 +00:00
remove = append(remove, f)
2024-02-18 10:42:21 +00:00
} else {
2024-02-18 10:42:21 +00:00
remaining = append(remaining, f)
2024-02-18 10:42:21 +00:00
}
2024-02-18 10:42:21 +00:00
}
2024-02-18 10:42:21 +00:00
files = remaining
2024-02-18 10:42:21 +00:00
}
if l.Compress {
2024-02-18 10:42:21 +00:00
for _, f := range files {
2024-02-18 10:42:21 +00:00
if !strings.HasSuffix(f.Name(), compressSuffix) {
2024-02-18 10:42:21 +00:00
compress = append(compress, f)
2024-02-18 10:42:21 +00:00
}
2024-02-18 10:42:21 +00:00
}
2024-02-18 10:42:21 +00:00
}
for _, f := range remove {
2024-02-18 10:42:21 +00:00
errRemove := os.Remove(filepath.Join(l.dir(), f.Name()))
2024-02-18 10:42:21 +00:00
if err == nil && errRemove != nil {
2024-02-18 10:42:21 +00:00
err = errRemove
2024-02-18 10:42:21 +00:00
}
2024-02-18 10:42:21 +00:00
}
2024-02-18 10:42:21 +00:00
for _, f := range compress {
2024-02-18 10:42:21 +00:00
fn := filepath.Join(l.dir(), f.Name())
2024-02-18 10:42:21 +00:00
errCompress := compressLogFile(fn, fn+compressSuffix)
2024-02-18 10:42:21 +00:00
if err == nil && errCompress != nil {
2024-02-18 10:42:21 +00:00
err = errCompress
2024-02-18 10:42:21 +00:00
}
2024-02-18 10:42:21 +00:00
}
return err
2024-02-18 10:42:21 +00:00
}
// millRun runs in a goroutine to manage post-rotation compression and removal
2024-02-18 10:42:21 +00:00
// of old log files.
2024-02-18 10:42:21 +00:00
func (l *Logger) millRun() {
2024-02-18 10:42:21 +00:00
for range l.millCh {
2024-02-18 10:42:21 +00:00
// what am I going to do, log this?
2024-02-18 10:42:21 +00:00
_ = l.millRunOnce()
2024-02-18 10:42:21 +00:00
}
2024-02-18 10:42:21 +00:00
}
// mill performs post-rotation compression and removal of stale log files,
2024-02-18 10:42:21 +00:00
// starting the mill goroutine if necessary.
2024-02-18 10:42:21 +00:00
func (l *Logger) mill() {
2024-02-18 10:42:21 +00:00
l.startMill.Do(func() {
2024-02-18 10:42:21 +00:00
l.millCh = make(chan bool, 1)
2024-02-18 10:42:21 +00:00
go l.millRun()
2024-02-18 10:42:21 +00:00
})
2024-02-18 10:42:21 +00:00
select {
2024-02-18 10:42:21 +00:00
case l.millCh <- true:
2024-02-18 10:42:21 +00:00
default:
2024-02-18 10:42:21 +00:00
}
2024-02-18 10:42:21 +00:00
}
// oldLogFiles returns the list of backup log files stored in the same
2024-02-18 10:42:21 +00:00
// directory as the current log file, sorted by ModTime
2024-02-18 10:42:21 +00:00
func (l *Logger) oldLogFiles() ([]logInfo, error) {
2024-02-18 10:42:21 +00:00
files, err := ioutil.ReadDir(l.dir())
2024-02-18 10:42:21 +00:00
if err != nil {
2024-02-18 10:42:21 +00:00
return nil, fmt.Errorf("can't read log file directory: %s", err)
2024-02-18 10:42:21 +00:00
}
2024-02-18 10:42:21 +00:00
logFiles := []logInfo{}
prefix, ext := l.prefixAndExt()
for _, f := range files {
2024-02-18 10:42:21 +00:00
if f.IsDir() {
2024-02-18 10:42:21 +00:00
continue
2024-02-18 10:42:21 +00:00
}
2024-02-18 10:42:21 +00:00
if t, err := l.timeFromName(f.Name(), prefix, ext); err == nil {
2024-02-18 10:42:21 +00:00
logFiles = append(logFiles, logInfo{t, f})
2024-02-18 10:42:21 +00:00
continue
2024-02-18 10:42:21 +00:00
}
2024-02-18 10:42:21 +00:00
if t, err := l.timeFromName(f.Name(), prefix, ext+compressSuffix); err == nil {
2024-02-18 10:42:21 +00:00
logFiles = append(logFiles, logInfo{t, f})
2024-02-18 10:42:21 +00:00
continue
2024-02-18 10:42:21 +00:00
}
2024-02-18 10:42:21 +00:00
// error parsing means that the suffix at the end was not generated
2024-02-18 10:42:21 +00:00
// by lumberjack, and therefore it's not a backup file.
2024-02-18 10:42:21 +00:00
}
sort.Sort(byFormatTime(logFiles))
return logFiles, nil
2024-02-18 10:42:21 +00:00
}
// timeFromName extracts the formatted time from the filename by stripping off
2024-02-18 10:42:21 +00:00
// the filename's prefix and extension. This prevents someone's filename from
2024-02-18 10:42:21 +00:00
// confusing time.parse.
2024-02-18 10:42:21 +00:00
func (l *Logger) timeFromName(filename, prefix, ext string) (time.Time, error) {
2024-02-18 10:42:21 +00:00
if !strings.HasPrefix(filename, prefix) {
2024-02-18 10:42:21 +00:00
return time.Time{}, errors.New("mismatched prefix")
2024-02-18 10:42:21 +00:00
}
2024-02-18 10:42:21 +00:00
if !strings.HasSuffix(filename, ext) {
2024-02-18 10:42:21 +00:00
return time.Time{}, errors.New("mismatched extension")
2024-02-18 10:42:21 +00:00
}
2024-02-18 10:42:21 +00:00
ts := filename[len(prefix) : len(filename)-len(ext)]
2024-02-18 10:42:21 +00:00
return time.Parse(backupTimeFormat, ts)
2024-02-18 10:42:21 +00:00
}
// max returns the maximum size in bytes of log files before rolling.
2024-02-18 10:42:21 +00:00
func (l *Logger) max() int64 {
2024-02-18 10:42:21 +00:00
if l.MaxSize == 0 {
2024-02-18 10:42:21 +00:00
return int64(defaultMaxSize * megabyte)
2024-02-18 10:42:21 +00:00
}
2024-02-18 10:42:21 +00:00
return int64(l.MaxSize) * int64(megabyte)
2024-02-18 10:42:21 +00:00
}
// dir returns the directory for the current filename.
2024-02-18 10:42:21 +00:00
func (l *Logger) dir() string {
2024-02-18 10:42:21 +00:00
return filepath.Dir(l.filename())
2024-02-18 10:42:21 +00:00
}
// prefixAndExt returns the filename part and extension part from the Logger's
2024-02-18 10:42:21 +00:00
// filename.
2024-02-18 10:42:21 +00:00
func (l *Logger) prefixAndExt() (prefix, ext string) {
2024-02-18 10:42:21 +00:00
filename := filepath.Base(l.filename())
2024-02-18 10:42:21 +00:00
ext = filepath.Ext(filename)
2024-02-18 10:42:21 +00:00
prefix = filename[:len(filename)-len(ext)] + "-"
2024-02-18 10:42:21 +00:00
return prefix, ext
2024-02-18 10:42:21 +00:00
}
// compressLogFile compresses the given log file, removing the
2024-02-18 10:42:21 +00:00
// uncompressed log file if successful.
2024-02-18 10:42:21 +00:00
func compressLogFile(src, dst string) (err error) {
2024-02-18 10:42:21 +00:00
f, err := os.Open(src)
2024-02-18 10:42:21 +00:00
if err != nil {
2024-02-18 10:42:21 +00:00
return fmt.Errorf("failed to open log file: %v", err)
2024-02-18 10:42:21 +00:00
}
2024-02-18 10:42:21 +00:00
defer f.Close()
fi, err := osStat(src)
2024-02-18 10:42:21 +00:00
if err != nil {
2024-02-18 10:42:21 +00:00
return fmt.Errorf("failed to stat log file: %v", err)
2024-02-18 10:42:21 +00:00
}
if err := chown(dst, fi); err != nil {
2024-02-18 10:42:21 +00:00
return fmt.Errorf("failed to chown compressed log file: %v", err)
2024-02-18 10:42:21 +00:00
}
// If this file already exists, we presume it was created by
2024-02-18 10:42:21 +00:00
// a previous attempt to compress the log file.
2024-02-18 10:42:21 +00:00
gzf, err := os.OpenFile(dst, os.O_CREATE|os.O_TRUNC|os.O_WRONLY, fi.Mode())
2024-02-18 10:42:21 +00:00
if err != nil {
2024-02-18 10:42:21 +00:00
return fmt.Errorf("failed to open compressed log file: %v", err)
2024-02-18 10:42:21 +00:00
}
2024-02-18 10:42:21 +00:00
defer gzf.Close()
gz := gzip.NewWriter(gzf)
defer func() {
2024-02-18 10:42:21 +00:00
if err != nil {
2024-02-18 10:42:21 +00:00
os.Remove(dst)
2024-02-18 10:42:21 +00:00
err = fmt.Errorf("failed to compress log file: %v", err)
2024-02-18 10:42:21 +00:00
}
2024-02-18 10:42:21 +00:00
}()
if _, err := io.Copy(gz, f); err != nil {
2024-02-18 10:42:21 +00:00
return err
2024-02-18 10:42:21 +00:00
}
2024-02-18 10:42:21 +00:00
if err := gz.Close(); err != nil {
2024-02-18 10:42:21 +00:00
return err
2024-02-18 10:42:21 +00:00
}
2024-02-18 10:42:21 +00:00
if err := gzf.Close(); err != nil {
2024-02-18 10:42:21 +00:00
return err
2024-02-18 10:42:21 +00:00
}
if err := f.Close(); err != nil {
2024-02-18 10:42:21 +00:00
return err
2024-02-18 10:42:21 +00:00
}
2024-02-18 10:42:21 +00:00
if err := os.Remove(src); err != nil {
2024-02-18 10:42:21 +00:00
return err
2024-02-18 10:42:21 +00:00
}
return nil
2024-02-18 10:42:21 +00:00
}
// logInfo is a convenience struct to return the filename and its embedded
2024-02-18 10:42:21 +00:00
// timestamp.
2024-02-18 10:42:21 +00:00
type logInfo struct {
timestamp time.Time
2024-02-18 10:42:21 +00:00
os.FileInfo
}
// byFormatTime sorts by newest time formatted in the name.
2024-02-18 10:42:21 +00:00
type byFormatTime []logInfo
func (b byFormatTime) Less(i, j int) bool {
2024-02-18 10:42:21 +00:00
return b[i].timestamp.After(b[j].timestamp)
2024-02-18 10:42:21 +00:00
}
func (b byFormatTime) Swap(i, j int) {
2024-02-18 10:42:21 +00:00
b[i], b[j] = b[j], b[i]
2024-02-18 10:42:21 +00:00
}
func (b byFormatTime) Len() int {
2024-02-18 10:42:21 +00:00
return len(b)
2024-02-18 10:42:21 +00:00
}