package autofile import ( "os" "sync" "time" cmn "github.com/tendermint/tendermint/libs/common" "github.com/tendermint/tendermint/libs/errors" ) /* AutoFile usage // Create/Append to ./autofile_test af, err := OpenAutoFile("autofile_test") if err != nil { panic(err) } // Stream of writes. // During this time, the file may be moved e.g. by logRotate. for i := 0; i < 60; i++ { af.Write([]byte(Fmt("LOOP(%v)", i))) time.Sleep(time.Second) } // Close the AutoFile err = af.Close() if err != nil { panic(err) } */ const ( autoFileOpenDuration = 1000 * time.Millisecond autoFilePerms = os.FileMode(0600) ) // Automatically closes and re-opens file for writing. // This is useful for using a log file with the logrotate tool. type AutoFile struct { ID string Path string ticker *time.Ticker tickerStopped chan struct{} // closed when ticker is stopped mtx sync.Mutex file *os.File } func OpenAutoFile(path string) (af *AutoFile, err error) { af = &AutoFile{ ID: cmn.RandStr(12) + ":" + path, Path: path, ticker: time.NewTicker(autoFileOpenDuration), tickerStopped: make(chan struct{}), } if err = af.openFile(); err != nil { return } go af.processTicks() sighupWatchers.addAutoFile(af) return } func (af *AutoFile) Close() error { af.ticker.Stop() close(af.tickerStopped) err := af.closeFile() sighupWatchers.removeAutoFile(af) return err } func (af *AutoFile) processTicks() { for { select { case <-af.ticker.C: af.closeFile() case <-af.tickerStopped: return } } } func (af *AutoFile) closeFile() (err error) { af.mtx.Lock() defer af.mtx.Unlock() file := af.file if file == nil { return nil } af.file = nil return file.Close() } func (af *AutoFile) Write(b []byte) (n int, err error) { af.mtx.Lock() defer af.mtx.Unlock() if af.file == nil { if err = af.openFile(); err != nil { return } } n, err = af.file.Write(b) return } func (af *AutoFile) Sync() error { af.mtx.Lock() defer af.mtx.Unlock() if af.file == nil { if err := af.openFile(); err != nil { return err } } return af.file.Sync() } func (af *AutoFile) openFile() error { file, err := os.OpenFile(af.Path, os.O_RDWR|os.O_CREATE|os.O_APPEND, autoFilePerms) if err != nil { return err } fileInfo, err := file.Stat() if err != nil { return err } if fileInfo.Mode() != autoFilePerms { return errors.NewErrPermissionsChanged(file.Name(), fileInfo.Mode(), autoFilePerms) } af.file = file return nil } func (af *AutoFile) Size() (int64, error) { af.mtx.Lock() defer af.mtx.Unlock() if af.file == nil { err := af.openFile() if err != nil { if err == os.ErrNotExist { return 0, nil } return -1, err } } stat, err := af.file.Stat() if err != nil { return -1, err } return stat.Size(), nil }