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 +4 -4
- data/CHANGELOG.md +4 -0
- data/docs/index.asciidoc +14 -0
- data/lib/filewatch/read_mode/processor.rb +11 -0
- data/lib/filewatch/settings.rb +2 -0
- data/lib/filewatch/watch.rb +6 -2
- data/lib/jars/filewatch-1.0.1.jar +0 -0
- data/lib/logstash/inputs/file.rb +10 -1
- data/lib/logstash/inputs/file_listener.rb +3 -0
- data/logstash-input-file.gemspec +2 -2
- data/spec/filewatch/reading_spec.rb +59 -0
- data/spec/filewatch/spec_helper.rb +4 -0
- data/spec/filewatch/tailing_spec.rb +1 -1
- data/spec/inputs/file_read_spec.rb +34 -0
- data/spec/inputs/file_tail_spec.rb +10 -1
- metadata +6 -6
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: dfc29a2b231d1d04667f304746d22fd1beec91cabeb8f6e05772395bc45e86a5
|
4
|
+
data.tar.gz: 952de67a21b89420a91e787935ed0617a798a4c738632f0870d1c54c1162b110
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 10b29419ea0fd69945fd0f9634d1a5c247516c14cec0b2d22e48155d4a53e71fd83551ffdd8df3dbff71e560bd5ba649deed94c549be92f582d05ec36c329ce1
|
7
|
+
data.tar.gz: 16b160f663671c865c72a09b991c6f58bc915b29266d21ffa9f2c5130f62ad1feb469d9cb850f7997cd5bd6a8ae261e24920892ec27cc1ae6196dd4bf93e2b9a
|
data/CHANGELOG.md
CHANGED
@@ -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
|
|
data/docs/index.asciidoc
CHANGED
@@ -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
|
data/lib/filewatch/settings.rb
CHANGED
@@ -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
|
|
data/lib/filewatch/watch.rb
CHANGED
@@ -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
|
-
@
|
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
|
data/lib/logstash/inputs/file.rb
CHANGED
@@ -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
|
data/logstash-input-file.gemspec
CHANGED
@@ -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.
|
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
|
@@ -448,7 +448,7 @@ module FileWatch
|
|
448
448
|
FileUtils.mv(file_path2, file_path3)
|
449
449
|
end
|
450
450
|
.then("wait") do
|
451
|
-
wait(
|
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.
|
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-
|
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: '
|
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: '
|
102
|
+
version: '1.3'
|
103
103
|
- !ruby/object:Gem::Dependency
|
104
104
|
requirement: !ruby/object:Gem::Requirement
|
105
105
|
requirements:
|