ccbe539e |
// Package jsonfilelog provides the default Logger implementation for
// Docker logging. This logger logs to files on the host server in the
// JSON format. |
47a6afb9 |
package jsonfilelog
import (
"bytes" |
c0391bf5 |
"encoding/json" |
9b782d3a |
"fmt"
"strconv" |
bd8661e8 |
"sync" |
47a6afb9 |
"github.com/docker/docker/daemon/logger" |
035604cc |
"github.com/docker/docker/daemon/logger/jsonfilelog/jsonlog" |
086c0b4a |
"github.com/docker/docker/daemon/logger/loggerutils" |
a06ad279 |
units "github.com/docker/go-units" |
e2209185 |
"github.com/pkg/errors" |
1009e6a4 |
"github.com/sirupsen/logrus" |
47a6afb9 |
)
|
c136a33c |
// Name is the name of the file that the jsonlogger logs to.
const Name = "json-file" |
3a8728b4 |
|
ccbe539e |
// JSONFileLogger is Logger implementation for default Docker logging. |
47a6afb9 |
type JSONFileLogger struct { |
16f7cd67 |
mu sync.Mutex |
e2209185 |
closed bool |
16f7cd67 |
writer *loggerutils.LogFile |
91fdfdd5 |
readers map[*logger.LogWatcher]struct{} // stores the active log followers |
5f50f4f5 |
tag string // tag values requested by the user to log |
3a8728b4 |
}
func init() {
if err := logger.RegisterLogDriver(Name, New); err != nil {
logrus.Fatal(err)
} |
9b782d3a |
if err := logger.RegisterLogOptValidator(Name, ValidateLogOpt); err != nil {
logrus.Fatal(err)
} |
47a6afb9 |
}
|
ccbe539e |
// New creates new JSONFileLogger which writes to filename passed in
// on given context. |
17ec911d |
func New(info logger.Info) (logger.Logger, error) { |
9b782d3a |
var capval int64 = -1 |
17ec911d |
if capacity, ok := info.Config["max-size"]; ok { |
9b782d3a |
var err error
capval, err = units.FromHumanSize(capacity)
if err != nil {
return nil, err
}
} |
ccbe539e |
var maxFiles = 1 |
17ec911d |
if maxFileString, ok := info.Config["max-file"]; ok { |
086c0b4a |
var err error |
9b782d3a |
maxFiles, err = strconv.Atoi(maxFileString)
if err != nil {
return nil, err
}
if maxFiles < 1 { |
cde60710 |
return nil, fmt.Errorf("max-file cannot be less than 1") |
9b782d3a |
}
} |
0083f6e9 |
|
e77267c5 |
attrs, err := info.ExtraAttributes(nil) |
5f50f4f5 |
if err != nil {
return nil, err
}
|
e77267c5 |
// no default template. only use a tag if the user asked for it
tag, err := loggerutils.ParseLogTag(info, "") |
9758a2a7 |
if err != nil {
return nil, err
} |
e77267c5 |
if tag != "" {
attrs["tag"] = tag
}
var extra []byte |
9758a2a7 |
if len(attrs) > 0 { |
0083f6e9 |
var err error
extra, err = json.Marshal(attrs)
if err != nil {
return nil, err
}
}
|
52d82b4f |
buf := bytes.NewBuffer(nil)
marshalFunc := func(msg *logger.Message) ([]byte, error) { |
e77267c5 |
if err := marshalMessage(msg, extra, buf); err != nil { |
52d82b4f |
return nil, err
}
b := buf.Bytes()
buf.Reset()
return b, nil
} |
16f7cd67 |
writer, err := loggerutils.NewLogFile(info.LogPath, capval, maxFiles, marshalFunc, decodeFunc) |
52d82b4f |
if err != nil {
return nil, err
}
|
47a6afb9 |
return &JSONFileLogger{ |
91fdfdd5 |
writer: writer,
readers: make(map[*logger.LogWatcher]struct{}), |
5f50f4f5 |
tag: tag, |
47a6afb9 |
}, nil
}
|
ccbe539e |
// Log converts logger.Message to jsonlog.JSONLog and serializes it to file. |
47a6afb9 |
func (l *JSONFileLogger) Log(msg *logger.Message) error { |
e2209185 |
l.mu.Lock() |
52d82b4f |
err := l.writer.WriteLogEntry(msg) |
e2209185 |
l.mu.Unlock()
return err
}
|
e77267c5 |
func marshalMessage(msg *logger.Message, extra json.RawMessage, buf *bytes.Buffer) error { |
e2209185 |
logLine := msg.Line |
513ec738 |
if !msg.Partial { |
e2209185 |
logLine = append(msg.Line, '\n') |
513ec738 |
} |
7de92de6 |
err := (&jsonlog.JSONLogs{ |
e2209185 |
Log: logLine, |
0083f6e9 |
Stream: msg.Source, |
7de92de6 |
Created: msg.Timestamp, |
e2209185 |
RawAttrs: extra,
}).MarshalJSONBuf(buf) |
47a6afb9 |
if err != nil { |
e2209185 |
return errors.Wrap(err, "error writing log message to buffer") |
47a6afb9 |
} |
e2209185 |
err = buf.WriteByte('\n')
return errors.Wrap(err, "error finalizing log buffer") |
9b782d3a |
}
|
ccbe539e |
// ValidateLogOpt looks for json specific log options max-file & max-size. |
9b782d3a |
func ValidateLogOpt(cfg map[string]string) error {
for key := range cfg {
switch key {
case "max-file":
case "max-size": |
0083f6e9 |
case "labels":
case "env": |
9758a2a7 |
case "env-regex": |
5f50f4f5 |
case "tag": |
9b782d3a |
default:
return fmt.Errorf("unknown log opt '%s' for json-file log driver", key)
}
}
return nil
}
|
ccbe539e |
// LogPath returns the location the given json logger logs to. |
3a8728b4 |
func (l *JSONFileLogger) LogPath() string { |
086c0b4a |
return l.writer.LogPath() |
3a8728b4 |
}
|
ccbe539e |
// Close closes underlying file and signals all readers to stop. |
47a6afb9 |
func (l *JSONFileLogger) Close() error { |
c0391bf5 |
l.mu.Lock() |
4fdb17c7 |
l.closed = true |
086c0b4a |
err := l.writer.Close() |
c0391bf5 |
for r := range l.readers {
r.Close()
delete(l.readers, r)
}
l.mu.Unlock()
return err |
47a6afb9 |
}
|
ccbe539e |
// Name returns name of this logger. |
47a6afb9 |
func (l *JSONFileLogger) Name() string { |
3a8728b4 |
return Name |
47a6afb9 |
} |