Merge branch 'stop-at-eof' of https://github.com/flynn/tail into flynn-stop-at-eof

This commit is contained in:
Nino Khodabandeh 2016-04-04 12:56:46 -07:00
commit 468784e1d4
5 changed files with 33 additions and 11 deletions

16
tail.go
View File

@ -4,6 +4,7 @@ package tail
import ( import (
"bufio" "bufio"
"errors"
"fmt" "fmt"
"io" "io"
"io/ioutil" "io/ioutil"
@ -156,6 +157,14 @@ func (tail *Tail) Stop() error {
return tail.Wait() return tail.Wait()
} }
// StopAtEOF stops tailing as soon as the end of the file is reached.
func (tail *Tail) StopAtEOF() error {
tail.Kill(errStopAtEOF)
return tail.Wait()
}
var errStopAtEOF = errors.New("tail: stop at eof")
func (tail *Tail) close() { func (tail *Tail) close() {
close(tail.Lines) close(tail.Lines)
tail.colseFile() tail.colseFile()
@ -236,6 +245,7 @@ func (tail *Tail) tailFileSync() {
var offset int64 = 0 var offset int64 = 0
var err error var err error
// Read line by line. // Read line by line.
for { for {
// do not seek in named pipes // do not seek in named pipes
@ -265,8 +275,7 @@ func (tail *Tail) tailFileSync() {
case <-tail.Dying(): case <-tail.Dying():
return return
} }
err = tail.seekEnd() if err := tail.seekEnd(); err != nil {
if err != nil {
tail.Kill(err) tail.Kill(err)
return return
} }
@ -307,6 +316,9 @@ func (tail *Tail) tailFileSync() {
select { select {
case <-tail.Dying(): case <-tail.Dying():
if tail.Err() == errStopAtEOF {
continue
}
return return
default: default:
} }

View File

@ -55,6 +55,25 @@ func TestStop(t *testing.T) {
tail.Cleanup() tail.Cleanup()
} }
func TestStopAtEOF(_t *testing.T) {
t := NewTailTest("maxlinesize", _t)
t.CreateFile("test.txt", "hello\nthere\nworld\n")
tail := t.StartTail("test.txt", Config{Follow: true, Location: nil})
// read "hello"
<-tail.Lines
done := make(chan struct{})
go func() {
<-time.After(100 * time.Millisecond)
t.VerifyTailOutput(tail, []string{"there", "world"})
close(done)
}()
tail.StopAtEOF()
<-done
tail.Cleanup()
}
func MaxLineSizeT(_t *testing.T, follow bool, fileContent string, expected []string) { func MaxLineSizeT(_t *testing.T, follow bool, fileContent string, expected []string) {
t := NewTailTest("maxlinesize", _t) t := NewTailTest("maxlinesize", _t)
t.CreateFile("test.txt", fileContent) t.CreateFile("test.txt", fileContent)

View File

@ -23,12 +23,6 @@ func (fc *FileChanges) NotifyDeleted() {
sendOnlyIfEmpty(fc.Deleted) sendOnlyIfEmpty(fc.Deleted)
} }
func (fc *FileChanges) Close() {
close(fc.Modified)
close(fc.Truncated)
close(fc.Deleted)
}
// sendOnlyIfEmpty sends on a bool channel only if the channel has no // sendOnlyIfEmpty sends on a bool channel only if the channel has no
// backlog to be read by other goroutines. This concurrency pattern // backlog to be read by other goroutines. This concurrency pattern
// can be used to notify other goroutines if and only if they are // can be used to notify other goroutines if and only if they are

View File

@ -66,7 +66,6 @@ func (fw *InotifyFileWatcher) ChangeEvents(t *tomb.Tomb, pos int64) (*FileChange
go func() { go func() {
defer RemoveWatch(fw.Filename) defer RemoveWatch(fw.Filename)
defer changes.Close()
events := Events(fw.Filename) events := Events(fw.Filename)

View File

@ -56,8 +56,6 @@ func (fw *PollingFileWatcher) ChangeEvents(t *tomb.Tomb, pos int64) (*FileChange
fw.Size = pos fw.Size = pos
go func() { go func() {
defer changes.Close()
prevSize := fw.Size prevSize := fw.Size
for { for {
select { select {