github.com/Heebron/moby@v0.0.0-20221111184709-6eab4f55faf7/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  )
    19  
    20  // Name is the name of the file that the jsonlogger logs to.
    21  const Name = "json-file"
    22  
    23  // Every buffer will have to store the same constant json structure with the message
    24  // len(`{"log":"","stream:"stdout","time":"2000-01-01T00:00:00.000000000Z"}\n`) = 68.
    25  // So let's start with a buffer bigger than this.
    26  const initialBufSize = 256
    27  
    28  var buffersPool = sync.Pool{New: func() interface{} { return bytes.NewBuffer(make([]byte, 0, initialBufSize)) }}
    29  
    30  // JSONFileLogger is Logger implementation for default Docker logging.
    31  type JSONFileLogger struct {
    32  	writer *loggerutils.LogFile
    33  	tag    string // tag values requested by the user to log
    34  	extra  json.RawMessage
    35  }
    36  
    37  func init() {
    38  	if err := logger.RegisterLogDriver(Name, New); err != nil {
    39  		panic(err)
    40  	}
    41  	if err := logger.RegisterLogOptValidator(Name, ValidateLogOpt); err != nil {
    42  		panic(err)
    43  	}
    44  }
    45  
    46  // New creates new JSONFileLogger which writes to filename passed in
    47  // on given context.
    48  func New(info logger.Info) (logger.Logger, error) {
    49  	var capval int64 = -1
    50  	if capacity, ok := info.Config["max-size"]; ok {
    51  		var err error
    52  		capval, err = units.FromHumanSize(capacity)
    53  		if err != nil {
    54  			return nil, err
    55  		}
    56  		if capval <= 0 {
    57  			return nil, fmt.Errorf("max-size must be a positive number")
    58  		}
    59  	}
    60  	var maxFiles = 1
    61  	if maxFileString, ok := info.Config["max-file"]; ok {
    62  		var err error
    63  		maxFiles, err = strconv.Atoi(maxFileString)
    64  		if err != nil {
    65  			return nil, err
    66  		}
    67  		if maxFiles < 1 {
    68  			return nil, fmt.Errorf("max-file cannot be less than 1")
    69  		}
    70  	}
    71  
    72  	var compress bool
    73  	if compressString, ok := info.Config["compress"]; ok {
    74  		var err error
    75  		compress, err = strconv.ParseBool(compressString)
    76  		if err != nil {
    77  			return nil, err
    78  		}
    79  		if compress && (maxFiles == 1 || capval == -1) {
    80  			return nil, fmt.Errorf("compress cannot be true when max-file is less than 2 or max-size is not set")
    81  		}
    82  	}
    83  
    84  	attrs, err := info.ExtraAttributes(nil)
    85  	if err != nil {
    86  		return nil, err
    87  	}
    88  
    89  	// no default template. only use a tag if the user asked for it
    90  	tag, err := loggerutils.ParseLogTag(info, "")
    91  	if err != nil {
    92  		return nil, err
    93  	}
    94  	if tag != "" {
    95  		attrs["tag"] = tag
    96  	}
    97  
    98  	var extra json.RawMessage
    99  	if len(attrs) > 0 {
   100  		var err error
   101  		extra, err = json.Marshal(attrs)
   102  		if err != nil {
   103  			return nil, err
   104  		}
   105  	}
   106  
   107  	writer, err := loggerutils.NewLogFile(info.LogPath, capval, maxFiles, compress, decodeFunc, 0640, getTailReader)
   108  	if err != nil {
   109  		return nil, err
   110  	}
   111  
   112  	return &JSONFileLogger{
   113  		writer: writer,
   114  		tag:    tag,
   115  		extra:  extra,
   116  	}, nil
   117  }
   118  
   119  // Log converts logger.Message to jsonlog.JSONLog and serializes it to file.
   120  func (l *JSONFileLogger) Log(msg *logger.Message) error {
   121  	buf := buffersPool.Get().(*bytes.Buffer)
   122  	buf.Reset()
   123  	defer buffersPool.Put(buf)
   124  
   125  	timestamp := msg.Timestamp
   126  	err := marshalMessage(msg, l.extra, buf)
   127  	logger.PutMessage(msg)
   128  
   129  	if err != nil {
   130  		return err
   131  	}
   132  
   133  	return l.writer.WriteLogEntry(timestamp, buf.Bytes())
   134  }
   135  
   136  func marshalMessage(msg *logger.Message, extra json.RawMessage, buf *bytes.Buffer) error {
   137  	logLine := msg.Line
   138  	if msg.PLogMetaData == nil || (msg.PLogMetaData != nil && msg.PLogMetaData.Last) {
   139  		logLine = append(msg.Line, '\n')
   140  	}
   141  	err := (&jsonlog.JSONLogs{
   142  		Log:      logLine,
   143  		Stream:   msg.Source,
   144  		Created:  msg.Timestamp,
   145  		RawAttrs: extra,
   146  	}).MarshalJSONBuf(buf)
   147  	if err != nil {
   148  		return errors.Wrap(err, "error writing log message to buffer")
   149  	}
   150  	err = buf.WriteByte('\n')
   151  	return errors.Wrap(err, "error finalizing log buffer")
   152  }
   153  
   154  // ValidateLogOpt looks for json specific log options max-file & max-size.
   155  func ValidateLogOpt(cfg map[string]string) error {
   156  	for key := range cfg {
   157  		switch key {
   158  		case "max-file":
   159  		case "max-size":
   160  		case "compress":
   161  		case "labels":
   162  		case "labels-regex":
   163  		case "env":
   164  		case "env-regex":
   165  		case "tag":
   166  		default:
   167  			return fmt.Errorf("unknown log opt '%s' for json-file log driver", key)
   168  		}
   169  	}
   170  	return nil
   171  }
   172  
   173  // Close closes underlying file and signals all the readers
   174  // that the logs producer is gone.
   175  func (l *JSONFileLogger) Close() error {
   176  	return l.writer.Close()
   177  }
   178  
   179  // Name returns name of this logger.
   180  func (l *JSONFileLogger) Name() string {
   181  	return Name
   182  }