github.com/mssola/docker@v1.8.1/daemon/logger/jsonfilelog/jsonfilelog.go (about) 1 package jsonfilelog 2 3 import ( 4 "bytes" 5 "encoding/json" 6 "fmt" 7 "io" 8 "os" 9 "strconv" 10 "sync" 11 "time" 12 13 "gopkg.in/fsnotify.v1" 14 15 "github.com/Sirupsen/logrus" 16 "github.com/docker/docker/daemon/logger" 17 "github.com/docker/docker/pkg/ioutils" 18 "github.com/docker/docker/pkg/jsonlog" 19 "github.com/docker/docker/pkg/pubsub" 20 "github.com/docker/docker/pkg/tailfile" 21 "github.com/docker/docker/pkg/timeutils" 22 "github.com/docker/docker/pkg/units" 23 ) 24 25 const ( 26 Name = "json-file" 27 maxJSONDecodeRetry = 10 28 ) 29 30 // JSONFileLogger is Logger implementation for default docker logging: 31 // JSON objects to file 32 type JSONFileLogger struct { 33 buf *bytes.Buffer 34 f *os.File // store for closing 35 mu sync.Mutex // protects buffer 36 capacity int64 //maximum size of each file 37 n int //maximum number of files 38 ctx logger.Context 39 readers map[*logger.LogWatcher]struct{} // stores the active log followers 40 notifyRotate *pubsub.Publisher 41 } 42 43 func init() { 44 if err := logger.RegisterLogDriver(Name, New); err != nil { 45 logrus.Fatal(err) 46 } 47 if err := logger.RegisterLogOptValidator(Name, ValidateLogOpt); err != nil { 48 logrus.Fatal(err) 49 } 50 } 51 52 // New creates new JSONFileLogger which writes to filename 53 func New(ctx logger.Context) (logger.Logger, error) { 54 log, err := os.OpenFile(ctx.LogPath, os.O_RDWR|os.O_APPEND|os.O_CREATE, 0600) 55 if err != nil { 56 return nil, err 57 } 58 var capval int64 = -1 59 if capacity, ok := ctx.Config["max-size"]; ok { 60 var err error 61 capval, err = units.FromHumanSize(capacity) 62 if err != nil { 63 return nil, err 64 } 65 } 66 var maxFiles int = 1 67 if maxFileString, ok := ctx.Config["max-file"]; ok { 68 maxFiles, err = strconv.Atoi(maxFileString) 69 if err != nil { 70 return nil, err 71 } 72 if maxFiles < 1 { 73 return nil, fmt.Errorf("max-files cannot be less than 1.") 74 } 75 } 76 return &JSONFileLogger{ 77 f: log, 78 buf: bytes.NewBuffer(nil), 79 ctx: ctx, 80 capacity: capval, 81 n: maxFiles, 82 readers: make(map[*logger.LogWatcher]struct{}), 83 notifyRotate: pubsub.NewPublisher(0, 1), 84 }, nil 85 } 86 87 // Log converts logger.Message to jsonlog.JSONLog and serializes it to file 88 func (l *JSONFileLogger) Log(msg *logger.Message) error { 89 l.mu.Lock() 90 defer l.mu.Unlock() 91 92 timestamp, err := timeutils.FastMarshalJSON(msg.Timestamp) 93 if err != nil { 94 return err 95 } 96 err = (&jsonlog.JSONLogBytes{Log: append(msg.Line, '\n'), Stream: msg.Source, Created: timestamp}).MarshalJSONBuf(l.buf) 97 if err != nil { 98 return err 99 } 100 l.buf.WriteByte('\n') 101 _, err = writeLog(l) 102 return err 103 } 104 105 func writeLog(l *JSONFileLogger) (int64, error) { 106 if l.capacity == -1 { 107 return writeToBuf(l) 108 } 109 meta, err := l.f.Stat() 110 if err != nil { 111 return -1, err 112 } 113 if meta.Size() >= l.capacity { 114 name := l.f.Name() 115 if err := l.f.Close(); err != nil { 116 return -1, err 117 } 118 if err := rotate(name, l.n); err != nil { 119 return -1, err 120 } 121 file, err := os.OpenFile(name, os.O_WRONLY|os.O_TRUNC|os.O_CREATE, 0666) 122 if err != nil { 123 return -1, err 124 } 125 l.f = file 126 l.notifyRotate.Publish(struct{}{}) 127 } 128 return writeToBuf(l) 129 } 130 131 func writeToBuf(l *JSONFileLogger) (int64, error) { 132 i, err := l.buf.WriteTo(l.f) 133 if err != nil { 134 l.buf = bytes.NewBuffer(nil) 135 } 136 return i, err 137 } 138 139 func rotate(name string, n int) error { 140 if n < 2 { 141 return nil 142 } 143 for i := n - 1; i > 1; i-- { 144 oldFile := name + "." + strconv.Itoa(i) 145 replacingFile := name + "." + strconv.Itoa(i-1) 146 if err := backup(oldFile, replacingFile); err != nil { 147 return err 148 } 149 } 150 if err := backup(name+".1", name); err != nil { 151 return err 152 } 153 return nil 154 } 155 156 func backup(old, curr string) error { 157 if _, err := os.Stat(old); !os.IsNotExist(err) { 158 err := os.Remove(old) 159 if err != nil { 160 return err 161 } 162 } 163 if _, err := os.Stat(curr); os.IsNotExist(err) { 164 f, err := os.Create(curr) 165 if err != nil { 166 return err 167 } 168 f.Close() 169 } 170 return os.Rename(curr, old) 171 } 172 173 func ValidateLogOpt(cfg map[string]string) error { 174 for key := range cfg { 175 switch key { 176 case "max-file": 177 case "max-size": 178 default: 179 return fmt.Errorf("unknown log opt '%s' for json-file log driver", key) 180 } 181 } 182 return nil 183 } 184 185 func (l *JSONFileLogger) LogPath() string { 186 return l.ctx.LogPath 187 } 188 189 // Close closes underlying file and signals all readers to stop 190 func (l *JSONFileLogger) Close() error { 191 l.mu.Lock() 192 err := l.f.Close() 193 for r := range l.readers { 194 r.Close() 195 delete(l.readers, r) 196 } 197 l.mu.Unlock() 198 return err 199 } 200 201 // Name returns name of this logger 202 func (l *JSONFileLogger) Name() string { 203 return Name 204 } 205 206 func decodeLogLine(dec *json.Decoder, l *jsonlog.JSONLog) (*logger.Message, error) { 207 l.Reset() 208 if err := dec.Decode(l); err != nil { 209 return nil, err 210 } 211 msg := &logger.Message{ 212 Source: l.Stream, 213 Timestamp: l.Created, 214 Line: []byte(l.Log), 215 } 216 return msg, nil 217 } 218 219 // Reads from the log file 220 func (l *JSONFileLogger) ReadLogs(config logger.ReadConfig) *logger.LogWatcher { 221 logWatcher := logger.NewLogWatcher() 222 223 go l.readLogs(logWatcher, config) 224 return logWatcher 225 } 226 227 func (l *JSONFileLogger) readLogs(logWatcher *logger.LogWatcher, config logger.ReadConfig) { 228 defer close(logWatcher.Msg) 229 230 pth := l.ctx.LogPath 231 var files []io.ReadSeeker 232 for i := l.n; i > 1; i-- { 233 f, err := os.Open(fmt.Sprintf("%s.%d", pth, i-1)) 234 if err != nil { 235 if !os.IsNotExist(err) { 236 logWatcher.Err <- err 237 break 238 } 239 continue 240 } 241 defer f.Close() 242 files = append(files, f) 243 } 244 245 latestFile, err := os.Open(pth) 246 if err != nil { 247 logWatcher.Err <- err 248 return 249 } 250 defer latestFile.Close() 251 252 files = append(files, latestFile) 253 tailer := ioutils.MultiReadSeeker(files...) 254 255 if config.Tail != 0 { 256 tailFile(tailer, logWatcher, config.Tail, config.Since) 257 } 258 259 if !config.Follow { 260 return 261 } 262 263 if config.Tail >= 0 { 264 latestFile.Seek(0, os.SEEK_END) 265 } 266 267 l.mu.Lock() 268 l.readers[logWatcher] = struct{}{} 269 l.mu.Unlock() 270 271 notifyRotate := l.notifyRotate.Subscribe() 272 followLogs(latestFile, logWatcher, notifyRotate, config.Since) 273 274 l.mu.Lock() 275 delete(l.readers, logWatcher) 276 l.mu.Unlock() 277 278 l.notifyRotate.Evict(notifyRotate) 279 } 280 281 func tailFile(f io.ReadSeeker, logWatcher *logger.LogWatcher, tail int, since time.Time) { 282 var rdr io.Reader = f 283 if tail > 0 { 284 ls, err := tailfile.TailFile(f, tail) 285 if err != nil { 286 logWatcher.Err <- err 287 return 288 } 289 rdr = bytes.NewBuffer(bytes.Join(ls, []byte("\n"))) 290 } 291 dec := json.NewDecoder(rdr) 292 l := &jsonlog.JSONLog{} 293 for { 294 msg, err := decodeLogLine(dec, l) 295 if err != nil { 296 if err != io.EOF { 297 logWatcher.Err <- err 298 } 299 return 300 } 301 if !since.IsZero() && msg.Timestamp.Before(since) { 302 continue 303 } 304 logWatcher.Msg <- msg 305 } 306 } 307 308 func followLogs(f *os.File, logWatcher *logger.LogWatcher, notifyRotate chan interface{}, since time.Time) { 309 dec := json.NewDecoder(f) 310 l := &jsonlog.JSONLog{} 311 fileWatcher, err := fsnotify.NewWatcher() 312 if err != nil { 313 logWatcher.Err <- err 314 return 315 } 316 defer fileWatcher.Close() 317 if err := fileWatcher.Add(f.Name()); err != nil { 318 logWatcher.Err <- err 319 return 320 } 321 322 var retries int 323 for { 324 msg, err := decodeLogLine(dec, l) 325 if err != nil { 326 if err != io.EOF { 327 // try again because this shouldn't happen 328 if _, ok := err.(*json.SyntaxError); ok && retries <= maxJSONDecodeRetry { 329 dec = json.NewDecoder(f) 330 retries += 1 331 continue 332 } 333 logWatcher.Err <- err 334 return 335 } 336 337 select { 338 case <-fileWatcher.Events: 339 dec = json.NewDecoder(f) 340 continue 341 case <-fileWatcher.Errors: 342 logWatcher.Err <- err 343 return 344 case <-logWatcher.WatchClose(): 345 return 346 case <-notifyRotate: 347 fileWatcher.Remove(f.Name()) 348 349 f, err = os.Open(f.Name()) 350 if err != nil { 351 logWatcher.Err <- err 352 return 353 } 354 if err := fileWatcher.Add(f.Name()); err != nil { 355 logWatcher.Err <- err 356 } 357 dec = json.NewDecoder(f) 358 continue 359 } 360 } 361 362 retries = 0 // reset retries since we've succeeded 363 if !since.IsZero() && msg.Timestamp.Before(since) { 364 continue 365 } 366 select { 367 case logWatcher.Msg <- msg: 368 case <-logWatcher.WatchClose(): 369 logWatcher.Msg <- msg 370 for { 371 msg, err := decodeLogLine(dec, l) 372 if err != nil { 373 return 374 } 375 if !since.IsZero() && msg.Timestamp.Before(since) { 376 continue 377 } 378 logWatcher.Msg <- msg 379 } 380 } 381 } 382 }