@@ -20,6 +20,7 @@ import (
20
20
"fmt"
21
21
"io"
22
22
"os"
23
+ "path/filepath"
23
24
"sync"
24
25
"time"
25
26
@@ -37,8 +38,10 @@ type Tail struct {
37
38
watcher * inotify.Watcher
38
39
}
39
40
40
- const retryOpenInterval = time .Second
41
- const maxOpenAttempts = 3
41
+ const (
42
+ defaultRetryInterval = 100 * time .Millisecond
43
+ maxRetryInterval = 30 * time .Second
44
+ )
42
45
43
46
// NewTail starts opens the given file and watches it for deletion/rotation
44
47
func NewTail (filename string ) (* Tail , error ) {
@@ -72,31 +75,15 @@ func (t *Tail) Close() {
72
75
close (t .stop )
73
76
}
74
77
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
-
93
78
func (t * Tail ) attemptOpen () error {
94
79
t .readerLock .Lock ()
95
80
defer t .readerLock .Unlock ()
96
81
t .reader = nil
97
82
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 )
100
87
var err error
101
88
t .file , err = os .Open (t .filename )
102
89
if err == nil {
@@ -105,8 +92,11 @@ func (t *Tail) attemptOpen() error {
105
92
t .reader = bufio .NewReader (t .file )
106
93
return nil
107
94
}
95
+ if interval >= maxRetryInterval {
96
+ break
97
+ }
108
98
select {
109
- case <- time .After (retryOpenInterval ):
99
+ case <- time .After (interval ):
110
100
case <- t .stop :
111
101
t .readerErr = io .EOF
112
102
return fmt .Errorf ("watch was cancelled" )
@@ -133,15 +123,24 @@ func (t *Tail) watchFile() error {
133
123
return err
134
124
}
135
125
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 )
137
129
if err != nil {
138
- return err
130
+ return fmt . Errorf ( "Failed to add watch to directory %s: %v" , watchDir , err )
139
131
}
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
+ }
144
145
}
145
- glog .V (4 ).Infof ("Log file %s moved/deleted" , t .filename )
146
- return nil
147
146
}
0 commit comments