logstash-input-file 4.1.3 → 4.1.4
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 +10 -0
- data/JAR_VERSION +1 -1
- data/README.md +0 -3
- data/docs/index.asciidoc +26 -16
- data/lib/filewatch/bootstrap.rb +10 -21
- data/lib/filewatch/discoverer.rb +35 -28
- data/lib/filewatch/observing_base.rb +2 -1
- data/lib/filewatch/read_mode/handlers/base.rb +19 -6
- data/lib/filewatch/read_mode/handlers/read_file.rb +43 -32
- data/lib/filewatch/read_mode/handlers/read_zip_file.rb +8 -3
- data/lib/filewatch/read_mode/processor.rb +8 -8
- data/lib/filewatch/settings.rb +3 -3
- data/lib/filewatch/sincedb_collection.rb +56 -42
- data/lib/filewatch/sincedb_value.rb +6 -0
- data/lib/filewatch/stat/generic.rb +34 -0
- data/lib/filewatch/stat/windows_path.rb +32 -0
- data/lib/filewatch/tail_mode/handlers/base.rb +40 -22
- data/lib/filewatch/tail_mode/handlers/create.rb +1 -2
- data/lib/filewatch/tail_mode/handlers/create_initial.rb +2 -1
- data/lib/filewatch/tail_mode/handlers/delete.rb +13 -1
- data/lib/filewatch/tail_mode/handlers/grow.rb +5 -2
- data/lib/filewatch/tail_mode/handlers/shrink.rb +7 -4
- data/lib/filewatch/tail_mode/handlers/unignore.rb +4 -2
- data/lib/filewatch/tail_mode/processor.rb +147 -58
- data/lib/filewatch/watch.rb +15 -35
- data/lib/filewatch/watched_file.rb +237 -41
- data/lib/filewatch/watched_files_collection.rb +2 -2
- data/lib/filewatch/winhelper.rb +167 -25
- data/lib/jars/filewatch-1.0.1.jar +0 -0
- data/lib/logstash/inputs/file.rb +9 -2
- data/logstash-input-file.gemspec +9 -2
- data/spec/file_ext/file_ext_windows_spec.rb +36 -0
- data/spec/filewatch/read_mode_handlers_read_file_spec.rb +2 -2
- data/spec/filewatch/reading_spec.rb +100 -57
- data/spec/filewatch/rotate_spec.rb +451 -0
- data/spec/filewatch/spec_helper.rb +33 -10
- data/spec/filewatch/tailing_spec.rb +273 -153
- data/spec/filewatch/watched_file_spec.rb +3 -3
- data/spec/filewatch/watched_files_collection_spec.rb +3 -3
- data/spec/filewatch/winhelper_spec.rb +4 -5
- data/spec/helpers/logging_level_helper.rb +8 -0
- data/spec/helpers/rspec_wait_handler_helper.rb +38 -0
- data/spec/helpers/spec_helper.rb +7 -1
- data/spec/inputs/file_read_spec.rb +54 -24
- data/spec/inputs/file_tail_spec.rb +244 -284
- metadata +13 -3
- data/lib/jars/filewatch-1.0.0.jar +0 -0
@@ -1,7 +1,8 @@
|
|
1
1
|
# encoding: utf-8
|
2
2
|
require "rspec_sequencing"
|
3
|
-
require 'rspec/wait'
|
3
|
+
# require 'rspec/wait'
|
4
4
|
require "logstash/devutils/rspec/spec_helper"
|
5
|
+
require "concurrent"
|
5
6
|
require "timecop"
|
6
7
|
|
7
8
|
def formatted_puts(text)
|
@@ -24,9 +25,32 @@ unless RSpec::Matchers.method_defined?(:receive_call_and_args)
|
|
24
25
|
end
|
25
26
|
end
|
26
27
|
|
28
|
+
require_relative "../helpers/rspec_wait_handler_helper" unless defined? RSPEC_WAIT_HANDLER_PATCHED
|
29
|
+
require_relative "../helpers/logging_level_helper" unless defined? LOG_AT_HANDLED
|
30
|
+
|
27
31
|
require 'filewatch/bootstrap'
|
28
32
|
|
29
33
|
module FileWatch
|
34
|
+
class DummyIO
|
35
|
+
def stat
|
36
|
+
self
|
37
|
+
end
|
38
|
+
def ino
|
39
|
+
23456
|
40
|
+
end
|
41
|
+
def size
|
42
|
+
65535
|
43
|
+
end
|
44
|
+
def mtime
|
45
|
+
Time.now
|
46
|
+
end
|
47
|
+
def dev_major
|
48
|
+
1
|
49
|
+
end
|
50
|
+
def dev_minor
|
51
|
+
5
|
52
|
+
end
|
53
|
+
end
|
30
54
|
|
31
55
|
class DummyFileReader
|
32
56
|
def initialize(read_size, iterations)
|
@@ -34,6 +58,7 @@ module FileWatch
|
|
34
58
|
@iterations = iterations
|
35
59
|
@closed = false
|
36
60
|
@accumulated = 0
|
61
|
+
@io = DummyIO.new
|
37
62
|
end
|
38
63
|
def file_seek(*)
|
39
64
|
end
|
@@ -43,6 +68,9 @@ module FileWatch
|
|
43
68
|
def closed?
|
44
69
|
@closed
|
45
70
|
end
|
71
|
+
def to_io
|
72
|
+
@io
|
73
|
+
end
|
46
74
|
def sysread(amount)
|
47
75
|
@accumulated += amount
|
48
76
|
if @accumulated > @read_size * @iterations
|
@@ -67,7 +95,7 @@ module FileWatch
|
|
67
95
|
|
68
96
|
class TracerBase
|
69
97
|
def initialize
|
70
|
-
@tracer =
|
98
|
+
@tracer = Concurrent::Array.new
|
71
99
|
end
|
72
100
|
|
73
101
|
def trace_for(symbol)
|
@@ -91,8 +119,8 @@ module FileWatch
|
|
91
119
|
|
92
120
|
def initialize(path)
|
93
121
|
@path = path
|
94
|
-
@lines =
|
95
|
-
@calls =
|
122
|
+
@lines = Concurrent::Array.new
|
123
|
+
@calls = Concurrent::Array.new
|
96
124
|
end
|
97
125
|
|
98
126
|
def add_lines(lines)
|
@@ -134,7 +162,7 @@ module FileWatch
|
|
134
162
|
else
|
135
163
|
lambda{|k| Listener.new(k).add_lines(combined_lines) }
|
136
164
|
end
|
137
|
-
@listeners = Hash.new {|hash, key| hash[key] = listener_proc.call(key) }
|
165
|
+
@listeners = Concurrent::Hash.new {|hash, key| hash[key] = listener_proc.call(key) }
|
138
166
|
end
|
139
167
|
|
140
168
|
def listener_for(path)
|
@@ -145,8 +173,3 @@ module FileWatch
|
|
145
173
|
@listeners.clear; end
|
146
174
|
end
|
147
175
|
end
|
148
|
-
|
149
|
-
ENV["LOG_AT"].tap do |level|
|
150
|
-
LogStash::Logging::Logger::configure_logging(level) unless level.nil?
|
151
|
-
end
|
152
|
-
|
@@ -5,42 +5,45 @@ require 'filewatch/observing_tail'
|
|
5
5
|
|
6
6
|
module FileWatch
|
7
7
|
describe Watch do
|
8
|
-
before(:all) do
|
9
|
-
@thread_abort = Thread.abort_on_exception
|
10
|
-
Thread.abort_on_exception = true
|
11
|
-
end
|
12
|
-
|
13
|
-
after(:all) do
|
14
|
-
Thread.abort_on_exception = @thread_abort
|
15
|
-
end
|
16
|
-
|
17
8
|
let(:directory) { Stud::Temporary.directory }
|
18
|
-
let(:watch_dir)
|
19
|
-
let(:file_path)
|
9
|
+
let(:watch_dir) { ::File.join(directory, "*#{suffix}.log") }
|
10
|
+
let(:file_path) { ::File.join(directory, "1#{suffix}.log") }
|
11
|
+
let(:file_path2) { ::File.join(directory, "2#{suffix}.log") }
|
12
|
+
let(:file_path3) { ::File.join(directory, "3#{suffix}.log") }
|
20
13
|
let(:max) { 4095 }
|
21
14
|
let(:stat_interval) { 0.1 }
|
22
15
|
let(:discover_interval) { 4 }
|
23
|
-
let(:start_new_files_at) { :
|
16
|
+
let(:start_new_files_at) { :end }
|
24
17
|
let(:sincedb_path) { ::File.join(directory, "tailing.sdb") }
|
25
18
|
let(:opts) do
|
26
19
|
{
|
27
|
-
:stat_interval => stat_interval, :start_new_files_at => start_new_files_at, :
|
28
|
-
:delimiter => "\n", :discover_interval => discover_interval, :sincedb_path => sincedb_path
|
20
|
+
:stat_interval => stat_interval, :start_new_files_at => start_new_files_at, :max_open_files => max,
|
21
|
+
:delimiter => "\n", :discover_interval => discover_interval, :sincedb_path => sincedb_path,
|
22
|
+
:file_sort_by => "path"
|
29
23
|
}
|
30
24
|
end
|
31
25
|
let(:observer) { TestObserver.new }
|
26
|
+
let(:listener1) { observer.listener_for(file_path) }
|
27
|
+
let(:listener2) { observer.listener_for(file_path2) }
|
28
|
+
let(:listener3) { observer.listener_for(file_path3) }
|
32
29
|
let(:tailing) { ObservingTail.new(opts) }
|
33
30
|
|
31
|
+
before do
|
32
|
+
directory
|
33
|
+
wait(1.0).for{Dir.exist?(directory)}.to eq(true)
|
34
|
+
end
|
35
|
+
|
34
36
|
after do
|
35
37
|
FileUtils.rm_rf(directory)
|
38
|
+
wait(1.0).for{Dir.exist?(directory)}.to eq(false)
|
36
39
|
end
|
37
40
|
|
38
41
|
describe "max open files (set to 1)" do
|
39
42
|
let(:max) { 1 }
|
40
|
-
let(:file_path2) { File.join(directory, "2.log") }
|
41
43
|
let(:wait_before_quit) { 0.15 }
|
42
44
|
let(:stat_interval) { 0.01 }
|
43
45
|
let(:discover_interval) { 4 }
|
46
|
+
let(:start_new_files_at) { :beginning }
|
44
47
|
let(:actions) do
|
45
48
|
RSpec::Sequencing
|
46
49
|
.run_after(wait_before_quit, "quit after a short time") do
|
@@ -51,94 +54,110 @@ module FileWatch
|
|
51
54
|
before do
|
52
55
|
ENV["FILEWATCH_MAX_FILES_WARN_INTERVAL"] = "0"
|
53
56
|
File.open(file_path, "wb") { |file| file.write("line1\nline2\n") }
|
54
|
-
File.open(file_path2, "wb") { |file| file.write("
|
57
|
+
File.open(file_path2, "wb") { |file| file.write("line-A\nline-B\n") }
|
55
58
|
end
|
56
59
|
|
57
60
|
context "when max_active is 1" do
|
61
|
+
let(:suffix) { "A" }
|
58
62
|
it "without close_older set, opens only 1 file" do
|
59
|
-
actions.
|
63
|
+
actions.activate_quietly
|
64
|
+
# create files before first discovery, they will be read from the end
|
60
65
|
tailing.watch_this(watch_dir)
|
61
66
|
tailing.subscribe(observer)
|
67
|
+
actions.assert_no_errors
|
62
68
|
expect(tailing.settings.max_active).to eq(max)
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
if file1_calls.empty?
|
67
|
-
expect(observer.listener_for(file_path2).lines).to eq(["lineA", "lineB"])
|
68
|
-
expect(file2_calls).to eq([:open, :accept, :accept])
|
69
|
-
else
|
70
|
-
expect(observer.listener_for(file_path).lines).to eq(["line1", "line2"])
|
71
|
-
expect(file1_calls).to eq([:open, :accept, :accept])
|
72
|
-
expect(file2_calls).to be_empty
|
73
|
-
end
|
69
|
+
expect(listener1.lines).to eq(["line1", "line2"])
|
70
|
+
expect(listener1.calls).to eq([:open, :accept, :accept])
|
71
|
+
expect(listener2.calls).to be_empty
|
74
72
|
end
|
75
73
|
end
|
76
74
|
|
77
75
|
context "when close_older is set" do
|
78
76
|
let(:wait_before_quit) { 0.8 }
|
79
|
-
let(:opts) { super.merge(:close_older => 0.
|
77
|
+
let(:opts) { super.merge(:close_older => 0.1, :max_open_files => 1, :stat_interval => 0.1) }
|
78
|
+
let(:suffix) { "B" }
|
80
79
|
it "opens both files" do
|
81
|
-
actions.
|
80
|
+
actions.activate_quietly
|
82
81
|
tailing.watch_this(watch_dir)
|
83
82
|
tailing.subscribe(observer)
|
83
|
+
actions.assert_no_errors
|
84
84
|
expect(tailing.settings.max_active).to eq(1)
|
85
|
-
|
86
|
-
|
87
|
-
expect(
|
88
|
-
expect(
|
89
|
-
expect(filelistener_1.calls).to eq([:open, :accept, :accept, :timed_out])
|
90
|
-
expect(filelistener_1.lines).to eq(["line1", "line2"])
|
85
|
+
expect(listener2.calls).to eq([:open, :accept, :accept, :timed_out])
|
86
|
+
expect(listener2.lines).to eq(["line-A", "line-B"])
|
87
|
+
expect(listener1.calls).to eq([:open, :accept, :accept, :timed_out])
|
88
|
+
expect(listener1.lines).to eq(["line1", "line2"])
|
91
89
|
end
|
92
90
|
end
|
93
91
|
end
|
94
92
|
|
95
|
-
context "when watching a directory with files" do
|
96
|
-
let(:
|
93
|
+
context "when watching a directory with files, exisiting content is skipped" do
|
94
|
+
let(:suffix) { "C" }
|
97
95
|
let(:actions) do
|
98
|
-
RSpec::Sequencing
|
99
|
-
|
100
|
-
|
96
|
+
RSpec::Sequencing
|
97
|
+
.run("create file") do
|
98
|
+
File.open(file_path, "wb") { |file| file.write("lineA\nlineB\n") }
|
99
|
+
end
|
100
|
+
.then_after(0.1, "begin watching") do
|
101
|
+
tailing.watch_this(watch_dir)
|
102
|
+
end
|
103
|
+
.then_after(0.2, "add content") do
|
104
|
+
File.open(file_path, "ab") { |file| file.write("line1\nline2\n") }
|
105
|
+
end
|
106
|
+
.then("wait") do
|
107
|
+
wait(0.75).for{listener1.lines.size}.to eq(2)
|
108
|
+
end
|
109
|
+
.then("quit") do
|
110
|
+
tailing.quit
|
111
|
+
end
|
101
112
|
end
|
102
113
|
|
103
|
-
it "the
|
104
|
-
|
105
|
-
actions.activate
|
114
|
+
it "only the new content is read" do
|
115
|
+
actions.activate_quietly
|
106
116
|
tailing.watch_this(watch_dir)
|
107
117
|
tailing.subscribe(observer)
|
108
|
-
|
109
|
-
expect(
|
118
|
+
actions.assert_no_errors
|
119
|
+
expect(listener1.calls).to eq([:open, :accept, :accept])
|
120
|
+
expect(listener1.lines).to eq(["line1", "line2"])
|
110
121
|
end
|
111
122
|
end
|
112
123
|
|
113
124
|
context "when watching a directory without files and one is added" do
|
114
|
-
let(:
|
115
|
-
|
116
|
-
tailing.watch_this(watch_dir)
|
125
|
+
let(:suffix) { "D" }
|
126
|
+
let(:actions) do
|
117
127
|
RSpec::Sequencing
|
118
|
-
.
|
128
|
+
.run("begin watching") do
|
129
|
+
tailing.watch_this(watch_dir)
|
130
|
+
end
|
131
|
+
.then_after(0.1, "create file") do
|
119
132
|
File.open(file_path, "wb") { |file| file.write("line1\nline2\n") }
|
120
133
|
end
|
121
|
-
.
|
134
|
+
.then("wait") do
|
135
|
+
wait(0.75).for{listener1.lines.size}.to eq(2)
|
136
|
+
end
|
137
|
+
.then("quit") do
|
122
138
|
tailing.quit
|
123
139
|
end
|
124
140
|
end
|
125
141
|
|
126
|
-
it "the file is read" do
|
142
|
+
it "the file is read from the beginning" do
|
143
|
+
actions.activate_quietly
|
127
144
|
tailing.subscribe(observer)
|
128
|
-
|
129
|
-
expect(
|
145
|
+
actions.assert_no_errors
|
146
|
+
expect(listener1.calls).to eq([:open, :accept, :accept])
|
147
|
+
expect(listener1.lines).to eq(["line1", "line2"])
|
130
148
|
end
|
131
149
|
end
|
132
150
|
|
133
|
-
|
151
|
+
context "given a previously discovered file" do
|
134
152
|
# these tests rely on the fact that the 'filepath' does not exist on disk
|
135
153
|
# it simulates that the user deleted the file
|
136
154
|
# so when a stat is taken on the file an error is raised
|
155
|
+
let(:suffix) { "E" }
|
137
156
|
let(:quit_after) { 0.2 }
|
138
|
-
let(:stat) { double("stat", :size => 100, :
|
157
|
+
let(:stat) { double("stat", :size => 100, :modified_at => Time.now.to_f, :identifier => nil, :inode => 234567, :inode_struct => InodeStruct.new("234567", 1, 5)) }
|
139
158
|
let(:watched_file) { WatchedFile.new(file_path, stat, tailing.settings) }
|
140
|
-
|
141
159
|
before do
|
160
|
+
allow(stat).to receive(:restat).and_raise(Errno::ENOENT)
|
142
161
|
tailing.watch.watched_files_collection.add(watched_file)
|
143
162
|
watched_file.initial_completed
|
144
163
|
end
|
@@ -150,7 +169,7 @@ module FileWatch
|
|
150
169
|
RSpec::Sequencing.run_after(quit_after, "quit") { tailing.quit }
|
151
170
|
tailing.subscribe(observer)
|
152
171
|
expect(tailing.watch.watched_files_collection).to be_empty
|
153
|
-
expect(
|
172
|
+
expect(listener1.calls).to eq([:delete])
|
154
173
|
end
|
155
174
|
end
|
156
175
|
|
@@ -160,7 +179,7 @@ module FileWatch
|
|
160
179
|
RSpec::Sequencing.run_after(quit_after, "quit") { tailing.quit }
|
161
180
|
tailing.subscribe(observer)
|
162
181
|
expect(tailing.watch.watched_files_collection).to be_empty
|
163
|
-
expect(
|
182
|
+
expect(listener1.calls).to eq([:delete])
|
164
183
|
end
|
165
184
|
end
|
166
185
|
|
@@ -170,7 +189,7 @@ module FileWatch
|
|
170
189
|
RSpec::Sequencing.run_after(quit_after, "quit") { tailing.quit }
|
171
190
|
tailing.subscribe(observer)
|
172
191
|
expect(tailing.watch.watched_files_collection).to be_empty
|
173
|
-
expect(
|
192
|
+
expect(listener1.calls).to eq([:delete])
|
174
193
|
end
|
175
194
|
end
|
176
195
|
|
@@ -180,172 +199,214 @@ module FileWatch
|
|
180
199
|
RSpec::Sequencing.run_after(quit_after, "quit") { tailing.quit }
|
181
200
|
tailing.subscribe(observer)
|
182
201
|
expect(tailing.watch.watched_files_collection).to be_empty
|
183
|
-
expect(
|
202
|
+
expect(listener1.calls).to eq([:delete])
|
184
203
|
end
|
185
204
|
end
|
186
205
|
end
|
187
206
|
|
188
207
|
context "when a processed file shrinks" do
|
189
|
-
let(:discover_interval) {
|
190
|
-
|
208
|
+
let(:discover_interval) { 1 }
|
209
|
+
let(:suffix) { "F" }
|
210
|
+
let(:actions) do
|
191
211
|
RSpec::Sequencing
|
192
|
-
.
|
212
|
+
.run_after(0.1, "start watching") do
|
213
|
+
tailing.watch_this(watch_dir)
|
214
|
+
end
|
215
|
+
.then_after(0.1, "create file") do
|
216
|
+
# create file after first discovery, will be read from the start
|
193
217
|
File.open(file_path, "wb") { |file| file.write("line1\nline2\nline3\nline4\n") }
|
194
218
|
end
|
195
|
-
.
|
196
|
-
|
219
|
+
.then("wait for initial lines to be read") do
|
220
|
+
wait(0.8).for{listener1.lines.size}.to eq(4), "listener1.lines.size not eq 4"
|
197
221
|
end
|
198
222
|
.then_after(0.25, "truncate file and write new content") do
|
199
223
|
File.truncate(file_path, 0)
|
200
|
-
File.open(file_path, "
|
224
|
+
File.open(file_path, "ab") { |file| file.write("lineA\nlineB\n") }
|
225
|
+
wait(0.5).for{listener1.lines.size}.to eq(6), "listener1.lines.size not eq 6"
|
201
226
|
end
|
202
|
-
.
|
227
|
+
.then("quit") do
|
203
228
|
tailing.quit
|
204
229
|
end
|
205
230
|
end
|
206
231
|
|
207
232
|
it "new changes to the shrunk file are read from the beginning" do
|
233
|
+
actions.activate_quietly
|
208
234
|
tailing.subscribe(observer)
|
209
|
-
|
210
|
-
expect(
|
235
|
+
actions.assert_no_errors
|
236
|
+
expect(listener1.calls).to eq([:open, :accept, :accept, :accept, :accept, :accept, :accept])
|
237
|
+
expect(listener1.lines).to eq(["line1", "line2", "line3", "line4", "lineA", "lineB"])
|
211
238
|
end
|
212
239
|
end
|
213
240
|
|
214
|
-
context "when watching a directory with files and a file is renamed to not match glob" do
|
241
|
+
context "when watching a directory with files and a file is renamed to not match glob", :unix => true do
|
242
|
+
let(:suffix) { "G" }
|
215
243
|
let(:new_file_path) { file_path + ".old" }
|
216
|
-
|
244
|
+
let(:new_file_listener) { observer.listener_for(new_file_path) }
|
245
|
+
let(:actions) do
|
217
246
|
RSpec::Sequencing
|
218
|
-
.run("
|
219
|
-
File.open(file_path, "wb") { |file| file.write("line1\nline2\n") }
|
220
|
-
end
|
221
|
-
.then_after(0.25, "start watching after files are written") do
|
247
|
+
.run("start watching") do
|
222
248
|
tailing.watch_this(watch_dir)
|
223
249
|
end
|
250
|
+
.then_after(0.1, "create file") do
|
251
|
+
# create file after first discovery, will be read from the beginning
|
252
|
+
File.open(file_path, "wb") { |file| file.write("line1\nline2\n") }
|
253
|
+
end
|
224
254
|
.then_after(0.55, "rename file") do
|
225
255
|
FileUtils.mv(file_path, new_file_path)
|
226
256
|
end
|
227
257
|
.then_after(0.55, "then write to renamed file") do
|
228
258
|
File.open(new_file_path, "ab") { |file| file.write("line3\nline4\n") }
|
259
|
+
wait(0.5).for{listener1.lines.size}.to eq(2), "listener1.lines.size not eq(2)"
|
229
260
|
end
|
230
|
-
.then_after(0.
|
261
|
+
.then_after(0.1, "quit") do
|
231
262
|
tailing.quit
|
232
263
|
end
|
233
264
|
end
|
234
265
|
|
235
266
|
it "changes to the renamed file are not read" do
|
267
|
+
actions.activate_quietly
|
236
268
|
tailing.subscribe(observer)
|
237
|
-
|
238
|
-
expect(
|
239
|
-
expect(
|
240
|
-
expect(
|
269
|
+
actions.assert_no_errors
|
270
|
+
expect(listener1.calls).to eq([:open, :accept, :accept, :delete])
|
271
|
+
expect(listener1.lines).to eq(["line1", "line2"])
|
272
|
+
expect(new_file_listener.calls).to eq([])
|
273
|
+
expect(new_file_listener.lines).to eq([])
|
241
274
|
end
|
242
275
|
end
|
243
276
|
|
244
|
-
context "when watching a directory with files and a file is renamed to match glob" do
|
245
|
-
let(:
|
277
|
+
context "when watching a directory with files and a file is renamed to match glob", :unix => true do
|
278
|
+
let(:suffix) { "H" }
|
246
279
|
let(:opts) { super.merge(:close_older => 0) }
|
247
|
-
|
280
|
+
let(:listener2) { observer.listener_for(file_path2) }
|
281
|
+
let(:actions) do
|
248
282
|
RSpec::Sequencing
|
249
|
-
.run("
|
283
|
+
.run("file created") do
|
284
|
+
# create file before first discovery, will be read from the end
|
250
285
|
File.open(file_path, "wb") { |file| file.write("line1\nline2\n") }
|
251
286
|
end
|
252
287
|
.then_after(0.15, "start watching after files are written") do
|
253
288
|
tailing.watch_this(watch_dir)
|
254
289
|
end
|
255
|
-
.
|
256
|
-
|
290
|
+
.then("wait") do
|
291
|
+
wait(0.5).for{listener1.calls.last}.to eq(:timed_out)
|
257
292
|
end
|
258
|
-
.then("
|
259
|
-
|
293
|
+
.then("rename file") do
|
294
|
+
FileUtils.mv(file_path, file_path2)
|
295
|
+
end
|
296
|
+
.then_after(0.1, "then write to renamed file") do
|
297
|
+
File.open(file_path2, "ab") { |file| file.write("line3\nline4\n") }
|
260
298
|
end
|
261
|
-
.then_after(0.
|
299
|
+
.then_after(0.1, "wait for lines") do
|
300
|
+
wait(0.5).for{listener2.lines.size}.to eq(2)
|
301
|
+
end
|
302
|
+
.then_after(0.1, "quit") do
|
262
303
|
tailing.quit
|
263
304
|
end
|
264
305
|
end
|
265
306
|
|
266
307
|
it "the first set of lines are not re-read" do
|
308
|
+
actions.activate_quietly
|
267
309
|
tailing.subscribe(observer)
|
268
|
-
|
269
|
-
expect(
|
270
|
-
expect(
|
271
|
-
expect(
|
310
|
+
actions.assert_no_errors
|
311
|
+
expect(listener1.lines).to eq([])
|
312
|
+
expect(listener1.calls).to eq([:open, :timed_out, :delete])
|
313
|
+
expect(listener2.lines).to eq(["line3", "line4"])
|
314
|
+
expect(listener2.calls).to eq([:open, :accept, :accept, :timed_out])
|
272
315
|
end
|
273
316
|
end
|
274
317
|
|
275
318
|
context "when watching a directory with files and data is appended" do
|
276
|
-
|
319
|
+
let(:suffix) { "I" }
|
320
|
+
let(:actions) do
|
277
321
|
RSpec::Sequencing
|
278
|
-
.run("
|
322
|
+
.run("file created") do
|
279
323
|
File.open(file_path, "wb") { |file| file.write("line1\nline2\n") }
|
280
324
|
end
|
281
|
-
.then_after(0.
|
325
|
+
.then_after(0.15, "start watching after file is written") do
|
282
326
|
tailing.watch_this(watch_dir)
|
283
327
|
end
|
284
328
|
.then_after(0.45, "append more lines to the file") do
|
285
329
|
File.open(file_path, "ab") { |file| file.write("line3\nline4\n") }
|
330
|
+
wait(0.5).for{listener1.lines.size}.to eq(2)
|
286
331
|
end
|
287
|
-
.then_after(0.
|
332
|
+
.then_after(0.1, "quit") do
|
288
333
|
tailing.quit
|
289
334
|
end
|
290
335
|
end
|
291
336
|
|
292
|
-
it "appended lines are read
|
337
|
+
it "appended lines are read only" do
|
338
|
+
actions.activate_quietly
|
293
339
|
tailing.subscribe(observer)
|
294
|
-
|
295
|
-
expect(
|
340
|
+
actions.assert_no_errors
|
341
|
+
expect(listener1.calls).to eq([:open, :accept, :accept])
|
342
|
+
expect(listener1.lines).to eq(["line3", "line4"])
|
296
343
|
end
|
297
344
|
end
|
298
345
|
|
299
346
|
context "when close older expiry is enabled" do
|
300
347
|
let(:opts) { super.merge(:close_older => 1) }
|
301
|
-
|
302
|
-
|
303
|
-
|
304
|
-
|
305
|
-
|
306
|
-
|
307
|
-
|
308
|
-
|
309
|
-
|
310
|
-
|
311
|
-
|
348
|
+
let(:suffix) { "J" }
|
349
|
+
let(:actions) do
|
350
|
+
RSpec::Sequencing.run("create file") do
|
351
|
+
File.open(file_path, "wb") { |file| file.write("line1\nline2\n") }
|
352
|
+
end
|
353
|
+
.then("watch and wait") do
|
354
|
+
tailing.watch_this(watch_dir)
|
355
|
+
wait(1.25).for{listener1.calls}.to eq([:open, :timed_out])
|
356
|
+
end
|
357
|
+
.then("quit") do
|
358
|
+
tailing.quit
|
359
|
+
end
|
312
360
|
end
|
313
361
|
|
314
|
-
it "lines are read and the file times out" do
|
362
|
+
it "existing lines are not read and the file times out" do
|
363
|
+
actions.activate_quietly
|
315
364
|
tailing.subscribe(observer)
|
316
|
-
|
317
|
-
expect(
|
365
|
+
actions.assert_no_errors
|
366
|
+
expect(listener1.lines).to eq([])
|
318
367
|
end
|
319
368
|
end
|
320
369
|
|
321
370
|
context "when close older expiry is enabled and after timeout the file is appended-to" do
|
322
|
-
let(:opts) { super.merge(:close_older =>
|
323
|
-
|
371
|
+
let(:opts) { super.merge(:close_older => 0.5) }
|
372
|
+
let(:suffix) { "K" }
|
373
|
+
let(:actions) do
|
324
374
|
RSpec::Sequencing
|
325
|
-
.run("
|
375
|
+
.run("start watching") do
|
376
|
+
tailing.watch_this(watch_dir)
|
377
|
+
end
|
378
|
+
.then("create file") do
|
326
379
|
File.open(file_path, "wb") { |file| file.write("line1\nline2\n") }
|
327
380
|
end
|
328
|
-
.then("
|
329
|
-
|
381
|
+
.then("wait for file to be read") do
|
382
|
+
wait(0.5).for{listener1.calls}.to eq([:open, :accept, :accept]), "file is not read"
|
330
383
|
end
|
331
|
-
.
|
384
|
+
.then("wait for file to be read and time out") do
|
385
|
+
wait(0.75).for{listener1.calls}.to eq([:open, :accept, :accept, :timed_out]), "file did not timeout the first time"
|
386
|
+
end
|
387
|
+
.then("append more lines to file after file ages more than close_older") do
|
332
388
|
File.open(file_path, "ab") { |file| file.write("line3\nline4\n") }
|
333
389
|
end
|
334
|
-
.
|
390
|
+
.then("wait for last timeout") do
|
391
|
+
wait(0.75).for{listener1.calls}.to eq([:open, :accept, :accept, :timed_out, :open, :accept, :accept, :timed_out]), "file did not timeout the second time"
|
392
|
+
end
|
393
|
+
.then("quit") do
|
335
394
|
tailing.quit
|
336
395
|
end
|
337
396
|
end
|
338
397
|
|
339
398
|
it "all lines are read" do
|
399
|
+
actions.activate_quietly
|
340
400
|
tailing.subscribe(observer)
|
341
|
-
|
342
|
-
expect(
|
401
|
+
actions.assert_no_errors
|
402
|
+
expect(listener1.lines).to eq(["line1", "line2", "line3", "line4"])
|
343
403
|
end
|
344
404
|
end
|
345
405
|
|
346
406
|
context "when ignore older expiry is enabled and all files are already expired" do
|
347
407
|
let(:opts) { super.merge(:ignore_older => 1) }
|
348
|
-
|
408
|
+
let(:suffix) { "L" }
|
409
|
+
let(:actions) do
|
349
410
|
RSpec::Sequencing
|
350
411
|
.run("create file older than ignore_older and watch") do
|
351
412
|
File.open(file_path, "wb") { |file| file.write("line1\nline2\n") }
|
@@ -358,60 +419,112 @@ module FileWatch
|
|
358
419
|
end
|
359
420
|
|
360
421
|
it "no files are read" do
|
422
|
+
actions.activate_quietly
|
423
|
+
tailing.subscribe(observer)
|
424
|
+
expect(listener1.calls).to eq([])
|
425
|
+
expect(listener1.lines).to eq([])
|
426
|
+
end
|
427
|
+
end
|
428
|
+
|
429
|
+
context "when a file is renamed before it gets activated", :unix => true do
|
430
|
+
let(:max) { 1 }
|
431
|
+
let(:opts) { super.merge(:file_chunk_count => 8, :file_chunk_size => 6, :close_older => 0.1, :discover_interval => 6) }
|
432
|
+
let(:suffix) { "M" }
|
433
|
+
let(:start_new_files_at) { :beginning } # we are creating files and sincedb record before hand
|
434
|
+
let(:actions) do
|
435
|
+
RSpec::Sequencing
|
436
|
+
.run("create files and sincedb record") do
|
437
|
+
File.open(file_path, "wb") { |file| 32.times{file.write("line1\n")} }
|
438
|
+
File.open(file_path2, "wb") { |file| file.write("line2\n") }
|
439
|
+
# synthesize a sincedb record
|
440
|
+
stat = File.stat(file_path2)
|
441
|
+
record = [stat.ino.to_s, stat.dev_major.to_s, stat.dev_minor.to_s, "0", "1526220348.083179", file_path2]
|
442
|
+
File.open(sincedb_path, "wb") { |file| file.puts(record.join(" ")) }
|
443
|
+
end
|
444
|
+
.then_after(0.2, "watch") do
|
445
|
+
tailing.watch_this(watch_dir)
|
446
|
+
end
|
447
|
+
.then_after(0.1, "rename file 2") do
|
448
|
+
FileUtils.mv(file_path2, file_path3)
|
449
|
+
end
|
450
|
+
.then("wait") do
|
451
|
+
wait(2).for do
|
452
|
+
listener1.lines.size == 32 && listener2.calls == [:delete] && listener3.calls == [:open, :accept, :timed_out]
|
453
|
+
end.to eq(true), "listener1.lines != 32 or listener2.calls != [:delete] or listener3.calls != [:open, :accept, :timed_out]"
|
454
|
+
end
|
455
|
+
.then("quit") do
|
456
|
+
tailing.quit
|
457
|
+
end
|
458
|
+
end
|
459
|
+
|
460
|
+
it "files are read correctly" do
|
461
|
+
actions.activate_quietly
|
361
462
|
tailing.subscribe(observer)
|
362
|
-
|
363
|
-
expect(
|
463
|
+
actions.assert_no_errors
|
464
|
+
expect(listener2.lines).to eq([])
|
465
|
+
expect(listener3.lines).to eq(["line2"])
|
364
466
|
end
|
365
467
|
end
|
366
468
|
|
367
469
|
context "when ignore_older is less than close_older and all files are not expired" do
|
368
|
-
let(:opts) { super.merge(:ignore_older => 1, :close_older => 1.
|
369
|
-
|
470
|
+
let(:opts) { super.merge(:ignore_older => 1, :close_older => 1.1) }
|
471
|
+
let(:suffix) { "N" }
|
472
|
+
let(:start_new_files_at) { :beginning }
|
473
|
+
let(:actions) do
|
370
474
|
RSpec::Sequencing
|
371
|
-
.
|
475
|
+
.run_after(0.1, "file created") do
|
372
476
|
File.open(file_path, "wb") { |file| file.write("line1\nline2\n") }
|
373
477
|
end
|
374
478
|
.then("start watching before file age reaches ignore_older") do
|
375
479
|
tailing.watch_this(watch_dir)
|
376
480
|
end
|
377
|
-
.
|
481
|
+
.then("wait for lines") do
|
482
|
+
wait(1.5).for{listener1.calls}.to eq([:open, :accept, :accept, :timed_out])
|
483
|
+
end
|
484
|
+
.then("quit") do
|
378
485
|
tailing.quit
|
379
486
|
end
|
380
487
|
end
|
381
488
|
|
382
489
|
it "reads lines normally" do
|
490
|
+
actions.activate_quietly
|
383
491
|
tailing.subscribe(observer)
|
384
|
-
|
385
|
-
expect(
|
492
|
+
actions.assert_no_errors
|
493
|
+
expect(listener1.lines).to eq(["line1", "line2"])
|
386
494
|
end
|
387
495
|
end
|
388
496
|
|
389
497
|
context "when ignore_older is less than close_older and all files are expired" do
|
390
498
|
let(:opts) { super.merge(:ignore_older => 10, :close_older => 1) }
|
391
|
-
|
499
|
+
let(:suffix) { "P" }
|
500
|
+
let(:actions) do
|
392
501
|
RSpec::Sequencing
|
393
|
-
.run("
|
502
|
+
.run("creating file") do
|
394
503
|
File.open(file_path, "wb") { |file| file.write("line1\nline2\n") }
|
504
|
+
end
|
505
|
+
.then("making it older by 15 seconds and watch") do
|
395
506
|
FileWatch.make_file_older(file_path, 15)
|
396
507
|
tailing.watch_this(watch_dir)
|
397
508
|
end
|
398
|
-
.then_after(
|
509
|
+
.then_after(0.75, "quit after allowing time to check the files") do
|
399
510
|
tailing.quit
|
400
511
|
end
|
401
512
|
end
|
402
513
|
|
403
514
|
it "no files are read" do
|
515
|
+
actions.activate_quietly
|
404
516
|
tailing.subscribe(observer)
|
405
|
-
expect(
|
406
|
-
expect(
|
517
|
+
expect(listener1.calls).to eq([])
|
518
|
+
expect(listener1.lines).to eq([])
|
407
519
|
end
|
408
520
|
end
|
409
521
|
|
410
522
|
context "when ignore older and close older expiry is enabled and after timeout the file is appended-to" do
|
411
|
-
let(:opts) { super.merge(:ignore_older => 20, :close_older =>
|
412
|
-
|
523
|
+
let(:opts) { super.merge(:ignore_older => 20, :close_older => 0.5) }
|
524
|
+
let(:suffix) { "Q" }
|
525
|
+
let(:actions) do
|
413
526
|
RSpec::Sequencing
|
414
|
-
.run("
|
527
|
+
.run("file older than ignore_older created and watching") do
|
415
528
|
File.open(file_path, "wb") { |file| file.write("line1\nline2\n") }
|
416
529
|
FileWatch.make_file_older(file_path, 25)
|
417
530
|
tailing.watch_this(watch_dir)
|
@@ -419,37 +532,46 @@ module FileWatch
|
|
419
532
|
.then_after(0.15, "append more lines to file after file ages more than ignore_older") do
|
420
533
|
File.open(file_path, "ab") { |file| file.write("line3\nline4\n") }
|
421
534
|
end
|
422
|
-
.
|
535
|
+
.then("wait for lines") do
|
536
|
+
wait(2).for{listener1.calls}.to eq([:open, :accept, :accept, :timed_out])
|
537
|
+
end
|
538
|
+
.then_after(0.1, "quit after allowing time to close the file") do
|
423
539
|
tailing.quit
|
424
540
|
end
|
425
541
|
end
|
426
542
|
|
427
543
|
it "reads the added lines only" do
|
544
|
+
actions.activate_quietly
|
428
545
|
tailing.subscribe(observer)
|
429
|
-
|
430
|
-
expect(
|
546
|
+
actions.assert_no_errors
|
547
|
+
expect(listener1.lines).to eq(["line3", "line4"])
|
431
548
|
end
|
432
549
|
end
|
433
550
|
|
434
551
|
context "when a non default delimiter is specified and it is not in the content" do
|
435
552
|
let(:opts) { super.merge(:ignore_older => 20, :close_older => 1, :delimiter => "\nø") }
|
436
|
-
|
553
|
+
let(:suffix) { "R" }
|
554
|
+
let(:actions) do
|
437
555
|
RSpec::Sequencing
|
438
|
-
.run("
|
556
|
+
.run("start watching") do
|
557
|
+
tailing.watch_this(watch_dir)
|
558
|
+
end
|
559
|
+
.then("creating file") do
|
439
560
|
File.open(file_path, "wb") { |file| file.write("line1\nline2") }
|
440
561
|
end
|
441
|
-
.then("
|
442
|
-
|
562
|
+
.then("wait for :timeout") do
|
563
|
+
wait(2).for{listener1.calls}.to eq([:open, :timed_out])
|
443
564
|
end
|
444
|
-
.then_after(
|
565
|
+
.then_after(0.75, "quit after allowing time to close the file") do
|
445
566
|
tailing.quit
|
446
567
|
end
|
447
568
|
end
|
448
569
|
|
449
570
|
it "the file is opened, data is read, but no lines are found, the file times out" do
|
571
|
+
actions.activate_quietly
|
450
572
|
tailing.subscribe(observer)
|
451
|
-
|
452
|
-
expect(
|
573
|
+
actions.assert_no_errors
|
574
|
+
expect(listener1.lines).to eq([])
|
453
575
|
sincedb_record_fields = File.read(sincedb_path).split(" ")
|
454
576
|
position_field_index = 3
|
455
577
|
# tailing, no delimiter, we are expecting one, if it grows we read from the start.
|
@@ -459,5 +581,3 @@ module FileWatch
|
|
459
581
|
end
|
460
582
|
end
|
461
583
|
end
|
462
|
-
|
463
|
-
|