diff --git a/common/os.go b/common/os.go index 53febecbe..58222c995 100644 --- a/common/os.go +++ b/common/os.go @@ -5,6 +5,8 @@ import ( "io/ioutil" "os" "os/signal" + "sync" + "time" ) func TrapSignal(cb func()) { @@ -96,3 +98,101 @@ func WriteFileAtomic(filePath string, newBytes []byte) error { err = os.Rename(filePath+".new", filePath) return err } + +//-------------------------------------------------------------------------------- + +/* 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 + +// Automatically closes and re-opens file for writing. +// This is useful for using a log file with the logrotate tool. +type AutoFile struct { + Path string + ticker *time.Ticker + mtx sync.Mutex + file *os.File +} + +func OpenAutoFile(path string) (af *AutoFile, err error) { + af = &AutoFile{ + Path: path, + ticker: time.NewTicker(autoFileOpenDuration), + } + if err = af.openFile(); err != nil { + return + } + go af.processTicks() + return +} + +func (af *AutoFile) Close() error { + af.ticker.Stop() + af.mtx.Lock() + err := af.closeFile() + af.mtx.Unlock() + return err +} + +func (af *AutoFile) processTicks() { + for { + _, ok := <-af.ticker.C + if !ok { + return // Done. + } + fmt.Println("closeFile()") + af.mtx.Lock() + af.closeFile() + af.mtx.Unlock() + } +} + +func (af *AutoFile) closeFile() (err error) { + 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 + } + } + fmt.Println("Write:", string(b)) + return af.file.Write(b) +} + +func (af *AutoFile) openFile() error { + file, err := os.OpenFile(af.Path, os.O_RDWR|os.O_CREATE|os.O_APPEND, 0600) + if err != nil { + return err + } + af.file = file + return nil +} diff --git a/process/process.go b/process/process.go index 308c11a60..0d297432a 100644 --- a/process/process.go +++ b/process/process.go @@ -8,6 +8,8 @@ import ( "os" "os/exec" "time" + + . "github.com/tendermint/tendermint/common" ) type Process struct { @@ -20,7 +22,7 @@ type Process struct { OutputPath string Cmd *exec.Cmd `json:"-"` ExitState *os.ProcessState `json:"-"` - OutputFile *os.File `json:"-"` + OutputFile *AutoFile `json:"-"` WaitCh chan struct{} `json:"-"` } @@ -32,7 +34,7 @@ const ( // execPath: command name // args: args to command. (should not include name) func Create(mode int, label string, execPath string, args []string, input string, outPath string) (*Process, error) { - outFile, err := os.OpenFile(outPath, os.O_RDWR|os.O_CREATE|os.O_APPEND, 0600) + outFile, err := OpenAutoFile(outPath) if err != nil { return nil, err }