Skip to content

Commit 27d47f3

Browse files
committed
#5 add file tailer test for multiple log files
1 parent f6cf484 commit 27d47f3

File tree

2 files changed

+78
-62
lines changed

2 files changed

+78
-62
lines changed

.travis.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,7 @@ script:
7979
./integration-test.sh ;
8080
;;
8181
*)
82-
go test -count=1 -v ./... ;
82+
go test -failfast -count=1 -v ./... ;
8383
go install ;
8484
if ! $LDD_COMMAND $GOPATH/bin/grok_exporter | grep libonig ;
8585
then

tailer/fswatcher_test.go

Lines changed: 77 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,27 @@ const tests = `
4949
- [expect, test line 4, logdir/logfile.log]
5050
- [log, test line 5, logdir/logfile.log]
5151
- [expect, test line 5, logdir/logfile.log]
52+
53+
- name: multiple logfiles
54+
commands:
55+
- [mkdir, logdir]
56+
- [log, test line 1 logfile 1, logdir/logfile1.log]
57+
- [start file tailer, readall=true, logdir/*.log]
58+
- [expect, test line 1 logfile 1, logdir/logfile1.log]
59+
- [log, test line 2 logfile 1, logdir/logfile1.log]
60+
- [log, test line 1 logfile 2, logdir/logfile2.log]
61+
- [log, test line 2 logfile 2, logdir/logfile2.log]
62+
- [expect, test line 2 logfile 1, logdir/logfile1.log]
63+
- [expect, test line 1 logfile 2, logdir/logfile2.log]
64+
- [expect, test line 2 logfile 2, logdir/logfile2.log]
65+
- [logrotate, logdir/logfile1.log, logdir/logfile1.log.1]
66+
- [logrotate, logdir/logfile2.log, logdir/logfile2.log.1]
67+
- [log, test line 3 logfile 2, logdir/logfile2.log]
68+
- [log, test line 3 logfile 1, logdir/logfile1.log]
69+
- [expect, test line 3 logfile 2, logdir/logfile2.log]
70+
- [expect, test line 3 logfile 1, logdir/logfile1.log]
71+
- [log, test line 4 logfile 2, logdir/logfile2.log]
72+
- [expect, test line 4 logfile 2, logdir/logfile2.log]
5273
`
5374

5475
// // The following test fails on Windows in tearDown() when removing logdir.
@@ -158,7 +179,6 @@ func runTest(t *testing.T, ctx *context, cmds [][]string) {
158179
for _, writer := range ctx.logFileWriters {
159180
writer.close(t, ctx)
160181
}
161-
fmt.Println()
162182
})
163183
}
164184

@@ -208,7 +228,6 @@ func setUp(t *testing.T, testName string, loggerCfg loggerConfig, tailerCfg file
208228
tailerCfg: tailerCfg,
209229
logrotateCfg: logrotateCfg,
210230
logrotateMvCfg: logrotateMvCfg,
211-
lines: make(map[string]chan string),
212231
}
213232
logger := logrus.New()
214233
logger.Level = logrus.DebugLevel
@@ -223,16 +242,16 @@ func params(ctx *context) string {
223242
}
224243

225244
type context struct {
226-
basedir string
227-
logFileWriters map[string]logFileWriter // path -> writer
228-
testName string
229-
loggerCfg loggerConfig
230-
tailerCfg fileTailerConfig
231-
logrotateCfg logrotateConfig
232-
logrotateMvCfg logrotateMoveConfig
233-
log logrus.FieldLogger
234-
tailer fswatcher.FileTailer
235-
lines map[string]chan string
245+
basedir string
246+
logFileWriters map[string]logFileWriter // path -> writer
247+
testName string
248+
loggerCfg loggerConfig
249+
tailerCfg fileTailerConfig
250+
logrotateCfg logrotateConfig
251+
logrotateMvCfg logrotateMoveConfig
252+
log logrus.FieldLogger
253+
tailer fswatcher.FileTailer
254+
linesFromTailer *linesFromTailer
236255
}
237256

238257
func exec(t *testing.T, ctx *context, cmd []string) {
@@ -493,64 +512,22 @@ func startFileTailer(t *testing.T, ctx *context, params []string) {
493512
fatalf(t, ctx, "%v", err)
494513
}
495514
tailer = BufferedTailer(tailer)
496-
497-
// We don't expect errors. However, start a go-routine listening on
498-
// the tailer's errorChannel in case something goes wrong.
499-
go func() {
500-
for {
501-
select {
502-
case line, open := <-tailer.Lines():
503-
if !open {
504-
return // tailer closed
505-
}
506-
c, ok := ctx.lines[line.File]
507-
if !ok {
508-
c = make(chan string)
509-
ctx.log.Debugf("adding lines channel for %v", line.File)
510-
ctx.lines[line.File] = c
511-
}
512-
c <- line.Line
513-
case err, open := <-tailer.Errors():
514-
if !open {
515-
return // tailer closed
516-
} else {
517-
ctx.log.Errorf("tailer failed: %v", err)
518-
t.Errorf("tailer failed: %v", err.Error()) // Cannot call fatalf(t, ctx, ) in goroutine.
519-
return
520-
}
521-
}
522-
}
523-
}()
524515
ctx.tailer = tailer
516+
ctx.linesFromTailer = makeLinesFromTailer(tailer)
525517
}
526518

527519
func expect(t *testing.T, ctx *context, line string, file string) {
528-
var (
529-
timeout = 5 * time.Second
530-
c = ctx.lines[filepath.Join(ctx.basedir, file)]
531-
)
532-
for c == nil {
533-
time.Sleep(100 * time.Millisecond)
534-
timeout = timeout - 10*time.Millisecond
535-
if timeout < 0 {
536-
fatalf(t, ctx, "timeout waiting for lines from file %q", file)
537-
return
538-
}
539-
ctx.log.Debugf("waiting for lines channel for %v", filepath.Join(ctx.basedir, file))
540-
c = ctx.lines[filepath.Join(ctx.basedir, file)]
520+
actualLine, err := ctx.linesFromTailer.nextLine(filepath.Join(ctx.basedir, file), 500*time.Millisecond)
521+
if err != nil {
522+
fatalf(t, ctx, "%v: failed to read line %q: %v", file, line, err)
541523
}
542-
select {
543-
case l := <-c:
544-
if l != line {
545-
fatalf(t, ctx, "%v: expected line %q but got line %q", file, line, l)
546-
}
547-
case <-time.After(timeout):
548-
fatalf(t, ctx, "timeout waiting for line %q from file %q", line, file)
524+
if line != actualLine {
525+
fatalf(t, ctx, "%v: expected line %q but got line %q", file, line, actualLine)
549526
}
550527
}
551528

552529
func fatalf(t *testing.T, ctx *context, format string, args ...interface{}) {
553-
ctx.log.Fatalf(format, args...)
530+
ctx.log.Errorf(format, args...) // Don't use ctx.log.Fatalf() here because this calls logger.Exit()
554531
t.Fatalf(format, args...)
555532
}
556533

@@ -883,3 +860,42 @@ func runTestShutdown(t *testing.T, mode string) {
883860
}
884861
assertGoroutinesTerminated(t, ctx, nGoroutinesBefore)
885862
}
863+
864+
func makeLinesFromTailer(tailer fswatcher.FileTailer) *linesFromTailer {
865+
return &linesFromTailer{
866+
tailer: tailer,
867+
buf: make(map[string][]string),
868+
}
869+
}
870+
871+
// Wrapper around FileTailer to get the next lines for a specific file.
872+
type linesFromTailer struct {
873+
tailer fswatcher.FileTailer
874+
buf map[string][]string
875+
}
876+
877+
// Reads the next line for a specific file.
878+
// If the tailer produces lines for other files they are buffered.
879+
// This call may take longer than timeout if we keep reading lines from another file
880+
func (l *linesFromTailer) nextLine(file string, timeout time.Duration) (string, error) {
881+
if len(l.buf[file]) > 0 {
882+
result := l.buf[file][0]
883+
l.buf[file] = l.buf[file][1:]
884+
return result, nil
885+
} else {
886+
for {
887+
select {
888+
case line := <-l.tailer.Lines():
889+
if line.File == file {
890+
return line.Line, nil
891+
} else {
892+
l.buf[line.File] = append(l.buf[line.File], line.Line)
893+
}
894+
case err := <-l.tailer.Errors():
895+
return "", err
896+
case <-time.After(5 * time.Second):
897+
return "", fmt.Errorf("timeout after %v", timeout)
898+
}
899+
}
900+
}
901+
}

0 commit comments

Comments
 (0)