diff --git a/tail.go b/tail.go index 77020de..fbf3129 100644 --- a/tail.go +++ b/tail.go @@ -8,6 +8,7 @@ import ( "github.com/ActiveState/tail/util" "github.com/ActiveState/tail/watch" "io" + "io/ioutil" "launchpad.net/tomb" "log" "os" @@ -47,6 +48,10 @@ type Config struct { // Generic IO Follow bool // Continue looking for new lines (tail -f) MaxLineSize int // If non-zero, split longer lines into multiple lines + + // Logger, when nil, is set to tail.DefaultLogger + // To disable logging: set field to tail.DiscardingLogger + Logger *log.Logger } type Tail struct { @@ -63,6 +68,13 @@ type Tail struct { tomb.Tomb // provides: Done, Kill, Dying } +var ( + // DefaultLogger is used when Config.Logger == nil + DefaultLogger = log.New(os.Stderr, "", log.LstdFlags) + // DiscardingLogger can be used to disable logging output + DiscardingLogger = log.New(ioutil.Discard, "", 0) +) + // 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 @@ -75,7 +87,13 @@ func TailFile(filename string, config Config) (*Tail, error) { t := &Tail{ Filename: filename, Lines: make(chan *Line), - Config: config} + Config: config, + } + + // when Logger was not specified in config, use default logger + if t.Logger == nil { + t.Logger = log.New(os.Stderr, "", log.LstdFlags) + } t.rateMon = new(RateMonitor) @@ -135,7 +153,7 @@ func (tail *Tail) reopen() error { tail.file, err = os.Open(tail.Filename) if err != nil { if os.IsNotExist(err) { - log.Printf("Waiting for %s to appear...", tail.Filename) + tail.Logger.Printf("Waiting for %s to appear...", tail.Filename) if err := tail.watcher.BlockUntilExists(&tail.Tomb); err != nil { if err == tomb.ErrDying { return err @@ -174,7 +192,7 @@ func (tail *Tail) tailFileSync() { // Seek to requested location on first open of the file. if tail.Location != nil { _, err := tail.file.Seek(tail.Location.Offset, tail.Location.Whence) - // log.Printf("Seeked %s - %+v\n", tail.Filename, tail.Location) + tail.Logger.Printf("Seeked %s - %+v\n", tail.Filename, tail.Location) if err != nil { tail.Killf("Seek error on %s: %s", tail.Filename, err) return @@ -257,24 +275,24 @@ func (tail *Tail) waitForChanges() error { tail.changes = nil if tail.ReOpen { // XXX: we must not log from a library. - log.Printf("Re-opening moved/deleted file %s ...", tail.Filename) + tail.Logger.Printf("Re-opening moved/deleted file %s ...", tail.Filename) if err := tail.reopen(); err != nil { return err } - log.Printf("Successfully reopened %s", tail.Filename) + tail.Logger.Printf("Successfully reopened %s", tail.Filename) tail.reader = bufio.NewReader(tail.file) return nil } else { - log.Printf("Stopping tail as file no longer exists: %s", tail.Filename) + tail.Logger.Printf("Stopping tail as file no longer exists: %s", tail.Filename) return ErrStop } case <-tail.changes.Truncated: // Always reopen truncated files (Follow is true) - log.Printf("Re-opening truncated file %s ...", tail.Filename) + tail.Logger.Printf("Re-opening truncated file %s ...", tail.Filename) if err := tail.reopen(); err != nil { return err } - log.Printf("Successfully reopened truncated %s", tail.Filename) + tail.Logger.Printf("Successfully reopened truncated %s", tail.Filename) tail.reader = bufio.NewReader(tail.file) return nil case <-tail.Dying(): @@ -300,7 +318,7 @@ func (tail *Tail) sendLine(line []byte) bool { tail.Lines <- &Line{line, now, nil} rate := tail.rateMon.Tick(nowUnix) if tail.LimitRate > 0 && rate > tail.LimitRate { - log.Printf("Rate limit (%v < %v) reached on file (%v); entering 1s cooloff period.\n", + tail.Logger.Printf("Rate limit (%v < %v) reached on file (%v); entering 1s cooloff period.\n", tail.LimitRate, rate, tail.Filename)