mirror of
https://github.com/moby/moby.git
synced 2022-11-09 12:21:53 -05:00
07b51ed300
When closing the log-file, and the file is already closed, there's no need to log an error. This patch adds a `closed` boolean to check if the file was closed, and if so, skip closing the file. This prevents errors like this being logged: level=error msg="Error closing logger: invalid argument" Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
141 lines
3.1 KiB
Go
141 lines
3.1 KiB
Go
package loggerutils
|
|
|
|
import (
|
|
"errors"
|
|
"os"
|
|
"strconv"
|
|
"sync"
|
|
|
|
"github.com/docker/docker/pkg/pubsub"
|
|
)
|
|
|
|
// RotateFileWriter is Logger implementation for default Docker logging.
|
|
type RotateFileWriter struct {
|
|
f *os.File // store for closing
|
|
closed bool
|
|
mu sync.Mutex
|
|
capacity int64 //maximum size of each file
|
|
currentSize int64 // current size of the latest file
|
|
maxFiles int //maximum number of files
|
|
notifyRotate *pubsub.Publisher
|
|
}
|
|
|
|
//NewRotateFileWriter creates new RotateFileWriter
|
|
func NewRotateFileWriter(logPath string, capacity int64, maxFiles int) (*RotateFileWriter, error) {
|
|
log, err := os.OpenFile(logPath, os.O_WRONLY|os.O_APPEND|os.O_CREATE, 0640)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
size, err := log.Seek(0, os.SEEK_END)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return &RotateFileWriter{
|
|
f: log,
|
|
capacity: capacity,
|
|
currentSize: size,
|
|
maxFiles: maxFiles,
|
|
notifyRotate: pubsub.NewPublisher(0, 1),
|
|
}, nil
|
|
}
|
|
|
|
//WriteLog write log message to File
|
|
func (w *RotateFileWriter) Write(message []byte) (int, error) {
|
|
w.mu.Lock()
|
|
if w.closed {
|
|
w.mu.Unlock()
|
|
return -1, errors.New("cannot write because the output file was closed")
|
|
}
|
|
if err := w.checkCapacityAndRotate(); err != nil {
|
|
w.mu.Unlock()
|
|
return -1, err
|
|
}
|
|
|
|
n, err := w.f.Write(message)
|
|
if err == nil {
|
|
w.currentSize += int64(n)
|
|
}
|
|
w.mu.Unlock()
|
|
return n, err
|
|
}
|
|
|
|
func (w *RotateFileWriter) checkCapacityAndRotate() error {
|
|
if w.capacity == -1 {
|
|
return nil
|
|
}
|
|
|
|
if w.currentSize >= w.capacity {
|
|
name := w.f.Name()
|
|
if err := w.f.Close(); err != nil {
|
|
return err
|
|
}
|
|
if err := rotate(name, w.maxFiles); err != nil {
|
|
return err
|
|
}
|
|
file, err := os.OpenFile(name, os.O_WRONLY|os.O_TRUNC|os.O_CREATE, 06400)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
w.f = file
|
|
w.currentSize = 0
|
|
w.notifyRotate.Publish(struct{}{})
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func rotate(name string, maxFiles int) error {
|
|
if maxFiles < 2 {
|
|
return nil
|
|
}
|
|
for i := maxFiles - 1; i > 1; i-- {
|
|
toPath := name + "." + strconv.Itoa(i)
|
|
fromPath := name + "." + strconv.Itoa(i-1)
|
|
if err := os.Rename(fromPath, toPath); err != nil && !os.IsNotExist(err) {
|
|
return err
|
|
}
|
|
}
|
|
|
|
if err := os.Rename(name, name+".1"); err != nil && !os.IsNotExist(err) {
|
|
return err
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// LogPath returns the location the given writer logs to.
|
|
func (w *RotateFileWriter) LogPath() string {
|
|
w.mu.Lock()
|
|
defer w.mu.Unlock()
|
|
return w.f.Name()
|
|
}
|
|
|
|
// MaxFiles return maximum number of files
|
|
func (w *RotateFileWriter) MaxFiles() int {
|
|
return w.maxFiles
|
|
}
|
|
|
|
//NotifyRotate returns the new subscriber
|
|
func (w *RotateFileWriter) NotifyRotate() chan interface{} {
|
|
return w.notifyRotate.Subscribe()
|
|
}
|
|
|
|
//NotifyRotateEvict removes the specified subscriber from receiving any more messages.
|
|
func (w *RotateFileWriter) NotifyRotateEvict(sub chan interface{}) {
|
|
w.notifyRotate.Evict(sub)
|
|
}
|
|
|
|
// Close closes underlying file and signals all readers to stop.
|
|
func (w *RotateFileWriter) Close() error {
|
|
w.mu.Lock()
|
|
defer w.mu.Unlock()
|
|
if w.closed {
|
|
return nil
|
|
}
|
|
if err := w.f.Close(); err != nil {
|
|
return err
|
|
}
|
|
w.closed = true
|
|
return nil
|
|
}
|