Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

StopAtEOF: keep sending lines until EOF #1

Merged
merged 1 commit into from
Jul 23, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 19 additions & 6 deletions tail.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,11 @@
// Copyright (c) 2015 HPE Software Inc. All rights reserved.
// Copyright (c) 2013 ActiveState Software Inc. All rights reserved.

//nxadm/tail provides a Go library that emulates the features of the BSD `tail`
//program. The library comes with full support for truncation/move detection as
//it is designed to work with log rotation tools. The library works on all
//operating systems supported by Go, including POSIX systems like Linux and
//*BSD, and MS Windows. Go 1.9 is the oldest compiler release supported.
// nxadm/tail provides a Go library that emulates the features of the BSD `tail`
// program. The library comes with full support for truncation/move detection as
// it is designed to work with log rotation tools. The library works on all
// operating systems supported by Go, including POSIX systems like Linux and
// *BSD, and MS Windows. Go 1.9 is the oldest compiler release supported.
package tail

import (
Expand Down Expand Up @@ -450,12 +450,25 @@ func (tail *Tail) sendLine(line string) bool {
lines = util.PartitionString(line, tail.MaxLineSize)
}

// This is a bit weird here, when a users requests stopAtEof we
// must keep sending all lines however <-tail.Dying() will return
// immediately at this point so the select below may not have
// chance to send the line if the reader side has is not yet ready.
// But if StopAtEOF was not set and it is a "normal" Kill then we
// should exit right away still thus the special logic here.
earlyExitChan := tail.Dying()
if tail.Err() == errStopAtEOF {
// Note that receive from a nil channel blocks forever so
// below we know it can only take the tail.Lines case.
earlyExitChan = nil
}

for _, line := range lines {
tail.lineNum++
offset, _ := tail.Tell()
select {
case tail.Lines <- &Line{line, tail.lineNum, SeekInfo{Offset: offset}, now, nil}:
case <-tail.Dying():
case <-earlyExitChan:
return true
}
}
Expand Down
32 changes: 30 additions & 2 deletions tail_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -622,6 +622,26 @@ func TestIncompleteLinesWithoutFollow(t *testing.T) {
tail.Cleanup()
}

func TestFollowUntilEof(t *testing.T) {
tailTest, cleanup := NewTailTest("incomplete-lines-no-follow", t)
defer cleanup()
filename := "test.txt"
config := Config{
Follow: false,
}
tailTest.CreateFile(filename, "hello\nworld\n")
tail := tailTest.StartTail(filename, config)

// StopAtEOF blocks until the read is done and in order to do so
// we have to drain the lines channel first which ReadLinesWithError does.
go tail.StopAtEOF()
tailTest.ReadLinesWithError(tail, []string{"hello", "world"}, false, errStopAtEOF)

tailTest.RemoveFile(filename)
tail.Stop()
tail.Cleanup()
}

func reSeek(t *testing.T, poll bool) {
var name string
if poll {
Expand Down Expand Up @@ -765,6 +785,14 @@ func (t TailTest) VerifyTailOutputUsingCursor(tail *Tail, lines []string, expect
}

func (t TailTest) ReadLines(tail *Tail, lines []string, useCursor bool) {
t.readLines(tail, lines, useCursor, nil)
}

func (t TailTest) ReadLinesWithError(tail *Tail, lines []string, useCursor bool, err error) {
t.readLines(tail, lines, useCursor, err)
}

func (t TailTest) readLines(tail *Tail, lines []string, useCursor bool, expectErr error) {
cursor := 1

for _, line := range lines {
Expand All @@ -773,8 +801,8 @@ func (t TailTest) ReadLines(tail *Tail, lines []string, useCursor bool) {
if !ok {
// tail.Lines is closed and empty.
err := tail.Err()
if err != nil {
t.Fatalf("tail ended with error: %v", err)
if err != expectErr {
t.Fatalf("tail ended with unexpected error: %v", err)
}
t.Fatalf("tail ended early; expecting more: %v", lines[cursor:])
}
Expand Down