mirror of
https://github.com/moby/moby.git
synced 2022-11-09 12:21:53 -05:00
b1594c59f5
inotify event is trigged immediately there's data written to disk. But at the time that the inotify event is received, the json line might not fully saved to disk. If the json decoder tries to decode in such case, an io.UnexpectedEOF will be trigged. We used to retry for several times to mitigate the io.UnexpectedEOF error. But there are still flaky tests caused by the partial log entries. The daemon knows exactly when there are new log entries emitted. We can use the pubsub package to notify all the log readers instead of inotify. Signed-off-by: Shijiang Wei <mountkin@gmail.com> try to fix broken test. will squash once tests pass Signed-off-by: Shijiang Wei <mountkin@gmail.com>
152 lines
3.6 KiB
Go
152 lines
3.6 KiB
Go
// Package jsonfilelog provides the default Logger implementation for
|
|
// Docker logging. This logger logs to files on the host server in the
|
|
// JSON format.
|
|
package jsonfilelog
|
|
|
|
import (
|
|
"bytes"
|
|
"encoding/json"
|
|
"fmt"
|
|
"strconv"
|
|
"sync"
|
|
|
|
"github.com/Sirupsen/logrus"
|
|
"github.com/docker/docker/daemon/logger"
|
|
"github.com/docker/docker/daemon/logger/loggerutils"
|
|
"github.com/docker/docker/pkg/jsonlog"
|
|
"github.com/docker/docker/pkg/pubsub"
|
|
"github.com/docker/go-units"
|
|
)
|
|
|
|
// Name is the name of the file that the jsonlogger logs to.
|
|
const Name = "json-file"
|
|
|
|
// JSONFileLogger is Logger implementation for default Docker logging.
|
|
type JSONFileLogger struct {
|
|
buf *bytes.Buffer
|
|
writer *loggerutils.RotateFileWriter
|
|
mu sync.Mutex
|
|
ctx logger.Context
|
|
readers map[*logger.LogWatcher]struct{} // stores the active log followers
|
|
extra []byte // json-encoded extra attributes
|
|
writeNotifier *pubsub.Publisher
|
|
}
|
|
|
|
func init() {
|
|
if err := logger.RegisterLogDriver(Name, New); err != nil {
|
|
logrus.Fatal(err)
|
|
}
|
|
if err := logger.RegisterLogOptValidator(Name, ValidateLogOpt); err != nil {
|
|
logrus.Fatal(err)
|
|
}
|
|
}
|
|
|
|
// New creates new JSONFileLogger which writes to filename passed in
|
|
// on given context.
|
|
func New(ctx logger.Context) (logger.Logger, error) {
|
|
var capval int64 = -1
|
|
if capacity, ok := ctx.Config["max-size"]; ok {
|
|
var err error
|
|
capval, err = units.FromHumanSize(capacity)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
}
|
|
var maxFiles = 1
|
|
if maxFileString, ok := ctx.Config["max-file"]; ok {
|
|
var err error
|
|
maxFiles, err = strconv.Atoi(maxFileString)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if maxFiles < 1 {
|
|
return nil, fmt.Errorf("max-file cannot be less than 1")
|
|
}
|
|
}
|
|
|
|
writer, err := loggerutils.NewRotateFileWriter(ctx.LogPath, capval, maxFiles)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
var extra []byte
|
|
if attrs := ctx.ExtraAttributes(nil); len(attrs) > 0 {
|
|
var err error
|
|
extra, err = json.Marshal(attrs)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
}
|
|
|
|
return &JSONFileLogger{
|
|
buf: bytes.NewBuffer(nil),
|
|
writer: writer,
|
|
readers: make(map[*logger.LogWatcher]struct{}),
|
|
extra: extra,
|
|
writeNotifier: pubsub.NewPublisher(0, 10),
|
|
}, nil
|
|
}
|
|
|
|
// Log converts logger.Message to jsonlog.JSONLog and serializes it to file.
|
|
func (l *JSONFileLogger) Log(msg *logger.Message) error {
|
|
timestamp, err := jsonlog.FastTimeMarshalJSON(msg.Timestamp)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
l.mu.Lock()
|
|
defer l.mu.Unlock()
|
|
err = (&jsonlog.JSONLogs{
|
|
Log: append(msg.Line, '\n'),
|
|
Stream: msg.Source,
|
|
Created: timestamp,
|
|
RawAttrs: l.extra,
|
|
}).MarshalJSONBuf(l.buf)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
l.buf.WriteByte('\n')
|
|
_, err = l.writer.Write(l.buf.Bytes())
|
|
l.writeNotifier.Publish(struct{}{})
|
|
l.buf.Reset()
|
|
|
|
return err
|
|
}
|
|
|
|
// ValidateLogOpt looks for json specific log options max-file & max-size.
|
|
func ValidateLogOpt(cfg map[string]string) error {
|
|
for key := range cfg {
|
|
switch key {
|
|
case "max-file":
|
|
case "max-size":
|
|
case "labels":
|
|
case "env":
|
|
default:
|
|
return fmt.Errorf("unknown log opt '%s' for json-file log driver", key)
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// LogPath returns the location the given json logger logs to.
|
|
func (l *JSONFileLogger) LogPath() string {
|
|
return l.writer.LogPath()
|
|
}
|
|
|
|
// Close closes underlying file and signals all readers to stop.
|
|
func (l *JSONFileLogger) Close() error {
|
|
l.mu.Lock()
|
|
err := l.writer.Close()
|
|
for r := range l.readers {
|
|
r.Close()
|
|
delete(l.readers, r)
|
|
}
|
|
l.writeNotifier.Close()
|
|
l.mu.Unlock()
|
|
return err
|
|
}
|
|
|
|
// Name returns name of this logger.
|
|
func (l *JSONFileLogger) Name() string {
|
|
return Name
|
|
}
|