fluentd 1.12.0.rc1 → 1.12.0.rc2
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of fluentd might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/.github/ISSUE_TEMPLATE/bug_report.md +1 -1
- data/.github/ISSUE_TEMPLATE/config.yml +5 -0
- data/.github/workflows/stale-actions.yml +22 -0
- data/bin/fluent-ctl +7 -0
- data/lib/fluent/command/ctl.rb +177 -0
- data/lib/fluent/command/plugin_config_formatter.rb +2 -1
- data/lib/fluent/plugin.rb +5 -0
- data/lib/fluent/plugin/in_http.rb +23 -2
- data/lib/fluent/plugin/in_tail.rb +101 -40
- data/lib/fluent/plugin/in_tail/position_file.rb +39 -14
- data/lib/fluent/plugin/output.rb +7 -1
- data/lib/fluent/plugin_helper/http_server/compat/server.rb +1 -1
- data/lib/fluent/plugin_helper/retry_state.rb +4 -0
- data/lib/fluent/supervisor.rb +140 -42
- data/lib/fluent/version.rb +1 -1
- data/lib/fluent/winsvc.rb +22 -4
- data/test/command/test_ctl.rb +57 -0
- data/test/command/test_plugin_config_formatter.rb +57 -2
- data/test/plugin/in_tail/test_position_file.rb +45 -25
- data/test/plugin/test_in_http.rb +25 -0
- data/test/plugin/test_in_tail.rb +383 -29
- data/test/test_supervisor.rb +102 -10
- metadata +9 -3
- data/.github/stale.yml +0 -22
@@ -0,0 +1,57 @@
|
|
1
|
+
require_relative '../helper'
|
2
|
+
|
3
|
+
require 'test-unit'
|
4
|
+
require 'win32/event' if Fluent.windows?
|
5
|
+
|
6
|
+
require 'fluent/command/ctl'
|
7
|
+
|
8
|
+
class TestFluentdCtl < ::Test::Unit::TestCase
|
9
|
+
def assert_win32_event(event_name, command, pid_or_svcname)
|
10
|
+
command, event_suffix = data
|
11
|
+
event = Win32::Event.new(event_name)
|
12
|
+
ipc = Win32::Ipc.new(event.handle)
|
13
|
+
ret = Win32::Ipc::TIMEOUT
|
14
|
+
|
15
|
+
wait_thread = Thread.new do
|
16
|
+
ret = ipc.wait(1)
|
17
|
+
end
|
18
|
+
Fluent::Ctl.new([command, pid_or_svcname]).call
|
19
|
+
wait_thread.join
|
20
|
+
assert_equal(Win32::Ipc::SIGNALED, ret)
|
21
|
+
end
|
22
|
+
|
23
|
+
data("shutdown" => ["shutdown", "TERM", ""],
|
24
|
+
"restart" => ["restart", "HUP", "HUP"],
|
25
|
+
"flush" => ["flush", "USR1", "USR1"],
|
26
|
+
"reload" => ["reload", "USR2", "USR2"])
|
27
|
+
def test_commands(data)
|
28
|
+
command, signal, event_suffix = data
|
29
|
+
|
30
|
+
if Fluent.windows?
|
31
|
+
event_name = "fluentd_54321"
|
32
|
+
event_name << "_#{event_suffix}" unless event_suffix.empty?
|
33
|
+
assert_win32_event(event_name, command, "54321")
|
34
|
+
else
|
35
|
+
got_signal = false
|
36
|
+
Signal.trap(signal) do
|
37
|
+
got_signal = true
|
38
|
+
end
|
39
|
+
Fluent::Ctl.new([command, "#{$$}"]).call
|
40
|
+
assert_true(got_signal)
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
data("shutdown" => ["shutdown", ""],
|
45
|
+
"restart" => ["restart", "HUP"],
|
46
|
+
"flush" => ["flush", "USR1"],
|
47
|
+
"reload" => ["reload", "USR2"])
|
48
|
+
def test_commands_with_winsvcname(data)
|
49
|
+
omit "Only for Windows" unless Fluent.windows?
|
50
|
+
|
51
|
+
command, event_suffix = data
|
52
|
+
event_name = "testfluentdwinsvc"
|
53
|
+
event_name << "_#{event_suffix}" unless event_suffix.empty?
|
54
|
+
|
55
|
+
assert_win32_event(event_name, command, "testfluentdwinsvc")
|
56
|
+
end
|
57
|
+
end
|
@@ -56,6 +56,32 @@ class TestFluentPluginConfigFormatter < Test::Unit::TestCase
|
|
56
56
|
end
|
57
57
|
end
|
58
58
|
|
59
|
+
class FakeStorage < ::Fluent::Plugin::Storage
|
60
|
+
::Fluent::Plugin.register_storage('fake', self)
|
61
|
+
|
62
|
+
def get(key)
|
63
|
+
end
|
64
|
+
|
65
|
+
def fetch(key, defval)
|
66
|
+
end
|
67
|
+
|
68
|
+
def put(key, value)
|
69
|
+
end
|
70
|
+
|
71
|
+
def delete(key)
|
72
|
+
end
|
73
|
+
|
74
|
+
def update(key, &block)
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
class FakeServiceDiscovery < ::Fluent::Plugin::ServiceDiscovery
|
79
|
+
::Fluent::Plugin.register_sd('fake', self)
|
80
|
+
|
81
|
+
desc "hostname"
|
82
|
+
config_param :hostname, :string
|
83
|
+
end
|
84
|
+
|
59
85
|
class SimpleInput < ::Fluent::Plugin::Input
|
60
86
|
::Fluent::Plugin.register_input("simple", self)
|
61
87
|
helpers :inject, :compat_parameters
|
@@ -88,6 +114,13 @@ class TestFluentPluginConfigFormatter < Test::Unit::TestCase
|
|
88
114
|
end
|
89
115
|
end
|
90
116
|
|
117
|
+
class SimpleServiceDiscovery < ::Fluent::Plugin::ServiceDiscovery
|
118
|
+
::Fluent::Plugin.register_sd('simple', self)
|
119
|
+
|
120
|
+
desc "servers"
|
121
|
+
config_param :servers, :array
|
122
|
+
end
|
123
|
+
|
91
124
|
sub_test_case "json" do
|
92
125
|
data(input: [FakeInput, "input"],
|
93
126
|
output: [FakeOutput, "output"],
|
@@ -196,6 +229,28 @@ TEXT
|
|
196
229
|
assert_equal(expected, dumped_config)
|
197
230
|
end
|
198
231
|
|
232
|
+
data("abbrev" => "sd",
|
233
|
+
"normal" => "service_discovery")
|
234
|
+
test "service_discovery simple" do |data|
|
235
|
+
plugin_type = data
|
236
|
+
dumped_config = capture_stdout do
|
237
|
+
FluentPluginConfigFormatter.new(["--format=markdown", plugin_type, "simple"]).call
|
238
|
+
end
|
239
|
+
expected = <<TEXT
|
240
|
+
* See also: [ServiceDiscovery Plugin Overview](https://docs.fluentd.org/v/1.0/servicediscovery#overview)
|
241
|
+
|
242
|
+
## TestFluentPluginConfigFormatter::SimpleServiceDiscovery
|
243
|
+
|
244
|
+
### servers (array) (required)
|
245
|
+
|
246
|
+
servers
|
247
|
+
|
248
|
+
|
249
|
+
TEXT
|
250
|
+
assert_equal(expected, dumped_config)
|
251
|
+
end
|
252
|
+
|
253
|
+
|
199
254
|
test "output complex" do
|
200
255
|
dumped_config = capture_stdout do
|
201
256
|
FluentPluginConfigFormatter.new(["--format=markdown", "output", "complex"]).call
|
@@ -251,7 +306,7 @@ TEXT
|
|
251
306
|
sub_test_case "arguments" do
|
252
307
|
data do
|
253
308
|
hash = {}
|
254
|
-
["input", "output", "filter", "parser", "formatter"].each do |type|
|
309
|
+
["input", "output", "filter", "parser", "formatter", "storage", "service_discovery"].each do |type|
|
255
310
|
["txt", "json", "markdown"].each do |format|
|
256
311
|
argv = ["--format=#{format}"]
|
257
312
|
[
|
@@ -259,7 +314,7 @@ TEXT
|
|
259
314
|
["--verbose"],
|
260
315
|
["--compact"]
|
261
316
|
].each do |options|
|
262
|
-
hash[(argv + options).join(" ")] = argv + options + [type, "fake"]
|
317
|
+
hash["[#{type}] " + (argv + options).join(" ")] = argv + options + [type, "fake"]
|
263
318
|
end
|
264
319
|
end
|
265
320
|
end
|
@@ -1,5 +1,6 @@
|
|
1
1
|
require_relative '../../helper'
|
2
2
|
require 'fluent/plugin/in_tail/position_file'
|
3
|
+
require 'fluent/plugin/in_tail'
|
3
4
|
|
4
5
|
require 'fileutils'
|
5
6
|
require 'tempfile'
|
@@ -27,9 +28,15 @@ class IntailPositionFileTest < Test::Unit::TestCase
|
|
27
28
|
f.seek(0)
|
28
29
|
end
|
29
30
|
|
31
|
+
def follow_inodes_block
|
32
|
+
[true, false].each do |follow_inodes|
|
33
|
+
yield follow_inodes
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
30
37
|
test '.load' do
|
31
38
|
write_data(@file, TEST_CONTENT)
|
32
|
-
Fluent::Plugin::TailInput::PositionFile.load(@file, logger: $log)
|
39
|
+
Fluent::Plugin::TailInput::PositionFile.load(@file, false, {}, **{logger: $log})
|
33
40
|
|
34
41
|
@file.seek(0)
|
35
42
|
lines = @file.readlines
|
@@ -41,7 +48,7 @@ class IntailPositionFileTest < Test::Unit::TestCase
|
|
41
48
|
sub_test_case '#try_compact' do
|
42
49
|
test 'compact invalid and convert 32 bit inode value' do
|
43
50
|
write_data(@file, TEST_CONTENT)
|
44
|
-
Fluent::Plugin::TailInput::PositionFile.new(@file, logger: $log).try_compact
|
51
|
+
Fluent::Plugin::TailInput::PositionFile.new(@file, false, {}, **{logger: $log}).try_compact
|
45
52
|
|
46
53
|
@file.seek(0)
|
47
54
|
lines = @file.readlines
|
@@ -55,7 +62,7 @@ class IntailPositionFileTest < Test::Unit::TestCase
|
|
55
62
|
valid_path\t0000000000000002\t0000000000000001
|
56
63
|
valid_path\t0000000000000003\t0000000000000004
|
57
64
|
EOF
|
58
|
-
Fluent::Plugin::TailInput::PositionFile.new(@file, logger: $log).try_compact
|
65
|
+
Fluent::Plugin::TailInput::PositionFile.new(@file, false, {}, **{logger: $log}).try_compact
|
59
66
|
|
60
67
|
@file.seek(0)
|
61
68
|
lines = @file.readlines
|
@@ -64,7 +71,7 @@ class IntailPositionFileTest < Test::Unit::TestCase
|
|
64
71
|
|
65
72
|
test 'does not change when the file is changed' do
|
66
73
|
write_data(@file, TEST_CONTENT)
|
67
|
-
pf = Fluent::Plugin::TailInput::PositionFile.new(@file, logger: $log)
|
74
|
+
pf = Fluent::Plugin::TailInput::PositionFile.new(@file, false, {}, **{logger: $log})
|
68
75
|
|
69
76
|
mock.proxy(pf).fetch_compacted_entries do |r|
|
70
77
|
@file.write("unwatched\t#{UNWATCHED_STR}\t0000000000000000\n")
|
@@ -79,11 +86,16 @@ class IntailPositionFileTest < Test::Unit::TestCase
|
|
79
86
|
end
|
80
87
|
|
81
88
|
test 'update seek postion of remained position entry' do
|
82
|
-
pf = Fluent::Plugin::TailInput::PositionFile.new(@file, logger: $log)
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
pf
|
89
|
+
pf = Fluent::Plugin::TailInput::PositionFile.new(@file, false, {}, **{logger: $log})
|
90
|
+
target_info1 = Fluent::Plugin::TailInput::TargetInfo.new('path1', -1)
|
91
|
+
target_info2 = Fluent::Plugin::TailInput::TargetInfo.new('path2', -1)
|
92
|
+
target_info3 = Fluent::Plugin::TailInput::TargetInfo.new('path3', -1)
|
93
|
+
pf[target_info1]
|
94
|
+
pf[target_info2]
|
95
|
+
pf[target_info3]
|
96
|
+
|
97
|
+
target_info1_2 = Fluent::Plugin::TailInput::TargetInfo.new('path1', 1234)
|
98
|
+
pf.unwatch(target_info1_2)
|
87
99
|
|
88
100
|
pf.try_compact
|
89
101
|
|
@@ -93,8 +105,10 @@ class IntailPositionFileTest < Test::Unit::TestCase
|
|
93
105
|
assert_equal "path3\t0000000000000000\t0000000000000000\n", lines[1]
|
94
106
|
assert_equal 2, lines.size
|
95
107
|
|
96
|
-
|
97
|
-
|
108
|
+
target_info2_2 = Fluent::Plugin::TailInput::TargetInfo.new('path2', 1235)
|
109
|
+
target_info3_2 = Fluent::Plugin::TailInput::TargetInfo.new('path3', 1236)
|
110
|
+
pf.unwatch(target_info2_2)
|
111
|
+
pf.unwatch(target_info3_2)
|
98
112
|
@file.seek(0)
|
99
113
|
lines = @file.readlines
|
100
114
|
assert_equal "path2\t#{UNWATCHED_STR}\t0000000000000000\n", lines[0]
|
@@ -106,7 +120,7 @@ class IntailPositionFileTest < Test::Unit::TestCase
|
|
106
120
|
sub_test_case '#load' do
|
107
121
|
test 'compact invalid and convert 32 bit inode value' do
|
108
122
|
write_data(@file, TEST_CONTENT)
|
109
|
-
Fluent::Plugin::TailInput::PositionFile.load(@file, logger: $log)
|
123
|
+
Fluent::Plugin::TailInput::PositionFile.load(@file, false, {}, **{logger: $log})
|
110
124
|
|
111
125
|
@file.seek(0)
|
112
126
|
lines = @file.readlines
|
@@ -120,7 +134,7 @@ class IntailPositionFileTest < Test::Unit::TestCase
|
|
120
134
|
valid_path\t0000000000000002\t0000000000000001
|
121
135
|
valid_path\t0000000000000003\t0000000000000004
|
122
136
|
EOF
|
123
|
-
Fluent::Plugin::TailInput::PositionFile.new(@file, logger: $log).load
|
137
|
+
Fluent::Plugin::TailInput::PositionFile.new(@file, false, {}, **{logger: $log}).load
|
124
138
|
|
125
139
|
@file.seek(0)
|
126
140
|
lines = @file.readlines
|
@@ -131,9 +145,10 @@ class IntailPositionFileTest < Test::Unit::TestCase
|
|
131
145
|
sub_test_case '#[]' do
|
132
146
|
test 'return entry' do
|
133
147
|
write_data(@file, TEST_CONTENT)
|
134
|
-
pf = Fluent::Plugin::TailInput::PositionFile.load(@file, logger: $log)
|
148
|
+
pf = Fluent::Plugin::TailInput::PositionFile.load(@file, false, {}, **{logger: $log})
|
135
149
|
|
136
|
-
|
150
|
+
valid_target_info = Fluent::Plugin::TailInput::TargetInfo.new('valid_path', Fluent::FileWrapper.stat(@file).ino)
|
151
|
+
f = pf[valid_target_info]
|
137
152
|
assert_equal Fluent::Plugin::TailInput::FilePositionEntry, f.class
|
138
153
|
assert_equal 2, f.read_pos
|
139
154
|
assert_equal 1, f.read_inode
|
@@ -142,7 +157,8 @@ class IntailPositionFileTest < Test::Unit::TestCase
|
|
142
157
|
lines = @file.readlines
|
143
158
|
assert_equal 2, lines.size
|
144
159
|
|
145
|
-
|
160
|
+
nonexistent_target_info = Fluent::Plugin::TailInput::TargetInfo.new('nonexist_path', -1)
|
161
|
+
f = pf[nonexistent_target_info]
|
146
162
|
assert_equal Fluent::Plugin::TailInput::FilePositionEntry, f.class
|
147
163
|
assert_equal 0, f.read_pos
|
148
164
|
assert_equal 0, f.read_inode
|
@@ -155,19 +171,19 @@ class IntailPositionFileTest < Test::Unit::TestCase
|
|
155
171
|
|
156
172
|
test 'does not change other value position if other entry try to write' do
|
157
173
|
write_data(@file, TEST_CONTENT)
|
158
|
-
pf = Fluent::Plugin::TailInput::PositionFile.load(@file, logger: $log)
|
174
|
+
pf = Fluent::Plugin::TailInput::PositionFile.load(@file, false, {}, logger: $log)
|
159
175
|
|
160
|
-
f = pf['nonexist_path']
|
176
|
+
f = pf[Fluent::Plugin::TailInput::TargetInfo.new('nonexist_path', -1)]
|
161
177
|
assert_equal 0, f.read_inode
|
162
178
|
assert_equal 0, f.read_pos
|
163
179
|
|
164
|
-
pf['valid_path'].update(1, 2)
|
180
|
+
pf[Fluent::Plugin::TailInput::TargetInfo.new('valid_path', Fluent::FileWrapper.stat(@file).ino)].update(1, 2)
|
165
181
|
|
166
|
-
f = pf['nonexist_path']
|
182
|
+
f = pf[Fluent::Plugin::TailInput::TargetInfo.new('nonexist_path', -1)]
|
167
183
|
assert_equal 0, f.read_inode
|
168
184
|
assert_equal 0, f.read_pos
|
169
185
|
|
170
|
-
pf['nonexist_path'].update(1, 2)
|
186
|
+
pf[Fluent::Plugin::TailInput::TargetInfo.new('nonexist_path', -1)].update(1, 2)
|
171
187
|
assert_equal 1, f.read_inode
|
172
188
|
assert_equal 2, f.read_pos
|
173
189
|
end
|
@@ -176,14 +192,18 @@ class IntailPositionFileTest < Test::Unit::TestCase
|
|
176
192
|
sub_test_case '#unwatch' do
|
177
193
|
test 'deletes entry by path' do
|
178
194
|
write_data(@file, TEST_CONTENT)
|
179
|
-
pf = Fluent::Plugin::TailInput::PositionFile.load(@file, logger: $log)
|
180
|
-
|
195
|
+
pf = Fluent::Plugin::TailInput::PositionFile.load(@file, false, {}, logger: $log)
|
196
|
+
inode1 = Fluent::FileWrapper.stat(@file).ino
|
197
|
+
target_info1 = Fluent::Plugin::TailInput::TargetInfo.new('valid_path', inode1)
|
198
|
+
p1 = pf[target_info1]
|
181
199
|
assert_equal Fluent::Plugin::TailInput::FilePositionEntry, p1.class
|
182
200
|
|
183
|
-
pf.unwatch(
|
201
|
+
pf.unwatch(target_info1)
|
184
202
|
assert_equal p1.read_pos, Fluent::Plugin::TailInput::PositionFile::UNWATCHED_POSITION
|
185
203
|
|
186
|
-
|
204
|
+
inode2 = Fluent::FileWrapper.stat(@file).ino
|
205
|
+
target_info2 = Fluent::Plugin::TailInput::TargetInfo.new('valid_path', inode2)
|
206
|
+
p2 = pf[target_info2]
|
187
207
|
assert_equal Fluent::Plugin::TailInput::FilePositionEntry, p2.class
|
188
208
|
|
189
209
|
assert_not_equal p1, p2
|
data/test/plugin/test_in_http.rb
CHANGED
@@ -46,6 +46,7 @@ class HttpInputTest < Test::Unit::TestCase
|
|
46
46
|
assert_equal 10*1024*1024, d.instance.body_size_limit
|
47
47
|
assert_equal 5, d.instance.keepalive_timeout
|
48
48
|
assert_equal false, d.instance.add_http_headers
|
49
|
+
assert_equal false, d.instance.add_query_params
|
49
50
|
end
|
50
51
|
|
51
52
|
def test_time
|
@@ -907,6 +908,30 @@ class HttpInputTest < Test::Unit::TestCase
|
|
907
908
|
assert_equal ["403", "403"], res_codes
|
908
909
|
end
|
909
910
|
|
911
|
+
def test_add_query_params
|
912
|
+
d = create_driver(CONFIG + "add_query_params true")
|
913
|
+
assert_equal true, d.instance.add_query_params
|
914
|
+
|
915
|
+
time = event_time("2011-01-02 13:14:15 UTC")
|
916
|
+
time_i = time.to_i
|
917
|
+
events = [
|
918
|
+
["tag1", time, {"a"=>1, "QUERY_A"=>"b"}],
|
919
|
+
["tag2", time, {"a"=>2, "QUERY_A"=>"b"}],
|
920
|
+
]
|
921
|
+
res_codes = []
|
922
|
+
res_bodies = []
|
923
|
+
|
924
|
+
d.run do
|
925
|
+
events.each do |tag, _t, record|
|
926
|
+
res = post("/#{tag}?a=b", {"json"=>record.to_json, "time"=>time_i.to_s})
|
927
|
+
res_codes << res.code
|
928
|
+
end
|
929
|
+
end
|
930
|
+
assert_equal ["200", "200"], res_codes
|
931
|
+
assert_equal [], res_bodies
|
932
|
+
assert_equal events, d.events
|
933
|
+
end
|
934
|
+
|
910
935
|
$test_in_http_connection_object_ids = []
|
911
936
|
$test_in_http_content_types = []
|
912
937
|
$test_in_http_content_types_flag = false
|
data/test/plugin/test_in_tail.rb
CHANGED
@@ -17,29 +17,62 @@ class TailInputTest < Test::Unit::TestCase
|
|
17
17
|
|
18
18
|
def teardown
|
19
19
|
super
|
20
|
+
cleanup_directory(TMP_DIR)
|
20
21
|
Fluent::Engine.stop
|
21
22
|
end
|
22
23
|
|
23
24
|
def cleanup_directory(path)
|
24
|
-
|
25
|
+
begin
|
26
|
+
FileUtils.rm_f(path, secure: true)
|
27
|
+
rescue ArgumentError
|
28
|
+
FileUtils.rm_f(path) # For Ruby 2.6 or before.
|
29
|
+
end
|
25
30
|
if File.exist?(path)
|
26
31
|
FileUtils.remove_entry_secure(path, true)
|
27
32
|
end
|
28
33
|
FileUtils.mkdir_p(path)
|
29
34
|
end
|
30
35
|
|
36
|
+
def cleanup_file(path)
|
37
|
+
begin
|
38
|
+
FileUtils.rm_f(path, secure: true)
|
39
|
+
rescue ArgumentError
|
40
|
+
FileUtils.rm_f(path) # For Ruby 2.6 or before.
|
41
|
+
end
|
42
|
+
if File.exist?(path)
|
43
|
+
# ensure files are closed for Windows, on which deleted files
|
44
|
+
# are still visible from filesystem
|
45
|
+
GC.start(full_mark: true, immediate_mark: true, immediate_sweep: true)
|
46
|
+
FileUtils.remove_entry_secure(path, true)
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
def create_target_info(path)
|
51
|
+
Fluent::Plugin::TailInput::TargetInfo.new(path, Fluent::FileWrapper.stat(path).ino)
|
52
|
+
end
|
53
|
+
|
31
54
|
TMP_DIR = File.dirname(__FILE__) + "/../tmp/tail#{ENV['TEST_ENV_NUMBER']}"
|
32
55
|
|
33
56
|
CONFIG = config_element("ROOT", "", {
|
34
57
|
"path" => "#{TMP_DIR}/tail.txt",
|
35
58
|
"tag" => "t1",
|
36
|
-
"rotate_wait" => "2s"
|
59
|
+
"rotate_wait" => "2s",
|
60
|
+
"refresh_interval" => "1s"
|
37
61
|
})
|
38
62
|
COMMON_CONFIG = CONFIG + config_element("", "", { "pos_file" => "#{TMP_DIR}/tail.pos" })
|
39
63
|
CONFIG_READ_FROM_HEAD = config_element("", "", { "read_from_head" => true })
|
40
64
|
CONFIG_ENABLE_WATCH_TIMER = config_element("", "", { "enable_watch_timer" => false })
|
41
65
|
CONFIG_DISABLE_STAT_WATCHER = config_element("", "", { "enable_stat_watcher" => false })
|
42
66
|
CONFIG_OPEN_ON_EVERY_UPDATE = config_element("", "", { "open_on_every_update" => true })
|
67
|
+
COMMON_FOLLOW_INODE_CONFIG = config_element("ROOT", "", {
|
68
|
+
"path" => "#{TMP_DIR}/tail.txt*",
|
69
|
+
"pos_file" => "#{TMP_DIR}/tail.pos",
|
70
|
+
"tag" => "t1",
|
71
|
+
"refresh_interval" => "1s",
|
72
|
+
"read_from_head" => "true",
|
73
|
+
"format" => "none",
|
74
|
+
"follow_inodes" => "true"
|
75
|
+
})
|
43
76
|
SINGLE_LINE_CONFIG = config_element("", "", { "format" => "/(?<message>.*)/" })
|
44
77
|
PARSE_SINGLE_LINE_CONFIG = config_element("", "", {}, [config_element("parse", "", { "@type" => "/(?<message>.*)/" })])
|
45
78
|
MULTILINE_CONFIG = config_element(
|
@@ -118,6 +151,12 @@ class TailInputTest < Test::Unit::TestCase
|
|
118
151
|
end
|
119
152
|
end
|
120
153
|
|
154
|
+
test "follow_inodes w/o pos file" do
|
155
|
+
assert_raise(Fluent::ConfigError) do
|
156
|
+
create_driver(CONFIG + config_element('', '', {'follow_inodes' => 'true'}))
|
157
|
+
end
|
158
|
+
end
|
159
|
+
|
121
160
|
test "both enable_watch_timer and enable_stat_watcher are false" do
|
122
161
|
assert_raise(Fluent::ConfigError) do
|
123
162
|
create_driver(CONFIG_ENABLE_WATCH_TIMER + CONFIG_DISABLE_STAT_WATCHER + PARSE_SINGLE_LINE_CONFIG)
|
@@ -1002,6 +1041,7 @@ class TailInputTest < Test::Unit::TestCase
|
|
1002
1041
|
# * path test
|
1003
1042
|
# TODO: Clean up tests
|
1004
1043
|
EX_ROTATE_WAIT = 0
|
1044
|
+
EX_FOLLOW_INODES = false
|
1005
1045
|
|
1006
1046
|
EX_CONFIG = config_element("", "", {
|
1007
1047
|
"tag" => "tail",
|
@@ -1011,38 +1051,43 @@ class TailInputTest < Test::Unit::TestCase
|
|
1011
1051
|
"read_from_head" => true,
|
1012
1052
|
"refresh_interval" => 30,
|
1013
1053
|
"rotate_wait" => "#{EX_ROTATE_WAIT}s",
|
1054
|
+
"follow_inodes" => "#{EX_FOLLOW_INODES}",
|
1014
1055
|
})
|
1015
|
-
EX_PATHS = [
|
1016
|
-
'test/plugin/data/2010/01/20100102-030405.log',
|
1017
|
-
'test/plugin/data/log/foo/bar.log',
|
1018
|
-
'test/plugin/data/log/test.log'
|
1019
|
-
]
|
1020
|
-
|
1021
1056
|
def test_expand_paths
|
1057
|
+
ex_paths = [
|
1058
|
+
create_target_info('test/plugin/data/2010/01/20100102-030405.log'),
|
1059
|
+
create_target_info('test/plugin/data/log/foo/bar.log'),
|
1060
|
+
create_target_info('test/plugin/data/log/test.log')
|
1061
|
+
]
|
1022
1062
|
plugin = create_driver(EX_CONFIG, false).instance
|
1023
1063
|
flexstub(Time) do |timeclass|
|
1024
1064
|
timeclass.should_receive(:now).with_no_args.and_return(Time.new(2010, 1, 2, 3, 4, 5))
|
1025
|
-
assert_equal
|
1065
|
+
assert_equal ex_paths, plugin.expand_paths.values.sort_by { |path_ino| path_ino.path }
|
1026
1066
|
end
|
1027
1067
|
|
1028
1068
|
# Test exclusion
|
1029
|
-
exclude_config = EX_CONFIG + config_element("", "", { "exclude_path" => %Q(["#{
|
1069
|
+
exclude_config = EX_CONFIG + config_element("", "", { "exclude_path" => %Q(["#{ex_paths.last.path}"]) })
|
1030
1070
|
plugin = create_driver(exclude_config, false).instance
|
1031
|
-
assert_equal
|
1071
|
+
assert_equal ex_paths - [ex_paths.last], plugin.expand_paths.values.sort_by { |path_ino| path_ino.path }
|
1032
1072
|
end
|
1033
1073
|
|
1034
1074
|
def test_expand_paths_with_duplicate_configuration
|
1035
1075
|
expanded_paths = [
|
1036
|
-
'test/plugin/data/log/foo/bar.log',
|
1037
|
-
'test/plugin/data/log/test.log'
|
1076
|
+
create_target_info('test/plugin/data/log/foo/bar.log'),
|
1077
|
+
create_target_info('test/plugin/data/log/test.log')
|
1038
1078
|
]
|
1039
1079
|
duplicate_config = EX_CONFIG.dup
|
1040
1080
|
duplicate_config["path"]="test/plugin/data/log/**/*.log, test/plugin/data/log/**/*.log"
|
1041
1081
|
plugin = create_driver(EX_CONFIG, false).instance
|
1042
|
-
assert_equal expanded_paths, plugin.expand_paths.
|
1082
|
+
assert_equal expanded_paths, plugin.expand_paths.values.sort_by { |path_ino| path_ino.path }
|
1043
1083
|
end
|
1044
1084
|
|
1045
1085
|
def test_expand_paths_with_timezone
|
1086
|
+
ex_paths = [
|
1087
|
+
create_target_info('test/plugin/data/2010/01/20100102-030405.log'),
|
1088
|
+
create_target_info('test/plugin/data/log/foo/bar.log'),
|
1089
|
+
create_target_info('test/plugin/data/log/test.log')
|
1090
|
+
]
|
1046
1091
|
['Asia/Taipei', '+08'].each do |tz_type|
|
1047
1092
|
taipei_config = EX_CONFIG + config_element("", "", {"path_timezone" => tz_type})
|
1048
1093
|
plugin = create_driver(taipei_config, false).instance
|
@@ -1056,8 +1101,8 @@ class TailInputTest < Test::Unit::TestCase
|
|
1056
1101
|
# env : 2010-01-01 19:04:05 (UTC), tail path : 2010-01-02 03:04:05 (Asia/Taipei)
|
1057
1102
|
timeclass.should_receive(:now).with_no_args.and_return(Time.new(2010, 1, 1, 19, 4, 5))
|
1058
1103
|
|
1059
|
-
assert_equal
|
1060
|
-
assert_equal
|
1104
|
+
assert_equal ex_paths, plugin.expand_paths.values.sort_by { |path_ino| path_ino.path }
|
1105
|
+
assert_equal ex_paths - [ex_paths.first], exclude_plugin.expand_paths.values.sort_by { |path_ino| path_ino.path }
|
1061
1106
|
end
|
1062
1107
|
end
|
1063
1108
|
end
|
@@ -1065,10 +1110,10 @@ class TailInputTest < Test::Unit::TestCase
|
|
1065
1110
|
|
1066
1111
|
def test_log_file_without_extension
|
1067
1112
|
expected_files = [
|
1068
|
-
'test/plugin/data/log/bar',
|
1069
|
-
'test/plugin/data/log/foo/bar.log',
|
1070
|
-
'test/plugin/data/log/foo/bar2',
|
1071
|
-
'test/plugin/data/log/test.log'
|
1113
|
+
create_target_info('test/plugin/data/log/bar'),
|
1114
|
+
create_target_info('test/plugin/data/log/foo/bar.log'),
|
1115
|
+
create_target_info('test/plugin/data/log/foo/bar2'),
|
1116
|
+
create_target_info('test/plugin/data/log/test.log')
|
1072
1117
|
]
|
1073
1118
|
|
1074
1119
|
config = config_element("", "", {
|
@@ -1079,7 +1124,7 @@ class TailInputTest < Test::Unit::TestCase
|
|
1079
1124
|
})
|
1080
1125
|
|
1081
1126
|
plugin = create_driver(config, false).instance
|
1082
|
-
assert_equal expected_files, plugin.expand_paths.
|
1127
|
+
assert_equal expected_files, plugin.expand_paths.values.sort_by { |path_ino| path_ino.path }
|
1083
1128
|
end
|
1084
1129
|
|
1085
1130
|
def test_unwatched_files_should_be_removed
|
@@ -1205,25 +1250,35 @@ class TailInputTest < Test::Unit::TestCase
|
|
1205
1250
|
end
|
1206
1251
|
|
1207
1252
|
def test_z_refresh_watchers
|
1253
|
+
ex_paths = [
|
1254
|
+
create_target_info('test/plugin/data/2010/01/20100102-030405.log'),
|
1255
|
+
create_target_info('test/plugin/data/log/foo/bar.log'),
|
1256
|
+
create_target_info('test/plugin/data/log/test.log'),
|
1257
|
+
]
|
1208
1258
|
plugin = create_driver(EX_CONFIG, false).instance
|
1209
1259
|
sio = StringIO.new
|
1210
1260
|
plugin.instance_eval do
|
1211
|
-
@pf = Fluent::Plugin::TailInput::PositionFile.load(sio, logger: $log)
|
1261
|
+
@pf = Fluent::Plugin::TailInput::PositionFile.load(sio, EX_FOLLOW_INODES, {}, logger: $log)
|
1212
1262
|
@loop = Coolio::Loop.new
|
1213
1263
|
end
|
1214
1264
|
|
1215
1265
|
Timecop.freeze(2010, 1, 2, 3, 4, 5) do
|
1216
|
-
|
1217
|
-
mock.proxy(Fluent::Plugin::TailInput::TailWatcher).new(
|
1266
|
+
ex_paths.each do |target_info|
|
1267
|
+
mock.proxy(Fluent::Plugin::TailInput::TailWatcher).new(target_info, anything, anything, true, false, anything, nil, anything).once
|
1218
1268
|
end
|
1219
1269
|
|
1220
1270
|
plugin.refresh_watchers
|
1221
1271
|
end
|
1222
1272
|
|
1223
|
-
|
1273
|
+
path = 'test/plugin/data/2010/01/20100102-030405.log'
|
1274
|
+
target_info = Fluent::Plugin::TailInput::TargetInfo.new(path, Fluent::FileWrapper.stat(path).ino)
|
1275
|
+
mock.proxy(plugin).detach_watcher_after_rotate_wait(plugin.instance_variable_get(:@tails)[target_info], target_info.ino)
|
1224
1276
|
|
1225
1277
|
Timecop.freeze(2010, 1, 2, 3, 4, 6) do
|
1226
|
-
|
1278
|
+
path = "test/plugin/data/2010/01/20100102-030406.log"
|
1279
|
+
inode = Fluent::FileWrapper.stat(path).ino
|
1280
|
+
target_info = Fluent::Plugin::TailInput::TargetInfo.new(path, inode)
|
1281
|
+
mock.proxy(Fluent::Plugin::TailInput::TailWatcher).new(target_info, anything, anything, true, false, anything, nil, anything).once
|
1227
1282
|
plugin.refresh_watchers
|
1228
1283
|
|
1229
1284
|
flexstub(Fluent::Plugin::TailInput::TailWatcher) do |watcherclass|
|
@@ -1369,6 +1424,305 @@ class TailInputTest < Test::Unit::TestCase
|
|
1369
1424
|
end
|
1370
1425
|
end
|
1371
1426
|
|
1427
|
+
sub_test_case 'inode_processing' do
|
1428
|
+
def test_should_delete_file_pos_entry_for_non_existing_file_with_follow_inodes
|
1429
|
+
config = COMMON_FOLLOW_INODE_CONFIG
|
1430
|
+
|
1431
|
+
path = "#{TMP_DIR}/tail.txt"
|
1432
|
+
ino = 1
|
1433
|
+
pos = 1234
|
1434
|
+
File.open("#{TMP_DIR}/tail.pos", "wb") {|f|
|
1435
|
+
f.puts ("%s\t%016x\t%016x\n" % [path, pos, ino])
|
1436
|
+
}
|
1437
|
+
|
1438
|
+
d = create_driver(config, false)
|
1439
|
+
d.run
|
1440
|
+
|
1441
|
+
pos_file = File.open("#{TMP_DIR}/tail.pos", "r")
|
1442
|
+
pos_file.pos = 0
|
1443
|
+
|
1444
|
+
assert_raise(EOFError) do
|
1445
|
+
pos_file.readline
|
1446
|
+
end
|
1447
|
+
end
|
1448
|
+
|
1449
|
+
def test_should_write_latest_offset_after_rotate_wait
|
1450
|
+
config = COMMON_FOLLOW_INODE_CONFIG
|
1451
|
+
File.open("#{TMP_DIR}/tail.txt", "wb") {|f|
|
1452
|
+
f.puts "test1"
|
1453
|
+
f.puts "test2"
|
1454
|
+
}
|
1455
|
+
|
1456
|
+
d = create_driver(config, false)
|
1457
|
+
d.run(expect_emits: 2, shutdown: false) do
|
1458
|
+
File.open("#{TMP_DIR}/tail.txt", "ab") {|f| f.puts "test3\n"}
|
1459
|
+
FileUtils.move("#{TMP_DIR}/tail.txt", "#{TMP_DIR}/tail.txt" + "1")
|
1460
|
+
sleep 1
|
1461
|
+
File.open("#{TMP_DIR}/tail.txt" + "1", "ab") {|f| f.puts "test4\n"}
|
1462
|
+
end
|
1463
|
+
|
1464
|
+
pos_file = File.open("#{TMP_DIR}/tail.pos", "r")
|
1465
|
+
pos_file.pos = 0
|
1466
|
+
line_parts = /^([^\t]+)\t([0-9a-fA-F]+)\t([0-9a-fA-F]+)/.match(pos_file.readline)
|
1467
|
+
waiting(5) {
|
1468
|
+
while line_parts[2].to_i(16) != 24
|
1469
|
+
sleep(0.1)
|
1470
|
+
pos_file.pos = 0
|
1471
|
+
line_parts = /^([^\t]+)\t([0-9a-fA-F]+)\t([0-9a-fA-F]+)/.match(pos_file.readline)
|
1472
|
+
end
|
1473
|
+
}
|
1474
|
+
assert_equal(24, line_parts[2].to_i(16))
|
1475
|
+
d.instance_shutdown
|
1476
|
+
end
|
1477
|
+
|
1478
|
+
def test_should_keep_and_update_existing_file_pos_entry_for_deleted_file_when_new_file_with_same_name_created
|
1479
|
+
config = config_element("", "", {"format" => "none"})
|
1480
|
+
|
1481
|
+
path = "#{TMP_DIR}/tail.txt"
|
1482
|
+
ino = 1
|
1483
|
+
pos = 1234
|
1484
|
+
File.open("#{TMP_DIR}/tail.pos", "wb") {|f|
|
1485
|
+
f.puts ("%s\t%016x\t%016x\n" % [path, pos, ino])
|
1486
|
+
}
|
1487
|
+
|
1488
|
+
d = create_driver(config)
|
1489
|
+
d.run(shutdown: false)
|
1490
|
+
|
1491
|
+
pos_file = File.open("#{TMP_DIR}/tail.pos", "r")
|
1492
|
+
pos_file.pos = 0
|
1493
|
+
|
1494
|
+
path_pos_ino = /^([^\t]+)\t([0-9a-fA-F]+)\t([0-9a-fA-F]+)/.match(pos_file.readline)
|
1495
|
+
assert_equal(path, path_pos_ino[1])
|
1496
|
+
assert_equal(pos, path_pos_ino[2].to_i(16))
|
1497
|
+
assert_equal(ino, path_pos_ino[3].to_i(16))
|
1498
|
+
|
1499
|
+
File.open("#{TMP_DIR}/tail.txt", "wb") {|f|
|
1500
|
+
f.puts "test1"
|
1501
|
+
f.puts "test2"
|
1502
|
+
}
|
1503
|
+
Timecop.travel(Time.now + 10) do
|
1504
|
+
sleep 5
|
1505
|
+
pos_file.pos = 0
|
1506
|
+
tuple = create_target_info("#{TMP_DIR}/tail.txt")
|
1507
|
+
path_pos_ino = /^([^\t]+)\t([0-9a-fA-F]+)\t([0-9a-fA-F]+)/.match(pos_file.readline)
|
1508
|
+
assert_equal(tuple.path, path_pos_ino[1])
|
1509
|
+
assert_equal(12, path_pos_ino[2].to_i(16))
|
1510
|
+
assert_equal(tuple.ino, path_pos_ino[3].to_i(16))
|
1511
|
+
end
|
1512
|
+
d.instance_shutdown
|
1513
|
+
end
|
1514
|
+
|
1515
|
+
def test_should_mark_file_unwatched_after_limit_recently_modified_and_rotate_wait
|
1516
|
+
config = config_element("ROOT", "", {
|
1517
|
+
"path" => "#{TMP_DIR}/tail.txt*",
|
1518
|
+
"pos_file" => "#{TMP_DIR}/tail.pos",
|
1519
|
+
"tag" => "t1",
|
1520
|
+
"rotate_wait" => "1s",
|
1521
|
+
"refresh_interval" => "1s",
|
1522
|
+
"limit_recently_modified" => "1s",
|
1523
|
+
"read_from_head" => "true",
|
1524
|
+
"format" => "none",
|
1525
|
+
"follow_inodes" => "true",
|
1526
|
+
})
|
1527
|
+
|
1528
|
+
d = create_driver(config, false)
|
1529
|
+
|
1530
|
+
File.open("#{TMP_DIR}/tail.txt", "wb") {|f|
|
1531
|
+
f.puts "test1"
|
1532
|
+
f.puts "test2"
|
1533
|
+
}
|
1534
|
+
target_info = create_target_info("#{TMP_DIR}/tail.txt")
|
1535
|
+
|
1536
|
+
d.run(expect_emits: 1, shutdown: false) do
|
1537
|
+
File.open("#{TMP_DIR}/tail.txt", "ab") {|f| f.puts "test3\n"}
|
1538
|
+
end
|
1539
|
+
|
1540
|
+
|
1541
|
+
Timecop.travel(Time.now + 10) do
|
1542
|
+
waiting(5) {
|
1543
|
+
# @pos will be reset as 0 when UNWATCHED_POSITION is specified.
|
1544
|
+
sleep 0.1 until d.instance.instance_variable_get(:@pf)[target_info].read_pos == 0
|
1545
|
+
}
|
1546
|
+
end
|
1547
|
+
|
1548
|
+
assert_equal(0, d.instance.instance_variable_get(:@pf)[target_info].read_pos)
|
1549
|
+
|
1550
|
+
d.instance_shutdown
|
1551
|
+
end
|
1552
|
+
|
1553
|
+
def test_should_read_from_head_on_file_renaming_with_star_in_pattern
|
1554
|
+
config = config_element("ROOT", "", {
|
1555
|
+
"path" => "#{TMP_DIR}/tail.txt*",
|
1556
|
+
"pos_file" => "#{TMP_DIR}/tail.pos",
|
1557
|
+
"tag" => "t1",
|
1558
|
+
"rotate_wait" => "10s",
|
1559
|
+
"refresh_interval" => "1s",
|
1560
|
+
"limit_recently_modified" => "60s",
|
1561
|
+
"read_from_head" => "true",
|
1562
|
+
"format" => "none",
|
1563
|
+
"follow_inodes" => "true"
|
1564
|
+
})
|
1565
|
+
|
1566
|
+
d = create_driver(config, false)
|
1567
|
+
|
1568
|
+
File.open("#{TMP_DIR}/tail.txt", "wb") {|f|
|
1569
|
+
f.puts "test1"
|
1570
|
+
f.puts "test2"
|
1571
|
+
}
|
1572
|
+
|
1573
|
+
d.run(expect_emits: 2, shutdown: false) do
|
1574
|
+
File.open("#{TMP_DIR}/tail.txt", "ab") {|f| f.puts "test3\n"}
|
1575
|
+
FileUtils.move("#{TMP_DIR}/tail.txt", "#{TMP_DIR}/tail.txt1")
|
1576
|
+
end
|
1577
|
+
|
1578
|
+
events = d.events
|
1579
|
+
assert_equal(3, events.length)
|
1580
|
+
d.instance_shutdown
|
1581
|
+
end
|
1582
|
+
|
1583
|
+
def test_should_not_read_from_head_on_rotation_when_watching_inodes
|
1584
|
+
config = COMMON_FOLLOW_INODE_CONFIG
|
1585
|
+
|
1586
|
+
d = create_driver(config, false)
|
1587
|
+
|
1588
|
+
File.open("#{TMP_DIR}/tail.txt", "wb") {|f|
|
1589
|
+
f.puts "test1"
|
1590
|
+
f.puts "test2"
|
1591
|
+
}
|
1592
|
+
|
1593
|
+
d.run(expect_emits: 1, shutdown: false) do
|
1594
|
+
File.open("#{TMP_DIR}/tail.txt", "ab") {|f| f.puts "test3\n"}
|
1595
|
+
end
|
1596
|
+
|
1597
|
+
FileUtils.move("#{TMP_DIR}/tail.txt", "#{TMP_DIR}/tail.txt1")
|
1598
|
+
Timecop.travel(Time.now + 10) do
|
1599
|
+
sleep 2
|
1600
|
+
events = d.events
|
1601
|
+
assert_equal(3, events.length)
|
1602
|
+
end
|
1603
|
+
|
1604
|
+
d.instance_shutdown
|
1605
|
+
end
|
1606
|
+
|
1607
|
+
def test_should_mark_file_unwatched_if_same_name_file_created_with_different_inode
|
1608
|
+
config = COMMON_FOLLOW_INODE_CONFIG
|
1609
|
+
|
1610
|
+
d = create_driver(config, false)
|
1611
|
+
|
1612
|
+
File.open("#{TMP_DIR}/tail.txt", "wb") {|f|
|
1613
|
+
f.puts "test1"
|
1614
|
+
f.puts "test2"
|
1615
|
+
}
|
1616
|
+
target_info = create_target_info("#{TMP_DIR}/tail.txt")
|
1617
|
+
|
1618
|
+
d.run(expect_emits: 2, shutdown: false) do
|
1619
|
+
File.open("#{TMP_DIR}/tail.txt", "ab") {|f| f.puts "test3\n"}
|
1620
|
+
cleanup_file("#{TMP_DIR}/tail.txt")
|
1621
|
+
File.open("#{TMP_DIR}/tail.txt", "wb") {|f| f.puts "test4\n"}
|
1622
|
+
end
|
1623
|
+
|
1624
|
+
new_target_info = create_target_info("#{TMP_DIR}/tail.txt")
|
1625
|
+
|
1626
|
+
pos_file = d.instance.instance_variable_get(:@pf)
|
1627
|
+
|
1628
|
+
waiting(10) {
|
1629
|
+
# @pos will be reset as 0 when UNWATCHED_POSITION is specified.
|
1630
|
+
sleep 0.1 until pos_file[target_info].read_pos == 0
|
1631
|
+
}
|
1632
|
+
new_position = pos_file[new_target_info].read_pos
|
1633
|
+
assert_equal(6, new_position)
|
1634
|
+
|
1635
|
+
d.instance_shutdown
|
1636
|
+
end
|
1637
|
+
|
1638
|
+
def test_should_close_watcher_after_rotate_wait
|
1639
|
+
now = Time.now
|
1640
|
+
config = COMMON_FOLLOW_INODE_CONFIG + config_element('', '', {"rotate_wait" => "1s", "limit_recently_modified" => "1s"})
|
1641
|
+
|
1642
|
+
d = create_driver(config, false)
|
1643
|
+
|
1644
|
+
File.open("#{TMP_DIR}/tail.txt", "wb") {|f|
|
1645
|
+
f.puts "test1"
|
1646
|
+
f.puts "test2"
|
1647
|
+
}
|
1648
|
+
target_info = create_target_info("#{TMP_DIR}/tail.txt")
|
1649
|
+
mock.proxy(Fluent::Plugin::TailInput::TailWatcher).new(target_info, anything, anything, true, true, anything, nil, anything).once
|
1650
|
+
d.run(shutdown: false)
|
1651
|
+
assert d.instance.instance_variable_get(:@tails)[target_info]
|
1652
|
+
|
1653
|
+
Timecop.travel(now + 10) do
|
1654
|
+
d.instance.instance_eval do
|
1655
|
+
sleep 0.1 until @tails[target_info] == nil
|
1656
|
+
end
|
1657
|
+
assert_nil d.instance.instance_variable_get(:@tails)[target_info]
|
1658
|
+
end
|
1659
|
+
d.instance_shutdown
|
1660
|
+
end
|
1661
|
+
|
1662
|
+
def test_should_create_new_watcher_for_new_file_with_same_name
|
1663
|
+
now = Time.now
|
1664
|
+
config = COMMON_FOLLOW_INODE_CONFIG + config_element('', '', {"limit_recently_modified" => "2s"})
|
1665
|
+
|
1666
|
+
d = create_driver(config, false)
|
1667
|
+
|
1668
|
+
File.open("#{TMP_DIR}/tail.txt", "wb") {|f|
|
1669
|
+
f.puts "test1"
|
1670
|
+
f.puts "test2"
|
1671
|
+
}
|
1672
|
+
path_ino = create_target_info("#{TMP_DIR}/tail.txt")
|
1673
|
+
|
1674
|
+
d.run(expect_emits: 1, shutdown: false) do
|
1675
|
+
File.open("#{TMP_DIR}/tail.txt", "ab") {|f| f.puts "test3\n"}
|
1676
|
+
end
|
1677
|
+
|
1678
|
+
cleanup_file("#{TMP_DIR}/tail.txt")
|
1679
|
+
File.open("#{TMP_DIR}/tail.txt", "wb") {|f|
|
1680
|
+
f.puts "test3"
|
1681
|
+
f.puts "test4"
|
1682
|
+
}
|
1683
|
+
new_path_ino = create_target_info("#{TMP_DIR}/tail.txt")
|
1684
|
+
|
1685
|
+
Timecop.travel(now + 10) do
|
1686
|
+
sleep 3
|
1687
|
+
d.instance.instance_eval do
|
1688
|
+
@tails[path_ino] == nil
|
1689
|
+
@tails[new_path_ino] != nil
|
1690
|
+
end
|
1691
|
+
end
|
1692
|
+
|
1693
|
+
events = d.events
|
1694
|
+
|
1695
|
+
assert_equal(5, events.length)
|
1696
|
+
|
1697
|
+
d.instance_shutdown
|
1698
|
+
end
|
1699
|
+
|
1700
|
+
def test_truncate_file_with_follow_inodes
|
1701
|
+
config = COMMON_FOLLOW_INODE_CONFIG
|
1702
|
+
|
1703
|
+
d = create_driver(config, false)
|
1704
|
+
|
1705
|
+
File.open("#{TMP_DIR}/tail.txt", "wb") {|f|
|
1706
|
+
f.puts "test1"
|
1707
|
+
f.puts "test2"
|
1708
|
+
}
|
1709
|
+
|
1710
|
+
d.run(expect_emits: 3, shutdown: false) do
|
1711
|
+
File.open("#{TMP_DIR}/tail.txt", "ab") {|f| f.puts "test3\n"}
|
1712
|
+
sleep 2
|
1713
|
+
File.open("#{TMP_DIR}/tail.txt", "w+b") {|f| f.puts "test4\n"}
|
1714
|
+
end
|
1715
|
+
|
1716
|
+
events = d.events
|
1717
|
+
assert_equal(4, events.length)
|
1718
|
+
assert_equal({"message" => "test1"}, events[0][2])
|
1719
|
+
assert_equal({"message" => "test2"}, events[1][2])
|
1720
|
+
assert_equal({"message" => "test3"}, events[2][2])
|
1721
|
+
assert_equal({"message" => "test4"}, events[3][2])
|
1722
|
+
d.instance_shutdown
|
1723
|
+
end
|
1724
|
+
end
|
1725
|
+
|
1372
1726
|
sub_test_case "tail_path" do
|
1373
1727
|
def test_tail_path_with_singleline
|
1374
1728
|
File.open("#{TMP_DIR}/tail.txt", "wb") {|f|
|
@@ -1498,13 +1852,13 @@ class TailInputTest < Test::Unit::TestCase
|
|
1498
1852
|
})
|
1499
1853
|
|
1500
1854
|
expected_files = [
|
1501
|
-
"#{TMP_DIR}/tail_watch1.txt",
|
1502
|
-
"#{TMP_DIR}/tail_watch2.txt"
|
1855
|
+
create_target_info("#{TMP_DIR}/tail_watch1.txt"),
|
1856
|
+
create_target_info("#{TMP_DIR}/tail_watch2.txt")
|
1503
1857
|
]
|
1504
1858
|
|
1505
1859
|
Timecop.freeze(now) do
|
1506
1860
|
plugin = create_driver(config, false).instance
|
1507
|
-
assert_equal expected_files, plugin.expand_paths.
|
1861
|
+
assert_equal expected_files, plugin.expand_paths.values.sort_by { |path_ino| path_ino.path }
|
1508
1862
|
end
|
1509
1863
|
end
|
1510
1864
|
|