// TailFile begins tailing the file. Output stream is made available // via the `Tail.Lines` channel. To handle errors during tailing, // invoke the `Wait` or `Err` method after finishing reading from the // `Lines` channel. func TailFile(filename string, config Config) (*Tail, error) { if config.ReOpen && !config.Follow { util.Fatal("cannot set ReOpen without Follow.") } t := &Tail{ Filename: filename, Lines: make(chan *Line), Config: config, } // when Logger was not specified in config, use default logger if t.Logger == nil { t.Logger = log.New(os.Stderr, "", log.LstdFlags) } if t.Poll { t.watcher = watch.NewPollingFileWatcher(filename) } else { t.tracker = watch.NewInotifyTracker() w, err := t.tracker.NewWatcher() if err != nil { return nil, err } t.Logger.Printf("start InotifyFileWatcher: %s", filename) t.watcher = watch.NewInotifyFileWatcher(filename, w) } if t.MustExist { var err error t.file, err = OpenFile(t.Filename) if err != nil { return nil, err } } go t.tailFileSync() return t, nil }
func (fw *InotifyFileWatcher) ChangeEvents(t *tomb.Tomb, fi os.FileInfo) *FileChanges { changes := NewFileChanges() err := fw.w.Watch(fw.Filename) if err != nil { util.Fatal("Error watching %v: %v", fw.Filename, err) } fw.Size = fi.Size() fw.ModTime = fi.ModTime() go func() { defer fw.w.RemoveWatch(fw.Filename) defer changes.Close() for { prevSize := fw.Size var evt *fsnotify.FileEvent var ok bool select { case evt, ok = <-fw.w.Event: if !ok { return } case <-t.Dying(): return } switch { case evt.IsDelete(): fallthrough case evt.IsRename(): changes.NotifyDeleted() continue //return case evt.IsModify(): fi, err := os.Stat(fw.Filename) if err != nil { if os.IsNotExist(err) { changes.NotifyDeleted() return } // XXX: report this error back to the user util.Fatal("Failed to stat file %v: %v", fw.Filename, err) } fw.Size = fi.Size() if prevSize > 0 && prevSize > fw.Size { log.Printf("prevSize:%d,fw.Size:%d", prevSize, fw.Size) changes.NotifyTruncated() } else if prevSize > 0 && prevSize == fw.Size && fw.Size <= headerSize && fi.ModTime().Sub(fw.ModTime) > logrotateTime { log.Printf("logrotateTime:%s", logrotateTime) // also capture log_header only updates changes.NotifyTruncated() } else { changes.NotifyModified() } prevSize = fw.Size } } }() return changes }