mirror of
https://github.com/moby/moby.git
synced 2022-11-09 12:21:53 -05:00
Refactor log file writer
Make the `*RotateFileWriter` specifically about writing `logger.Message`'s, which is what it's used for. This allows for future changes where the log writer can cache details about log entries such as (e.g.) the timestamps included in a particular log file, which can be used to optimize reads. Signed-off-by: Brian Goff <cpuguy83@gmail.com>
This commit is contained in:
parent
2c1043c913
commit
52d82b4fbc
3 changed files with 44 additions and 37 deletions
|
@ -7,7 +7,6 @@ import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
|
||||||
"strconv"
|
"strconv"
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
|
@ -24,10 +23,7 @@ const Name = "json-file"
|
||||||
|
|
||||||
// JSONFileLogger is Logger implementation for default Docker logging.
|
// JSONFileLogger is Logger implementation for default Docker logging.
|
||||||
type JSONFileLogger struct {
|
type JSONFileLogger struct {
|
||||||
extra []byte // json-encoded extra attributes
|
|
||||||
|
|
||||||
mu sync.RWMutex
|
mu sync.RWMutex
|
||||||
buf *bytes.Buffer // avoids allocating a new buffer on each call to `Log()`
|
|
||||||
closed bool
|
closed bool
|
||||||
writer *loggerutils.RotateFileWriter
|
writer *loggerutils.RotateFileWriter
|
||||||
readers map[*logger.LogWatcher]struct{} // stores the active log followers
|
readers map[*logger.LogWatcher]struct{} // stores the active log followers
|
||||||
|
@ -65,11 +61,6 @@ func New(info logger.Info) (logger.Logger, error) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
writer, err := loggerutils.NewRotateFileWriter(info.LogPath, capval, maxFiles)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
var extra []byte
|
var extra []byte
|
||||||
attrs, err := info.ExtraAttributes(nil)
|
attrs, err := info.ExtraAttributes(nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -83,33 +74,34 @@ func New(info logger.Info) (logger.Logger, error) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
buf := bytes.NewBuffer(nil)
|
||||||
|
marshalFunc := func(msg *logger.Message) ([]byte, error) {
|
||||||
|
if err := marshalMessage(msg, extra, buf); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
b := buf.Bytes()
|
||||||
|
buf.Reset()
|
||||||
|
return b, nil
|
||||||
|
}
|
||||||
|
writer, err := loggerutils.NewRotateFileWriter(info.LogPath, capval, maxFiles, marshalFunc)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
return &JSONFileLogger{
|
return &JSONFileLogger{
|
||||||
buf: bytes.NewBuffer(nil),
|
|
||||||
writer: writer,
|
writer: writer,
|
||||||
readers: make(map[*logger.LogWatcher]struct{}),
|
readers: make(map[*logger.LogWatcher]struct{}),
|
||||||
extra: extra,
|
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Log converts logger.Message to jsonlog.JSONLog and serializes it to file.
|
// Log converts logger.Message to jsonlog.JSONLog and serializes it to file.
|
||||||
func (l *JSONFileLogger) Log(msg *logger.Message) error {
|
func (l *JSONFileLogger) Log(msg *logger.Message) error {
|
||||||
l.mu.Lock()
|
l.mu.Lock()
|
||||||
err := writeMessageBuf(l.writer, msg, l.extra, l.buf)
|
err := l.writer.WriteLogEntry(msg)
|
||||||
l.buf.Reset()
|
|
||||||
l.mu.Unlock()
|
l.mu.Unlock()
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
func writeMessageBuf(w io.Writer, m *logger.Message, extra json.RawMessage, buf *bytes.Buffer) error {
|
|
||||||
if err := marshalMessage(m, extra, buf); err != nil {
|
|
||||||
logger.PutMessage(m)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
logger.PutMessage(m)
|
|
||||||
_, err := w.Write(buf.Bytes())
|
|
||||||
return errors.Wrap(err, "error writing log entry")
|
|
||||||
}
|
|
||||||
|
|
||||||
func marshalMessage(msg *logger.Message, extra json.RawMessage, buf *bytes.Buffer) error {
|
func marshalMessage(msg *logger.Message, extra json.RawMessage, buf *bytes.Buffer) error {
|
||||||
logLine := msg.Line
|
logLine := msg.Line
|
||||||
if !msg.Partial {
|
if !msg.Partial {
|
||||||
|
|
|
@ -140,3 +140,6 @@ type Capability struct {
|
||||||
// Determines if a log driver can read back logs
|
// Determines if a log driver can read back logs
|
||||||
ReadLogs bool
|
ReadLogs bool
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// MarshalFunc is a func that marshals a message into an arbitrary format
|
||||||
|
type MarshalFunc func(*Message) ([]byte, error)
|
||||||
|
|
|
@ -1,12 +1,13 @@
|
||||||
package loggerutils
|
package loggerutils
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
|
||||||
"os"
|
"os"
|
||||||
"strconv"
|
"strconv"
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
|
"github.com/docker/docker/daemon/logger"
|
||||||
"github.com/docker/docker/pkg/pubsub"
|
"github.com/docker/docker/pkg/pubsub"
|
||||||
|
"github.com/pkg/errors"
|
||||||
)
|
)
|
||||||
|
|
||||||
// RotateFileWriter is Logger implementation for default Docker logging.
|
// RotateFileWriter is Logger implementation for default Docker logging.
|
||||||
|
@ -18,10 +19,11 @@ type RotateFileWriter struct {
|
||||||
currentSize int64 // current size of the latest file
|
currentSize int64 // current size of the latest file
|
||||||
maxFiles int //maximum number of files
|
maxFiles int //maximum number of files
|
||||||
notifyRotate *pubsub.Publisher
|
notifyRotate *pubsub.Publisher
|
||||||
|
marshal logger.MarshalFunc
|
||||||
}
|
}
|
||||||
|
|
||||||
//NewRotateFileWriter creates new RotateFileWriter
|
//NewRotateFileWriter creates new RotateFileWriter
|
||||||
func NewRotateFileWriter(logPath string, capacity int64, maxFiles int) (*RotateFileWriter, error) {
|
func NewRotateFileWriter(logPath string, capacity int64, maxFiles int, marshaller logger.MarshalFunc) (*RotateFileWriter, error) {
|
||||||
log, err := os.OpenFile(logPath, os.O_WRONLY|os.O_APPEND|os.O_CREATE, 0640)
|
log, err := os.OpenFile(logPath, os.O_WRONLY|os.O_APPEND|os.O_CREATE, 0640)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
@ -38,27 +40,37 @@ func NewRotateFileWriter(logPath string, capacity int64, maxFiles int) (*RotateF
|
||||||
currentSize: size,
|
currentSize: size,
|
||||||
maxFiles: maxFiles,
|
maxFiles: maxFiles,
|
||||||
notifyRotate: pubsub.NewPublisher(0, 1),
|
notifyRotate: pubsub.NewPublisher(0, 1),
|
||||||
|
marshal: marshaller,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
//WriteLog write log message to File
|
// WriteLogEntry writes the provided log message to the current log file.
|
||||||
func (w *RotateFileWriter) Write(message []byte) (int, error) {
|
// This may trigger a rotation event if the max file/capacity limits are hit.
|
||||||
|
func (w *RotateFileWriter) WriteLogEntry(msg *logger.Message) error {
|
||||||
|
b, err := w.marshal(msg)
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrap(err, "error marshalling log message")
|
||||||
|
}
|
||||||
|
|
||||||
|
logger.PutMessage(msg)
|
||||||
|
|
||||||
w.mu.Lock()
|
w.mu.Lock()
|
||||||
if w.closed {
|
if w.closed {
|
||||||
w.mu.Unlock()
|
w.mu.Unlock()
|
||||||
return -1, errors.New("cannot write because the output file was closed")
|
return 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 := w.checkCapacityAndRotate(); err != nil {
|
||||||
|
w.mu.Unlock()
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
n, err := w.f.Write(b)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
w.currentSize += int64(n)
|
w.currentSize += int64(n)
|
||||||
}
|
}
|
||||||
w.mu.Unlock()
|
w.mu.Unlock()
|
||||||
return n, err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (w *RotateFileWriter) checkCapacityAndRotate() error {
|
func (w *RotateFileWriter) checkCapacityAndRotate() error {
|
||||||
|
@ -69,7 +81,7 @@ func (w *RotateFileWriter) checkCapacityAndRotate() error {
|
||||||
if w.currentSize >= w.capacity {
|
if w.currentSize >= w.capacity {
|
||||||
name := w.f.Name()
|
name := w.f.Name()
|
||||||
if err := w.f.Close(); err != nil {
|
if err := w.f.Close(); err != nil {
|
||||||
return err
|
return errors.Wrap(err, "error closing file")
|
||||||
}
|
}
|
||||||
if err := rotate(name, w.maxFiles); err != nil {
|
if err := rotate(name, w.maxFiles); err != nil {
|
||||||
return err
|
return err
|
||||||
|
@ -94,12 +106,12 @@ func rotate(name string, maxFiles int) error {
|
||||||
toPath := name + "." + strconv.Itoa(i)
|
toPath := name + "." + strconv.Itoa(i)
|
||||||
fromPath := name + "." + strconv.Itoa(i-1)
|
fromPath := name + "." + strconv.Itoa(i-1)
|
||||||
if err := os.Rename(fromPath, toPath); err != nil && !os.IsNotExist(err) {
|
if err := os.Rename(fromPath, toPath); err != nil && !os.IsNotExist(err) {
|
||||||
return err
|
return errors.Wrap(err, "error rotating old log entries")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := os.Rename(name, name+".1"); err != nil && !os.IsNotExist(err) {
|
if err := os.Rename(name, name+".1"); err != nil && !os.IsNotExist(err) {
|
||||||
return err
|
return errors.Wrap(err, "error rotating current log")
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue