github.com/Heebron/moby@v0.0.0-20221111184709-6eab4f55faf7/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 // import "github.com/docker/docker/daemon/logger/jsonfilelog" 5 6 import ( 7 "bytes" 8 "encoding/json" 9 "fmt" 10 "strconv" 11 "sync" 12 13 "github.com/docker/docker/daemon/logger" 14 "github.com/docker/docker/daemon/logger/jsonfilelog/jsonlog" 15 "github.com/docker/docker/daemon/logger/loggerutils" 16 units "github.com/docker/go-units" 17 "github.com/pkg/errors" 18 ) 19 20 // Name is the name of the file that the jsonlogger logs to. 21 const Name = "json-file" 22 23 // Every buffer will have to store the same constant json structure with the message 24 // len(`{"log":"","stream:"stdout","time":"2000-01-01T00:00:00.000000000Z"}\n`) = 68. 25 // So let's start with a buffer bigger than this. 26 const initialBufSize = 256 27 28 var buffersPool = sync.Pool{New: func() interface{} { return bytes.NewBuffer(make([]byte, 0, initialBufSize)) }} 29 30 // JSONFileLogger is Logger implementation for default Docker logging. 31 type JSONFileLogger struct { 32 writer *loggerutils.LogFile 33 tag string // tag values requested by the user to log 34 extra json.RawMessage 35 } 36 37 func init() { 38 if err := logger.RegisterLogDriver(Name, New); err != nil { 39 panic(err) 40 } 41 if err := logger.RegisterLogOptValidator(Name, ValidateLogOpt); err != nil { 42 panic(err) 43 } 44 } 45 46 // New creates new JSONFileLogger which writes to filename passed in 47 // on given context. 48 func New(info logger.Info) (logger.Logger, error) { 49 var capval int64 = -1 50 if capacity, ok := info.Config["max-size"]; ok { 51 var err error 52 capval, err = units.FromHumanSize(capacity) 53 if err != nil { 54 return nil, err 55 } 56 if capval <= 0 { 57 return nil, fmt.Errorf("max-size must be a positive number") 58 } 59 } 60 var maxFiles = 1 61 if maxFileString, ok := info.Config["max-file"]; ok { 62 var err error 63 maxFiles, err = strconv.Atoi(maxFileString) 64 if err != nil { 65 return nil, err 66 } 67 if maxFiles < 1 { 68 return nil, fmt.Errorf("max-file cannot be less than 1") 69 } 70 } 71 72 var compress bool 73 if compressString, ok := info.Config["compress"]; ok { 74 var err error 75 compress, err = strconv.ParseBool(compressString) 76 if err != nil { 77 return nil, err 78 } 79 if compress && (maxFiles == 1 || capval == -1) { 80 return nil, fmt.Errorf("compress cannot be true when max-file is less than 2 or max-size is not set") 81 } 82 } 83 84 attrs, err := info.ExtraAttributes(nil) 85 if err != nil { 86 return nil, err 87 } 88 89 // no default template. only use a tag if the user asked for it 90 tag, err := loggerutils.ParseLogTag(info, "") 91 if err != nil { 92 return nil, err 93 } 94 if tag != "" { 95 attrs["tag"] = tag 96 } 97 98 var extra json.RawMessage 99 if len(attrs) > 0 { 100 var err error 101 extra, err = json.Marshal(attrs) 102 if err != nil { 103 return nil, err 104 } 105 } 106 107 writer, err := loggerutils.NewLogFile(info.LogPath, capval, maxFiles, compress, decodeFunc, 0640, getTailReader) 108 if err != nil { 109 return nil, err 110 } 111 112 return &JSONFileLogger{ 113 writer: writer, 114 tag: tag, 115 extra: extra, 116 }, nil 117 } 118 119 // Log converts logger.Message to jsonlog.JSONLog and serializes it to file. 120 func (l *JSONFileLogger) Log(msg *logger.Message) error { 121 buf := buffersPool.Get().(*bytes.Buffer) 122 buf.Reset() 123 defer buffersPool.Put(buf) 124 125 timestamp := msg.Timestamp 126 err := marshalMessage(msg, l.extra, buf) 127 logger.PutMessage(msg) 128 129 if err != nil { 130 return err 131 } 132 133 return l.writer.WriteLogEntry(timestamp, buf.Bytes()) 134 } 135 136 func marshalMessage(msg *logger.Message, extra json.RawMessage, buf *bytes.Buffer) error { 137 logLine := msg.Line 138 if msg.PLogMetaData == nil || (msg.PLogMetaData != nil && msg.PLogMetaData.Last) { 139 logLine = append(msg.Line, '\n') 140 } 141 err := (&jsonlog.JSONLogs{ 142 Log: logLine, 143 Stream: msg.Source, 144 Created: msg.Timestamp, 145 RawAttrs: extra, 146 }).MarshalJSONBuf(buf) 147 if err != nil { 148 return errors.Wrap(err, "error writing log message to buffer") 149 } 150 err = buf.WriteByte('\n') 151 return errors.Wrap(err, "error finalizing log buffer") 152 } 153 154 // ValidateLogOpt looks for json specific log options max-file & max-size. 155 func ValidateLogOpt(cfg map[string]string) error { 156 for key := range cfg { 157 switch key { 158 case "max-file": 159 case "max-size": 160 case "compress": 161 case "labels": 162 case "labels-regex": 163 case "env": 164 case "env-regex": 165 case "tag": 166 default: 167 return fmt.Errorf("unknown log opt '%s' for json-file log driver", key) 168 } 169 } 170 return nil 171 } 172 173 // Close closes underlying file and signals all the readers 174 // that the logs producer is gone. 175 func (l *JSONFileLogger) Close() error { 176 return l.writer.Close() 177 } 178 179 // Name returns name of this logger. 180 func (l *JSONFileLogger) Name() string { 181 return Name 182 }