Skip to content

Commit fb5fb83

Browse files
committed
Merge pull request #1292 from timstclair/tail
Fixes for log tailing
2 parents d4bed01 + 2f0af7b commit fb5fb83

File tree

1 file changed

+30
-31
lines changed

1 file changed

+30
-31
lines changed

utils/tail/tail.go

+30-31
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ import (
2020
"fmt"
2121
"io"
2222
"os"
23+
"path/filepath"
2324
"sync"
2425
"time"
2526

@@ -37,8 +38,10 @@ type Tail struct {
3738
watcher *inotify.Watcher
3839
}
3940

40-
const retryOpenInterval = time.Second
41-
const maxOpenAttempts = 3
41+
const (
42+
defaultRetryInterval = 100 * time.Millisecond
43+
maxRetryInterval = 30 * time.Second
44+
)
4245

4346
// NewTail starts opens the given file and watches it for deletion/rotation
4447
func NewTail(filename string) (*Tail, error) {
@@ -72,31 +75,15 @@ func (t *Tail) Close() {
7275
close(t.stop)
7376
}
7477

75-
func isEvent(event *inotify.Event, flag uint32) bool {
76-
return event.Mask&flag == flag
77-
}
78-
79-
func (t *Tail) fileChanged() error {
80-
for {
81-
select {
82-
case event := <-t.watcher.Event:
83-
// We don't get IN_DELETE because we are holding the file open
84-
if isEvent(event, inotify.IN_ATTRIB) || isEvent(event, inotify.IN_MOVE_SELF) {
85-
return nil
86-
}
87-
case <-t.stop:
88-
return fmt.Errorf("watch was cancelled")
89-
}
90-
}
91-
}
92-
9378
func (t *Tail) attemptOpen() error {
9479
t.readerLock.Lock()
9580
defer t.readerLock.Unlock()
9681
t.reader = nil
9782
t.readerErr = nil
98-
for attempt := 1; attempt <= maxOpenAttempts; attempt++ {
99-
glog.V(4).Infof("Opening %s (attempt %d of %d)", t.filename, attempt, maxOpenAttempts)
83+
attempt := 0
84+
for interval := defaultRetryInterval; ; interval *= 2 {
85+
attempt++
86+
glog.V(4).Infof("Opening %s (attempt %d)", t.filename, attempt)
10087
var err error
10188
t.file, err = os.Open(t.filename)
10289
if err == nil {
@@ -105,8 +92,11 @@ func (t *Tail) attemptOpen() error {
10592
t.reader = bufio.NewReader(t.file)
10693
return nil
10794
}
95+
if interval >= maxRetryInterval {
96+
break
97+
}
10898
select {
109-
case <-time.After(retryOpenInterval):
99+
case <-time.After(interval):
110100
case <-t.stop:
111101
t.readerErr = io.EOF
112102
return fmt.Errorf("watch was cancelled")
@@ -133,15 +123,24 @@ func (t *Tail) watchFile() error {
133123
return err
134124
}
135125
defer t.file.Close()
136-
err = t.watcher.Watch(t.filename)
126+
127+
watchDir := filepath.Dir(t.filename)
128+
err = t.watcher.AddWatch(watchDir, inotify.IN_MOVED_FROM|inotify.IN_DELETE)
137129
if err != nil {
138-
return err
130+
return fmt.Errorf("Failed to add watch to directory %s: %v", watchDir, err)
139131
}
140-
defer t.watcher.RemoveWatch(t.filename)
141-
err = t.fileChanged()
142-
if err != nil {
143-
return err
132+
defer t.watcher.RemoveWatch(watchDir)
133+
134+
for {
135+
select {
136+
case event := <-t.watcher.Event:
137+
eventPath := filepath.Clean(event.Name) // Directory events have an extra '/'
138+
if eventPath == t.filename {
139+
glog.V(4).Infof("Log file %s moved/deleted", t.filename)
140+
return nil
141+
}
142+
case <-t.stop:
143+
return fmt.Errorf("watch was cancelled")
144+
}
144145
}
145-
glog.V(4).Infof("Log file %s moved/deleted", t.filename)
146-
return nil
147146
}

0 commit comments

Comments
 (0)