logstash-input-file 4.1.17 → 4.2.3

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.
Files changed (37) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +16 -0
  3. data/lib/filewatch/discoverer.rb +9 -8
  4. data/lib/filewatch/observing_base.rb +1 -12
  5. data/lib/filewatch/processor.rb +55 -0
  6. data/lib/filewatch/read_mode/handlers/base.rb +12 -11
  7. data/lib/filewatch/read_mode/handlers/read_file.rb +26 -8
  8. data/lib/filewatch/read_mode/handlers/read_zip_file.rb +8 -6
  9. data/lib/filewatch/read_mode/processor.rb +22 -36
  10. data/lib/filewatch/settings.rb +1 -2
  11. data/lib/filewatch/sincedb_collection.rb +39 -40
  12. data/lib/filewatch/sincedb_record_serializer.rb +5 -11
  13. data/lib/filewatch/stat/generic.rb +8 -13
  14. data/lib/filewatch/stat/windows_path.rb +7 -9
  15. data/lib/filewatch/tail_mode/handlers/base.rb +32 -23
  16. data/lib/filewatch/tail_mode/handlers/delete.rb +2 -4
  17. data/lib/filewatch/tail_mode/handlers/shrink.rb +2 -3
  18. data/lib/filewatch/tail_mode/handlers/unignore.rb +4 -4
  19. data/lib/filewatch/tail_mode/processor.rb +47 -54
  20. data/lib/filewatch/watch.rb +12 -14
  21. data/lib/filewatch/watched_file.rb +25 -14
  22. data/lib/filewatch/watched_files_collection.rb +11 -74
  23. data/lib/jars/filewatch-1.0.1.jar +0 -0
  24. data/lib/logstash/inputs/delete_completed_file_handler.rb +5 -0
  25. data/lib/logstash/inputs/file.rb +28 -13
  26. data/lib/logstash/inputs/file_listener.rb +3 -14
  27. data/logstash-input-file.gemspec +2 -1
  28. data/spec/filewatch/reading_spec.rb +60 -9
  29. data/spec/filewatch/settings_spec.rb +3 -0
  30. data/spec/filewatch/sincedb_record_serializer_spec.rb +6 -2
  31. data/spec/filewatch/spec_helper.rb +12 -14
  32. data/spec/filewatch/tailing_spec.rb +14 -12
  33. data/spec/filewatch/watched_file_spec.rb +30 -0
  34. data/spec/filewatch/watched_files_collection_spec.rb +62 -8
  35. data/spec/helpers/spec_helper.rb +1 -0
  36. data/spec/inputs/file_read_spec.rb +119 -0
  37. metadata +17 -2
@@ -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", "watched file" => watched_file.details, "sincedb value" => sincedb_value)
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 |val|
17
- val.set_watched_file(watched_file)
18
- logger.trace("-------------------- >>>>> get_new_value_specifically: unignore", "watched file" => watched_file.details, "sincedb value" => val)
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("-------------------- >>>>> update_existing_specifically: unignore", "watched file" => watched_file.details, "sincedb value" => sincedb_value)
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 "logstash/util/loggable"
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
- # logger.trace("Closed processing")
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.select {|wf| wf.closed? }.each do |watched_file|
98
- common_restat_with_delay(watched_file, "Closed") do
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
- # logger.trace("Ignored processing")
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.select {|wf| wf.ignored? }.each do |watched_file|
117
- common_restat_with_delay(watched_file, "Ignored") do
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("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)
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("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")
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("Rotation In Progress processing")
151
- watched_files.select {|wf| wf.rotation_in_progress?}.each do |watched_file|
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", "watched_file details" => watched_file.details)
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", "watched_file details" => watched_file.details)
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", "watched_file" => watched_file.details, "found_sdb_value" => sdb_value, "potential_key" => potential_key, "potential_sdb_value" => potential_sdb_value)
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...", "this watched_file details" => watched_file.details, "other watched_file details" => other_watched_file.details)
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", "this watched_file details" => watched_file.details, "sincedb_value" => (potential_sdb_value || sdb_value))
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("Watched processing")
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{|wf| wf.active?}
203
+ to_take = @settings.max_active - watched_files.count(&:active?)
212
204
  if to_take > 0
213
- watched_files.select {|wf| wf.watched?}.take(to_take).each do |watched_file|
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 + ", files yet to open: #{waiting}")
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
- # logger.trace("Active processing")
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.select {|wf| wf.active? }.each do |watched_file|
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("Active - file grew: #{path}: new size is #{watched_file.last_stat_size}, bytes read #{watched_file.bytes_read}")
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("Active - shrunk: DATA LOSS!! truncate detected with #{watched_file.bytes_unread} unread bytes: #{path}")
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("Active - file shrunk #{path}: new size is #{watched_file.last_stat_size}, old size #{watched_file.bytes_read}")
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("Active - no change", "watched_file" => watched_file.details)
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("Watch each: active: file expired: #{path}")
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.restat
266
+ restat(watched_file)
274
267
  if watched_file.rotation_in_progress?
