MaxLineSize now splits longer lines without discarding the rest
implements http://bugs.activestate.com/show_bug.cgi?id=95745
This commit is contained in:
parent
d214cebdfd
commit
b8bf75b80a
52
tail.go
52
tail.go
|
@ -21,7 +21,7 @@ type Config struct {
|
||||||
ReOpen bool // -F
|
ReOpen bool // -F
|
||||||
MustExist bool // if false, wait for the file to exist before beginning to tail.
|
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
|
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 {
|
type Tail struct {
|
||||||
|
@ -114,18 +114,7 @@ func (tail *Tail) reopen() error {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (tail *Tail) readLine() ([]byte, error) {
|
func (tail *Tail) readLine() ([]byte, error) {
|
||||||
line, isPrefix, err := tail.reader.ReadLine()
|
line, _, 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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return line, err
|
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 {
|
for {
|
||||||
line, err := tail.readLine()
|
line, err := tail.readLine()
|
||||||
|
|
||||||
if err == nil {
|
if err == nil {
|
||||||
if line != 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 {
|
} else {
|
||||||
if err != io.EOF {
|
if err != io.EOF {
|
||||||
|
@ -192,7 +188,7 @@ func (tail *Tail) tailFileSync() {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
log.Printf("Successfully reopened %s", tail.Filename)
|
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?
|
changes = nil // XXX: how to kill changes' goroutine?
|
||||||
continue
|
continue
|
||||||
} else {
|
} else {
|
||||||
|
@ -221,3 +217,27 @@ func (tail *Tail) tailFileSync() {
|
||||||
func getCurrentTime() int64 {
|
func getCurrentTime() int64 {
|
||||||
return time.Now().UTC().Unix()
|
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
|
||||||
|
}
|
||||||
|
|
20
tail_test.go
20
tail_test.go
|
@ -1,3 +1,6 @@
|
||||||
|
// TODO:
|
||||||
|
// * repeat all the tests with Poll:true
|
||||||
|
|
||||||
package tail
|
package tail
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
@ -33,6 +36,19 @@ func TestMustExist(t *testing.T) {
|
||||||
tail.Stop()
|
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) {
|
func TestLocationFull(t *testing.T) {
|
||||||
fix := NewFixture("location-full", t)
|
fix := NewFixture("location-full", t)
|
||||||
fix.CreateFile("test.txt", "hello\nworld\n")
|
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
|
// Delete after a reasonable delay, to give tail sufficient time
|
||||||
// to read all lines.
|
// to read all lines.
|
||||||
<-time.After(100 * time.Millisecond)
|
<-time.After(100 * time.Millisecond)
|
||||||
fix.RemoveFile("test.txt") // TODO
|
fix.RemoveFile("test.txt")
|
||||||
|
|
||||||
tail.Stop()
|
tail.Stop()
|
||||||
}
|
}
|
||||||
|
@ -153,7 +169,7 @@ func (fix Fixture) VerifyTail(tail *Tail, lines []string) {
|
||||||
for idx, line := range lines {
|
for idx, line := range lines {
|
||||||
tailedLine, ok := <-tail.Lines
|
tailedLine, ok := <-tail.Lines
|
||||||
if !ok {
|
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 {
|
if tailedLine == nil {
|
||||||
fix.t.Fatalf("tail.Lines returned nil; not possible")
|
fix.t.Fatalf("tail.Lines returned nil; not possible")
|
||||||
|
|
Loading…
Reference in New Issue