logstash-input-file 4.1.3 → 4.1.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|