github.com/Heebron/moby@v0.0.0-20221111184709-6eab4f55faf7/daemon/logger/local/local.go (about)

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