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 }