update readme, comments; gofmt

This commit is contained in:
Sridhar Ratnakumar 2013-05-29 16:32:01 -07:00
parent 4deef2319f
commit 92ad722d56
3 changed files with 26 additions and 31 deletions

View File

@ -11,23 +11,17 @@ for line := range t.Lines {
See [API documentation](http://godoc.org/github.com/ActiveState/tail). See [API documentation](http://godoc.org/github.com/ActiveState/tail).
## Log rotation
Tail comes with full support for truncation/move detection as it is
designed to work with log rotation tools.
## Installing ## Installing
go get github.com/ActiveState/tail go get github.com/ActiveState/tail/...
## Building
To build and test the package,
make test
To build the toy command-line program `gotail`,
cd cmd/gotail
make
./gotail -h
## TODO ## TODO
* Support arbitrary values for `Location` * Arbitrary values for `Location`
* `Location` could be specified in both lines and bytes metrics.

23
tail.go
View File

@ -22,7 +22,7 @@ type Line struct {
Time time.Time Time time.Time
} }
// Tail configuration // Config is used to specify how a file must be tailed.
type Config struct { type Config struct {
Location int // Tail from last N lines (tail -n) Location int // Tail from last N lines (tail -n)
Follow bool // Continue looking for new lines (tail -f) Follow bool // Continue looking for new lines (tail -f)
@ -45,10 +45,10 @@ type Tail struct {
tomb.Tomb // provides: Done, Kill, Dying tomb.Tomb // provides: Done, Kill, Dying
} }
// TailFile begins tailing the file with the specified // TailFile begins tailing the file. Output stream is made available
// configuration. Output stream is made available via the `Tail.Lines` // via the `Tail.Lines` channel. To handle errors during tailing,
// channel. To handle errors during tailing, invoke the `Wait` method // invoke the `Wait` or `Err` method after finishing reading from the
// after finishing reading from the `Lines` channel. // `Lines` channel.
func TailFile(filename string, config Config) (*Tail, error) { func TailFile(filename string, config Config) (*Tail, error) {
if !(config.Location == 0 || config.Location == -1) { if !(config.Location == 0 || config.Location == -1) {
panic("only 0/-1 values are supported for Location.") panic("only 0/-1 values are supported for Location.")
@ -154,7 +154,7 @@ func (tail *Tail) tailFileSync() {
for { for {
line, err := tail.readLine() line, err := tail.readLine()
switch(err) { switch err {
case nil: case nil:
if line != nil { if line != nil {
tail.sendLine(line) tail.sendLine(line)
@ -164,7 +164,7 @@ func (tail *Tail) tailFileSync() {
// available. Wait strategy is based on the `tail.watcher` // available. Wait strategy is based on the `tail.watcher`
// implementation (inotify or polling). // implementation (inotify or polling).
err := tail.waitForChanges() err := tail.waitForChanges()
if err != nil { if err != nil {
if err != ErrStop { if err != ErrStop {
tail.Kill(err) tail.Kill(err)
} }
@ -184,8 +184,8 @@ func (tail *Tail) tailFileSync() {
} }
// waitForChanges waits until the file has been appended, deleted, // waitForChanges waits until the file has been appended, deleted,
// moved or truncated. When moved, deleted or truncated - the file // moved or truncated. When moved or deleted - the file will be
// will be re-opened if ReOpen is true. // reopened if ReOpen is true. Truncated files are always reopened.
func (tail *Tail) waitForChanges() error { func (tail *Tail) waitForChanges() error {
if tail.changes == nil { if tail.changes == nil {
st, err := tail.file.Stat() st, err := tail.file.Stat()
@ -197,6 +197,7 @@ func (tail *Tail) waitForChanges() error {
select { select {
case <-tail.changes.Modified: case <-tail.changes.Modified:
return nil
case <-tail.changes.Deleted: case <-tail.changes.Deleted:
tail.changes = nil tail.changes = nil
if tail.ReOpen { if tail.ReOpen {
@ -216,7 +217,7 @@ func (tail *Tail) waitForChanges() error {
// Always reopen truncated files (Follow is true) // Always reopen truncated files (Follow is true)
log.Printf("Re-opening truncated file %s ...", tail.Filename) log.Printf("Re-opening truncated file %s ...", tail.Filename)
if err := tail.reopen(); err != nil { if err := tail.reopen(); err != nil {
return err return err
} }
log.Printf("Successfully reopened truncated %s", tail.Filename) log.Printf("Successfully reopened truncated %s", tail.Filename)
tail.reader = bufio.NewReader(tail.file) tail.reader = bufio.NewReader(tail.file)
@ -224,7 +225,7 @@ func (tail *Tail) waitForChanges() error {
case <-tail.Dying(): case <-tail.Dying():
return ErrStop return ErrStop
} }
return nil panic("unreachable")
} }
// sendLine sends the line(s) to Lines channel, splitting longer lines // sendLine sends the line(s) to Lines channel, splitting longer lines

View File

@ -9,13 +9,13 @@ import (
// FileWatcher monitors file-level events. // FileWatcher monitors file-level events.
type FileWatcher interface { type FileWatcher interface {
// BlockUntilExists blocks until the missing file comes into // BlockUntilExists blocks until the file comes into existence.
// existence. If the file already exists, returns immediately.
BlockUntilExists(tomb.Tomb) error BlockUntilExists(tomb.Tomb) error
// ChangeEvents returns a channel of events corresponding to the // ChangeEvents reports on changes to a file, be it modification,
// times the file is ready to be read. The channel will be closed // deletion, renames or truncations. Returned FileChanges group of
// if the file gets deleted, renamed or truncated. // channels will be closed, thus become unusable, after a deletion
// or truncation event.
ChangeEvents(tomb.Tomb, os.FileInfo) *FileChanges ChangeEvents(tomb.Tomb, os.FileInfo) *FileChanges
} }