logstash-input-file 4.1.15 → 4.1.16

Sign up to get free protection for your applications and to get access to all the features.
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: