MaxLineSize now splits longer lines without discarding the rest

implements http://bugs.activestate.com/show_bug.cgi?id=95745
This commit is contained in:
Sridhar Ratnakumar 2012-10-13 12:50:27 -07:00
parent d214cebdfd
commit b8bf75b80a
2 changed files with 54 additions and 18 deletions

52
tail.go
View File

@ -21,7 +21,7 @@ type Config struct {
ReOpen bool // -F
MustExist bool // if false, wait for the file to exist before beginning to tail.
Poll bool // if true, do not use inotify but use polling
MaxLineSize int // if > 0, limit the line size (discarding the rest)
MaxLineSize int // if > 0, limit the line size (rest of the line would be returned as next lines)
}
type Tail struct {
@ -114,18 +114,7 @@ func (tail *Tail) reopen() error {
}
func (tail *Tail) readLine() ([]byte, error) {
line, isPrefix, err := tail.reader.ReadLine()
if isPrefix && err == nil {
// line is longer than what we can accept.
// ignore the rest of this line.
for {
_, isPrefix, err := tail.reader.ReadLine()
if !isPrefix || err != nil {
return line, err
}
}
}
line, _, err := tail.reader.ReadLine()
return line, err
}
@ -154,14 +143,21 @@ func (tail *Tail) tailFileSync() {
}
}
tail.reader = bufio.NewReaderSize(tail.file, tail.MaxLineSize)
tail.reader = bufio.NewReader(tail.file)
for {
line, err := tail.readLine()
if err == nil {
if line != nil {
tail.Lines <- &Line{string(line), getCurrentTime()}
now := getCurrentTime()
if tail.MaxLineSize > 0 && len(line) > tail.MaxLineSize {
for _, line := range partitionString(string(line), tail.MaxLineSize) {
tail.Lines <- &Line{line, now}
}
}else{
tail.Lines <- &Line{string(line), now}
}
}
} else {
if err != io.EOF {
@ -192,7 +188,7 @@ func (tail *Tail) tailFileSync() {
return
}
log.Printf("Successfully reopened %s", tail.Filename)
tail.reader = bufio.NewReaderSize(tail.file, tail.MaxLineSize)
tail.reader = bufio.NewReader(tail.file)
changes = nil // XXX: how to kill changes' goroutine?
continue
} else {
@ -221,3 +217,27 @@ func (tail *Tail) tailFileSync() {
func getCurrentTime() int64 {
return time.Now().UTC().Unix()
}
// partitionString partitions the string into chunks of given size,
// with the last chunk of variable size.
func partitionString(s string, chunkSize int) []string {
if chunkSize <= 0 {
panic("invalid chunkSize")
}
length := len(s)
chunks := 1 + length/chunkSize
start := 0
end := chunkSize
parts := make([]string, 0, chunks)
for {
if end > length {
end = length
}
parts = append(parts, s[start:end])
if end == length {
break
}
start, end = end, end+chunkSize
}
return parts
}

View File

@ -1,3 +1,6 @@
// TODO:
// * repeat all the tests with Poll:true
package tail
import (
@ -33,6 +36,19 @@ func TestMustExist(t *testing.T) {
tail.Stop()
}
func TestMaxLineSize(t *testing.T) {
fix := NewFixture("maxlinesize", t)
fix.CreateFile("test.txt", "hello\nworld\n")
tail := fix.StartTail("test.txt", Config{Follow: true, Location: -1, MaxLineSize: 3})
go fix.VerifyTail(tail, []string{"hel", "lo", "wor", "ld"})
// Delete after a reasonable delay, to give tail sufficient time
// to read all lines.
<-time.After(100 * time.Millisecond)
fix.RemoveFile("test.txt")
tail.Stop()
}
func TestLocationFull(t *testing.T) {
fix := NewFixture("location-full", t)
fix.CreateFile("test.txt", "hello\nworld\n")
@ -83,7 +99,7 @@ func TestReOpen(t *testing.T) {
// Delete after a reasonable delay, to give tail sufficient time
// to read all lines.
<-time.After(100 * time.Millisecond)
fix.RemoveFile("test.txt") // TODO
fix.RemoveFile("test.txt")
tail.Stop()
}
@ -153,7 +169,7 @@ func (fix Fixture) VerifyTail(tail *Tail, lines []string) {
for idx, line := range lines {
tailedLine, ok := <-tail.Lines
if !ok {
fix.t.Fatalf("insufficient lines from tail; expecting %v", lines[idx+1:])
fix.t.Fatalf("tail ended early; expecting more: %v", lines[idx:])
}
if tailedLine == nil {
fix.t.Fatalf("tail.Lines returned nil; not possible")