logstash-input-file 4.1.18 → 4.2.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 +16 -0
- data/README.md +1 -1
- data/lib/filewatch/discoverer.rb +9 -8
- data/lib/filewatch/observing_base.rb +1 -12
- data/lib/filewatch/processor.rb +55 -0
- data/lib/filewatch/read_mode/handlers/base.rb +12 -11
- data/lib/filewatch/read_mode/handlers/read_file.rb +23 -8
- data/lib/filewatch/read_mode/handlers/read_zip_file.rb +8 -6
- data/lib/filewatch/read_mode/processor.rb +22 -36
- data/lib/filewatch/settings.rb +1 -2
- data/lib/filewatch/sincedb_collection.rb +40 -41
- data/lib/filewatch/sincedb_record_serializer.rb +5 -11
- data/lib/filewatch/stat/generic.rb +8 -13
- data/lib/filewatch/stat/windows_path.rb +7 -9
- data/lib/filewatch/tail_mode/handlers/base.rb +32 -23
- data/lib/filewatch/tail_mode/handlers/delete.rb +2 -4
- data/lib/filewatch/tail_mode/handlers/shrink.rb +2 -3
- data/lib/filewatch/tail_mode/handlers/unignore.rb +4 -4
- data/lib/filewatch/tail_mode/processor.rb +47 -54
- data/lib/filewatch/watch.rb +12 -14
- data/lib/filewatch/watched_file.rb +25 -14
- data/lib/filewatch/watched_files_collection.rb +11 -78
- data/lib/jars/filewatch-1.0.1.jar +0 -0
- data/lib/logstash/inputs/file.rb +4 -3
- data/lib/logstash/inputs/file_listener.rb +3 -14
- data/logstash-input-file.gemspec +2 -1
- data/spec/filewatch/reading_spec.rb +63 -12
- data/spec/filewatch/rotate_spec.rb +4 -4
- data/spec/filewatch/settings_spec.rb +3 -0
- data/spec/filewatch/sincedb_record_serializer_spec.rb +6 -2
- data/spec/filewatch/spec_helper.rb +12 -14
- data/spec/filewatch/tailing_spec.rb +24 -22
- data/spec/filewatch/watched_file_spec.rb +30 -0
- data/spec/filewatch/watched_files_collection_spec.rb +62 -8
- data/spec/inputs/file_read_spec.rb +58 -14
- metadata +17 -2
@@ -7,11 +7,9 @@ module FileWatch module TailMode module Handlers
|
|
7
7
|
# TODO consider trying to find the renamed file - it will have the same inode.
|
8
8
|
# Needs a rotate scheme rename hint from user e.g. "<name>-YYYY-MM-DD-N.<ext>" or "<name>.<ext>.N"
|
9
9
|
# send the found content to the same listener (stream identity)
|
10
|
-
logger.trace(
|
11
|
-
"watched_file details" => watched_file.details,
|
12
|
-
"path" => watched_file.path)
|
10
|
+
logger.trace? && logger.trace(__method__.to_s, :path => watched_file.path, :watched_file => watched_file.details)
|
13
11
|
if watched_file.bytes_unread > 0
|
14
|
-
logger.warn(DATA_LOSS_WARNING,
|
12
|
+
logger.warn(DATA_LOSS_WARNING, :path => watched_file.path, :unread_bytes => watched_file.bytes_unread)
|
15
13
|
end
|
16
14
|
watched_file.listener.deleted
|
17
15
|
# no need to worry about data in the buffer
|
@@ -14,11 +14,10 @@ module FileWatch module TailMode module Handlers
|
|
14
14
|
end
|
15
15
|
|
16
16
|
def update_existing_specifically(watched_file, sincedb_value)
|
17
|
-
# we have a match but size is smaller
|
18
|
-
# set all to zero
|
17
|
+
# we have a match but size is smaller - set all to zero
|
19
18
|
watched_file.reset_bytes_unread
|
20
19
|
sincedb_value.update_position(0)
|
21
|
-
logger.trace("update_existing_specifically: was truncated seeking to beginning",
|
20
|
+
logger.trace? && logger.trace("update_existing_specifically: was truncated seeking to beginning", :watched_file => watched_file.details, :sincedb_value => sincedb_value)
|
22
21
|
end
|
23
22
|
end
|
24
23
|
end end end
|
@@ -13,9 +13,9 @@ module FileWatch module TailMode module Handlers
|
|
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
15
|
# logger.trace("get_new_value_specifically", "watched_file" => watched_file.inspect)
|
16
|
-
SincedbValue.new(watched_file.bytes_read).tap do |
|
17
|
-
|
18
|
-
logger.trace("
|
16
|
+
SincedbValue.new(watched_file.bytes_read).tap do |sincedb_value|
|
17
|
+
sincedb_value.set_watched_file(watched_file)
|
18
|
+
logger.trace? && logger.trace("get_new_value_specifically: unignore", :watched_file => watched_file.details, :sincedb_value => sincedb_value)
|
19
19
|
end
|
20
20
|
end
|
21
21
|
|
@@ -26,7 +26,7 @@ module FileWatch module TailMode module Handlers
|
|
26
26
|
# we will handle grow or shrink
|
27
27
|
# for now we seek to where we were before the file got ignored (grow)
|
28
28
|
# or to the start (shrink)
|
29
|
-
logger.trace("
|
29
|
+
logger.trace? && logger.trace("update_existing_specifically: unignore", :watched_file => watched_file.details, :sincedb_value => sincedb_value)
|
30
30
|
position = 0
|
31
31
|
if watched_file.shrunk?
|
32
32
|
watched_file.update_bytes_read(0)
|
@@ -1,5 +1,5 @@
|
|
1
1
|
# encoding: utf-8
|
2
|
-
require
|
2
|
+
require 'filewatch/processor'
|
3
3
|
require_relative "handlers/base"
|
4
4
|
require_relative "handlers/create_initial"
|
5
5
|
require_relative "handlers/create"
|
@@ -18,20 +18,7 @@ module FileWatch module TailMode
|
|
18
18
|
# :delete - file can't be read
|
19
19
|
# :timeout - file is closable
|
20
20
|
# :unignore - file was ignored, but have now received new content
|
21
|
-
class Processor
|
22
|
-
include LogStash::Util::Loggable
|
23
|
-
|
24
|
-
attr_reader :watch, :deletable_filepaths
|
25
|
-
|
26
|
-
def initialize(settings)
|
27
|
-
@settings = settings
|
28
|
-
@deletable_filepaths = []
|
29
|
-
end
|
30
|
-
|
31
|
-
def add_watch(watch)
|
32
|
-
@watch = watch
|
33
|
-
self
|
34
|
-
end
|
21
|
+
class Processor < FileWatch::Processor
|
35
22
|
|
36
23
|
def initialize_handlers(sincedb_collection, observer)
|
37
24
|
@sincedb_collection = sincedb_collection
|
@@ -91,11 +78,12 @@ module FileWatch module TailMode
|
|
91
78
|
private
|
92
79
|
|
93
80
|
def process_closed(watched_files)
|
94
|
-
|
81
|
+
logger.trace(__method__.to_s)
|
95
82
|
# Handles watched_files in the closed state.
|
96
83
|
# if its size changed it is put into the watched state
|
97
|
-
watched_files.
|
98
|
-
|
84
|
+
watched_files.each do |watched_file|
|
85
|
+
next unless watched_file.closed?
|
86
|
+
common_restat_with_delay(watched_file, __method__) do
|
99
87
|
# it won't do this if rotation is detected
|
100
88
|
if watched_file.size_changed?
|
101
89
|
# if the closed file changed, move it to the watched state
|
@@ -108,13 +96,14 @@ module FileWatch module TailMode
|
|
108
96
|
end
|
109
97
|
|
110
98
|
def process_ignored(watched_files)
|
111
|
-
|
99
|
+
logger.trace(__method__.to_s)
|
112
100
|
# Handles watched_files in the ignored state.
|
113
101
|
# if its size changed:
|
114
102
|
# put it in the watched state
|
115
103
|
# invoke unignore
|
116
|
-
watched_files.
|
117
|
-
|
104
|
+
watched_files.each do |watched_file|
|
105
|
+
next unless watched_file.ignored?
|
106
|
+
common_restat_with_delay(watched_file, __method__) do
|
118
107
|
# it won't do this if rotation is detected
|
119
108
|
if watched_file.size_changed?
|
120
109
|
watched_file.watch
|
@@ -128,11 +117,12 @@ module FileWatch module TailMode
|
|
128
117
|
def process_delayed_delete(watched_files)
|
129
118
|
# defer the delete to one loop later to ensure that the stat really really can't find a renamed file
|
130
119
|
# because a `stat` can be called right in the middle of the rotation rename cascade
|
131
|
-
logger.trace(
|
132
|
-
watched_files.
|
133
|
-
|
134
|
-
|
135
|
-
|
120
|
+
logger.trace(__method__.to_s)
|
121
|
+
watched_files.each do |watched_file|
|
122
|
+
next unless watched_file.delayed_delete?
|
123
|
+
logger.trace(">>> Delayed Delete", :path => watched_file.path)
|
124
|
+
common_restat_without_delay(watched_file, __method__) do
|
125
|
+
logger.trace(">>> Delayed Delete: file at path found again", :watched_file => watched_file.details)
|
136
126
|
watched_file.file_at_path_found_again
|
137
127
|
end
|
138
128
|
end
|
@@ -140,33 +130,35 @@ module FileWatch module TailMode
|
|
140
130
|
|
141
131
|
def process_restat_for_watched_and_active(watched_files)
|
142
132
|
# do restat on all watched and active states once now. closed and ignored have been handled already
|
143
|
-
logger.trace(
|
144
|
-
watched_files.
|
145
|
-
|
133
|
+
logger.trace(__method__.to_s)
|
134
|
+
watched_files.each do |watched_file|
|
135
|
+
next if !watched_file.watched? && !watched_file.active?
|
136
|
+
common_restat_with_delay(watched_file, __method__)
|
146
137
|
end
|
147
138
|
end
|
148
139
|
|
149
140
|
def process_rotation_in_progress(watched_files)
|
150
|
-
logger.trace(
|
151
|
-
watched_files.
|
141
|
+
logger.trace(__method__.to_s)
|
142
|
+
watched_files.each do |watched_file|
|
143
|
+
next unless watched_file.rotation_in_progress?
|
152
144
|
if !watched_file.all_read?
|
153
145
|
if watched_file.file_open?
|
154
146
|
# rotated file but original opened file is not fully read
|
155
147
|
# 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",
|
148
|
+
logger.trace(">>> Rotation In Progress - inode change detected and original content is not fully read, reading all", :watched_file => watched_file.details)
|
157
149
|
# need to fully read open file while we can
|
158
150
|
watched_file.set_maximum_read_loop
|
159
151
|
grow(watched_file)
|
160
152
|
watched_file.set_standard_read_loop
|
161
153
|
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",
|
154
|
+
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 => watched_file.details)
|
163
155
|
end
|
164
156
|
end
|
165
157
|
current_key = watched_file.sincedb_key
|
166
158
|
sdb_value = @sincedb_collection.get(current_key)
|
167
159
|
potential_key = watched_file.stat_sincedb_key
|
168
160
|
potential_sdb_value = @sincedb_collection.get(potential_key)
|
169
|
-
logger.trace(">>> Rotation In Progress",
|
161
|
+
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
162
|
if potential_sdb_value.nil?
|
171
163
|
logger.trace("---------- >>>> Rotation In Progress: rotating as existing file")
|
172
164
|
watched_file.rotate_as_file
|
@@ -189,13 +181,13 @@ module FileWatch module TailMode
|
|
189
181
|
sdb_value.clear_watched_file unless sdb_value.nil?
|
190
182
|
potential_sdb_value.set_watched_file(watched_file)
|
191
183
|
else
|
192
|
-
logger.trace("---------- >>>> Rotation In Progress: rotating from...",
|
184
|
+
logger.trace("---------- >>>> Rotation In Progress: rotating from...", :this_watched_file => watched_file.details, :other_watched_file => other_watched_file.details)
|
193
185
|
watched_file.rotate_from(other_watched_file)
|
194
186
|
sdb_value.clear_watched_file unless sdb_value.nil?
|
195
187
|
potential_sdb_value.set_watched_file(watched_file)
|
196
188
|
end
|
197
189
|
end
|
198
|
-
logger.trace("---------- >>>> Rotation In Progress: after handling rotation",
|
190
|
+
logger.trace("---------- >>>> Rotation In Progress: after handling rotation", :this_watched_file => watched_file.details, :sincedb_value => (potential_sdb_value || sdb_value))
|
199
191
|
end
|
200
192
|
end
|
201
193
|
|
@@ -206,11 +198,11 @@ module FileWatch module TailMode
|
|
206
198
|
# and we allow the block to open the file and create a sincedb collection record if needed
|
207
199
|
# some have never been active and some have
|
208
200
|
# those that were active before but are watched now were closed under constraint
|
209
|
-
logger.trace(
|
201
|
+
logger.trace(__method__.to_s)
|
210
202
|
# how much of the max active window is available
|
211
|
-
to_take = @settings.max_active - watched_files.count
|
203
|
+
to_take = @settings.max_active - watched_files.count(&:active?)
|
212
204
|
if to_take > 0
|
213
|
-
watched_files.select
|
205
|
+
watched_files.select(&:watched?).take(to_take).each do |watched_file|
|
214
206
|
watched_file.activate
|
215
207
|
if watched_file.initial?
|
216
208
|
create_initial(watched_file)
|
@@ -223,36 +215,37 @@ module FileWatch module TailMode
|
|
223
215
|
now = Time.now.to_i
|
224
216
|
if (now - watch.lastwarn_max_files) > MAX_FILES_WARN_INTERVAL
|
225
217
|
waiting = watched_files.size - @settings.max_active
|
226
|
-
logger.warn(@settings.max_warn_msg
|
218
|
+
logger.warn("#{@settings.max_warn_msg}, files yet to open: #{waiting}")
|
227
219
|
watch.lastwarn_max_files = now
|
228
220
|
end
|
229
221
|
end
|
230
222
|
end
|
231
223
|
|
232
224
|
def process_active(watched_files)
|
233
|
-
|
225
|
+
logger.trace(__method__.to_s)
|
234
226
|
# Handles watched_files in the active state.
|
235
227
|
# files have been opened at this point
|
236
|
-
watched_files.
|
228
|
+
watched_files.each do |watched_file|
|
229
|
+
next unless watched_file.active?
|
237
230
|
break if watch.quit?
|
238
231
|
path = watched_file.filename
|
239
232
|
if watched_file.grown?
|
240
|
-
logger.trace("
|
233
|
+
logger.trace("#{__method__} file grew: new size is #{watched_file.last_stat_size}, bytes read #{watched_file.bytes_read}", :path => path)
|
241
234
|
grow(watched_file)
|
242
235
|
elsif watched_file.shrunk?
|
243
236
|
if watched_file.bytes_unread > 0
|
244
|
-
logger.warn("
|
237
|
+
logger.warn("potential data loss, file truncate detected with #{watched_file.bytes_unread} unread bytes", :path => path)
|
245
238
|
end
|
246
239
|
# we don't update the size here, its updated when we actually read
|
247
|
-
logger.trace("
|
240
|
+
logger.trace("#{__method__} file shrunk: new size is #{watched_file.last_stat_size}, old size #{watched_file.bytes_read}", :path => path)
|
248
241
|
shrink(watched_file)
|
249
242
|
else
|
250
243
|
# same size, do nothing
|
251
|
-
logger.trace("
|
244
|
+
logger.trace("#{__method__} no change", :path => path)
|
252
245
|
end
|
253
246
|
# can any active files be closed to make way for waiting files?
|
254
247
|
if watched_file.file_closable?
|
255
|
-
logger.trace("
|
248
|
+
logger.trace("#{__method__} file expired", :path => path)
|
256
249
|
timeout(watched_file)
|
257
250
|
watched_file.close
|
258
251
|
end
|
@@ -270,28 +263,28 @@ module FileWatch module TailMode
|
|
270
263
|
def common_restat(watched_file, action, delay, &block)
|
271
264
|
all_ok = true
|
272
265
|
begin
|
273
|
-
watched_file
|
266
|
+
restat(watched_file)
|
274
267
|
if watched_file.rotation_in_progress?
|
275
|
-
logger.trace("-------------------- >>>>> restat - rotation_detected",
|
268
|
+
logger.trace("-------------------- >>>>> restat - rotation_detected", :watched_file => watched_file.details, :new_sincedb_key => watched_file.stat_sincedb_key)
|
276
269
|
# don't yield to closed and ignore processing
|
277
270
|
else
|
278
271
|
yield if block_given?
|
279
272
|
end
|
280
273
|
rescue Errno::ENOENT
|
281
274
|
if delay
|
282
|
-
logger.trace("#{action} - delaying the stat fail on:
|
275
|
+
logger.trace("#{action} - delaying the stat fail on", :filename => watched_file.filename)
|
283
276
|
watched_file.delay_delete
|
284
277
|
else
|
285
278
|
# file has gone away or we can't read it anymore.
|
286
|
-
logger.trace("#{action} - after a delay, really can't find this file:
|
279
|
+
logger.trace("#{action} - after a delay, really can't find this file", :path => watched_file.path)
|
287
280
|
watched_file.unwatch
|
288
|
-
logger.trace("#{action} - removing from collection:
|
281
|
+
logger.trace("#{action} - removing from collection", :filename => watched_file.filename)
|
289
282
|
delete(watched_file)
|
290
|
-
|
283
|
+
add_deletable_path watched_file.path
|
291
284
|
all_ok = false
|
292
285
|
end
|
293
286
|
rescue => e
|
294
|
-
logger.error("#{action} - other error
|
287
|
+
logger.error("#{action} - other error", error_details(e, watched_file))
|
295
288
|
all_ok = false
|
296
289
|
end
|
297
290
|
all_ok
|
data/lib/filewatch/watch.rb
CHANGED
@@ -1,26 +1,25 @@
|
|
1
1
|
# encoding: utf-8
|
2
2
|
require "logstash/util/loggable"
|
3
|
+
require "concurrent/atomic/atomic_boolean"
|
3
4
|
|
4
5
|
module FileWatch
|
5
6
|
class Watch
|
6
7
|
include LogStash::Util::Loggable
|
7
8
|
|
8
9
|
attr_accessor :lastwarn_max_files
|
9
|
-
attr_reader :discoverer, :watched_files_collection
|
10
|
+
attr_reader :discoverer, :processor, :watched_files_collection
|
10
11
|
|
11
|
-
def initialize(discoverer,
|
12
|
+
def initialize(discoverer, processor, settings)
|
13
|
+
@discoverer = discoverer
|
14
|
+
@watched_files_collection = discoverer.watched_files_collection
|
12
15
|
@settings = settings
|
16
|
+
|
13
17
|
# we need to be threadsafe about the quit mutation
|
14
18
|
@quit = Concurrent::AtomicBoolean.new(false)
|
15
19
|
@lastwarn_max_files = 0
|
16
|
-
@discoverer = discoverer
|
17
|
-
@watched_files_collection = watched_files_collection
|
18
|
-
end
|
19
20
|
|
20
|
-
def add_processor(processor)
|
21
21
|
@processor = processor
|
22
22
|
@processor.add_watch(self)
|
23
|
-
self
|
24
23
|
end
|
25
24
|
|
26
25
|
def watch(path)
|
@@ -52,7 +51,10 @@ module FileWatch
|
|
52
51
|
glob = 0
|
53
52
|
end
|
54
53
|
break if quit?
|
54
|
+
# NOTE: maybe the plugin should validate stat_interval <= sincedb_write_interval <= sincedb_clean_after
|
55
55
|
sleep(@settings.stat_interval)
|
56
|
+
# we need to check potential expired keys (sincedb_clean_after) periodically
|
57
|
+
sincedb_collection.flush_at_interval
|
56
58
|
end
|
57
59
|
sincedb_collection.write_if_requested # does nothing if no requests to write were lodged.
|
58
60
|
@watched_files_collection.close_all
|
@@ -67,20 +69,16 @@ module FileWatch
|
|
67
69
|
watched_files = @watched_files_collection.values
|
68
70
|
@processor.process_all_states(watched_files)
|
69
71
|
ensure
|
70
|
-
@watched_files_collection.remove_paths(@processor.
|
71
|
-
@processor.deletable_filepaths.clear
|
72
|
+
@watched_files_collection.remove_paths(@processor.clear_deletable_paths)
|
72
73
|
end
|
73
|
-
end
|
74
|
+
end
|
74
75
|
|
75
76
|
def quit
|
76
77
|
@quit.make_true
|
77
78
|
end
|
78
79
|
|
79
80
|
def quit?
|
80
|
-
|
81
|
-
@exit = @watched_files_collection.empty?
|
82
|
-
end
|
83
|
-
@quit.true? || @exit
|
81
|
+
@quit.true? || (@settings.exit_after_read && @watched_files_collection.empty?)
|
84
82
|
end
|
85
83
|
|
86
84
|
private
|
@@ -6,7 +6,7 @@ module FileWatch
|
|
6
6
|
IO_BASED_STAT = 1
|
7
7
|
|
8
8
|
attr_reader :bytes_read, :state, :file, :buffer, :recent_states, :bytes_unread
|
9
|
-
attr_reader :path, :accessed_at, :
|
9
|
+
attr_reader :path, :accessed_at, :pathname, :filename
|
10
10
|
attr_reader :listener, :read_loop_count, :read_chunk_size, :stat
|
11
11
|
attr_reader :loop_count_type, :loop_count_mode
|
12
12
|
attr_accessor :last_open_warning_at
|
@@ -16,7 +16,7 @@ module FileWatch
|
|
16
16
|
def initialize(pathname, stat, settings)
|
17
17
|
@settings = settings
|
18
18
|
@pathname = Pathname.new(pathname) # given arg pathname might be a string or a Pathname object
|
19
|
-
@path = @pathname.to_path
|
19
|
+
@path = @pathname.to_path.freeze
|
20
20
|
@filename = @pathname.basename.to_s
|
21
21
|
full_state_reset(stat)
|
22
22
|
watch
|
@@ -24,10 +24,6 @@ module FileWatch
|
|
24
24
|
set_accessed_at
|
25
25
|
end
|
26
26
|
|
27
|
-
def no_restat_reset
|
28
|
-
full_state_reset(@stat)
|
29
|
-
end
|
30
|
-
|
31
27
|
def full_state_reset(this_stat = nil)
|
32
28
|
if this_stat.nil?
|
33
29
|
begin
|
@@ -75,6 +71,7 @@ module FileWatch
|
|
75
71
|
@size = @stat.size
|
76
72
|
@sdb_key_v1 = @stat.inode_struct
|
77
73
|
end
|
74
|
+
private :set_stat
|
78
75
|
|
79
76
|
def rotate_as_file(bytes_read = 0)
|
80
77
|
# rotation, when a sincedb record exists for new inode, but no watched file to rotate from
|
@@ -100,19 +97,33 @@ module FileWatch
|
|
100
97
|
stat_sincedb_key != sincedb_key
|
101
98
|
end
|
102
99
|
|
103
|
-
|
100
|
+
# @return true if the file was modified since last stat
|
101
|
+
def restat!
|
102
|
+
modified_at # to always be able to detect changes
|
104
103
|
@stat.restat
|
105
104
|
if rotation_detected?
|
106
105
|
# switch to new state now
|
107
106
|
rotation_in_progress
|
107
|
+
return true
|
108
108
|
else
|
109
109
|
@size = @stat.size
|
110
110
|
update_bytes_unread
|
111
|
+
modified_at_changed?
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
115
|
+
def modified_at(update = false)
|
116
|
+
if update || @modified_at.nil?
|
117
|
+
@modified_at = @stat.modified_at
|
118
|
+
else
|
119
|
+
@modified_at
|
111
120
|
end
|
112
121
|
end
|
113
122
|
|
114
|
-
|
115
|
-
|
123
|
+
# @return whether modified_at changed since it was last read
|
124
|
+
# @see #restat!
|
125
|
+
def modified_at_changed?
|
126
|
+
modified_at != @stat.modified_at
|
116
127
|
end
|
117
128
|
|
118
129
|
def position_for_new_sincedb_value
|
@@ -405,14 +416,14 @@ module FileWatch
|
|
405
416
|
end
|
406
417
|
|
407
418
|
def details
|
408
|
-
detail = "@filename='#{filename}', @state
|
409
|
-
detail.concat("@bytes_read
|
410
|
-
detail.concat("last_stat_size
|
411
|
-
"<FileWatch::WatchedFile: #{detail},
|
419
|
+
detail = "@filename='#{@filename}', @state=#{@state.inspect}, @recent_states=#{@recent_states.inspect}, "
|
420
|
+
detail.concat("@bytes_read=#{@bytes_read}, @bytes_unread=#{@bytes_unread}, current_size=#{current_size}, ")
|
421
|
+
detail.concat("last_stat_size=#{last_stat_size}, file_open?=#{file_open?}, @initial=#{@initial}")
|
422
|
+
"<FileWatch::WatchedFile: #{detail}, sincedb_key='#{sincedb_key}'>"
|
412
423
|
end
|
413
424
|
|
414
425
|
def inspect
|
415
|
-
"
|
426
|
+
"<FileWatch::WatchedFile: @filename='#{@filename}', @state=#{@state.inspect}, current_size=#{current_size}, sincedb_key='#{sincedb_key}'>"
|
416
427
|
end
|
417
428
|
|
418
429
|
def to_s
|
@@ -1,89 +1,22 @@
|
|
1
1
|
# encoding: utf-8
|
2
|
-
module FileWatch
|
3
|
-
class WatchedFilesCollection
|
4
|
-
|
5
|
-
def initialize(settings)
|
6
|
-
@sort_by = settings.file_sort_by # "last_modified" | "path"
|
7
|
-
@sort_direction = settings.file_sort_direction # "asc" | "desc"
|
8
|
-
@sort_method = method("#{@sort_by}_#{@sort_direction}".to_sym)
|
9
|
-
@files = Concurrent::Array.new
|
10
|
-
@pointers = Concurrent::Hash.new
|
11
|
-
end
|
12
2
|
|
13
|
-
|
14
|
-
@files << watched_file
|
15
|
-
@sort_method.call
|
16
|
-
end
|
3
|
+
require 'java'
|
17
4
|
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
if index
|
22
|
-
watched_file = @files.delete_at(index)
|
23
|
-
refresh_pointers
|
24
|
-
watched_file
|
25
|
-
end
|
26
|
-
end
|
27
|
-
@sort_method.call
|
28
|
-
removed_files
|
29
|
-
end
|
5
|
+
module FileWatch
|
6
|
+
# @see `org.logstash.filewatch.WatchedFilesCollection`
|
7
|
+
class WatchedFilesCollection
|
30
8
|
|
9
|
+
# Closes all managed watched files.
|
10
|
+
# @see FileWatch::WatchedFile#file_close
|
31
11
|
def close_all
|
32
|
-
|
33
|
-
end
|
34
|
-
|
35
|
-
def empty?
|
36
|
-
@files.empty?
|
37
|
-
end
|
38
|
-
|
39
|
-
def keys
|
40
|
-
@pointers.keys
|
12
|
+
each_file(&:file_close) # synchronized
|
41
13
|
end
|
42
14
|
|
43
|
-
|
44
|
-
|
45
|
-
end
|
46
|
-
|
47
|
-
def watched_file_by_path(path)
|
48
|
-
index = @pointers[path]
|
49
|
-
return nil unless index
|
50
|
-
@files[index]
|
51
|
-
end
|
52
|
-
|
53
|
-
private
|
54
|
-
|
55
|
-
def last_modified_asc
|
56
|
-
@files.sort! do |left, right|
|
57
|
-
left.modified_at <=> right.modified_at
|
58
|
-
end
|
59
|
-
refresh_pointers
|
60
|
-
end
|
15
|
+
# @return [Enumerable<String>] managed path keys (snapshot)
|
16
|
+
alias keys paths
|
61
17
|
|
62
|
-
|
63
|
-
|
64
|
-
right.modified_at <=> left.modified_at
|
65
|
-
end
|
66
|
-
refresh_pointers
|
67
|
-
end
|
68
|
-
|
69
|
-
def path_asc
|
70
|
-
@files.sort! do |left, right|
|
71
|
-
left.path <=> right.path
|
72
|
-
end
|
73
|
-
refresh_pointers
|
74
|
-
end
|
18
|
+
# @return [Enumerable<WatchedFile>] managed files (snapshot)
|
19
|
+
alias values files
|
75
20
|
|
76
|
-
def path_desc
|
77
|
-
@files.sort! do |left, right|
|
78
|
-
right.path <=> left.path
|
79
|
-
end
|
80
|
-
refresh_pointers
|
81
|
-
end
|
82
|
-
|
83
|
-
def refresh_pointers
|
84
|
-
@files.each_with_index do |watched_file, index|
|
85
|
-
@pointers[watched_file.path] = index
|
86
|
-
end
|
87
|
-
end
|
88
21
|
end
|
89
22
|
end
|