275
- logger.trace("-------------------- >>>>> restat - rotation_detected", "watched_file details" => watched_file.details, "new sincedb key" => watched_file.stat_sincedb_key)
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: #{watched_file.filename}")
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: #{watched_file.filename}")
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: #{watched_file.filename}")
281
+ logger.trace("#{action} - removing from collection", :filename => watched_file.filename)
289
282
  delete(watched_file)
290
- deletable_filepaths << watched_file.path
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 #{watched_file.path}: (#{e.message}, #{e.backtrace.take(8).inspect})")
287
+ logger.error("#{action} - other error", error_details(e, watched_file))
295
288
  all_ok = false
296
289
  end
297
290
  all_ok
@@ -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, watched_files_collection, settings)
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.delete(@processor.deletable_filepaths)
71
- @processor.deletable_filepaths.clear
72
+ @watched_files_collection.remove_paths(@processor.clear_deletable_paths)
72
73
  end
73
- end # def each
74
+ end
74
75
 
75
76
  def quit
76
77
  @quit.make_true
77
78
  end
78
79
 
79
80
  def quit?
80
- if @settings.exit_after_read
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, :modified_at, :pathname, :filename
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
- def restat
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
- def modified_at
115
- @stat.modified_at
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='#{state}', @recent_states='#{@recent_states.inspect}', "
409
- detail.concat("@bytes_read='#{@bytes_read}', @bytes_unread='#{@bytes_unread}', current_size='#{current_size}', ")
410
- detail.concat("last_stat_size='#{last_stat_size}', file_open?='#{file_open?}', @initial=#{@initial}")
411
- "<FileWatch::WatchedFile: #{detail}, @sincedb_key='#{sincedb_key}'>"
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
- "\"<FileWatch::WatchedFile: @filename='#{filename}', @state='#{state}', @sincedb_key='#{sincedb_key}, size=#{@size}>\""
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,85 +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
- def add(watched_file)
14
- @files << watched_file
15
- @sort_method.call
16
- end
3
+ require 'java'
17
4
 
18
- def delete(paths)
19
- Array(paths).each do |f|
20
- index = @pointers.delete(f)
21
- @files.delete_at(index)
22
- refresh_pointers
23
- end
24
- @sort_method.call
25
- end
5
+ module FileWatch
6
+ # @see `org.logstash.filewatch.WatchedFilesCollection`
7
+ class WatchedFilesCollection
26
8
 
9
+ # Closes all managed watched files.
10
+ # @see FileWatch::WatchedFile#file_close
27
11
  def close_all
28
- @files.each(&:file_close)
29
- end
30
-
31
- def empty?
32
- @files.empty?
33
- end
34
-
35
- def keys
36
- @pointers.keys
12
+ each_file(&:file_close) # synchronized
37
13
  end
38
14
 
39
- def values
40
- @files
41
- end
42
-
43
- def watched_file_by_path(path)
44
- index = @pointers[path]
45
- return nil unless index
46
- @files[index]
47
- end
48
-
49
- private
50
-
51
- def last_modified_asc
52
- @files.sort! do |left, right|
53
- left.modified_at <=> right.modified_at
54
- end
55
- refresh_pointers
56
- end
15
+ # @return [Enumerable<String>] managed path keys (snapshot)
16
+ alias keys paths
57
17
 
58
- def last_modified_desc
59
- @files.sort! do |left, right|
60
- right.modified_at <=> left.modified_at
61
- end
62
- refresh_pointers
63
- end
64
-
65
- def path_asc
66
- @files.sort! do |left, right|
67
- left.path <=> right.path
68
- end
69
- refresh_pointers
70
- end
18
+ # @return [Enumerable<WatchedFile>] managed files (snapshot)
19
+ alias values files
71
20
 
72
- def path_desc
73
- @files.sort! do |left, right|
74
- right.path <=> left.path
75
- end
76
- refresh_pointers
77
- end
78
-
79
- def refresh_pointers
80
- @files.each_with_index do |watched_file, index|
81
- @pointers[watched_file.path] = index
82
- end
83
- end
84
21
  end
85
22
  end