github.com/sams1990/dockerrepo@v17.12.1-ce-rc2+incompatible/daemon/logger/loggerutils/logfile.go (about) 1 package loggerutils 2 3 import ( 4 "bytes" 5 "context" 6 "fmt" 7 "io" 8 "os" 9 "strconv" 10 "sync" 11 "time" 12 13 "github.com/docker/docker/daemon/logger" 14 "github.com/docker/docker/daemon/logger/loggerutils/multireader" 15 "github.com/docker/docker/pkg/filenotify" 16 "github.com/docker/docker/pkg/pubsub" 17 "github.com/docker/docker/pkg/tailfile" 18 "github.com/fsnotify/fsnotify" 19 "github.com/pkg/errors" 20 "github.com/sirupsen/logrus" 21 ) 22 23 // LogFile is Logger implementation for default Docker logging. 24 type LogFile struct { 25 f *os.File // store for closing 26 closed bool 27 mu sync.RWMutex 28 capacity int64 //maximum size of each file 29 currentSize int64 // current size of the latest file 30 maxFiles int //maximum number of files 31 notifyRotate *pubsub.Publisher 32 marshal logger.MarshalFunc 33 createDecoder makeDecoderFunc 34 } 35 36 type makeDecoderFunc func(rdr io.Reader) func() (*logger.Message, error) 37 38 //NewLogFile creates new LogFile 39 func NewLogFile(logPath string, capacity int64, maxFiles int, marshaller logger.MarshalFunc, decodeFunc makeDecoderFunc) (*LogFile, error) { 40 log, err := os.OpenFile(logPath, os.O_WRONLY|os.O_APPEND|os.O_CREATE, 0640) 41 if err != nil { 42 return nil, err 43 } 44 45 size, err := log.Seek(0, os.SEEK_END) 46 if err != nil { 47 return nil, err 48 } 49 50 return &LogFile{ 51 f: log, 52 capacity: capacity, 53 currentSize: size, 54 maxFiles: maxFiles, 55 notifyRotate: pubsub.NewPublisher(0, 1), 56 marshal: marshaller, 57 createDecoder: decodeFunc, 58 }, nil 59 } 60 61 // WriteLogEntry writes the provided log message to the current log file. 62 // This may trigger a rotation event if the max file/capacity limits are hit. 63 func (w *LogFile) WriteLogEntry(msg *logger.Message) error { 64 b, err := w.marshal(msg) 65 if err != nil { 66 return errors.Wrap(err, "error marshalling log message") 67 } 68 69 logger.PutMessage(msg) 70 71 w.mu.Lock() 72 if w.closed { 73 w.mu.Unlock() 74 return errors.New("cannot write because the output file was closed") 75 } 76 77 if err := w.checkCapacityAndRotate(); err != nil { 78 w.mu.Unlock() 79 return err 80 } 81 82 n, err := w.f.Write(b) 83 if err == nil { 84 w.currentSize += int64(n) 85 } 86 w.mu.Unlock() 87 return err 88 } 89 90 func (w *LogFile) checkCapacityAndRotate() error { 91 if w.capacity == -1 { 92 return nil 93 } 94 95 if w.currentSize >= w.capacity { 96 name := w.f.Name() 97 if err := w.f.Close(); err != nil { 98 return errors.Wrap(err, "error closing file") 99 } 100 if err := rotate(name, w.maxFiles); err != nil { 101 return err 102 } 103 file, err := os.OpenFile(name, os.O_WRONLY|os.O_TRUNC|os.O_CREATE, 0640) 104 if err != nil { 105 return err 106 } 107 w.f = file 108 w.currentSize = 0 109 w.notifyRotate.Publish(struct{}{}) 110 } 111 112 return nil 113 } 114 115 func rotate(name string, maxFiles int) error { 116 if maxFiles < 2 { 117 return nil 118 } 119 for i := maxFiles - 1; i > 1; i-- { 120 toPath := name + "." + strconv.Itoa(i) 121 fromPath := name + "." + strconv.Itoa(i-1) 122 if err := os.Rename(fromPath, toPath); err != nil && !os.IsNotExist(err) { 123 return errors.Wrap(err, "error rotating old log entries") 124 } 125 } 126 127 if err := os.Rename(name, name+".1"); err != nil && !os.IsNotExist(err) { 128 return errors.Wrap(err, "error rotating current log") 129 } 130 return nil 131 } 132 133 // LogPath returns the location the given writer logs to. 134 func (w *LogFile) LogPath() string { 135 w.mu.Lock() 136 defer w.mu.Unlock() 137 return w.f.Name() 138 } 139 140 // MaxFiles return maximum number of files 141 func (w *LogFile) MaxFiles() int { 142 return w.maxFiles 143 } 144 145 // Close closes underlying file and signals all readers to stop. 146 func (w *LogFile) Close() error { 147 w.mu.Lock() 148 defer w.mu.Unlock() 149 if w.closed { 150 return nil 151 } 152 if err := w.f.Close(); err != nil { 153 return err 154 } 155 w.closed = true 156 return nil 157 } 158 159 // ReadLogs decodes entries from log files and sends them the passed in watcher 160 func (w *LogFile) ReadLogs(config logger.ReadConfig, watcher *logger.LogWatcher) { 161 w.mu.RLock() 162 files, err := w.openRotatedFiles() 163 if err != nil { 164 w.mu.RUnlock() 165 watcher.Err <- err 166 return 167 } 168 defer func() { 169 for _, f := range files { 170 f.Close() 171 } 172 }() 173 174 currentFile, err := os.Open(w.f.Name()) 175 if err != nil { 176 w.mu.RUnlock() 177 watcher.Err <- err 178 return 179 } 180 defer currentFile.Close() 181 182 currentChunk, err := newSectionReader(currentFile) 183 w.mu.RUnlock() 184 185 if err != nil { 186 watcher.Err <- err 187 return 188 } 189 190 if config.Tail != 0 { 191 seekers := make([]io.ReadSeeker, 0, len(files)+1) 192 for _, f := range files { 193 seekers = append(seekers, f) 194 } 195 seekers = append(seekers, currentChunk) 196 tailFile(multireader.MultiReadSeeker(seekers...), watcher, w.createDecoder, config) 197 } 198 199 w.mu.RLock() 200 if !config.Follow || w.closed { 201 w.mu.RUnlock() 202 return 203 } 204 w.mu.RUnlock() 205 206 notifyRotate := w.notifyRotate.Subscribe() 207 defer w.notifyRotate.Evict(notifyRotate) 208 followLogs(currentFile, watcher, notifyRotate, w.createDecoder, config.Since, config.Until) 209 } 210 211 func (w *LogFile) openRotatedFiles() (files []*os.File, err error) { 212 defer func() { 213 if err == nil { 214 return 215 } 216 for _, f := range files { 217 f.Close() 218 } 219 }() 220 221 for i := w.maxFiles; i > 1; i-- { 222 f, err := os.Open(fmt.Sprintf("%s.%d", w.f.Name(), i-1)) 223 if err != nil { 224 if !os.IsNotExist(err) { 225 return nil, err 226 } 227 continue 228 } 229 files = append(files, f) 230 } 231 232 return files, nil 233 } 234 235 func newSectionReader(f *os.File) (*io.SectionReader, error) { 236 // seek to the end to get the size 237 // we'll leave this at the end of the file since section reader does not advance the reader 238 size, err := f.Seek(0, os.SEEK_END) 239 if err != nil { 240 return nil, errors.Wrap(err, "error getting current file size") 241 } 242 return io.NewSectionReader(f, 0, size), nil 243 } 244 245 type decodeFunc func() (*logger.Message, error) 246 247 func tailFile(f io.ReadSeeker, watcher *logger.LogWatcher, createDecoder makeDecoderFunc, config logger.ReadConfig) { 248 var rdr io.Reader = f 249 if config.Tail > 0 { 250 ls, err := tailfile.TailFile(f, config.Tail) 251 if err != nil { 252 watcher.Err <- err 253 return 254 } 255 rdr = bytes.NewBuffer(bytes.Join(ls, []byte("\n"))) 256 } 257 258 decodeLogLine := createDecoder(rdr) 259 for { 260 msg, err := decodeLogLine() 261 if err != nil { 262 if err != io.EOF { 263 watcher.Err <- err 264 } 265 return 266 } 267 if !config.Since.IsZero() && msg.Timestamp.Before(config.Since) { 268 continue 269 } 270 if !config.Until.IsZero() && msg.Timestamp.After(config.Until) { 271 return 272 } 273 select { 274 case <-watcher.WatchClose(): 275 return 276 case watcher.Msg <- msg: 277 } 278 } 279 } 280 281 func followLogs(f *os.File, logWatcher *logger.LogWatcher, notifyRotate chan interface{}, createDecoder makeDecoderFunc, since, until time.Time) { 282 decodeLogLine := createDecoder(f) 283 284 name := f.Name() 285 fileWatcher, err := watchFile(name) 286 if err != nil { 287 logWatcher.Err <- err 288 return 289 } 290 defer func() { 291 f.Close() 292 fileWatcher.Remove(name) 293 fileWatcher.Close() 294 }() 295 296 ctx, cancel := context.WithCancel(context.Background()) 297 defer cancel() 298 go func() { 299 select { 300 case <-logWatcher.WatchClose(): 301 fileWatcher.Remove(name) 302 cancel() 303 case <-ctx.Done(): 304 return 305 } 306 }() 307 308 var retries int 309 handleRotate := func() error { 310 f.Close() 311 fileWatcher.Remove(name) 312 313 // retry when the file doesn't exist 314 for retries := 0; retries <= 5; retries++ { 315 f, err = os.Open(name) 316 if err == nil || !os.IsNotExist(err) { 317 break 318 } 319 } 320 if err != nil { 321 return err 322 } 323 if err := fileWatcher.Add(name); err != nil { 324 return err 325 } 326 decodeLogLine = createDecoder(f) 327 return nil 328 } 329 330 errRetry := errors.New("retry") 331 errDone := errors.New("done") 332 waitRead := func() error { 333 select { 334 case e := <-fileWatcher.Events(): 335 switch e.Op { 336 case fsnotify.Write: 337 decodeLogLine = createDecoder(f) 338 return nil 339 case fsnotify.Rename, fsnotify.Remove: 340 select { 341 case <-notifyRotate: 342 case <-ctx.Done(): 343 return errDone 344 } 345 if err := handleRotate(); err != nil { 346 return err 347 } 348 return nil 349 } 350 return errRetry 351 case err := <-fileWatcher.Errors(): 352 logrus.Debug("logger got error watching file: %v", err) 353 // Something happened, let's try and stay alive and create a new watcher 354 if retries <= 5 { 355 fileWatcher.Close() 356 fileWatcher, err = watchFile(name) 357 if err != nil { 358 return err 359 } 360 retries++ 361 return errRetry 362 } 363 return err 364 case <-ctx.Done(): 365 return errDone 366 } 367 } 368 369 handleDecodeErr := func(err error) error { 370 if err != io.EOF { 371 return err 372 } 373 374 for { 375 err := waitRead() 376 if err == nil { 377 break 378 } 379 if err == errRetry { 380 continue 381 } 382 return err 383 } 384 return nil 385 } 386 387 // main loop 388 for { 389 msg, err := decodeLogLine() 390 if err != nil { 391 if err := handleDecodeErr(err); err != nil { 392 if err == errDone { 393 return 394 } 395 // we got an unrecoverable error, so return 396 logWatcher.Err <- err 397 return 398 } 399 // ready to try again 400 continue 401 } 402 403 retries = 0 // reset retries since we've succeeded 404 if !since.IsZero() && msg.Timestamp.Before(since) { 405 continue 406 } 407 if !until.IsZero() && msg.Timestamp.After(until) { 408 return 409 } 410 select { 411 case logWatcher.Msg <- msg: 412 case <-ctx.Done(): 413 logWatcher.Msg <- msg 414 for { 415 msg, err := decodeLogLine() 416 if err != nil { 417 return 418 } 419 if !since.IsZero() && msg.Timestamp.Before(since) { 420 continue 421 } 422 if !until.IsZero() && msg.Timestamp.After(until) { 423 return 424 } 425 logWatcher.Msg <- msg 426 } 427 } 428 } 429 } 430 431 func watchFile(name string) (filenotify.FileWatcher, error) { 432 fileWatcher, err := filenotify.New() 433 if err != nil { 434 return nil, err 435 } 436 437 logger := logrus.WithFields(logrus.Fields{ 438 "module": "logger", 439 "fille": name, 440 }) 441 442 if err := fileWatcher.Add(name); err != nil { 443 logger.WithError(err).Warnf("falling back to file poller") 444 fileWatcher.Close() 445 fileWatcher = filenotify.NewPollingWatcher() 446 447 if err := fileWatcher.Add(name); err != nil { 448 fileWatcher.Close() 449 logger.WithError(err).Debugf("error watching log file for modifications") 450 return nil, err 451 } 452 } 453 return fileWatcher, nil 454 }