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 }