logstash-input-file 4.1.3 → 4.1.4
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +10 -0
- data/JAR_VERSION +1 -1
- data/README.md +0 -3
- data/docs/index.asciidoc +26 -16
- data/lib/filewatch/bootstrap.rb +10 -21
- data/lib/filewatch/discoverer.rb +35 -28
- data/lib/filewatch/observing_base.rb +2 -1
- data/lib/filewatch/read_mode/handlers/base.rb +19 -6
- data/lib/filewatch/read_mode/handlers/read_file.rb +43 -32
- data/lib/filewatch/read_mode/handlers/read_zip_file.rb +8 -3
- data/lib/filewatch/read_mode/processor.rb +8 -8
- data/lib/filewatch/settings.rb +3 -3
- data/lib/filewatch/sincedb_collection.rb +56 -42
- data/lib/filewatch/sincedb_value.rb +6 -0
- data/lib/filewatch/stat/generic.rb +34 -0
- data/lib/filewatch/stat/windows_path.rb +32 -0
- data/lib/filewatch/tail_mode/handlers/base.rb +40 -22
- data/lib/filewatch/tail_mode/handlers/create.rb +1 -2
- data/lib/filewatch/tail_mode/handlers/create_initial.rb +2 -1
- data/lib/filewatch/tail_mode/handlers/delete.rb +13 -1
- data/lib/filewatch/tail_mode/handlers/grow.rb +5 -2
- data/lib/filewatch/tail_mode/handlers/shrink.rb +7 -4
- data/lib/filewatch/tail_mode/handlers/unignore.rb +4 -2
- data/lib/filewatch/tail_mode/processor.rb +147 -58
- data/lib/filewatch/watch.rb +15 -35
- data/lib/filewatch/watched_file.rb +237 -41
- data/lib/filewatch/watched_files_collection.rb +2 -2
- data/lib/filewatch/winhelper.rb +167 -25
- data/lib/jars/filewatch-1.0.1.jar +0 -0
- data/lib/logstash/inputs/file.rb +9 -2
- data/logstash-input-file.gemspec +9 -2
- data/spec/file_ext/file_ext_windows_spec.rb +36 -0
- data/spec/filewatch/read_mode_handlers_read_file_spec.rb +2 -2
- data/spec/filewatch/reading_spec.rb +100 -57
- data/spec/filewatch/rotate_spec.rb +451 -0
- data/spec/filewatch/spec_helper.rb +33 -10
- data/spec/filewatch/tailing_spec.rb +273 -153
- data/spec/filewatch/watched_file_spec.rb +3 -3
- data/spec/filewatch/watched_files_collection_spec.rb +3 -3
- data/spec/filewatch/winhelper_spec.rb +4 -5
- data/spec/helpers/logging_level_helper.rb +8 -0
- data/spec/helpers/rspec_wait_handler_helper.rb +38 -0
- data/spec/helpers/spec_helper.rb +7 -1
- data/spec/inputs/file_read_spec.rb +54 -24
- data/spec/inputs/file_tail_spec.rb +244 -284
- metadata +13 -3
- data/lib/jars/filewatch-1.0.0.jar +0 -0
@@ -10,8 +10,7 @@ module FileWatch module TailMode module Handlers
|
|
10
10
|
|
11
11
|
def update_existing_specifically(watched_file, sincedb_value)
|
12
12
|
# sincedb_value is the source of truth
|
13
|
-
|
14
|
-
watched_file.update_bytes_read(position)
|
13
|
+
watched_file.update_bytes_read(sincedb_value.position)
|
15
14
|
end
|
16
15
|
end
|
17
16
|
end end end
|
@@ -4,6 +4,7 @@ module FileWatch module TailMode module Handlers
|
|
4
4
|
class CreateInitial < Base
|
5
5
|
def handle_specifically(watched_file)
|
6
6
|
if open_file(watched_file)
|
7
|
+
logger.trace("handle_specifically opened file handle: #{watched_file.file.fileno}, path: #{watched_file.filename}")
|
7
8
|
add_or_update_sincedb_collection(watched_file)
|
8
9
|
end
|
9
10
|
end
|
@@ -13,7 +14,7 @@ module FileWatch module TailMode module Handlers
|
|
13
14
|
if @settings.start_new_files_at == :beginning
|
14
15
|
position = 0
|
15
16
|
end
|
16
|
-
logger.
|
17
|
+
logger.trace("update_existing_specifically - #{watched_file.path}: seeking to #{position}")
|
17
18
|
watched_file.update_bytes_read(position)
|
18
19
|
sincedb_value.update_position(position)
|
19
20
|
end
|
@@ -2,9 +2,21 @@
|
|
2
2
|
|
3
3
|
module FileWatch module TailMode module Handlers
|
4
4
|
class Delete < Base
|
5
|
+
DATA_LOSS_WARNING = "watched file path was deleted or rotated before all content was read, if the file is found again it will be read from the last position"
|
5
6
|
def handle_specifically(watched_file)
|
7
|
+
# TODO consider trying to find the renamed file - it will have the same inode.
|
8
|
+
# Needs a rotate scheme rename hint from user e.g. "<name>-YYYY-MM-DD-N.<ext>" or "<name>.<ext>.N"
|
9
|
+
# send the found content to the same listener (stream identity)
|
10
|
+
logger.trace("info",
|
11
|
+
"watched_file details" => watched_file.details,
|
12
|
+
"path" => watched_file.path)
|
13
|
+
if watched_file.bytes_unread > 0
|
14
|
+
logger.warn(DATA_LOSS_WARNING, "unread_bytes" => watched_file.bytes_unread, "path" => watched_file.path)
|
15
|
+
end
|
6
16
|
watched_file.listener.deleted
|
7
|
-
|
17
|
+
# no need to worry about data in the buffer
|
18
|
+
# if found it will be associated by inode and read from last position
|
19
|
+
sincedb_collection.watched_file_deleted(watched_file)
|
8
20
|
watched_file.file_close
|
9
21
|
end
|
10
22
|
end
|
@@ -4,8 +4,11 @@ module FileWatch module TailMode module Handlers
|
|
4
4
|
class Grow < Base
|
5
5
|
def handle_specifically(watched_file)
|
6
6
|
watched_file.file_seek(watched_file.bytes_read)
|
7
|
-
|
8
|
-
|
7
|
+
loop do
|
8
|
+
loop_control = watched_file.loop_control_adjusted_for_stat_size
|
9
|
+
controlled_read(watched_file, loop_control)
|
10
|
+
break unless loop_control.more
|
11
|
+
end
|
9
12
|
end
|
10
13
|
end
|
11
14
|
end end end
|
@@ -5,16 +5,19 @@ module FileWatch module TailMode module Handlers
|
|
5
5
|
def handle_specifically(watched_file)
|
6
6
|
add_or_update_sincedb_collection(watched_file)
|
7
7
|
watched_file.file_seek(watched_file.bytes_read)
|
8
|
-
|
9
|
-
|
8
|
+
loop do
|
9
|
+
loop_control = watched_file.loop_control_adjusted_for_stat_size
|
10
|
+
controlled_read(watched_file, loop_control)
|
11
|
+
break unless loop_control.more
|
12
|
+
end
|
10
13
|
end
|
11
14
|
|
12
15
|
def update_existing_specifically(watched_file, sincedb_value)
|
13
16
|
# we have a match but size is smaller
|
14
17
|
# set all to zero
|
15
|
-
|
16
|
-
watched_file.update_bytes_read(0) if watched_file.bytes_read != 0
|
18
|
+
watched_file.reset_bytes_unread
|
17
19
|
sincedb_value.update_position(0)
|
20
|
+
logger.trace("update_existing_specifically: was truncated seeking to beginning", "watched file" => watched_file.details, "sincedb value" => sincedb_value)
|
18
21
|
end
|
19
22
|
end
|
20
23
|
end end end
|
@@ -6,15 +6,16 @@ module FileWatch module TailMode module Handlers
|
|
6
6
|
# before any other handling has been done
|
7
7
|
# at a minimum we create or associate a sincedb value
|
8
8
|
def handle_specifically(watched_file)
|
9
|
-
add_or_update_sincedb_collection(watched_file)
|
9
|
+
add_or_update_sincedb_collection(watched_file)
|
10
10
|
end
|
11
11
|
|
12
12
|
def get_new_value_specifically(watched_file)
|
13
13
|
# for file initially ignored their bytes_read was set to stat.size
|
14
14
|
# use this value not the `start_new_files_at` for the position
|
15
|
-
# logger.
|
15
|
+
# logger.trace("get_new_value_specifically", "watched_file" => watched_file.inspect)
|
16
16
|
SincedbValue.new(watched_file.bytes_read).tap do |val|
|
17
17
|
val.set_watched_file(watched_file)
|
18
|
+
logger.trace("-------------------- >>>>> get_new_value_specifically: unignore", "watched file" => watched_file.details, "sincedb value" => val)
|
18
19
|
end
|
19
20
|
end
|
20
21
|
|
@@ -25,6 +26,7 @@ module FileWatch module TailMode module Handlers
|
|
25
26
|
# we will handle grow or shrink
|
26
27
|
# for now we seek to where we were before the file got ignored (grow)
|
27
28
|
# or to the start (shrink)
|
29
|
+
logger.trace("-------------------- >>>>> update_existing_specifically: unignore", "watched file" => watched_file.details, "sincedb value" => sincedb_value)
|
28
30
|
position = 0
|
29
31
|
if watched_file.shrunk?
|
30
32
|
watched_file.update_bytes_read(0)
|
@@ -34,6 +34,7 @@ module FileWatch module TailMode
|
|
34
34
|
end
|
35
35
|
|
36
36
|
def initialize_handlers(sincedb_collection, observer)
|
37
|
+
@sincedb_collection = sincedb_collection
|
37
38
|
@create_initial = Handlers::CreateInitial.new(sincedb_collection, observer, @settings)
|
38
39
|
@create = Handlers::Create.new(sincedb_collection, observer, @settings)
|
39
40
|
@grow = Handlers::Grow.new(sincedb_collection, observer, @settings)
|
@@ -71,80 +72,148 @@ module FileWatch module TailMode
|
|
71
72
|
@unignore.handle(watched_file)
|
72
73
|
end
|
73
74
|
|
75
|
+
def process_all_states(watched_files)
|
76
|
+
process_closed(watched_files)
|
77
|
+
return if watch.quit?
|
78
|
+
process_ignored(watched_files)
|
79
|
+
return if watch.quit?
|
80
|
+
process_delayed_delete(watched_files)
|
81
|
+
return if watch.quit?
|
82
|
+
process_restat_for_watched_and_active(watched_files)
|
83
|
+
return if watch.quit?
|
84
|
+
process_rotation_in_progress(watched_files)
|
85
|
+
return if watch.quit?
|
86
|
+
process_watched(watched_files)
|
87
|
+
return if watch.quit?
|
88
|
+
process_active(watched_files)
|
89
|
+
end
|
90
|
+
|
91
|
+
private
|
92
|
+
|
74
93
|
def process_closed(watched_files)
|
75
|
-
logger.
|
94
|
+
# logger.trace("Closed processing")
|
76
95
|
# Handles watched_files in the closed state.
|
77
96
|
# if its size changed it is put into the watched state
|
78
97
|
watched_files.select {|wf| wf.closed? }.each do |watched_file|
|
79
|
-
|
80
|
-
|
81
|
-
watched_file.restat
|
98
|
+
common_restat_with_delay(watched_file, "Closed") do
|
99
|
+
# it won't do this if rotation is detected
|
82
100
|
if watched_file.size_changed?
|
83
101
|
# if the closed file changed, move it to the watched state
|
84
102
|
# not to active state because we want to respect the active files window.
|
85
103
|
watched_file.watch
|
86
104
|
end
|
87
|
-
rescue Errno::ENOENT
|
88
|
-
# file has gone away or we can't read it anymore.
|
89
|
-
common_deleted_reaction(watched_file, "Closed")
|
90
|
-
rescue => e
|
91
|
-
common_error_reaction(path, e, "Closed")
|
92
105
|
end
|
93
106
|
break if watch.quit?
|
94
107
|
end
|
95
108
|
end
|
96
109
|
|
97
110
|
def process_ignored(watched_files)
|
98
|
-
logger.
|
111
|
+
# logger.trace("Ignored processing")
|
99
112
|
# Handles watched_files in the ignored state.
|
100
113
|
# if its size changed:
|
101
114
|
# put it in the watched state
|
102
115
|
# invoke unignore
|
103
116
|
watched_files.select {|wf| wf.ignored? }.each do |watched_file|
|
104
|
-
|
105
|
-
|
106
|
-
watched_file.restat
|
117
|
+
common_restat_with_delay(watched_file, "Ignored") do
|
118
|
+
# it won't do this if rotation is detected
|
107
119
|
if watched_file.size_changed?
|
108
120
|
watched_file.watch
|
109
121
|
unignore(watched_file)
|
110
122
|
end
|
111
|
-
rescue Errno::ENOENT
|
112
|
-
# file has gone away or we can't read it anymore.
|
113
|
-
common_deleted_reaction(watched_file, "Ignored")
|
114
|
-
rescue => e
|
115
|
-
common_error_reaction(path, e, "Ignored")
|
116
123
|
end
|
117
124
|
break if watch.quit?
|
118
125
|
end
|
119
126
|
end
|
120
127
|
|
128
|
+
def process_delayed_delete(watched_files)
|
129
|
+
# defer the delete to one loop later to ensure that the stat really really can't find a renamed file
|
130
|
+
# because a `stat` can be called right in the middle of the rotation rename cascade
|
131
|
+
logger.trace("Delayed Delete processing")
|
132
|
+
watched_files.select {|wf| wf.delayed_delete?}.each do |watched_file|
|
133
|
+
logger.trace(">>> Delayed Delete", "path" => watched_file.filename)
|
134
|
+
common_restat_without_delay(watched_file, ">>> Delayed Delete") do
|
135
|
+
logger.trace(">>> Delayed Delete: file at path found again", "watched_file" => watched_file.details)
|
136
|
+
watched_file.file_at_path_found_again
|
137
|
+
end
|
138
|
+
end
|
139
|
+
end
|
140
|
+
|
141
|
+
def process_restat_for_watched_and_active(watched_files)
|
142
|
+
# do restat on all watched and active states once now. closed and ignored have been handled already
|
143
|
+
logger.trace("Watched + Active restat processing")
|
144
|
+
watched_files.select {|wf| wf.watched? || wf.active?}.each do |watched_file|
|
145
|
+
common_restat_with_delay(watched_file, "Watched")
|
146
|
+
end
|
147
|
+
end
|
148
|
+
|
149
|
+
def process_rotation_in_progress(watched_files)
|
150
|
+
logger.trace("Rotation In Progress processing")
|
151
|
+
watched_files.select {|wf| wf.rotation_in_progress?}.each do |watched_file|
|
152
|
+
if !watched_file.all_read?
|
153
|
+
if watched_file.file_open?
|
154
|
+
# rotated file but original opened file is not fully read
|
155
|
+
# we need to keep reading the open file, if we close it we lose it because the path is now pointing at a different file.
|
156
|
+
logger.trace(">>> Rotation In Progress - inode change detected and original content is not fully read, reading all", "watched_file details" => watched_file.details)
|
157
|
+
# need to fully read open file while we can
|
158
|
+
watched_file.set_maximum_read_loop
|
159
|
+
grow(watched_file)
|
160
|
+
watched_file.set_standard_read_loop
|
161
|
+
else
|
162
|
+
logger.warn(">>> Rotation In Progress - inode change detected and original content is not fully read, file is closed and path points to new content", "watched_file details" => watched_file.details)
|
163
|
+
end
|
164
|
+
end
|
165
|
+
current_key = watched_file.sincedb_key
|
166
|
+
sdb_value = @sincedb_collection.get(current_key)
|
167
|
+
potential_key = watched_file.stat_sincedb_key
|
168
|
+
potential_sdb_value = @sincedb_collection.get(potential_key)
|
169
|
+
logger.trace(">>> Rotation In Progress", "watched_file" => watched_file.details, "found_sdb_value" => sdb_value, "potential_key" => potential_key, "potential_sdb_value" => potential_sdb_value)
|
170
|
+
if potential_sdb_value.nil?
|
171
|
+
if sdb_value.nil?
|
172
|
+
logger.trace("---------- >>> Rotation In Progress: rotating as initial file, no potential sincedb value AND no found sincedb value")
|
173
|
+
watched_file.rotate_as_initial_file
|
174
|
+
else
|
175
|
+
logger.trace("---------- >>>> Rotation In Progress: rotating as existing file, no potential sincedb value BUT found sincedb value")
|
176
|
+
watched_file.rotate_as_file
|
177
|
+
sdb_value.clear_watched_file
|
178
|
+
end
|
179
|
+
new_sdb_value = SincedbValue.new(0)
|
180
|
+
new_sdb_value.set_watched_file(watched_file)
|
181
|
+
@sincedb_collection.set(potential_key, new_sdb_value)
|
182
|
+
else
|
183
|
+
other_watched_file = potential_sdb_value.watched_file
|
184
|
+
if other_watched_file.nil?
|
185
|
+
logger.trace("---------- >>>> Rotation In Progress: rotating as existing file WITH potential sincedb value that does not have a watched file reference !!!!!!!!!!!!!!!!!")
|
186
|
+
watched_file.rotate_as_file(potential_sdb_value.position)
|
187
|
+
sdb_value.clear_watched_file unless sdb_value.nil?
|
188
|
+
potential_sdb_value.set_watched_file(watched_file)
|
189
|
+
else
|
190
|
+
logger.trace("---------- >>>> Rotation In Progress: rotating from...", "this watched_file details" => watched_file.details, "other watched_file details" => other_watched_file.details)
|
191
|
+
watched_file.rotate_from(other_watched_file)
|
192
|
+
sdb_value.clear_watched_file unless sdb_value.nil?
|
193
|
+
potential_sdb_value.set_watched_file(watched_file)
|
194
|
+
end
|
195
|
+
end
|
196
|
+
logger.trace("---------- >>>> Rotation In Progress: after handling rotation", "this watched_file details" => watched_file.details, "sincedb_value" => (potential_sdb_value || sdb_value))
|
197
|
+
end
|
198
|
+
end
|
199
|
+
|
121
200
|
def process_watched(watched_files)
|
122
|
-
logger.debug("Watched processing")
|
123
201
|
# Handles watched_files in the watched state.
|
124
202
|
# for a slice of them:
|
125
203
|
# move to the active state
|
126
204
|
# and we allow the block to open the file and create a sincedb collection record if needed
|
127
205
|
# some have never been active and some have
|
128
206
|
# those that were active before but are watched now were closed under constraint
|
129
|
-
|
207
|
+
logger.trace("Watched processing")
|
130
208
|
# how much of the max active window is available
|
131
209
|
to_take = @settings.max_active - watched_files.count{|wf| wf.active?}
|
132
210
|
if to_take > 0
|
133
211
|
watched_files.select {|wf| wf.watched?}.take(to_take).each do |watched_file|
|
134
|
-
|
135
|
-
|
136
|
-
watched_file
|
137
|
-
|
138
|
-
|
139
|
-
create_initial(watched_file)
|
140
|
-
else
|
141
|
-
create(watched_file)
|
142
|
-
end
|
143
|
-
rescue Errno::ENOENT
|
144
|
-
# file has gone away or we can't read it anymore.
|
145
|
-
common_deleted_reaction(watched_file, "Watched")
|
146
|
-
rescue => e
|
147
|
-
common_error_reaction(path, e, "Watched")
|
212
|
+
watched_file.activate
|
213
|
+
if watched_file.initial?
|
214
|
+
create_initial(watched_file)
|
215
|
+
else
|
216
|
+
create(watched_file)
|
148
217
|
end
|
149
218
|
break if watch.quit?
|
150
219
|
end
|
@@ -159,51 +228,71 @@ module FileWatch module TailMode
|
|
159
228
|
end
|
160
229
|
|
161
230
|
def process_active(watched_files)
|
162
|
-
logger.
|
231
|
+
# logger.trace("Active processing")
|
163
232
|
# Handles watched_files in the active state.
|
164
|
-
#
|
233
|
+
# files have been opened at this point
|
165
234
|
watched_files.select {|wf| wf.active? }.each do |watched_file|
|
166
|
-
path = watched_file.path
|
167
|
-
begin
|
168
|
-
watched_file.restat
|
169
|
-
rescue Errno::ENOENT
|
170
|
-
# file has gone away or we can't read it anymore.
|
171
|
-
common_deleted_reaction(watched_file, "Active")
|
172
|
-
next
|
173
|
-
rescue => e
|
174
|
-
common_error_reaction(path, e, "Active")
|
175
|
-
next
|
176
|
-
end
|
177
235
|
break if watch.quit?
|
236
|
+
path = watched_file.filename
|
178
237
|
if watched_file.grown?
|
179
|
-
logger.
|
238
|
+
logger.trace("Active - file grew: #{path}: new size is #{watched_file.last_stat_size}, bytes read #{watched_file.bytes_read}")
|
180
239
|
grow(watched_file)
|
181
240
|
elsif watched_file.shrunk?
|
241
|
+
if watched_file.bytes_unread > 0
|
242
|
+
logger.warn("Active - shrunk: DATA LOSS!! truncate detected with #{watched_file.bytes_unread} unread bytes: #{path}")
|
243
|
+
end
|
182
244
|
# we don't update the size here, its updated when we actually read
|
183
|
-
logger.
|
245
|
+
logger.trace("Active - file shrunk #{path}: new size is #{watched_file.last_stat_size}, old size #{watched_file.bytes_read}")
|
184
246
|
shrink(watched_file)
|
185
247
|
else
|
186
248
|
# same size, do nothing
|
249
|
+
logger.trace("Active - no change", "watched_file" => watched_file.details)
|
187
250
|
end
|
188
251
|
# can any active files be closed to make way for waiting files?
|
189
252
|
if watched_file.file_closable?
|
190
|
-
logger.
|
253
|
+
logger.trace("Watch each: active: file expired: #{path}")
|
191
254
|
timeout(watched_file)
|
192
255
|
watched_file.close
|
193
256
|
end
|
194
257
|
end
|
195
258
|
end
|
196
259
|
|
197
|
-
def
|
198
|
-
|
199
|
-
watched_file.unwatch
|
200
|
-
delete(watched_file)
|
201
|
-
deletable_filepaths << watched_file.path
|
202
|
-
logger.debug("#{action} - stat failed: #{watched_file.path}, removing from collection")
|
260
|
+
def common_restat_with_delay(watched_file, action, &block)
|
261
|
+
common_restat(watched_file, action, true, &block)
|
203
262
|
end
|
204
263
|
|
205
|
-
def
|
206
|
-
|
264
|
+
def common_restat_without_delay(watched_file, action, &block)
|
265
|
+
common_restat(watched_file, action, false, &block)
|
266
|
+
end
|
267
|
+
|
268
|
+
def common_restat(watched_file, action, delay, &block)
|
269
|
+
all_ok = true
|
270
|
+
begin
|
271
|
+
watched_file.restat
|
272
|
+
if watched_file.rotation_in_progress?
|
273
|
+
logger.trace("-------------------- >>>>> restat - rotation_detected", "watched_file details" => watched_file.details, "new sincedb key" => watched_file.stat_sincedb_key)
|
274
|
+
# don't yield to closed and ignore processing
|
275
|
+
else
|
276
|
+
yield if block_given?
|
277
|
+
end
|
278
|
+
rescue Errno::ENOENT
|
279
|
+
if delay
|
280
|
+
logger.trace("#{action} - delaying the stat fail on: #{watched_file.filename}")
|
281
|
+
watched_file.delay_delete
|
282
|
+
else
|
283
|
+
# file has gone away or we can't read it anymore.
|
284
|
+
logger.trace("#{action} - after a delay, really can't find this file: #{watched_file.filename}")
|
285
|
+
watched_file.unwatch
|
286
|
+
logger.trace("#{action} - removing from collection: #{watched_file.filename}")
|
287
|
+
delete(watched_file)
|
288
|
+
deletable_filepaths << watched_file.path
|
289
|
+
all_ok = false
|
290
|
+
end
|
291
|
+
rescue => e
|
292
|
+
logger.error("#{action} - other error #{watched_file.path}: (#{e.message}, #{e.backtrace.take(8).inspect})")
|
293
|
+
all_ok = false
|
294
|
+
end
|
295
|
+
all_ok
|
207
296
|
end
|
208
297
|
end
|
209
298
|
end end
|
data/lib/filewatch/watch.rb
CHANGED
@@ -10,11 +10,8 @@ module FileWatch
|
|
10
10
|
|
11
11
|
def initialize(discoverer, watched_files_collection, settings)
|
12
12
|
@settings = settings
|
13
|
-
# watch and iterate_on_state can be called from different threads.
|
14
|
-
@lock = Mutex.new
|
15
13
|
# we need to be threadsafe about the quit mutation
|
16
|
-
@quit = false
|
17
|
-
@quit_lock = Mutex.new
|
14
|
+
@quit = Concurrent::AtomicBoolean.new(false)
|
18
15
|
@lastwarn_max_files = 0
|
19
16
|
@discoverer = discoverer
|
20
17
|
@watched_files_collection = watched_files_collection
|
@@ -27,17 +24,13 @@ module FileWatch
|
|
27
24
|
end
|
28
25
|
|
29
26
|
def watch(path)
|
30
|
-
|
31
|
-
@discoverer.add_path(path)
|
32
|
-
end
|
27
|
+
@discoverer.add_path(path)
|
33
28
|
# don't return whatever @discoverer.add_path returns
|
34
29
|
return true
|
35
30
|
end
|
36
31
|
|
37
32
|
def discover
|
38
|
-
|
39
|
-
@discoverer.discover
|
40
|
-
end
|
33
|
+
@discoverer.discover
|
41
34
|
# don't return whatever @discoverer.discover returns
|
42
35
|
return true
|
43
36
|
end
|
@@ -60,6 +53,7 @@ module FileWatch
|
|
60
53
|
break if quit?
|
61
54
|
sleep(@settings.stat_interval)
|
62
55
|
end
|
56
|
+
sincedb_collection.write_if_requested # does nothing if no requests to write were lodged.
|
63
57
|
@watched_files_collection.close_all
|
64
58
|
end # def subscribe
|
65
59
|
|
@@ -67,42 +61,28 @@ module FileWatch
|
|
67
61
|
# differently from Tail mode - see the ReadMode::Processor and TailMode::Processor
|
68
62
|
def iterate_on_state
|
69
63
|
return if @watched_files_collection.empty?
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
return if quit?
|
78
|
-
@processor.process_watched(watched_files)
|
79
|
-
return if quit?
|
80
|
-
@processor.process_active(watched_files)
|
81
|
-
ensure
|
82
|
-
@watched_files_collection.delete(@processor.deletable_filepaths)
|
83
|
-
@processor.deletable_filepaths.clear
|
84
|
-
end
|
64
|
+
begin
|
65
|
+
# creates this snapshot of watched_file values just once
|
66
|
+
watched_files = @watched_files_collection.values
|
67
|
+
@processor.process_all_states(watched_files)
|
68
|
+
ensure
|
69
|
+
@watched_files_collection.delete(@processor.deletable_filepaths)
|
70
|
+
@processor.deletable_filepaths.clear
|
85
71
|
end
|
86
72
|
end # def each
|
87
73
|
|
88
74
|
def quit
|
89
|
-
@
|
90
|
-
|
91
|
-
end
|
92
|
-
end # def quit
|
75
|
+
@quit.make_true
|
76
|
+
end
|
93
77
|
|
94
78
|
def quit?
|
95
|
-
@
|
79
|
+
@quit.true?
|
96
80
|
end
|
97
81
|
|
98
82
|
private
|
99
83
|
|
100
|
-
def synchronized(&block)
|
101
|
-
@lock.synchronize { block.call }
|
102
|
-
end
|
103
|
-
|
104
84
|
def reset_quit
|
105
|
-
@
|
85
|
+
@quit.make_false
|
106
86
|
end
|
107
87
|
end
|
108
88
|
end
|