github.com/demonoid81/moby@v0.0.0-20200517203328-62dd8e17c460/daemon/logger/local/local.go (about)

     1  package local // import "github.com/demonoid81/moby/daemon/logger/local"
     2  
     3  import (
     4  	"encoding/binary"
     5  	"io"
     6  	"strconv"
     7  	"sync"
     8  	"time"
     9  
    10  	"github.com/demonoid81/moby/api/types/backend"
    11  	"github.com/demonoid81/moby/api/types/plugins/logdriver"
    12  	"github.com/demonoid81/moby/daemon/logger"
    13  	"github.com/demonoid81/moby/daemon/logger/loggerutils"
    14  	"github.com/demonoid81/moby/errdefs"
    15  	units "github.com/docker/go-units"
    16  	"github.com/pkg/errors"
    17  	"github.com/sirupsen/logrus"
    18  )
    19  
    20  const (
    21  	// Name is the name of the driver
    22  	Name = "local"
    23  
    24  	encodeBinaryLen = 4
    25  	initialBufSize  = 2048
    26  	maxDecodeRetry  = 20000
    27  
    28  	defaultMaxFileSize  int64 = 20 * 1024 * 1024
    29  	defaultMaxFileCount       = 5
    30  	defaultCompressLogs       = true
    31  )
    32  
    33  // LogOptKeys are the keys names used for log opts passed in to initialize the driver.
    34  var LogOptKeys = map[string]bool{
    35  	"max-file": true,
    36  	"max-size": true,
    37  	"compress": true,
    38  }
    39  
    40  // ValidateLogOpt looks for log driver specific options.
    41  func ValidateLogOpt(cfg map[string]string) error {
    42  	for key := range cfg {
    43  		if !LogOptKeys[key] {
    44  			return errors.Errorf("unknown log opt '%s' for log driver %s", key, Name)
    45  		}
    46  	}
    47  	return nil
    48  }
    49  
    50  func init() {
    51  	if err := logger.RegisterLogDriver(Name, New); err != nil {
    52  		logrus.Fatal(err)
    53  	}
    54  	if err := logger.RegisterLogOptValidator(Name, ValidateLogOpt); err != nil {
    55  		logrus.Fatal(err)
    56  	}
    57  }
    58  
    59  type driver struct {
    60  	mu      sync.Mutex
    61  	closed  bool
    62  	logfile *loggerutils.LogFile
    63  	readers map[*logger.LogWatcher]struct{} // stores the active log followers
    64  }
    65  
    66  // New creates a new local logger
    67  // You must provide the `LogPath` in the passed in info argument, this is the file path that logs are written to.
    68  func New(info logger.Info) (logger.Logger, error) {
    69  	if info.LogPath == "" {
    70  		return nil, errdefs.System(errors.New("log path is missing -- this is a bug and should not happen"))
    71  	}
    72  
    73  	cfg := newDefaultConfig()
    74  	if capacity, ok := info.Config["max-size"]; ok {
    75  		var err error
    76  		cfg.MaxFileSize, err = units.FromHumanSize(capacity)
    77  		if err != nil {
    78  			return nil, errdefs.InvalidParameter(errors.Wrapf(err, "invalid value for max-size: %s", capacity))
    79  		}
    80  	}
    81  
    82  	if userMaxFileCount, ok := info.Config["max-file"]; ok {
    83  		var err error
    84  		cfg.MaxFileCount, err = strconv.Atoi(userMaxFileCount)
    85  		if err != nil {
    86  			return nil, errdefs.InvalidParameter(errors.Wrapf(err, "invalid value for max-file: %s", userMaxFileCount))
    87  		}
    88  	}
    89  
    90  	if userCompress, ok := info.Config["compress"]; ok {
    91  		compressLogs, err := strconv.ParseBool(userCompress)
    92  		if err != nil {
    93  			return nil, errdefs.InvalidParameter(errors.Wrap(err, "error reading compress log option"))
    94  		}
    95  		cfg.DisableCompression = !compressLogs
    96  	}
    97  	return newDriver(info.LogPath, cfg)
    98  }
    99  
   100  func makeMarshaller() func(m *logger.Message) ([]byte, error) {
   101  	buf := make([]byte, initialBufSize)
   102  
   103  	// allocate the partial log entry separately, which allows for easier re-use
   104  	proto := &logdriver.LogEntry{}
   105  	md := &logdriver.PartialLogEntryMetadata{}
   106  
   107  	return func(m *logger.Message) ([]byte, error) {
   108  		resetProto(proto)
   109  
   110  		messageToProto(m, proto, md)
   111  		protoSize := proto.Size()
   112  		writeLen := protoSize + (2 * encodeBinaryLen) // + len(messageDelimiter)
   113  
   114  		if writeLen > len(buf) {
   115  			buf = make([]byte, writeLen)
   116  		} else {
   117  			// shrink the buffer back down
   118  			if writeLen <= initialBufSize {
   119  				buf = buf[:initialBufSize]
   120  			} else {
   121  				buf = buf[:writeLen]
   122  			}
   123  		}
   124  
   125  		binary.BigEndian.PutUint32(buf[:encodeBinaryLen], uint32(protoSize))
   126  		n, err := proto.MarshalTo(buf[encodeBinaryLen:writeLen])
   127  		if err != nil {
   128  			return nil, errors.Wrap(err, "error marshaling log entry")
   129  		}
   130  		if n+(encodeBinaryLen*2) != writeLen {
   131  			return nil, io.ErrShortWrite
   132  		}
   133  		binary.BigEndian.PutUint32(buf[writeLen-encodeBinaryLen:writeLen], uint32(protoSize))
   134  		return buf[:writeLen], nil
   135  	}
   136  }
   137  
   138  func newDriver(logPath string, cfg *CreateConfig) (logger.Logger, error) {
   139  	if err := validateConfig(cfg); err != nil {
   140  		return nil, errdefs.InvalidParameter(err)
   141  	}
   142  
   143  	lf, err := loggerutils.NewLogFile(logPath, cfg.MaxFileSize, cfg.MaxFileCount, !cfg.DisableCompression, makeMarshaller(), decodeFunc, 0640, getTailReader)
   144  	if err != nil {
   145  		return nil, err
   146  	}
   147  	return &driver{
   148  		logfile: lf,
   149  		readers: make(map[*logger.LogWatcher]struct{}),
   150  	}, nil
   151  }
   152  
   153  func (d *driver) Name() string {
   154  	return Name
   155  }
   156  
   157  func (d *driver) Log(msg *logger.Message) error {
   158  	d.mu.Lock()
   159  	err := d.logfile.WriteLogEntry(msg)
   160  	d.mu.Unlock()
   161  	return err
   162  }
   163  
   164  func (d *driver) Close() error {
   165  	d.mu.Lock()
   166  	d.closed = true
   167  	err := d.logfile.Close()
   168  	for r := range d.readers {
   169  		r.ProducerGone()
   170  		delete(d.readers, r)
   171  	}
   172  	d.mu.Unlock()
   173  	return err
   174  }
   175  
   176  func messageToProto(msg *logger.Message, proto *logdriver.LogEntry, partial *logdriver.PartialLogEntryMetadata) {
   177  	proto.Source = msg.Source
   178  	proto.TimeNano = msg.Timestamp.UnixNano()
   179  	proto.Line = append(proto.Line[:0], msg.Line...)
   180  	proto.Partial = msg.PLogMetaData != nil
   181  	if proto.Partial {
   182  		partial.Ordinal = int32(msg.PLogMetaData.Ordinal)
   183  		partial.Last = msg.PLogMetaData.Last
   184  		partial.Id = msg.PLogMetaData.ID
   185  		proto.PartialLogMetadata = partial
   186  	} else {
   187  		proto.PartialLogMetadata = nil
   188  	}
   189  }
   190  
   191  func protoToMessage(proto *logdriver.LogEntry) *logger.Message {
   192  	msg := &logger.Message{
   193  		Source:    proto.Source,
   194  		Timestamp: time.Unix(0, proto.TimeNano),
   195  	}
   196  	if proto.Partial {
   197  		var md backend.PartialLogMetaData
   198  		md.Last = proto.GetPartialLogMetadata().GetLast()
   199  		md.ID = proto.GetPartialLogMetadata().GetId()
   200  		md.Ordinal = int(proto.GetPartialLogMetadata().GetOrdinal())
   201  		msg.PLogMetaData = &md
   202  	}
   203  	msg.Line = append(msg.Line[:0], proto.Line...)
   204  	return msg
   205  }
   206  
   207  func resetProto(proto *logdriver.LogEntry) {
   208  	proto.Source = ""
   209  	proto.Line = proto.Line[:0]
   210  	proto.TimeNano = 0
   211  	proto.Partial = false
   212  	if proto.PartialLogMetadata != nil {
   213  		proto.PartialLogMetadata.Id = ""
   214  		proto.PartialLogMetadata.Last = false
   215  		proto.PartialLogMetadata.Ordinal = 0
   216  	}
   217  	proto.PartialLogMetadata = nil
   218  }