github.com/docker/docker@v299999999.0.0-20200612211812-aaf470eca7b5+incompatible/daemon/logger/jsonfilelog/jsonfilelog.go (about)

     1  // Package jsonfilelog provides the default Logger implementation for
     2  // Docker logging. This logger logs to files on the host server in the
     3  // JSON format.
     4  package jsonfilelog // import "github.com/docker/docker/daemon/logger/jsonfilelog"
     5  
     6  import (
     7  	"bytes"
     8  	"encoding/json"
     9  	"fmt"
    10  	"strconv"
    11  	"sync"
    12  
    13  	"github.com/docker/docker/daemon/logger"
    14  	"github.com/docker/docker/daemon/logger/jsonfilelog/jsonlog"
    15  	"github.com/docker/docker/daemon/logger/loggerutils"
    16  	units "github.com/docker/go-units"
    17  	"github.com/pkg/errors"
    18  	"github.com/sirupsen/logrus"
    19  )
    20  
    21  // Name is the name of the file that the jsonlogger logs to.
    22  const Name = "json-file"
    23  
    24  // JSONFileLogger is Logger implementation for default Docker logging.
    25  type JSONFileLogger struct {
    26  	mu      sync.Mutex
    27  	closed  bool
    28  	writer  *loggerutils.LogFile
    29  	readers map[*logger.LogWatcher]struct{} // stores the active log followers
    30  	tag     string                          // tag values requested by the user to log
    31  }
    32  
    33  func init() {
    34  	if err := logger.RegisterLogDriver(Name, New); err != nil {
    35  		logrus.Fatal(err)
    36  	}
    37  	if err := logger.RegisterLogOptValidator(Name, ValidateLogOpt); err != nil {
    38  		logrus.Fatal(err)
    39  	}
    40  }
    41  
    42  // New creates new JSONFileLogger which writes to filename passed in
    43  // on given context.
    44  func New(info logger.Info) (logger.Logger, error) {
    45  	var capval int64 = -1
    46  	if capacity, ok := info.Config["max-size"]; ok {
    47  		var err error
    48  		capval, err = units.FromHumanSize(capacity)
    49  		if err != nil {
    50  			return nil, err
    51  		}
    52  		if capval <= 0 {
    53  			return nil, fmt.Errorf("max-size must be a positive number")
    54  		}
    55  	}
    56  	var maxFiles = 1
    57  	if maxFileString, ok := info.Config["max-file"]; ok {
    58  		var err error
    59  		maxFiles, err = strconv.Atoi(maxFileString)
    60  		if err != nil {
    61  			return nil, err
    62  		}
    63  		if maxFiles < 1 {
    64  			return nil, fmt.Errorf("max-file cannot be less than 1")
    65  		}
    66  	}
    67  
    68  	var compress bool
    69  	if compressString, ok := info.Config["compress"]; ok {
    70  		var err error
    71  		compress, err = strconv.ParseBool(compressString)
    72  		if err != nil {
    73  			return nil, err
    74  		}
    75  		if compress && (maxFiles == 1 || capval == -1) {
    76  			return nil, fmt.Errorf("compress cannot be true when max-file is less than 2 or max-size is not set")
    77  		}
    78  	}
    79  
    80  	attrs, err := info.ExtraAttributes(nil)
    81  	if err != nil {
    82  		return nil, err
    83  	}
    84  
    85  	// no default template. only use a tag if the user asked for it
    86  	tag, err := loggerutils.ParseLogTag(info, "")
    87  	if err != nil {
    88  		return nil, err
    89  	}
    90  	if tag != "" {
    91  		attrs["tag"] = tag
    92  	}
    93  
    94  	var extra []byte
    95  	if len(attrs) > 0 {
    96  		var err error
    97  		extra, err = json.Marshal(attrs)
    98  		if err != nil {
    99  			return nil, err
   100  		}
   101  	}
   102  
   103  	buf := bytes.NewBuffer(nil)
   104  	marshalFunc := func(msg *logger.Message) ([]byte, error) {
   105  		if err := marshalMessage(msg, extra, buf); err != nil {
   106  			return nil, err
   107  		}
   108  		b := buf.Bytes()
   109  		buf.Reset()
   110  		return b, nil
   111  	}
   112  
   113  	writer, err := loggerutils.NewLogFile(info.LogPath, capval, maxFiles, compress, marshalFunc, decodeFunc, 0640, getTailReader)
   114  	if err != nil {
   115  		return nil, err
   116  	}
   117  
   118  	return &JSONFileLogger{
   119  		writer:  writer,
   120  		readers: make(map[*logger.LogWatcher]struct{}),
   121  		tag:     tag,
   122  	}, nil
   123  }
   124  
   125  // Log converts logger.Message to jsonlog.JSONLog and serializes it to file.
   126  func (l *JSONFileLogger) Log(msg *logger.Message) error {
   127  	l.mu.Lock()
   128  	err := l.writer.WriteLogEntry(msg)
   129  	l.mu.Unlock()
   130  	return err
   131  }
   132  
   133  func marshalMessage(msg *logger.Message, extra json.RawMessage, buf *bytes.Buffer) error {
   134  	logLine := msg.Line
   135  	if msg.PLogMetaData == nil || (msg.PLogMetaData != nil && msg.PLogMetaData.Last) {
   136  		logLine = append(msg.Line, '\n')
   137  	}
   138  	err := (&jsonlog.JSONLogs{
   139  		Log:      logLine,
   140  		Stream:   msg.Source,
   141  		Created:  msg.Timestamp,
   142  		RawAttrs: extra,
   143  	}).MarshalJSONBuf(buf)
   144  	if err != nil {
   145  		return errors.Wrap(err, "error writing log message to buffer")
   146  	}
   147  	err = buf.WriteByte('\n')
   148  	return errors.Wrap(err, "error finalizing log buffer")
   149  }
   150  
   151  // ValidateLogOpt looks for json specific log options max-file & max-size.
   152  func ValidateLogOpt(cfg map[string]string) error {
   153  	for key := range cfg {
   154  		switch key {
   155  		case "max-file":
   156  		case "max-size":
   157  		case "compress":
   158  		case "labels":
   159  		case "labels-regex":
   160  		case "env":
   161  		case "env-regex":
   162  		case "tag":
   163  		default:
   164  			return fmt.Errorf("unknown log opt '%s' for json-file log driver", key)
   165  		}
   166  	}
   167  	return nil
   168  }
   169  
   170  // Close closes underlying file and signals all the readers
   171  // that the logs producer is gone.
   172  func (l *JSONFileLogger) Close() error {
   173  	l.mu.Lock()
   174  	l.closed = true
   175  	err := l.writer.Close()
   176  	for r := range l.readers {
   177  		r.ProducerGone()
   178  		delete(l.readers, r)
   179  	}
   180  	l.mu.Unlock()
   181  	return err
   182  }
   183  
   184  // Name returns name of this logger.
   185  func (l *JSONFileLogger) Name() string {
   186  	return Name
   187  }