logstash-input-file 4.1.3 → 4.1.4
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
-
|