Merge pull request #30 from ActiveState/odd_line_splitting
Fix odd line splitting
This commit is contained in:
commit
c5d5673bde
|
@ -12,7 +12,9 @@ import (
|
|||
func args2config() (tail.Config, int64) {
|
||||
config := tail.Config{Follow: true}
|
||||
n := int64(0)
|
||||
maxlinesize := int(0)
|
||||
flag.Int64Var(&n, "n", 0, "tail from the last Nth location")
|
||||
flag.IntVar(&maxlinesize, "max", 0, "max line size")
|
||||
flag.BoolVar(&config.Follow, "f", false, "wait for additional data to be appended to the file")
|
||||
flag.BoolVar(&config.ReOpen, "F", false, "follow, and track file rename/rotation")
|
||||
flag.BoolVar(&config.Poll, "p", false, "use polling, instead of inotify")
|
||||
|
@ -20,6 +22,7 @@ func args2config() (tail.Config, int64) {
|
|||
if config.ReOpen {
|
||||
config.Follow = true
|
||||
}
|
||||
config.MaxLineSize = maxlinesize
|
||||
return config, n
|
||||
}
|
||||
|
||||
|
|
83
tail.go
83
tail.go
|
@ -13,6 +13,7 @@ import (
|
|||
"launchpad.net/tomb"
|
||||
"log"
|
||||
"os"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
|
@ -167,18 +168,18 @@ func (tail *Tail) reopen() error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func (tail *Tail) readLine() ([]byte, error) {
|
||||
line, isPrefix, err := tail.reader.ReadLine()
|
||||
if !isPrefix || tail.MaxLineSize > 0 {
|
||||
func (tail *Tail) readLine() (string, error) {
|
||||
line, err := tail.reader.ReadString('\n')
|
||||
if err != nil {
|
||||
// Note ReadString "returns the data read before the error" in
|
||||
// case of an error, including EOF, so we return it as is. The
|
||||
// caller is expected to process it if err is EOF.
|
||||
return line, err
|
||||
}
|
||||
|
||||
buf := append([]byte(nil), line...)
|
||||
for isPrefix && err == nil {
|
||||
line, isPrefix, err = tail.reader.ReadLine()
|
||||
buf = append(buf, line...)
|
||||
}
|
||||
return buf, err
|
||||
line = strings.TrimRight(line, "\n")
|
||||
|
||||
return line, err
|
||||
}
|
||||
|
||||
func (tail *Tail) tailFileSync() {
|
||||
|
@ -206,36 +207,34 @@ func (tail *Tail) tailFileSync() {
|
|||
}
|
||||
}
|
||||
|
||||
tail.reader = tail.newReader()
|
||||
tail.openReader()
|
||||
|
||||
// Read line by line.
|
||||
for {
|
||||
line, err := tail.readLine()
|
||||
|
||||
switch err {
|
||||
case nil:
|
||||
if line != nil {
|
||||
cooloff := !tail.sendLine(line)
|
||||
if cooloff {
|
||||
// Wait a second before seeking till the end of
|
||||
// file when rate limit is reached.
|
||||
msg := fmt.Sprintf(
|
||||
"Too much log activity; waiting a second " +
|
||||
"before resuming tailing")
|
||||
tail.Lines <- &Line{msg, time.Now(), fmt.Errorf(msg)}
|
||||
select {
|
||||
case <-time.After(time.Second):
|
||||
case <-tail.Dying():
|
||||
return
|
||||
}
|
||||
err = tail.seekEnd()
|
||||
if err != nil {
|
||||
tail.Kill(err)
|
||||
return
|
||||
}
|
||||
// Process `line` even if err is EOF.
|
||||
if err == nil || (err == io.EOF && line != "") {
|
||||
cooloff := !tail.sendLine(line)
|
||||
if cooloff {
|
||||
// Wait a second before seeking till the end of
|
||||
// file when rate limit is reached.
|
||||
msg := fmt.Sprintf(
|
||||
"Too much log activity; waiting a second " +
|
||||
"before resuming tailing")
|
||||
tail.Lines <- &Line{msg, time.Now(), fmt.Errorf(msg)}
|
||||
select {
|
||||
case <-time.After(time.Second):
|
||||
case <-tail.Dying():
|
||||
return
|
||||
}
|
||||
err = tail.seekEnd()
|
||||
if err != nil {
|
||||
tail.Kill(err)
|
||||
return
|
||||
}
|
||||
}
|
||||
case io.EOF:
|
||||
} else if err == io.EOF {
|
||||
if !tail.Follow {
|
||||
return
|
||||
}
|
||||
|
@ -249,7 +248,8 @@ func (tail *Tail) tailFileSync() {
|
|||
}
|
||||
return
|
||||
}
|
||||
default: // non-EOF error
|
||||
} else {
|
||||
// non-EOF error
|
||||
tail.Killf("Error reading %s: %s", tail.Filename, err)
|
||||
return
|
||||
}
|
||||
|
@ -286,7 +286,7 @@ func (tail *Tail) waitForChanges() error {
|
|||
return err
|
||||
}
|
||||
tail.Logger.Printf("Successfully reopened %s", tail.Filename)
|
||||
tail.reader = tail.newReader()
|
||||
tail.openReader()
|
||||
return nil
|
||||
} else {
|
||||
tail.Logger.Printf("Stopping tail as file no longer exists: %s", tail.Filename)
|
||||
|
@ -299,7 +299,7 @@ func (tail *Tail) waitForChanges() error {
|
|||
return err
|
||||
}
|
||||
tail.Logger.Printf("Successfully reopened truncated %s", tail.Filename)
|
||||
tail.reader = tail.newReader()
|
||||
tail.openReader()
|
||||
return nil
|
||||
case <-tail.Dying():
|
||||
return ErrStop
|
||||
|
@ -307,12 +307,12 @@ func (tail *Tail) waitForChanges() error {
|
|||
panic("unreachable")
|
||||
}
|
||||
|
||||
func (tail *Tail) newReader() *bufio.Reader {
|
||||
func (tail *Tail) openReader() {
|
||||
if tail.MaxLineSize > 0 {
|
||||
// add 2 to account for newline characters
|
||||
return bufio.NewReaderSize(tail.file, tail.MaxLineSize+2)
|
||||
tail.reader = bufio.NewReaderSize(tail.file, tail.MaxLineSize+2)
|
||||
} else {
|
||||
return bufio.NewReader(tail.file)
|
||||
tail.reader = bufio.NewReader(tail.file)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -328,14 +328,13 @@ func (tail *Tail) seekEnd() error {
|
|||
|
||||
// sendLine sends the line(s) to Lines channel, splitting longer lines
|
||||
// if necessary. Return false if rate limit is reached.
|
||||
func (tail *Tail) sendLine(line []byte) bool {
|
||||
func (tail *Tail) sendLine(line string) bool {
|
||||
now := time.Now()
|
||||
lines := []string{string(line)}
|
||||
lines := []string{line}
|
||||
|
||||
// Split longer lines
|
||||
if tail.MaxLineSize > 0 && len(line) > tail.MaxLineSize {
|
||||
lines = util.PartitionString(
|
||||
string(line), tail.MaxLineSize)
|
||||
lines = util.PartitionString(line, tail.MaxLineSize)
|
||||
}
|
||||
|
||||
for _, line := range lines {
|
||||
|
|
|
@ -85,7 +85,7 @@ func TestOver4096ByteLine(_t *testing.T) {
|
|||
func TestOver4096ByteLineWithSetMaxLineSize(_t *testing.T) {
|
||||
t := NewTailTest("Over4096ByteLineMaxLineSize", _t)
|
||||
testString := strings.Repeat("a", 4097)
|
||||
t.CreateFile("test.txt", "test\r\n"+testString+"\r\nhello\r\nworld\r\n")
|
||||
t.CreateFile("test.txt", "test\n"+testString+"\nhello\nworld\n")
|
||||
tail := t.StartTail("test.txt", Config{Follow: true, Location: nil, MaxLineSize: 4097})
|
||||
go t.VerifyTailOutput(tail, []string{"test", testString, "hello", "world"})
|
||||
|
||||
|
@ -93,7 +93,7 @@ func TestOver4096ByteLineWithSetMaxLineSize(_t *testing.T) {
|
|||
// to read all lines.
|
||||
<-time.After(100 * time.Millisecond)
|
||||
t.RemoveFile("test.txt")
|
||||
tail.Stop()
|
||||
// tail.Stop()
|
||||
Cleanup()
|
||||
}
|
||||
|
||||
|
@ -415,7 +415,9 @@ func (t TailTest) VerifyTailOutput(tail *Tail, lines []string) {
|
|||
// Note: not checking .Err as the `lines` argument is designed
|
||||
// to match error strings as well.
|
||||
if tailedLine.Text != line {
|
||||
t.Fatalf("unexpected line/err from tail: expecting ```%s```, but got ```%s```",
|
||||
t.Fatalf(
|
||||
"unexpected line/err from tail: "+
|
||||
"expecting <<%s>>>, but got <<<%s>>>",
|
||||
line, tailedLine.Text)
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue