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