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 +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:
|