-
Notifications
You must be signed in to change notification settings - Fork 2
/
Copy pathfile_detect.go
143 lines (126 loc) · 3.01 KB
/
file_detect.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
/* Copyright © INFINI LTD. All rights reserved.
* Web: https://infinilabs.com
* Email: hello#infini.ltd */
package logs
import (
"context"
"os"
"path/filepath"
log "github.com/cihub/seelog"
"infini.sh/agent/lib/util"
)
type Operation uint8
const (
OpDone Operation = iota
OpCreate
OpWrite
OpTruncate
)
type FSEvent struct {
Pattern *Pattern // matched pattern
Path string
Offset int64
Op Operation
Info os.FileInfo
State FileState
}
func NewFileDetector(rootPath string, patterns []*Pattern) *FileDetector {
return &FileDetector{
root: rootPath,
patterns: patterns,
events: make(chan FSEvent),
}
}
type FileDetector struct {
root string
patterns []*Pattern
prev map[string]os.FileInfo
events chan FSEvent
}
func (w *FileDetector) Detect(ctx context.Context) {
defer func() {
w.events <- doneEvent()
}()
if len(w.patterns) == 0 {
return
}
err := filepath.Walk(w.root, func(path string, info os.FileInfo, err error) error {
if ctx.Err() != nil {
return ctx.Err()
}
if info == nil {
log.Warnf("missing file info for path [%s]", path)
return nil
}
if info.IsDir() {
return nil
}
for _, pattern := range w.patterns {
if !pattern.patternRegex.MatchString(info.Name()) {
continue
}
w.judgeEvent(ctx, path, info, pattern)
break
}
return nil
})
if err != nil {
log.Errorf("failed to walk logs under [%s], err: %v", w.root, err)
}
}
func (w *FileDetector) judgeEvent(ctx context.Context, path string, info os.FileInfo, pattern *Pattern) {
if info.Mode()&os.ModeSymlink != 0 {
realPath, err := util.ResolveSymlink(path)
if err != nil {
log.Error(err)
return
}
info, err = os.Lstat(realPath)
if err != nil {
log.Error(err)
return
}
path = realPath
}
preState, err := GetFileState(path)
isSameFile := w.IsSameFile(preState, info, path)
if err != nil || preState == (FileState{}) || !isSameFile {
select {
case <-ctx.Done():
return
case w.events <- createEvent(path, info, pattern, preState):
}
return
}
if preState.ModTime.UnixNano() != info.ModTime().UnixNano() {
//mod time changed, if pre info has same size or bigger => truncate
if preState.Size >= info.Size() {
select {
case <-ctx.Done():
return
case w.events <- truncateEvent(path, info, pattern, preState):
}
} else {
select {
case <-ctx.Done():
return
case w.events <- writeEvent(path, info, pattern, preState):
}
}
}
}
func (w *FileDetector) Event() FSEvent {
return <-w.events
}
func createEvent(path string, fi os.FileInfo, pattern *Pattern, state FileState) FSEvent {
return FSEvent{pattern, path, -1, OpCreate, fi, state}
}
func writeEvent(path string, fi os.FileInfo, pattern *Pattern, state FileState) FSEvent {
return FSEvent{pattern, path, -1, OpWrite, fi, state}
}
func truncateEvent(path string, fi os.FileInfo, pattern *Pattern, state FileState) FSEvent {
return FSEvent{pattern, path, -1, OpTruncate, fi, state}
}
func doneEvent() FSEvent {
return FSEvent{nil, "", -1, OpDone, nil, FileState{}}
}