logstash-input-file 4.1.15 → 4.1.16

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: a3c11c5972a494975b2acfa7d7e4f67e0ca4720ff9ec78c864febf764c9b030b
4
- data.tar.gz: f80d8c34dd0848c07029199a6c57d39e25c343e32c499ecb4d350bfd50564119
3
+ metadata.gz: dfc29a2b231d1d04667f304746d22fd1beec91cabeb8f6e05772395bc45e86a5
4
+ data.tar.gz: 952de67a21b89420a91e787935ed0617a798a4c738632f0870d1c54c1162b110
5
5
  SHA512:
6
- metadata.gz: '00392e4f07fe9fdfd732419c4b56b680f5b07aa7e97149ba7af91bca8aee128f396ef2330ec46814b7b3fbc9b52fcc58766a27d50b72bbf28c9b7475dc0aeda0'
7
- data.tar.gz: 76079df610dfe177fd5c538b8ff5325d064f97826af30d441bd9abe54bfc1197f2178ba88862d6f144540a776493cce44efe130119ee8cc8e39c14492203f534
6
+ metadata.gz: 10b29419ea0fd69945fd0f9634d1a5c247516c14cec0b2d22e48155d4a53e71fd83551ffdd8df3dbff71e560bd5ba649deed94c549be92f582d05ec36c329ce1
7
+ data.tar.gz: 16b160f663671c865c72a09b991c6f58bc915b29266d21ffa9f2c5130f62ad1feb469d9cb850f7997cd5bd6a8ae261e24920892ec27cc1ae6196dd4bf93e2b9a
@@ -1,3 +1,7 @@
1
+ ## 4.1.16
2
+ - Added configuration setting exit_after_read to read to EOF and terminate
3
+ the input [#240](https://github.com/logstash-plugins/logstash-input-file/pull/240)
4
+
1
5
  ## 4.1.15
2
6
  - Fixed bug in conversion of sincedb_clean_after setting [#257](https://github.com/logstash-plugins/logstash-input-file/pull/257)
3
7
 
@@ -168,6 +168,7 @@ see <<plugins-{type}s-{plugin}-string_duration,string_duration>> for the details
168
168
  | <<plugins-{type}s-{plugin}-delimiter>> |<<string,string>>|No
169
169
  | <<plugins-{type}s-{plugin}-discover_interval>> |<<number,number>>|No
170
170
  | <<plugins-{type}s-{plugin}-exclude>> |<<array,array>>|No
171
+ | <<plugins-{type}s-{plugin}-exit_after_read>> |<<boolean,boolean>>|No
171
172
  | <<plugins-{type}s-{plugin}-file_chunk_count>> |<<number,number>>|No
172
173
  | <<plugins-{type}s-{plugin}-file_chunk_size>> |<<number,number>>|No
173
174
  | <<plugins-{type}s-{plugin}-file_completed_action>> |<<string,string>>, one of `["delete", "log", "log_and_delete"]`|No
@@ -241,6 +242,19 @@ In Tail mode, you might want to exclude gzipped files:
241
242
  [source,ruby]
242
243
  exclude => "*.gz"
243
244
 
245
+ [id="plugins-{type}s-{plugin}-exit_after_read"]
246
+ ===== `exit_after_read`
247
+
248
+ * Value type is <<boolean,boolean>>
249
+ * Default value is `false`
250
+
251
+ This option can be used in `read` mode to enforce closing all watchers when file gets read.
252
+ Can be used in situation when content of the file is static and won't change during execution.
253
+ When set to `true` it also disables active discovery of the files - only files that were in
254
+ the directories when process was started will be read.
255
+ It supports `sincedb` entries. When file was processed once, then modified - next run will only
256
+ read newly added entries.
257
+
244
258
  [id="plugins-{type}s-{plugin}-file_chunk_count"]
245
259
  ===== `file_chunk_count`
246
260
 
@@ -103,10 +103,21 @@ module FileWatch module ReadMode
103
103
  else
104
104
  read_file(watched_file)
105
105
  end
106
+
107
+ if @settings.exit_after_read
108
+ common_detach_when_allread(watched_file)
109
+ end
106
110
  # handlers take care of closing and unwatching
107
111
  end
108
112
  end
109
113
 
114
+ def common_detach_when_allread(watched_file)
115
+ watched_file.unwatch
116
+ watched_file.listener.reading_completed
117
+ deletable_filepaths << watched_file.path
118
+ logger.trace("Whole file read: #{watched_file.path}, removing from collection")
119
+ end
120
+
110
121
  def common_deleted_reaction(watched_file, action)
111
122
  # file has gone away or we can't read it anymore.
112
123
  watched_file.unwatch
@@ -8,6 +8,7 @@ module FileWatch
8
8
  attr_reader :exclude, :start_new_files_at, :file_chunk_count, :file_chunk_size
9
9
  attr_reader :sincedb_path, :sincedb_write_interval, :sincedb_expiry_duration
10
10
  attr_reader :file_sort_by, :file_sort_direction
11
+ attr_reader :exit_after_read
11
12
 
12
13
  def self.from_options(opts)
13
14
  new.add_options(opts)
@@ -50,6 +51,7 @@ module FileWatch
50
51
  @sincedb_expiry_duration = @opts.fetch(:sincedb_clean_after)
51
52
  @file_sort_by = @opts[:file_sort_by]
52
53
  @file_sort_direction = @opts[:file_sort_direction]
54
+ @exit_after_read = @opts[:exit_after_read]
53
55
  self
54
56
  end
55
57
 
@@ -43,10 +43,11 @@ module FileWatch
43
43
  reset_quit
44
44
  until quit?
45
45
  iterate_on_state
46
+ # Don't discover new files when files to read are known at the beginning
46
47
  break if quit?
47
48
  sincedb_collection.write_if_requested
48
49
  glob += 1
49
- if glob == interval
50
+ if glob == interval && !@settings.exit_after_read
50
51
  discover
51
52
  glob = 0
52
53
  end
@@ -76,7 +77,10 @@ module FileWatch
76
77
  end
77
78
 
78
79
  def quit?
79
- @quit.true?
80
+ if @settings.exit_after_read
81
+ @exit = @watched_files_collection.empty?
82
+ end
83
+ @quit.true? || @exit
80
84
  end
81
85
 
82
86
  private
Binary file
@@ -222,6 +222,11 @@ class File < LogStash::Inputs::Base
222
222
  # perhaps path + asc will help to achieve the goal of controlling the order of file ingestion
223
223
  config :file_sort_direction, :validate => ["asc", "desc"], :default => "asc"
224
224
 
225
+ # When in 'read' mode - this option is closing all file watchers when EOF is hit
226
+ # This option also disables discovery of new/changes files. It works only on files found at the beginning
227
+ # Sincedb still works, if you run LS once again after doing some changes - only new values will be read
228
+ config :exit_after_read, :validate => :boolean, :default => false
229
+
225
230
  public
226
231
 
227
232
  class << self
@@ -260,6 +265,7 @@ class File < LogStash::Inputs::Base
260
265
  :file_chunk_size => @file_chunk_size,
261
266
  :file_sort_by => @file_sort_by,
262
267
  :file_sort_direction => @file_sort_direction,
268
+ :exit_after_read => @exit_after_read,
263
269
  }
264
270
 
265
271
  @completed_file_handlers = []
@@ -280,7 +286,7 @@ class File < LogStash::Inputs::Base
280
286
  raise ArgumentError.new("The \"sincedb_path\" argument must point to a file, received a directory: \"#{@sincedb_path}\"")
281
287
  end
282
288
  end
283
-
289
+
284
290
  @filewatch_config[:sincedb_path] = @sincedb_path
285
291
 
286
292
  @filewatch_config[:start_new_files_at] = @start_position.to_sym
@@ -301,6 +307,9 @@ class File < LogStash::Inputs::Base
301
307
  end
302
308
 
303
309
  if tail_mode?
310
+ if @exit_after_read
311
+ raise ArgumentError.new('The "exit_after_read" setting only works when the "mode" is set to "read"')
312
+ end
304
313
  @watcher_class = FileWatch::ObservingTail
305
314
  else
306
315
  @watcher_class = FileWatch::ObservingRead
@@ -21,6 +21,9 @@ module LogStash module Inputs
21
21
  def error
22
22
  end
23
23
 
24
+ def reading_completed
25
+ end
26
+
24
27
  def timed_out
25
28
  input.codec.evict(path)
26
29
  end
@@ -1,7 +1,7 @@
1
1
  Gem::Specification.new do |s|
2
2
 
3
3
  s.name = 'logstash-input-file'
4
- s.version = '4.1.15'
4
+ s.version = '4.1.16'
5
5
  s.licenses = ['Apache-2.0']
6
6
  s.summary = "Streams events from files"
7
7
  s.description = "This gem is a Logstash plugin required to be installed on top of the Logstash core pipeline using $LS_HOME/bin/logstash-plugin install gemname. This gem is not a stand-alone program"
@@ -34,7 +34,7 @@ Gem::Specification.new do |s|
34
34
  s.add_runtime_dependency 'logstash-codec-multiline', ['~> 3.0']
35
35
 
36
36
  s.add_development_dependency 'stud', ['~> 0.0.19']
37
- s.add_development_dependency 'logstash-devutils'
37
+ s.add_development_dependency 'logstash-devutils', '~> 1.3'
38
38
  s.add_development_dependency 'logstash-codec-json'
39
39
  s.add_development_dependency 'rspec-sequencing'
40
40
  s.add_development_dependency "rspec-wait"
@@ -147,6 +147,65 @@ module FileWatch
147
147
  end
148
148
  end
149
149
 
150
+ context "when watching a directory with files using exit_after_read" do
151
+ let(:opts) { super.merge(:exit_after_read => true, :max_open_files => 2) }
152
+ let(:file_path3) { ::File.join(directory, "3.log") }
153
+ let(:file_path4) { ::File.join(directory, "4.log") }
154
+ let(:file_path5) { ::File.join(directory, "5.log") }
155
+ let(:lines) { [] }
156
+ let(:observer) { TestObserver.new(lines) }
157
+ let(:listener3) { observer.listener_for(file_path3) }
158
+ let(:file_path6) { ::File.join(directory, "6.log") }
159
+ let(:listener6) { observer.listener_for(file_path6) }
160
+
161
+ it "the file is read" do
162
+ File.open(file_path3, "w") { |file| file.write("line1\nline2\n") }
163
+ reading.watch_this(watch_dir)
164
+ reading.subscribe(observer)
165
+ expect(listener3.lines).to eq(["line1", "line2"])
166
+ end
167
+ it "multiple files are read" do
168
+ File.open(file_path3, "w") { |file| file.write("line1\nline2\n") }
169
+ File.open(file_path4, "w") { |file| file.write("line3\nline4\n") }
170
+ reading.watch_this(watch_dir)
171
+ reading.subscribe(observer)
172
+ expect(listener3.lines.sort).to eq(["line1", "line2", "line3", "line4"])
173
+ end
174
+ it "multiple files are read even if max_open_files is smaller then number of files" do
175
+ File.open(file_path3, "w") { |file| file.write("line1\nline2\n") }
176
+ File.open(file_path4, "w") { |file| file.write("line3\nline4\n") }
177
+ File.open(file_path5, "w") { |file| file.write("line5\nline6\n") }
178
+ reading.watch_this(watch_dir)
179
+ reading.subscribe(observer)
180
+ expect(listener3.lines.sort).to eq(["line1", "line2", "line3", "line4", "line5", "line6"])
181
+ end
182
+ it "file as marked as reading_completed" do
183
+ File.open(file_path3, "w") { |file| file.write("line1\nline2\n") }
184
+ reading.watch_this(watch_dir)
185
+ reading.subscribe(observer)
186
+ expect(listener3.calls).to eq([:open, :accept, :accept, :eof, :delete, :reading_completed])
187
+ end
188
+ it "sincedb works correctly" do
189
+ File.open(file_path3, "w") { |file| file.write("line1\nline2\n") }
190
+ reading.watch_this(watch_dir)
191
+ reading.subscribe(observer)
192
+ sincedb_record_fields = File.read(sincedb_path).split(" ")
193
+ position_field_index = 3
194
+ expect(sincedb_record_fields[position_field_index]).to eq("12")
195
+ end
196
+ it "does not include new files added after start" do
197
+ File.open(file_path3, "w") { |file| file.write("line1\nline2\n") }
198
+ reading.watch_this(watch_dir)
199
+ reading.subscribe(observer)
200
+ File.open(file_path6, "w") { |file| file.write("foob\nbar\n") }
201
+ expect(listener3.lines).to eq(["line1", "line2"])
202
+ expect(listener3.calls).to eq([:open, :accept, :accept, :eof, :delete, :reading_completed])
203
+ expect(listener6.calls).to eq([])
204
+
205
+ end
206
+
207
+ end
208
+
150
209
  describe "reading fixtures" do
151
210
  let(:directory) { FIXTURE_DIR }
152
211
  let(:actions) do
@@ -152,6 +152,10 @@ module FileWatch
152
152
  def timed_out
153
153
  @calls << :timed_out
154
154
  end
155
+
156
+ def reading_completed
157
+ @calls << :reading_completed
158
+ end
155
159
  end
156
160
 
157
161
  attr_reader :listeners
@@ -448,7 +448,7 @@ module FileWatch
448
448
  FileUtils.mv(file_path2, file_path3)
449
449
  end
450
450
  .then("wait") do
451
- wait(2).for do
451
+ wait(4).for do
452
452
  listener1.lines.size == 32 && listener2.calls == [:delete] && listener3.calls == [:open, :accept, :timed_out]
453
453
  end.to eq(true), "listener1.lines != 32 or listener2.calls != [:delete] or listener3.calls != [:open, :accept, :timed_out]"
454
454
  end
@@ -75,6 +75,40 @@ describe LogStash::Inputs::File do
75
75
  end
76
76
  expect(events.map{|e| e.get("message")}).to contain_exactly("hello", "world")
77
77
  end
78
+
79
+ it "should read whole file when exit_after_read is set to true" do
80
+ directory = Stud::Temporary.directory
81
+ tmpfile_path = ::File.join(directory, "B.log")
82
+ sincedb_path = ::File.join(directory, "readmode_B_sincedb.txt")
83
+ path_path = ::File.join(directory, "*.log")
84
+
85
+ conf = <<-CONFIG
86
+ input {
87
+ file {
88
+ id => "foo"
89
+ path => "#{path_path}"
90
+ sincedb_path => "#{sincedb_path}"
91
+ delimiter => "|"
92
+ mode => "read"
93
+ file_completed_action => "delete"
94
+ exit_after_read => true
95
+ }
96
+ }
97
+ CONFIG
98
+
99
+ File.open(tmpfile_path, "a") do |fd|
100
+ fd.write("exit|after|end")
101
+ fd.fsync
102
+ end
103
+
104
+ events = input(conf) do |pipeline, queue|
105
+ wait(0.5).for{File.exist?(tmpfile_path)}.to be_falsey
106
+ 3.times.collect { queue.pop }
107
+ end
108
+
109
+ expect(events.map{|e| e.get("message")}).to contain_exactly("exit", "after", "end")
110
+ end
111
+
78
112
  end
79
113
 
80
114
  describe "reading fixtures" do
@@ -175,7 +175,7 @@ describe LogStash::Inputs::File do
175
175
  context "when sincedb_path is a directory" do
176
176
  let(:name) { "E" }
177
177
  subject { LogStash::Inputs::File.new("path" => path_path, "sincedb_path" => directory) }
178
-
178
+
179
179
  after :each do
180
180
  FileUtils.rm_rf(sincedb_path)
181
181
  end
@@ -184,6 +184,15 @@ describe LogStash::Inputs::File do
184
184
  expect { subject.register }.to raise_error(ArgumentError)
185
185
  end
186
186
  end
187
+
188
+ context "when mode it set to tail and exit_after_read equals true" do
189
+ subject { LogStash::Inputs::File.new("path" => path_path, "exit_after_read" => true, "mode" => "tail") }
190
+
191
+ it "should raise exception" do
192
+ expect { subject.register }.to raise_error(ArgumentError)
193
+ end
194
+ end
195
+
187
196
  end
188
197
 
189
198
  describe "testing with new, register, run and stop" do
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: logstash-input-file
3
3
  version: !ruby/object:Gem::Version
4
- version: 4.1.15
4
+ version: 4.1.16
5
5
  platform: ruby
6
6
  authors:
7
7
  - Elastic
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2020-02-04 00:00:00.000000000 Z
11
+ date: 2020-02-20 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  requirement: !ruby/object:Gem::Requirement
@@ -89,17 +89,17 @@ dependencies:
89
89
  - !ruby/object:Gem::Dependency
90
90
  requirement: !ruby/object:Gem::Requirement
91
91
  requirements:
92
- - - ">="
92
+ - - "~>"
93
93
  - !ruby/object:Gem::Version
94
- version: '0'
94
+ version: '1.3'
95
95
  name: logstash-devutils
96
96
  prerelease: false
97
97
  type: :development
98
98
  version_requirements: !ruby/object:Gem::Requirement
99
99
  requirements:
100
- - - ">="
100
+ - - "~>"
101
101
  - !ruby/object:Gem::Version
102
- version: '0'
102
+ version: '1.3'
103
103
  - !ruby/object:Gem::Dependency
104
104
  requirement: !ruby/object:Gem::Requirement
105
105
  requirements: