github.com/yogeshlonkar/moby@v1.13.2-0.20201203103638-c0b64beaea94/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
     5  
     6  import (
     7  	"bytes"
     8  	"encoding/json"
     9  	"fmt"
    10  	"io"
    11  	"strconv"
    12  	"sync"
    13  
    14  	"github.com/sirupsen/logrus"
    15  	"github.com/docker/docker/daemon/logger"
    16  	"github.com/docker/docker/daemon/logger/loggerutils"
    17  	"github.com/docker/docker/pkg/jsonlog"
    18  	"github.com/docker/go-units"
    19  	"github.com/pkg/errors"
    20  )
    21  
    22  // Name is the name of the file that the jsonlogger logs to.
    23  const Name = "json-file"
    24  
    25  // JSONFileLogger is Logger implementation for default Docker logging.
    26  type JSONFileLogger struct {
    27  	extra []byte // json-encoded extra attributes
    28  
    29  	mu      sync.RWMutex
    30  	buf     *bytes.Buffer // avoids allocating a new buffer on each call to `Log()`
    31  	closed  bool
    32  	writer  *loggerutils.RotateFileWriter
    33  	readers map[*logger.LogWatcher]struct{} // stores the active log followers
    34  }
    35  
    36  func init() {
    37  	if err := logger.RegisterLogDriver(Name, New); err != nil {
    38  		logrus.Fatal(err)
    39  	}
    40  	if err := logger.RegisterLogOptValidator(Name, ValidateLogOpt); err != nil {
    41  		logrus.Fatal(err)
    42  	}
    43  }
    44  
    45  // New creates new JSONFileLogger which writes to filename passed in
    46  // on given context.
    47  func New(ctx logger.Context) (logger.Logger, error) {
    48  	var capval int64 = -1
    49  	if capacity, ok := ctx.Config["max-size"]; ok {
    50  		var err error
    51  		capval, err = units.FromHumanSize(capacity)
    52  		if err != nil {
    53  			return nil, err
    54  		}
    55  	}
    56  	var maxFiles = 1
    57  	if maxFileString, ok := ctx.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  	writer, err := loggerutils.NewRotateFileWriter(ctx.LogPath, capval, maxFiles)
    69  	if err != nil {
    70  		return nil, err
    71  	}
    72  
    73  	var extra []byte
    74  	if attrs := ctx.ExtraAttributes(nil); len(attrs) > 0 {
    75  		var err error
    76  		extra, err = json.Marshal(attrs)
    77  		if err != nil {
    78  			return nil, err
    79  		}
    80  	}
    81  
    82  	return &JSONFileLogger{
    83  		buf:     bytes.NewBuffer(nil),
    84  		writer:  writer,
    85  		readers: make(map[*logger.LogWatcher]struct{}),
    86  		extra:   extra,
    87  	}, nil
    88  }
    89  
    90  // Log converts logger.Message to jsonlog.JSONLog and serializes it to file.
    91  func (l *JSONFileLogger) Log(msg *logger.Message) error {
    92  	l.mu.Lock()
    93  	err := writeMessageBuf(l.writer, msg, l.extra, l.buf)
    94  	l.buf.Reset()
    95  	l.mu.Unlock()
    96  	return err
    97  }
    98  
    99  func writeMessageBuf(w io.Writer, m *logger.Message, extra json.RawMessage, buf *bytes.Buffer) error {
   100  	if err := marshalMessage(m, extra, buf); err != nil {
   101  		return err
   102  	}
   103  	if _, err := w.Write(buf.Bytes()); err != nil {
   104  		return errors.Wrap(err, "error writing log entry")
   105  	}
   106  	return nil
   107  }
   108  
   109  func marshalMessage(msg *logger.Message, extra json.RawMessage, buf *bytes.Buffer) error {
   110  	timestamp, err := jsonlog.FastTimeMarshalJSON(msg.Timestamp)
   111  	if err != nil {
   112  		return err
   113  	}
   114  	logLine := msg.Line
   115  	if !msg.Partial {
   116  		logLine = append(msg.Line, '\n')
   117  	}
   118  	err = (&jsonlog.JSONLogs{
   119  		Log:      logLine,
   120  		Stream:   msg.Source,
   121  		Created:  timestamp,
   122  		RawAttrs: extra,
   123  	}).MarshalJSONBuf(buf)
   124  	if err != nil {
   125  		return errors.Wrap(err, "error writing log message to buffer")
   126  	}
   127  	err = buf.WriteByte('\n')
   128  	return errors.Wrap(err, "error finalizing log buffer")
   129  }
   130  
   131  // ValidateLogOpt looks for json specific log options max-file & max-size.
   132  func ValidateLogOpt(cfg map[string]string) error {
   133  	for key := range cfg {
   134  		switch key {
   135  		case "max-file":
   136  		case "max-size":
   137  		case "labels":
   138  		case "env":
   139  		default:
   140  			return fmt.Errorf("unknown log opt '%s' for json-file log driver", key)
   141  		}
   142  	}
   143  	return nil
   144  }
   145  
   146  // LogPath returns the location the given json logger logs to.
   147  func (l *JSONFileLogger) LogPath() string {
   148  	return l.writer.LogPath()
   149  }
   150  
   151  // Close closes underlying file and signals all readers to stop.
   152  func (l *JSONFileLogger) Close() error {
   153  	l.mu.Lock()
   154  	err := l.writer.Close()
   155  	for r := range l.readers {
   156  		r.Close()
   157  		delete(l.readers, r)
   158  	}
   159  	l.mu.Unlock()
   160  	return err
   161  }
   162  
   163  // Name returns name of this logger.
   164  func (l *JSONFileLogger) Name() string {
   165  	return Name
   166  }