fluentd 0.14.2 → 0.14.5

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.
Files changed (83) hide show
  1. checksums.yaml +4 -4
  2. data/ChangeLog +77 -1131
  3. data/Rakefile +15 -0
  4. data/appveyor.yml +2 -2
  5. data/example/in_forward.conf +3 -0
  6. data/example/in_forward_client.conf +37 -0
  7. data/example/in_forward_shared_key.conf +15 -0
  8. data/example/in_forward_users.conf +24 -0
  9. data/example/multi_filters.conf +61 -0
  10. data/example/out_forward.conf +13 -13
  11. data/example/out_forward_client.conf +109 -0
  12. data/example/out_forward_shared_key.conf +36 -0
  13. data/example/out_forward_users.conf +65 -0
  14. data/example/{out_buffered_null.conf → out_null.conf} +10 -6
  15. data/example/secondary_file.conf +41 -0
  16. data/fluentd.gemspec +19 -16
  17. data/lib/fluent/agent.rb +5 -0
  18. data/lib/fluent/command/debug.rb +4 -4
  19. data/lib/fluent/compat/handle_tag_and_time_mixin.rb +2 -0
  20. data/lib/fluent/compat/output.rb +5 -2
  21. data/lib/fluent/config/configure_proxy.rb +26 -5
  22. data/lib/fluent/config/error.rb +3 -0
  23. data/lib/fluent/config/section.rb +15 -0
  24. data/lib/fluent/event_router.rb +77 -4
  25. data/lib/fluent/plugin/base.rb +12 -3
  26. data/lib/fluent/plugin/buffer.rb +5 -1
  27. data/lib/fluent/plugin/filter.rb +48 -6
  28. data/lib/fluent/plugin/filter_record_transformer.rb +3 -1
  29. data/lib/fluent/plugin/filter_stdout.rb +0 -4
  30. data/lib/fluent/plugin/in_debug_agent.rb +5 -5
  31. data/lib/fluent/plugin/in_dummy.rb +9 -1
  32. data/lib/fluent/plugin/in_forward.rb +330 -62
  33. data/lib/fluent/plugin/in_monitor_agent.rb +31 -77
  34. data/lib/fluent/plugin/in_tail.rb +76 -92
  35. data/lib/fluent/plugin/multi_output.rb +4 -0
  36. data/lib/fluent/plugin/out_forward.rb +328 -222
  37. data/lib/fluent/plugin/out_null.rb +37 -0
  38. data/lib/fluent/plugin/out_secondary_file.rb +128 -0
  39. data/lib/fluent/plugin/out_stdout.rb +38 -2
  40. data/lib/fluent/plugin/output.rb +29 -6
  41. data/lib/fluent/plugin/storage_local.rb +16 -0
  42. data/lib/fluent/plugin_helper/timer.rb +6 -1
  43. data/lib/fluent/root_agent.rb +4 -1
  44. data/lib/fluent/supervisor.rb +62 -9
  45. data/lib/fluent/test/base.rb +3 -0
  46. data/lib/fluent/test/driver/base.rb +5 -0
  47. data/lib/fluent/test/formatter_test.rb +2 -0
  48. data/lib/fluent/test/parser_test.rb +2 -0
  49. data/lib/fluent/test/startup_shutdown.rb +33 -0
  50. data/lib/fluent/version.rb +1 -1
  51. data/lib/fluent/winsvc.rb +1 -2
  52. data/test/compat/test_calls_super.rb +2 -0
  53. data/test/config/test_configurable.rb +88 -0
  54. data/test/config/test_types.rb +7 -3
  55. data/test/plugin/test_filter.rb +121 -23
  56. data/test/plugin/test_filter_record_transformer.rb +22 -6
  57. data/test/plugin/test_in_debug_agent.rb +2 -2
  58. data/test/plugin/test_in_forward.rb +920 -407
  59. data/test/plugin/test_in_monitor_agent.rb +333 -0
  60. data/test/plugin/test_in_tail.rb +686 -595
  61. data/test/plugin/test_out_forward.rb +150 -207
  62. data/test/plugin/test_out_null.rb +85 -9
  63. data/test/plugin/test_out_secondary_file.rb +432 -0
  64. data/test/plugin/test_out_stdout.rb +143 -45
  65. data/test/plugin/test_output.rb +53 -0
  66. data/test/plugin/test_output_as_buffered.rb +13 -0
  67. data/test/plugin/test_output_as_buffered_overflow.rb +3 -0
  68. data/test/plugin/test_output_as_buffered_retries.rb +11 -0
  69. data/test/plugin/test_output_as_buffered_secondary.rb +12 -0
  70. data/test/plugin/test_output_as_standard.rb +12 -0
  71. data/test/plugin_helper/test_compat_parameters.rb +2 -0
  72. data/test/plugin_helper/test_timer.rb +31 -0
  73. data/test/test_event_router.rb +87 -0
  74. data/test/test_filter.rb +48 -6
  75. data/test/test_log.rb +5 -2
  76. data/test/test_output.rb +41 -1
  77. data/test/test_plugin_classes.rb +17 -9
  78. data/test/test_root_agent.rb +188 -0
  79. metadata +62 -68
  80. data/lib/fluent/plugin/out_buffered_null.rb +0 -59
  81. data/lib/fluent/plugin/out_buffered_stdout.rb +0 -70
  82. data/test/plugin/test_out_buffered_null.rb +0 -79
  83. data/test/plugin/test_out_buffered_stdout.rb +0 -122
@@ -1,5 +1,5 @@
1
1
  require_relative '../helper'
2
- require 'fluent/test'
2
+ require 'fluent/test/driver/input'
3
3
  require 'fluent/plugin/in_tail'
4
4
  require 'fluent/plugin/buffer'
5
5
  require 'fluent/system_config'
@@ -16,7 +16,7 @@ class TailInputTest < Test::Unit::TestCase
16
16
  # ensure files are closed for Windows, on which deleted files
17
17
  # are still visible from filesystem
18
18
  GC.start(full_mark: true, immediate_mark: true, immediate_sweep: true)
19
- FileUtils.remove_entry_secure(TMP_DIR)
19
+ FileUtils.remove_entry_secure(TMP_DIR, true)
20
20
  end
21
21
  FileUtils.mkdir_p(TMP_DIR)
22
22
  end
@@ -28,76 +28,206 @@ class TailInputTest < Test::Unit::TestCase
28
28
 
29
29
  TMP_DIR = File.dirname(__FILE__) + "/../tmp/tail#{ENV['TEST_ENV_NUMBER']}"
30
30
 
31
- CONFIG = %[
32
- path #{TMP_DIR}/tail.txt
33
- tag t1
34
- rotate_wait 2s
35
- ]
36
- COMMON_CONFIG = CONFIG + %[
37
- pos_file #{TMP_DIR}/tail.pos
38
- ]
39
- CONFIG_READ_FROM_HEAD = %[
40
- read_from_head true
41
- ]
42
- CONFIG_ENABLE_WATCH_TIMER = %[
43
- enable_watch_timer false
44
- ]
45
- SINGLE_LINE_CONFIG = %[
46
- format /(?<message>.*)/
47
- ]
31
+ CONFIG = config_element("ROOT", "", {
32
+ "path" => "#{TMP_DIR}/tail.txt",
33
+ "tag" => "t1",
34
+ "rotate_wait" => "2s"
35
+ })
36
+ COMMON_CONFIG = CONFIG + config_element("", "", { "pos_file" => "#{TMP_DIR}/tail.pos" })
37
+ CONFIG_READ_FROM_HEAD = config_element("", "", { "read_from_head" => true })
38
+ CONFIG_ENABLE_WATCH_TIMER = config_element("", "", { "enable_watch_timer" => false })
39
+ SINGLE_LINE_CONFIG = config_element("", "", { "format" => "/(?<message>.*)/" })
40
+ PARSE_SINGLE_LINE_CONFIG = config_element("", "", {}, [config_element("parse", "", { "@type" => "/(?<message>.*)/" })])
41
+ MULTILINE_CONFIG = config_element(
42
+ "", "", {
43
+ "format" => "multiline",
44
+ "format1" => "/^s (?<message1>[^\\n]+)(\\nf (?<message2>[^\\n]+))?(\\nf (?<message3>.*))?/",
45
+ "format_firstline" => "/^[s]/"
46
+ })
47
+ PARSE_MULTILINE_CONFIG = config_element(
48
+ "", "", {},
49
+ [config_element("parse", "", {
50
+ "@type" => "multiline",
51
+ "format1" => "/^s (?<message1>[^\\n]+)(\\nf (?<message2>[^\\n]+))?(\\nf (?<message3>.*))?/",
52
+ "format_firstline" => "/^[s]/"
53
+ })
54
+ ])
48
55
 
49
56
  def create_driver(conf = SINGLE_LINE_CONFIG, use_common_conf = true)
50
57
  config = use_common_conf ? COMMON_CONFIG + conf : conf
51
- Fluent::Test::InputTestDriver.new(Fluent::NewTailInput).configure(config)
58
+ Fluent::Test::Driver::Input.new(Fluent::Plugin::TailInput).configure(config)
52
59
  end
53
60
 
54
- def test_configure
55
- d = create_driver
56
- assert_equal ["#{TMP_DIR}/tail.txt"], d.instance.paths
57
- assert_equal "t1", d.instance.tag
58
- assert_equal 2, d.instance.rotate_wait
59
- assert_equal "#{TMP_DIR}/tail.pos", d.instance.pos_file
60
- assert_equal 1000, d.instance.read_lines_limit
61
- end
61
+ sub_test_case "configure" do
62
+ test "plain single line" do
63
+ d = create_driver
64
+ assert_equal ["#{TMP_DIR}/tail.txt"], d.instance.paths
65
+ assert_equal "t1", d.instance.tag
66
+ assert_equal 2, d.instance.rotate_wait
67
+ assert_equal "#{TMP_DIR}/tail.pos", d.instance.pos_file
68
+ assert_equal 1000, d.instance.read_lines_limit
69
+ end
70
+
71
+ data("empty" => config_element,
72
+ "w/o @type" => config_element("", "", {}, [config_element("parse", "", {})]))
73
+ test "w/o parse section" do |conf|
74
+ assert_raise(Fluent::ConfigError) do
75
+ create_driver(conf)
76
+ end
77
+ end
62
78
 
63
- def test_configure_encoding
64
- # valid encoding
65
- d = create_driver(SINGLE_LINE_CONFIG + 'encoding utf-8')
66
- assert_equal Encoding::UTF_8, d.instance.encoding
79
+ sub_test_case "encoding" do
80
+ test "valid" do
81
+ conf = SINGLE_LINE_CONFIG + config_element("", "", { "encoding" => "utf-8" })
82
+ d = create_driver(conf)
83
+ assert_equal Encoding::UTF_8, d.instance.encoding
84
+ end
67
85
 
68
- # invalid encoding
69
- assert_raise(Fluent::ConfigError) do
70
- create_driver(SINGLE_LINE_CONFIG + 'encoding no-such-encoding')
86
+ test "invalid" do
87
+ conf = SINGLE_LINE_CONFIG + config_element("", "", { "encoding" => "no-such-encoding" })
88
+ assert_raise(Fluent::ConfigError) do
89
+ create_driver(conf)
90
+ end
91
+ end
92
+ end
93
+
94
+ sub_test_case "from_encoding" do
95
+ test "only specified from_encoding raise ConfigError" do
96
+ conf = SINGLE_LINE_CONFIG + config_element("", "", { "from_encoding" => "utf-8" })
97
+ assert_raise(Fluent::ConfigError) do
98
+ create_driver(conf)
99
+ end
100
+ end
101
+
102
+ test "valid" do
103
+ conf = SINGLE_LINE_CONFIG + config_element("", "", {
104
+ "from_encoding" => "utf-8",
105
+ "encoding" => "utf-8"
106
+ })
107
+ d = create_driver(conf)
108
+ assert_equal(Encoding::UTF_8, d.instance.from_encoding)
109
+ end
110
+
111
+ test "invalid" do
112
+ conf = SINGLE_LINE_CONFIG + config_element("", "", {
113
+ "from_encoding" => "no-such-encoding",
114
+ "encoding" => "utf-8"
115
+ })
116
+ assert_raise(Fluent::ConfigError) do
117
+ create_driver(conf)
118
+ end
119
+ end
71
120
  end
72
121
  end
73
122
 
74
- # TODO: Should using more better approach instead of sleep wait
123
+ sub_test_case "singleline" do
124
+ data(flat: SINGLE_LINE_CONFIG,
125
+ parse: PARSE_SINGLE_LINE_CONFIG)
126
+ def test_emit(data)
127
+ config = data
128
+ File.open("#{TMP_DIR}/tail.txt", "wb") {|f|
129
+ f.puts "test1"
130
+ f.puts "test2"
131
+ }
75
132
 
76
- def test_emit
77
- File.open("#{TMP_DIR}/tail.txt", "wb") {|f|
78
- f.puts "test1"
79
- f.puts "test2"
80
- }
133
+ d = create_driver(config)
81
134
 
82
- d = create_driver
135
+ d.run(expect_emits: 1) do
136
+ File.open("#{TMP_DIR}/tail.txt", "ab") {|f|
137
+ f.puts "test3"
138
+ f.puts "test4"
139
+ }
140
+ end
83
141
 
84
- d.run do
85
- sleep 1
142
+ events = d.events
143
+ assert_equal(true, events.length > 0)
144
+ assert_equal({"message" => "test3"}, events[0][2])
145
+ assert_equal({"message" => "test4"}, events[1][2])
146
+ assert(events[0][1].is_a?(Fluent::EventTime))
147
+ assert(events[1][1].is_a?(Fluent::EventTime))
148
+ assert_equal(1, d.emit_count)
149
+ end
150
+
151
+ data('flat 1' => [:flat, 1, 2],
152
+ 'flat 10' => [:flat, 10, 1],
153
+ 'parse 1' => [:parse, 1, 2],
154
+ 'parse 10' => [:parse, 10, 1])
155
+ def test_emit_with_read_lines_limit(data)
156
+ config_style, limit, num_events = data
157
+ case config_style
158
+ when :flat
159
+ config = CONFIG_READ_FROM_HEAD + SINGLE_LINE_CONFIG + config_element("", "", { "read_lines_limit" => limit })
160
+ when :parse
161
+ config = CONFIG_READ_FROM_HEAD + config_element("", "", { "read_lines_limit" => limit }) + PARSE_SINGLE_LINE_CONFIG
162
+ end
163
+ d = create_driver(config)
164
+ msg = 'test' * 500 # in_tail reads 2048 bytes at once.
86
165
 
87
- File.open("#{TMP_DIR}/tail.txt", "ab") {|f|
88
- f.puts "test3"
89
- f.puts "test4"
166
+ d.run(expect_emits: 1) do
167
+ File.open("#{TMP_DIR}/tail.txt", "ab") {|f|
168
+ f.puts msg
169
+ f.puts msg
170
+ }
171
+ end
172
+
173
+ events = d.events
174
+ assert_equal(true, events.length > 0)
175
+ assert_equal({"message" => msg}, events[0][2])
176
+ assert_equal({"message" => msg}, events[1][2])
177
+ assert_equal(num_events, d.emit_count)
178
+ end
179
+
180
+ data(flat: CONFIG_READ_FROM_HEAD + SINGLE_LINE_CONFIG,
181
+ parse: CONFIG_READ_FROM_HEAD + PARSE_SINGLE_LINE_CONFIG)
182
+ def test_emit_with_read_from_head(data)
183
+ config = data
184
+ File.open("#{TMP_DIR}/tail.txt", "wb") {|f|
185
+ f.puts "test1"
186
+ f.puts "test2"
90
187
  }
91
- sleep 1
188
+
189
+ d = create_driver(config)
190
+
191
+ d.run(expect_emits: 2) do
192
+ File.open("#{TMP_DIR}/tail.txt", "ab") {|f|
193
+ f.puts "test3"
194
+ f.puts "test4"
195
+ }
196
+ end
197
+
198
+ events = d.events
199
+ assert(events.length > 0)
200
+ assert_equal({"message" => "test1"}, events[0][2])
201
+ assert_equal({"message" => "test2"}, events[1][2])
202
+ assert_equal({"message" => "test3"}, events[2][2])
203
+ assert_equal({"message" => "test4"}, events[3][2])
92
204
  end
93
205
 
94
- emits = d.emits
95
- assert_equal(true, emits.length > 0)
96
- assert_equal({"message" => "test3"}, emits[0][2])
97
- assert_equal({"message" => "test4"}, emits[1][2])
98
- assert(emits[0][1].is_a?(Fluent::EventTime))
99
- assert(emits[1][1].is_a?(Fluent::EventTime))
100
- assert_equal(1, d.emit_streams.size)
206
+ data(flat: CONFIG_ENABLE_WATCH_TIMER + SINGLE_LINE_CONFIG,
207
+ parse: CONFIG_ENABLE_WATCH_TIMER + PARSE_SINGLE_LINE_CONFIG)
208
+ def test_emit_with_enable_watch_timer(data)
209
+ config = data
210
+ File.open("#{TMP_DIR}/tail.txt", "wb") {|f|
211
+ f.puts "test1"
212
+ f.puts "test2"
213
+ }
214
+
215
+ d = create_driver(config)
216
+
217
+ d.run(expect_emits: 1) do
218
+ File.open("#{TMP_DIR}/tail.txt", "ab") {|f|
219
+ f.puts "test3"
220
+ f.puts "test4"
221
+ }
222
+ # according to cool.io's stat_watcher.c, systems without inotify will use
223
+ # an "automatic" value, typically around 5 seconds
224
+ end
225
+
226
+ events = d.events
227
+ assert(events.length > 0)
228
+ assert_equal({"message" => "test3"}, events[0][2])
229
+ assert_equal({"message" => "test4"}, events[1][2])
230
+ end
101
231
  end
102
232
 
103
233
  class TestWithSystem < self
@@ -137,198 +267,133 @@ class TailInputTest < Test::Unit::TestCase
137
267
 
138
268
  d = create_driver
139
269
 
140
- d.run do
141
- sleep 1
142
-
270
+ d.run(expect_emits: 1) do
143
271
  File.open("#{TMP_DIR}/tail.txt", "ab") {|f|
144
272
  f.puts "test3"
145
273
  f.puts "test4"
146
274
  }
147
- sleep 1
148
275
  end
149
276
 
150
- emits = d.emits
151
- assert_equal(true, emits.length > 0)
152
- assert_equal({"message" => "test3"}, emits[0][2])
153
- assert_equal({"message" => "test4"}, emits[1][2])
154
- assert(emits[0][1].is_a?(Fluent::EventTime))
155
- assert(emits[1][1].is_a?(Fluent::EventTime))
156
- assert_equal(1, d.emit_streams.size)
277
+ events = d.events
278
+ assert_equal(true, events.length > 0)
279
+ assert_equal({"message" => "test3"}, events[0][2])
280
+ assert_equal({"message" => "test4"}, events[1][2])
281
+ assert(events[0][1].is_a?(Fluent::EventTime))
282
+ assert(events[1][1].is_a?(Fluent::EventTime))
283
+ assert_equal(1, d.emit_count)
157
284
  pos = d.instance.instance_variable_get(:@pf_file)
158
285
  mode = "%o" % File.stat(pos).mode
159
286
  assert_equal OVERRIDE_FILE_PERMISSION, mode[-3, 3].to_i
160
287
  end
161
288
  end
162
289
 
163
- data('1' => [1, 2], '10' => [10, 1])
164
- def test_emit_with_read_lines_limit(data)
165
- limit, num_emits = data
166
- d = create_driver(CONFIG_READ_FROM_HEAD + SINGLE_LINE_CONFIG + "read_lines_limit #{limit}")
167
- msg = 'test' * 500 # in_tail reads 2048 bytes at once.
168
-
169
- d.run do
170
- sleep 1
171
-
172
- File.open("#{TMP_DIR}/tail.txt", "ab") {|f|
173
- f.puts msg
174
- f.puts msg
175
- }
176
- sleep 1
177
- end
178
-
179
- emits = d.emits
180
- assert_equal(true, emits.length > 0)
181
- assert_equal({"message" => msg}, emits[0][2])
182
- assert_equal({"message" => msg}, emits[1][2])
183
- assert_equal(num_emits, d.emit_streams.size)
184
- end
185
-
186
- def test_emit_with_read_from_head
187
- File.open("#{TMP_DIR}/tail.txt", "wb") {|f|
188
- f.puts "test1"
189
- f.puts "test2"
190
- }
191
-
192
- d = create_driver(CONFIG_READ_FROM_HEAD + SINGLE_LINE_CONFIG)
193
-
194
- d.run do
195
- sleep 1
290
+ sub_test_case "rotate file" do
291
+ data(flat: SINGLE_LINE_CONFIG,
292
+ parse: PARSE_SINGLE_LINE_CONFIG)
293
+ def test_rotate_file(data)
294
+ config = data
295
+ events = sub_test_rotate_file(config, expect_emits: 2)
296
+ assert_equal(4, events.length)
297
+ assert_equal({"message" => "test3"}, events[0][2])
298
+ assert_equal({"message" => "test4"}, events[1][2])
299
+ assert_equal({"message" => "test5"}, events[2][2])
300
+ assert_equal({"message" => "test6"}, events[3][2])
301
+ end
302
+
303
+ data(flat: CONFIG_READ_FROM_HEAD + SINGLE_LINE_CONFIG,
304
+ parse: CONFIG_READ_FROM_HEAD + PARSE_SINGLE_LINE_CONFIG)
305
+ def test_rotate_file_with_read_from_head(data)
306
+ config = data
307
+ events = sub_test_rotate_file(config, expect_records: 6)
308
+ assert_equal(6, events.length)
309
+ assert_equal({"message" => "test1"}, events[0][2])
310
+ assert_equal({"message" => "test2"}, events[1][2])
311
+ assert_equal({"message" => "test3"}, events[2][2])
312
+ assert_equal({"message" => "test4"}, events[3][2])
313
+ assert_equal({"message" => "test5"}, events[4][2])
314
+ assert_equal({"message" => "test6"}, events[5][2])
315
+ end
316
+
317
+ data(flat: SINGLE_LINE_CONFIG,
318
+ parse: PARSE_SINGLE_LINE_CONFIG)
319
+ def test_rotate_file_with_write_old(data)
320
+ config = data
321
+ events = sub_test_rotate_file(config, expect_emits: 3) { |rotated_file|
322
+ File.open("#{TMP_DIR}/tail.txt", "wb") { |f| }
323
+ rotated_file.puts "test7"
324
+ rotated_file.puts "test8"
325
+ rotated_file.flush
196
326
 
197
- File.open("#{TMP_DIR}/tail.txt", "ab") {|f|
198
- f.puts "test3"
199
- f.puts "test4"
327
+ sleep 1
328
+ File.open("#{TMP_DIR}/tail.txt", "ab") { |f|
329
+ f.puts "test5"
330
+ f.puts "test6"
331
+ }
200
332
  }
201
- sleep 1
202
- end
203
-
204
- emits = d.emits
205
- assert(emits.length > 0)
206
- assert_equal({"message" => "test1"}, emits[0][2])
207
- assert_equal({"message" => "test2"}, emits[1][2])
208
- assert_equal({"message" => "test3"}, emits[2][2])
209
- assert_equal({"message" => "test4"}, emits[3][2])
210
- end
211
-
212
- def test_emit_with_enable_watch_timer
213
- File.open("#{TMP_DIR}/tail.txt", "wb") {|f|
214
- f.puts "test1"
215
- f.puts "test2"
216
- }
217
-
218
- d = create_driver(CONFIG_ENABLE_WATCH_TIMER + SINGLE_LINE_CONFIG)
219
-
220
- d.run do
221
- sleep 1
222
-
223
- File.open("#{TMP_DIR}/tail.txt", "ab") {|f|
224
- f.puts "test3"
225
- f.puts "test4"
333
+ assert_equal(6, events.length)
334
+ assert_equal({"message" => "test3"}, events[0][2])
335
+ assert_equal({"message" => "test4"}, events[1][2])
336
+ assert_equal({"message" => "test7"}, events[2][2])
337
+ assert_equal({"message" => "test8"}, events[3][2])
338
+ assert_equal({"message" => "test5"}, events[4][2])
339
+ assert_equal({"message" => "test6"}, events[5][2])
340
+ end
341
+
342
+ data(flat: SINGLE_LINE_CONFIG,
343
+ parse: PARSE_SINGLE_LINE_CONFIG)
344
+ def test_rotate_file_with_write_old_and_no_new_file(data)
345
+ config = data
346
+ events = sub_test_rotate_file(config, expect_emits: 2) { |rotated_file|
347
+ rotated_file.puts "test7"
348
+ rotated_file.puts "test8"
349
+ rotated_file.flush
226
350
  }
227
- # according to cool.io's stat_watcher.c, systems without inotify will use
228
- # an "automatic" value, typically around 5 seconds
229
- sleep 10
351
+ assert_equal(4, events.length)
352
+ assert_equal({"message" => "test3"}, events[0][2])
353
+ assert_equal({"message" => "test4"}, events[1][2])
354
+ assert_equal({"message" => "test7"}, events[2][2])
355
+ assert_equal({"message" => "test8"}, events[3][2])
230
356
  end
231
357
 
232
- emits = d.emits
233
- assert(emits.length > 0)
234
- assert_equal({"message" => "test3"}, emits[0][2])
235
- assert_equal({"message" => "test4"}, emits[1][2])
236
- end
237
-
238
- def test_rotate_file
239
- emits = sub_test_rotate_file(SINGLE_LINE_CONFIG)
240
- assert_equal(4, emits.length)
241
- assert_equal({"message" => "test3"}, emits[0][2])
242
- assert_equal({"message" => "test4"}, emits[1][2])
243
- assert_equal({"message" => "test5"}, emits[2][2])
244
- assert_equal({"message" => "test6"}, emits[3][2])
245
- end
246
-
247
- def test_rotate_file_with_read_from_head
248
- emits = sub_test_rotate_file(CONFIG_READ_FROM_HEAD + SINGLE_LINE_CONFIG)
249
- assert_equal(6, emits.length)
250
- assert_equal({"message" => "test1"}, emits[0][2])
251
- assert_equal({"message" => "test2"}, emits[1][2])
252
- assert_equal({"message" => "test3"}, emits[2][2])
253
- assert_equal({"message" => "test4"}, emits[3][2])
254
- assert_equal({"message" => "test5"}, emits[4][2])
255
- assert_equal({"message" => "test6"}, emits[5][2])
256
- end
257
-
258
- def test_rotate_file_with_write_old
259
- emits = sub_test_rotate_file(SINGLE_LINE_CONFIG) { |rotated_file|
260
- File.open("#{TMP_DIR}/tail.txt", "wb") { |f| }
261
- rotated_file.puts "test7"
262
- rotated_file.puts "test8"
263
- rotated_file.flush
264
-
265
- sleep 1
266
- File.open("#{TMP_DIR}/tail.txt", "ab") { |f|
267
- f.puts "test5"
268
- f.puts "test6"
269
- }
270
- }
271
- assert_equal(6, emits.length)
272
- assert_equal({"message" => "test3"}, emits[0][2])
273
- assert_equal({"message" => "test4"}, emits[1][2])
274
- assert_equal({"message" => "test7"}, emits[2][2])
275
- assert_equal({"message" => "test8"}, emits[3][2])
276
- assert_equal({"message" => "test5"}, emits[4][2])
277
- assert_equal({"message" => "test6"}, emits[5][2])
278
- end
279
-
280
- def test_rotate_file_with_write_old_and_no_new_file
281
- emits = sub_test_rotate_file(SINGLE_LINE_CONFIG) { |rotated_file|
282
- rotated_file.puts "test7"
283
- rotated_file.puts "test8"
284
- rotated_file.flush
285
- }
286
- assert_equal(4, emits.length)
287
- assert_equal({"message" => "test3"}, emits[0][2])
288
- assert_equal({"message" => "test4"}, emits[1][2])
289
- assert_equal({"message" => "test7"}, emits[2][2])
290
- assert_equal({"message" => "test8"}, emits[3][2])
291
- end
292
-
293
- def sub_test_rotate_file(config = nil)
294
- file = Fluent::FileWrapper.open("#{TMP_DIR}/tail.txt", "wb")
295
- file.puts "test1"
296
- file.puts "test2"
297
- file.flush
298
-
299
- d = create_driver(config)
300
- d.run do
301
- sleep 1
302
-
303
- file.puts "test3"
304
- file.puts "test4"
358
+ def sub_test_rotate_file(config = nil, expect_emits: nil, expect_records: nil, timeout: nil)
359
+ file = Fluent::FileWrapper.open("#{TMP_DIR}/tail.txt", "wb")
360
+ file.puts "test1"
361
+ file.puts "test2"
305
362
  file.flush
306
- sleep 1
307
363
 
308
- FileUtils.mv("#{TMP_DIR}/tail.txt", "#{TMP_DIR}/tail2.txt")
309
- if block_given?
310
- yield file
311
- sleep 1
312
- else
313
- sleep 1
314
- File.open("#{TMP_DIR}/tail.txt", "wb") { |f| }
315
- sleep 1
364
+ d = create_driver(config)
365
+ d.run(expect_emits: expect_emits, expect_records: expect_records, timeout: timeout) do
366
+ size = d.emit_count
367
+ file.puts "test3"
368
+ file.puts "test4"
369
+ file.flush
370
+ sleep(0.1) until d.emit_count >= size + 1
371
+ size = d.emit_count
372
+
373
+ if Fluent.windows?
374
+ file.close
375
+ FileUtils.mv("#{TMP_DIR}/tail.txt", "#{TMP_DIR}/tail2.txt", force: true)
376
+ file = File.open("#{TMP_DIR}/tail.txt", "ab")
377
+ else
378
+ FileUtils.mv("#{TMP_DIR}/tail.txt", "#{TMP_DIR}/tail2.txt")
379
+ end
380
+ if block_given?
381
+ yield file
382
+ else
383
+ File.open("#{TMP_DIR}/tail.txt", "wb") { |f| }
384
+ sleep 1
316
385
 
317
- File.open("#{TMP_DIR}/tail.txt", "ab") { |f|
318
- f.puts "test5"
319
- f.puts "test6"
320
- }
321
- sleep 1
386
+ File.open("#{TMP_DIR}/tail.txt", "ab") { |f|
387
+ f.puts "test5"
388
+ f.puts "test6"
389
+ }
390
+ end
322
391
  end
323
- end
324
392
 
325
- d.run do
326
- sleep 1
393
+ d.events
394
+ ensure
395
+ file.close if file && !file.closed?
327
396
  end
328
-
329
- d.emits
330
- ensure
331
- file.close if file
332
397
  end
333
398
 
334
399
  def test_lf
@@ -348,9 +413,9 @@ class TailInputTest < Test::Unit::TestCase
348
413
  sleep 1
349
414
  end
350
415
 
351
- emits = d.emits
352
- assert_equal(true, emits.length > 0)
353
- assert_equal({"message" => "test3test4"}, emits[0][2])
416
+ events = d.events
417
+ assert_equal(true, events.length > 0)
418
+ assert_equal({"message" => "test3test4"}, events[0][2])
354
419
  end
355
420
 
356
421
  def test_whitespace
@@ -358,9 +423,7 @@ class TailInputTest < Test::Unit::TestCase
358
423
 
359
424
  d = create_driver
360
425
 
361
- d.run do
362
- sleep 1
363
-
426
+ d.run(expect_emits: 1) do
364
427
  File.open("#{TMP_DIR}/tail.txt", "ab") {|f|
365
428
  f.puts " " # 4 spaces
366
429
  f.puts " 4 spaces"
@@ -369,281 +432,346 @@ class TailInputTest < Test::Unit::TestCase
369
432
  f.puts " tab"
370
433
  f.puts "tab "
371
434
  }
372
- sleep 1
373
435
  end
374
436
 
375
- emits = d.emits
376
- assert_equal(true, emits.length > 0)
377
- assert_equal({"message" => " "}, emits[0][2])
378
- assert_equal({"message" => " 4 spaces"}, emits[1][2])
379
- assert_equal({"message" => "4 spaces "}, emits[2][2])
380
- assert_equal({"message" => " "}, emits[3][2])
381
- assert_equal({"message" => " tab"}, emits[4][2])
382
- assert_equal({"message" => "tab "}, emits[5][2])
437
+ events = d.events
438
+ assert_equal(true, events.length > 0)
439
+ assert_equal({"message" => " "}, events[0][2])
440
+ assert_equal({"message" => " 4 spaces"}, events[1][2])
441
+ assert_equal({"message" => "4 spaces "}, events[2][2])
442
+ assert_equal({"message" => " "}, events[3][2])
443
+ assert_equal({"message" => " tab"}, events[4][2])
444
+ assert_equal({"message" => "tab "}, events[5][2])
383
445
  end
384
446
 
385
447
  data(
386
- 'default encoding' => ['', Encoding::ASCII_8BIT],
387
- 'explicit encoding config' => ['encoding utf-8', Encoding::UTF_8])
448
+ 'flat default encoding' => [SINGLE_LINE_CONFIG, Encoding::ASCII_8BIT],
449
+ 'flat explicit encoding config' => [SINGLE_LINE_CONFIG + config_element("", "", { "encoding" => "utf-8" }), Encoding::UTF_8],
450
+ 'parse default encoding' => [PARSE_SINGLE_LINE_CONFIG, Encoding::ASCII_8BIT],
451
+ 'parse explicit encoding config' => [PARSE_SINGLE_LINE_CONFIG + config_element("", "", { "encoding" => "utf-8" }), Encoding::UTF_8])
388
452
  def test_encoding(data)
389
453
  encoding_config, encoding = data
390
454
 
391
- d = create_driver(SINGLE_LINE_CONFIG + CONFIG_READ_FROM_HEAD + encoding_config)
392
-
393
- d.run do
394
- sleep 1
455
+ d = create_driver(CONFIG_READ_FROM_HEAD + encoding_config)
395
456
 
457
+ d.run(expect_emits: 1) do
396
458
  File.open("#{TMP_DIR}/tail.txt", "wb") {|f|
397
459
  f.puts "test"
398
460
  }
399
- sleep 1
400
461
  end
401
462
 
402
- emits = d.emits
403
- assert_equal(encoding, emits[0][2]['message'].encoding)
463
+ events = d.events
464
+ assert_equal(encoding, events[0][2]['message'].encoding)
404
465
  end
405
466
 
406
- # multiline mode test
407
-
408
- def test_multiline
409
- File.open("#{TMP_DIR}/tail.txt", "wb") { |f| }
410
-
411
- d = create_driver %[
412
- format multiline
413
- format1 /^s (?<message1>[^\\n]+)(\\nf (?<message2>[^\\n]+))?(\\nf (?<message3>.*))?/
414
- format_firstline /^[s]/
415
- ]
416
- d.run do
417
- File.open("#{TMP_DIR}/tail.txt", "ab") { |f|
418
- f.puts "f test1"
419
- f.puts "s test2"
420
- f.puts "f test3"
421
- f.puts "f test4"
422
- f.puts "s test5"
423
- f.puts "s test6"
424
- f.puts "f test7"
425
- f.puts "s test8"
467
+ def test_from_encoding
468
+ conf = config_element(
469
+ "", "", {
470
+ "format" => "/(?<message>.*)/",
471
+ "read_from_head" => "true",
472
+ "from_encoding" => "cp932",
473
+ "encoding" => "utf-8"
474
+ })
475
+ d = create_driver(conf)
476
+ cp932_message = "\x82\xCD\x82\xEB\x81\x5B\x82\xED\x81\x5B\x82\xE9\x82\xC7".force_encoding(Encoding::CP932)
477
+ utf8_message = cp932_message.encode(Encoding::UTF_8)
478
+
479
+ d.run(expect_emits: 1) do
480
+ File.open("#{TMP_DIR}/tail.txt", "w:cp932") {|f|
481
+ f.puts cp932_message
426
482
  }
427
- sleep 1
428
-
429
- emits = d.emits
430
- assert(emits.length == 3)
431
- assert_equal({"message1" => "test2", "message2" => "test3", "message3" => "test4"}, emits[0][2])
432
- assert_equal({"message1" => "test5"}, emits[1][2])
433
- assert_equal({"message1" => "test6", "message2" => "test7"}, emits[2][2])
434
-
435
- sleep 3
436
- emits = d.emits
437
- assert(emits.length == 3)
438
483
  end
439
484
 
440
- emits = d.emits
441
- assert(emits.length == 4)
442
- assert_equal({"message1" => "test8"}, emits[3][2])
485
+ events = d.events
486
+ assert_equal(utf8_message, events[0][2]['message'])
487
+ assert_equal(Encoding::UTF_8, events[0][2]['message'].encoding)
443
488
  end
444
489
 
445
- def test_multiline_with_flush_interval
446
- File.open("#{TMP_DIR}/tail.txt", "wb") { |f| }
490
+ sub_test_case "multiline" do
491
+ data(flat: MULTILINE_CONFIG,
492
+ parse: PARSE_MULTILINE_CONFIG)
493
+ def test_multiline(data)
494
+ config = data
495
+ File.open("#{TMP_DIR}/tail.txt", "wb") { |f| }
447
496
 
448
- d = create_driver %[
449
- format multiline
450
- format1 /^s (?<message1>[^\\n]+)(\\nf (?<message2>[^\\n]+))?(\\nf (?<message3>.*))?/
451
- format_firstline /^[s]/
452
- multiline_flush_interval 2s
453
- ]
497
+ d = create_driver(config)
498
+ d.run(expect_emits: 1) do
499
+ File.open("#{TMP_DIR}/tail.txt", "ab") { |f|
500
+ f.puts "f test1"
501
+ f.puts "s test2"
502
+ f.puts "f test3"
503
+ f.puts "f test4"
504
+ f.puts "s test5"
505
+ f.puts "s test6"
506
+ f.puts "f test7"
507
+ f.puts "s test8"
508
+ }
509
+ end
454
510
 
455
- assert_equal 2, d.instance.multiline_flush_interval
511
+ events = d.events
512
+ assert_equal(4, events.length)
513
+ assert_equal({"message1" => "test2", "message2" => "test3", "message3" => "test4"}, events[0][2])
514
+ assert_equal({"message1" => "test5"}, events[1][2])
515
+ assert_equal({"message1" => "test6", "message2" => "test7"}, events[2][2])
516
+ assert_equal({"message1" => "test8"}, events[3][2])
517
+ end
456
518
 
457
- d.run do
458
- File.open("#{TMP_DIR}/tail.txt", "ab") { |f|
459
- f.puts "f test1"
460
- f.puts "s test2"
461
- f.puts "f test3"
462
- f.puts "f test4"
463
- f.puts "s test5"
464
- f.puts "s test6"
465
- f.puts "f test7"
466
- f.puts "s test8"
467
- }
468
- sleep 1
519
+ data(flat: MULTILINE_CONFIG,
520
+ parse: PARSE_MULTILINE_CONFIG)
521
+ def test_multiline_with_flush_interval(data)
522
+ File.open("#{TMP_DIR}/tail.txt", "wb") { |f| }
469
523
 
470
- emits = d.emits
471
- assert(emits.length == 3)
472
- assert_equal({"message1" => "test2", "message2" => "test3", "message3" => "test4"}, emits[0][2])
473
- assert_equal({"message1" => "test5"}, emits[1][2])
474
- assert_equal({"message1" => "test6", "message2" => "test7"}, emits[2][2])
524
+ config = data + config_element("", "", { "multiline_flush_interval" => "2s" })
525
+ d = create_driver(config)
475
526
 
476
- sleep 3
477
- emits = d.emits
478
- assert(emits.length == 4)
479
- assert_equal({"message1" => "test8"}, emits[3][2])
480
- end
481
- end
527
+ assert_equal 2, d.instance.multiline_flush_interval
482
528
 
483
- data(
484
- 'default encoding' => ['', Encoding::ASCII_8BIT],
485
- 'explicit encoding config' => ['encoding utf-8', Encoding::UTF_8])
486
- def test_multiline_encoding_of_flushed_record(data)
487
- encoding_config, encoding = data
529
+ d.run(expect_emits: 1) do
530
+ File.open("#{TMP_DIR}/tail.txt", "ab") { |f|
531
+ f.puts "f test1"
532
+ f.puts "s test2"
533
+ f.puts "f test3"
534
+ f.puts "f test4"
535
+ f.puts "s test5"
536
+ f.puts "s test6"
537
+ f.puts "f test7"
538
+ f.puts "s test8"
539
+ }
540
+ end
488
541
 
489
- d = create_driver %[
490
- format multiline
491
- format1 /^s (?<message1>[^\\n]+)(\\nf (?<message2>[^\\n]+))?(\\nf (?<message3>.*))?/
492
- format_firstline /^[s]/
493
- multiline_flush_interval 2s
494
- read_from_head true
495
- #{encoding_config}
496
- ]
542
+ events = d.events
543
+ assert_equal(4, events.length)
544
+ assert_equal({"message1" => "test2", "message2" => "test3", "message3" => "test4"}, events[0][2])
545
+ assert_equal({"message1" => "test5"}, events[1][2])
546
+ assert_equal({"message1" => "test6", "message2" => "test7"}, events[2][2])
547
+ assert_equal({"message1" => "test8"}, events[3][2])
548
+ end
549
+
550
+ data(
551
+ 'flat default encoding' => [MULTILINE_CONFIG, Encoding::ASCII_8BIT],
552
+ 'flat explicit encoding config' => [MULTILINE_CONFIG + config_element("", "", { "encoding" => "utf-8" }), Encoding::UTF_8],
553
+ 'parse default encoding' => [PARSE_MULTILINE_CONFIG, Encoding::ASCII_8BIT],
554
+ 'parse explicit encoding config' => [PARSE_MULTILINE_CONFIG + config_element("", "", { "encoding" => "utf-8" }), Encoding::UTF_8])
555
+ def test_multiline_encoding_of_flushed_record(data)
556
+ encoding_config, encoding = data
557
+
558
+ config = config_element("", "", {
559
+ "multiline_flush_interval" => "2s",
560
+ "read_from_head" => "true",
561
+ })
562
+ d = create_driver(config + encoding_config)
563
+
564
+ d.run(expect_emits: 1) do
565
+ File.open("#{TMP_DIR}/tail.txt", "wb") { |f|
566
+ f.puts "s test"
567
+ }
568
+ end
569
+ events = d.events
570
+ assert_equal(1, events.length)
571
+ assert_equal(encoding, events[0][2]['message1'].encoding)
572
+ end
573
+
574
+ def test_multiline_from_encoding_of_flushed_record
575
+ conf = MULTILINE_CONFIG + config_element(
576
+ "", "",
577
+ {
578
+ "multiline_flush_interval" => "1s",
579
+ "read_from_head" => "true",
580
+ "from_encoding" => "cp932",
581
+ "encoding" => "utf-8"
582
+ })
583
+ d = create_driver(conf)
584
+
585
+ cp932_message = "s \x82\xCD\x82\xEB\x81\x5B\x82\xED\x81\x5B\x82\xE9\x82\xC7".force_encoding(Encoding::CP932)
586
+ utf8_message = "\x82\xCD\x82\xEB\x81\x5B\x82\xED\x81\x5B\x82\xE9\x82\xC7".encode(Encoding::UTF_8, Encoding::CP932)
587
+ d.run(expect_emits: 1) do
588
+ File.open("#{TMP_DIR}/tail.txt", "w:cp932") { |f|
589
+ f.puts cp932_message
590
+ }
591
+ end
497
592
 
498
- d.run do
499
- File.open("#{TMP_DIR}/tail.txt", "wb") { |f|
500
- f.puts "s test"
501
- }
593
+ events = d.events
594
+ assert_equal(1, events.length)
595
+ assert_equal(utf8_message, events[0][2]['message1'])
596
+ assert_equal(Encoding::UTF_8, events[0][2]['message1'].encoding)
597
+ end
598
+
599
+ data(flat: config_element(
600
+ "", "", {
601
+ "format" => "multiline",
602
+ "format1" => "/^s (?<message1>[^\\n]+)\\n?/",
603
+ "format2" => "/(f (?<message2>[^\\n]+)\\n?)?/",
604
+ "format3" => "/(f (?<message3>.*))?/",
605
+ "format_firstline" => "/^[s]/"
606
+ }),
607
+ parse: config_element(
608
+ "", "", {},
609
+ [config_element("parse", "", {
610
+ "@type" => "multiline",
611
+ "format1" => "/^s (?<message1>[^\\n]+)\\n?/",
612
+ "format2" => "/(f (?<message2>[^\\n]+)\\n?)?/",
613
+ "format3" => "/(f (?<message3>.*))?/",
614
+ "format_firstline" => "/^[s]/"
615
+ })
616
+ ])
617
+ )
618
+ def test_multiline_with_multiple_formats(data)
619
+ config = data
620
+ File.open("#{TMP_DIR}/tail.txt", "wb") { |f| }
502
621
 
503
- sleep 4
504
- emits = d.emits
505
- assert_equal(1, emits.length)
506
- assert_equal(encoding, emits[0][2]['message1'].encoding)
507
- end
508
- end
622
+ d = create_driver(config)
623
+ d.run(expect_emits: 1) do
624
+ File.open("#{TMP_DIR}/tail.txt", "ab") { |f|
625
+ f.puts "f test1"
626
+ f.puts "s test2"
627
+ f.puts "f test3"
628
+ f.puts "f test4"
629
+ f.puts "s test5"
630
+ f.puts "s test6"
631
+ f.puts "f test7"
632
+ f.puts "s test8"
633
+ }
634
+ end
509
635
 
510
- def test_multiline_with_multiple_formats
511
- File.open("#{TMP_DIR}/tail.txt", "wb") { |f| }
636
+ events = d.events
637
+ assert(events.length > 0)
638
+ assert_equal({"message1" => "test2", "message2" => "test3", "message3" => "test4"}, events[0][2])
639
+ assert_equal({"message1" => "test5"}, events[1][2])
640
+ assert_equal({"message1" => "test6", "message2" => "test7"}, events[2][2])
641
+ assert_equal({"message1" => "test8"}, events[3][2])
642
+ end
643
+
644
+ data(flat: config_element(
645
+ "", "", {
646
+ "format" => "multiline",
647
+ "format1" => "/^[s|f] (?<message>.*)/",
648
+ "format_firstline" => "/^[s]/"
649
+ }),
650
+ parse: config_element(
651
+ "", "", {},
652
+ [config_element("parse", "", {
653
+ "@type" => "multiline",
654
+ "format1" => "/^[s|f] (?<message>.*)/",
655
+ "format_firstline" => "/^[s]/"
656
+ })
657
+ ])
658
+ )
659
+ def test_multilinelog_with_multiple_paths(data)
660
+ files = ["#{TMP_DIR}/tail1.txt", "#{TMP_DIR}/tail2.txt"]
661
+ files.each { |file| File.open(file, "wb") { |f| } }
512
662
 
513
- d = create_driver %[
514
- format multiline
515
- format1 /^s (?<message1>[^\\n]+)\\n?/
516
- format2 /(f (?<message2>[^\\n]+)\\n?)?/
517
- format3 /(f (?<message3>.*))?/
518
- format_firstline /^[s]/
519
- ]
520
- d.run do
521
- File.open("#{TMP_DIR}/tail.txt", "ab") { |f|
522
- f.puts "f test1"
523
- f.puts "s test2"
524
- f.puts "f test3"
525
- f.puts "f test4"
526
- f.puts "s test5"
527
- f.puts "s test6"
528
- f.puts "f test7"
529
- f.puts "s test8"
530
- }
531
- sleep 1
532
- end
663
+ config = data + config_element("", "", {
664
+ "path" => "#{files[0]},#{files[1]}",
665
+ "tag" => "t1",
666
+ })
667
+ d = create_driver(config, false)
668
+ d.run(expect_emits: 2) do
669
+ files.each do |file|
670
+ File.open(file, 'ab') { |f|
671
+ f.puts "f #{file} line should be ignored"
672
+ f.puts "s test1"
673
+ f.puts "f test2"
674
+ f.puts "f test3"
675
+ f.puts "s test4"
676
+ }
677
+ end
678
+ end
533
679
 
534
- emits = d.emits
535
- assert(emits.length > 0)
536
- assert_equal({"message1" => "test2", "message2" => "test3", "message3" => "test4"}, emits[0][2])
537
- assert_equal({"message1" => "test5"}, emits[1][2])
538
- assert_equal({"message1" => "test6", "message2" => "test7"}, emits[2][2])
539
- assert_equal({"message1" => "test8"}, emits[3][2])
540
- end
680
+ events = d.events
681
+ assert_equal({"message" => "test1\nf test2\nf test3"}, events[0][2])
682
+ assert_equal({"message" => "test1\nf test2\nf test3"}, events[1][2])
683
+ # "test4" events are here because these events are flushed at shutdown phase
684
+ assert_equal({"message" => "test4"}, events[2][2])
685
+ assert_equal({"message" => "test4"}, events[3][2])
686
+ end
687
+
688
+ data(flat: config_element("", "", {
689
+ "format" => "multiline",
690
+ "format1" => "/(?<var1>foo \\d)\\n/",
691
+ "format2" => "/(?<var2>bar \\d)\\n/",
692
+ "format3" => "/(?<var3>baz \\d)/"
693
+ }),
694
+ parse: config_element(
695
+ "", "", {},
696
+ [config_element("parse", "", {
697
+ "@type" => "multiline",
698
+ "format1" => "/(?<var1>foo \\d)\\n/",
699
+ "format2" => "/(?<var2>bar \\d)\\n/",
700
+ "format3" => "/(?<var3>baz \\d)/"
701
+ })
702
+ ])
703
+ )
704
+ def test_multiline_without_firstline(data)
705
+ File.open("#{TMP_DIR}/tail.txt", "wb") { |f| }
541
706
 
542
- def test_multilinelog_with_multiple_paths
543
- files = ["#{TMP_DIR}/tail1.txt", "#{TMP_DIR}/tail2.txt"]
544
- files.each { |file| File.open(file, "wb") { |f| } }
545
-
546
- d = create_driver(%[
547
- path #{files[0]},#{files[1]}
548
- tag t1
549
- format multiline
550
- format1 /^[s|f] (?<message>.*)/
551
- format_firstline /^[s]/
552
- ], false)
553
- d.run do
554
- files.each do |file|
555
- File.open(file, 'ab') { |f|
556
- f.puts "f #{file} line should be ignored"
557
- f.puts "s test1"
558
- f.puts "f test2"
559
- f.puts "f test3"
560
- f.puts "s test4"
707
+ config = data
708
+ d = create_driver(config)
709
+ d.run(expect_emits: 1) do
710
+ File.open("#{TMP_DIR}/tail.txt", "ab") { |f|
711
+ f.puts "foo 1"
712
+ f.puts "bar 1"
713
+ f.puts "baz 1"
714
+ f.puts "foo 2"
715
+ f.puts "bar 2"
716
+ f.puts "baz 2"
561
717
  }
562
718
  end
563
- sleep 1
564
- end
565
719
 
566
- emits = d.emits
567
- assert_equal({"message" => "test1\nf test2\nf test3"}, emits[0][2])
568
- assert_equal({"message" => "test1\nf test2\nf test3"}, emits[1][2])
569
- # "test4" events are here because these events are flushed at shutdown phase
570
- assert_equal({"message" => "test4"}, emits[2][2])
571
- assert_equal({"message" => "test4"}, emits[3][2])
720
+ events = d.events
721
+ assert_equal(2, events.length)
722
+ assert_equal({"var1" => "foo 1", "var2" => "bar 1", "var3" => "baz 1"}, events[0][2])
723
+ assert_equal({"var1" => "foo 2", "var2" => "bar 2", "var3" => "baz 2"}, events[1][2])
724
+ end
572
725
  end
573
726
 
574
- def test_multiline_without_firstline
575
- File.open("#{TMP_DIR}/tail.txt", "wb") { |f| }
576
-
577
- d = create_driver %[
578
- format multiline
579
- format1 /(?<var1>foo \\d)\\n/
580
- format2 /(?<var2>bar \\d)\\n/
581
- format3 /(?<var3>baz \\d)/
727
+ sub_test_case "path" do
728
+ # * path test
729
+ # TODO: Clean up tests
730
+ EX_RORATE_WAIT = 0
731
+
732
+ EX_CONFIG = config_element("", "", {
733
+ "tag" => "tail",
734
+ "path" => "test/plugin/*/%Y/%m/%Y%m%d-%H%M%S.log,test/plugin/data/log/**/*.log",
735
+ "format" => "none",
736
+ "pos_file" => "#{TMP_DIR}/tail.pos",
737
+ "read_from_head" => true,
738
+ "refresh_interval" => 30,
739
+ "rotate_wait" => "#{EX_RORATE_WAIT}s",
740
+ })
741
+ EX_PATHS = [
742
+ 'test/plugin/data/2010/01/20100102-030405.log',
743
+ 'test/plugin/data/log/foo/bar.log',
744
+ 'test/plugin/data/log/test.log'
582
745
  ]
583
- d.run do
584
- File.open("#{TMP_DIR}/tail.txt", "ab") { |f|
585
- f.puts "foo 1"
586
- f.puts "bar 1"
587
- f.puts "baz 1"
588
- f.puts "foo 2"
589
- f.puts "bar 2"
590
- f.puts "baz 2"
591
- }
592
- sleep 1
593
- end
594
746
 
595
- emits = d.emits
596
- assert_equal(2, emits.length)
597
- assert_equal({"var1" => "foo 1", "var2" => "bar 1", "var3" => "baz 1"}, emits[0][2])
598
- assert_equal({"var1" => "foo 2", "var2" => "bar 2", "var3" => "baz 2"}, emits[1][2])
599
- end
747
+ def test_expand_paths
748
+ plugin = create_driver(EX_CONFIG, false).instance
749
+ flexstub(Time) do |timeclass|
750
+ timeclass.should_receive(:now).with_no_args.and_return(Time.new(2010, 1, 2, 3, 4, 5))
751
+ assert_equal EX_PATHS, plugin.expand_paths.sort
752
+ end
600
753
 
601
- # * path test
602
- # TODO: Clean up tests
603
- EX_RORATE_WAIT = 0
604
-
605
- EX_CONFIG = %[
606
- tag tail
607
- path test/plugin/*/%Y/%m/%Y%m%d-%H%M%S.log,test/plugin/data/log/**/*.log
608
- format none
609
- pos_file #{TMP_DIR}/tail.pos
610
- read_from_head true
611
- refresh_interval 30
612
- rotate_wait #{EX_RORATE_WAIT}s
613
- ]
614
- EX_PATHS = [
615
- 'test/plugin/data/2010/01/20100102-030405.log',
616
- 'test/plugin/data/log/foo/bar.log',
617
- 'test/plugin/data/log/test.log'
618
- ]
619
-
620
- def test_expand_paths
621
- plugin = create_driver(EX_CONFIG, false).instance
622
- flexstub(Time) do |timeclass|
623
- timeclass.should_receive(:now).with_no_args.and_return(Time.new(2010, 1, 2, 3, 4, 5))
624
- assert_equal EX_PATHS, plugin.expand_paths.sort
754
+ # Test exclusion
755
+ exclude_config = EX_CONFIG + config_element("", "", { "exclude_path" => %Q(["#{EX_PATHS.last}"]) })
756
+ plugin = create_driver(exclude_config, false).instance
757
+ assert_equal EX_PATHS - [EX_PATHS.last], plugin.expand_paths.sort
625
758
  end
626
-
627
- # Test exclusion
628
- exclude_config = EX_CONFIG + " exclude_path [\"#{EX_PATHS.last}\"]"
629
- plugin = create_driver(exclude_config, false).instance
630
- assert_equal EX_PATHS - [EX_PATHS.last], plugin.expand_paths.sort
631
759
  end
632
760
 
633
761
  def test_z_refresh_watchers
634
762
  plugin = create_driver(EX_CONFIG, false).instance
635
763
  sio = StringIO.new
636
764
  plugin.instance_eval do
637
- @pf = Fluent::NewTailInput::PositionFile.parse(sio)
765
+ @pf = Fluent::Plugin::TailInput::PositionFile.parse(sio)
638
766
  @loop = Coolio::Loop.new
639
767
  end
640
768
 
641
769
  flexstub(Time) do |timeclass|
642
770
  timeclass.should_receive(:now).with_no_args.and_return(Time.new(2010, 1, 2, 3, 4, 5), Time.new(2010, 1, 2, 3, 4, 6), Time.new(2010, 1, 2, 3, 4, 7))
643
771
 
644
- flexstub(Fluent::NewTailInput::TailWatcher) do |watcherclass|
772
+ flexstub(Fluent::Plugin::TailInput::TailWatcher) do |watcherclass|
645
773
  EX_PATHS.each do |path|
646
- watcherclass.should_receive(:new).with(path, EX_RORATE_WAIT, Fluent::NewTailInput::FilePositionEntry, any, true, true, 1000, any, any, any).once.and_return do
774
+ watcherclass.should_receive(:new).with(path, EX_RORATE_WAIT, Fluent::Plugin::TailInput::FilePositionEntry, any, true, true, 1000, any, any, any).once.and_return do
647
775
  flexmock('TailWatcher') { |watcher|
648
776
  watcher.should_receive(:attach).once
649
777
  watcher.should_receive(:unwatched=).zero_or_more_times
@@ -658,8 +786,8 @@ class TailInputTest < Test::Unit::TestCase
658
786
  @tails['test/plugin/data/2010/01/20100102-030405.log'].should_receive(:close).zero_or_more_times
659
787
  end
660
788
 
661
- flexstub(Fluent::NewTailInput::TailWatcher) do |watcherclass|
662
- watcherclass.should_receive(:new).with('test/plugin/data/2010/01/20100102-030406.log', EX_RORATE_WAIT, Fluent::NewTailInput::FilePositionEntry, any, true, true, 1000, any, any, any).once.and_return do
789
+ flexstub(Fluent::Plugin::TailInput::TailWatcher) do |watcherclass|
790
+ watcherclass.should_receive(:new).with('test/plugin/data/2010/01/20100102-030406.log', EX_RORATE_WAIT, Fluent::Plugin::TailInput::FilePositionEntry, any, true, true, 1000, any, any, any).once.and_return do
663
791
  flexmock('TailWatcher') do |watcher|
664
792
  watcher.should_receive(:attach).once
665
793
  watcher.should_receive(:unwatched=).zero_or_more_times
@@ -669,67 +797,77 @@ class TailInputTest < Test::Unit::TestCase
669
797
  plugin.refresh_watchers
670
798
  end
671
799
 
672
- flexstub(Fluent::NewTailInput::TailWatcher) do |watcherclass|
800
+ flexstub(Fluent::Plugin::TailInput::TailWatcher) do |watcherclass|
673
801
  watcherclass.should_receive(:new).never
674
802
  plugin.refresh_watchers
675
803
  end
676
804
  end
677
805
  end
678
806
 
679
- DummyWatcher = Struct.new("DummyWatcher", :tag)
807
+ sub_test_case "receive_lines" do
808
+ DummyWatcher = Struct.new("DummyWatcher", :tag)
680
809
 
681
- def test_receive_lines
682
- plugin = create_driver(EX_CONFIG, false).instance
683
- flexstub(plugin.router) do |engineclass|
684
- engineclass.should_receive(:emit_stream).with('tail', any).once
810
+ def test_tag
811
+ d = create_driver(EX_CONFIG, false)
812
+ d.run {}
813
+ plugin = d.instance
814
+ mock(plugin.router).emit_stream('tail', anything).once
685
815
  plugin.receive_lines(['foo', 'bar'], DummyWatcher.new('foo.bar.log'))
686
816
  end
687
817
 
688
- config = %[
689
- tag pre.*
690
- path test/plugin/*/%Y/%m/%Y%m%d-%H%M%S.log,test/plugin/data/log/**/*.log
691
- format none
692
- read_from_head true
693
- ]
694
- plugin = create_driver(config, false).instance
695
- flexstub(plugin.router) do |engineclass|
696
- engineclass.should_receive(:emit_stream).with('pre.foo.bar.log', any).once
818
+ def test_tag_prefix
819
+ config = config_element("", "", {
820
+ "tag" => "pre.*",
821
+ "path" => "test/plugin/*/%Y/%m/%Y%m%d-%H%M%S.log,test/plugin/data/log/**/*.log",
822
+ "format" => "none",
823
+ "read_from_head" => true
824
+ })
825
+ d = create_driver(config, false)
826
+ d.run {}
827
+ plugin = d.instance
828
+ mock(plugin.router).emit_stream('pre.foo.bar.log', anything).once
697
829
  plugin.receive_lines(['foo', 'bar'], DummyWatcher.new('foo.bar.log'))
698
830
  end
699
831
 
700
- config = %[
701
- tag *.post
702
- path test/plugin/*/%Y/%m/%Y%m%d-%H%M%S.log,test/plugin/data/log/**/*.log
703
- format none
704
- read_from_head true
705
- ]
706
- plugin = create_driver(config, false).instance
707
- flexstub(plugin.router) do |engineclass|
708
- engineclass.should_receive(:emit_stream).with('foo.bar.log.post', any).once
832
+ def test_tag_suffix
833
+ config = config_element("", "", {
834
+ "tag" => "*.post",
835
+ "path" => "test/plugin/*/%Y/%m/%Y%m%d-%H%M%S.log,test/plugin/data/log/**/*.log",
836
+ "format" => "none",
837
+ "read_from_head" => true
838
+ })
839
+ d = create_driver(config, false)
840
+ d.run {}
841
+ plugin = d.instance
842
+ mock(plugin.router).emit_stream('foo.bar.log.post', anything).once
709
843
  plugin.receive_lines(['foo', 'bar'], DummyWatcher.new('foo.bar.log'))
710
844
  end
711
845
 
712
- config = %[
713
- tag pre.*.post
714
- path test/plugin/*/%Y/%m/%Y%m%d-%H%M%S.log,test/plugin/data/log/**/*.log
715
- format none
716
- read_from_head true
717
- ]
718
- plugin = create_driver(config, false).instance
719
- flexstub(plugin.router) do |engineclass|
720
- engineclass.should_receive(:emit_stream).with('pre.foo.bar.log.post', any).once
846
+ def test_tag_prefix_and_suffix
847
+ config = config_element("", "", {
848
+ "tag" => "pre.*.post",
849
+ "path" => "test/plugin/*/%Y/%m/%Y%m%d-%H%M%S.log,test/plugin/data/log/**/*.log",
850
+ "format" => "none",
851
+ "read_from_head" => true
852
+ })
853
+ d = create_driver(config, false)
854
+ d.run {}
855
+ plugin = d.instance
856
+ mock(plugin.router).emit_stream('pre.foo.bar.log.post', anything).once
721
857
  plugin.receive_lines(['foo', 'bar'], DummyWatcher.new('foo.bar.log'))
722
858
  end
723
859
 
724
- config = %[
725
- tag pre.*.post*ignore
726
- path test/plugin/*/%Y/%m/%Y%m%d-%H%M%S.log,test/plugin/data/log/**/*.log
727
- format none
728
- read_from_head true
729
- ]
730
- plugin = create_driver(config, false).instance
731
- flexstub(plugin.router) do |engineclass|
732
- engineclass.should_receive(:emit_stream).with('pre.foo.bar.log.post', any).once
860
+ def test_tag_prefix_and_suffix_ignore
861
+ config = config_element("", "", {
862
+ "tag" => "pre.*.post*ignore",
863
+ "path" => "test/plugin/*/%Y/%m/%Y%m%d-%H%M%S.log,test/plugin/data/log/**/*.log",
864
+ "format" => "none",
865
+ "read_from_head" => true
866
+ })
867
+ d = create_driver(config, false)
868
+ d.run {}
869
+ plugin = d.instance
870
+ mock(plugin.router).emit_stream('pre.foo.bar.log.post', anything).once
733
871
  plugin.receive_lines(['foo', 'bar'], DummyWatcher.new('foo.bar.log'))
734
872
  end
735
873
  end
@@ -744,70 +882,26 @@ class TailInputTest < Test::Unit::TestCase
744
882
 
745
883
  # Try two different configs - one with read_from_head and one without,
746
884
  # since their interactions with the filesystem differ.
747
- config1 = %[
748
- tag t1
749
- path #{TMP_DIR}/non_existent_file.txt,#{TMP_DIR}/tail.txt
750
- format none
751
- rotate_wait 2s
752
- pos_file #{TMP_DIR}/tail.pos
753
- ]
754
- config2 = config1 + ' read_from_head true'
885
+ config1 = config_element("", "", {
886
+ "tag" => "t1",
887
+ "path" => "#{TMP_DIR}/non_existent_file.txt,#{TMP_DIR}/tail.txt",
888
+ "format" => "none",
889
+ "rotate_wait" => "2s",
890
+ "pos_file" => "#{TMP_DIR}/tail.pos"
891
+ })
892
+ config2 = config1 + config_element("", "", { "read_from_head" => true })
755
893
  [config1, config2].each do |config|
756
894
  d = create_driver(config, false)
757
- d.run do
758
- sleep 1
895
+ d.run(expect_emits: 1) do
759
896
  File.open("#{TMP_DIR}/tail.txt", "ab") {|f|
760
897
  f.puts "test3"
761
898
  f.puts "test4"
762
899
  }
763
- sleep 1
764
900
  end
765
- emits = d.emits
766
- assert_equal(2, emits.length)
767
- assert_equal({"message" => "test3"}, emits[0][2])
768
- assert_equal({"message" => "test4"}, emits[1][2])
769
- end
770
- end
771
-
772
- sub_test_case 'emit error cases' do
773
- def test_emit_error_with_buffer_queue_limit_error
774
- emits = execute_test(Fluent::Plugin::Buffer::BufferOverflowError, "buffer space has too many data")
775
- assert_equal(10, emits.length)
776
- 10.times { |i|
777
- assert_equal({"message" => "test#{i}"}, emits[i][2])
778
- }
779
- end
780
-
781
- def test_emit_error_with_non_buffer_queue_limit_error
782
- emits = execute_test(StandardError, "non BufferQueueLimitError error")
783
- assert_true(emits.size > 0 && emits.size != 10)
784
- emits.size.times { |i|
785
- assert_equal({"message" => "test#{10 - emits.size + i}"}, emits[i][2])
786
- }
787
- end
788
-
789
- def execute_test(error_class, error_message)
790
- d = create_driver(CONFIG_READ_FROM_HEAD + SINGLE_LINE_CONFIG)
791
- # Use define_singleton_method instead of d.emit_stream to capture local variable
792
- d.define_singleton_method(:emit_stream) do |tag, es|
793
- @test_num_errors ||= 0
794
- if @test_num_errors < 5
795
- @test_num_errors += 1
796
- raise error_class, error_message
797
- else
798
- @emit_streams << [tag, es.to_a]
799
- end
800
- end
801
-
802
- d.run do
803
- 10.times { |i|
804
- File.open("#{TMP_DIR}/tail.txt", "ab") { |f| f.puts "test#{i}" }
805
- sleep 0.5
806
- }
807
- sleep 1
808
- end
809
-
810
- d.emits
901
+ events = d.events
902
+ assert_equal(2, events.length)
903
+ assert_equal({"message" => "test3"}, events[0][2])
904
+ assert_equal({"message" => "test4"}, events[1][2])
811
905
  end
812
906
  end
813
907
 
@@ -818,21 +912,18 @@ class TailInputTest < Test::Unit::TestCase
818
912
  f.puts "test2"
819
913
  }
820
914
 
821
- d = create_driver(%[path_key path] + SINGLE_LINE_CONFIG)
822
-
823
- d.run do
824
- sleep 1
915
+ d = create_driver(SINGLE_LINE_CONFIG + config_element("", "", { "path_key" => "path" }))
825
916
 
917
+ d.run(expect_emits: 1) do
826
918
  File.open("#{TMP_DIR}/tail.txt", "ab") {|f|
827
919
  f.puts "test3"
828
920
  f.puts "test4"
829
921
  }
830
- sleep 1
831
922
  end
832
923
 
833
- emits = d.emits
834
- assert_equal(true, emits.length > 0)
835
- emits.each do |emit|
924
+ events = d.events
925
+ assert_equal(true, events.length > 0)
926
+ events.each do |emit|
836
927
  assert_equal("#{TMP_DIR}/tail.txt", emit[2]["path"])
837
928
  end
838
929
  end
@@ -840,13 +931,14 @@ class TailInputTest < Test::Unit::TestCase
840
931
  def test_tail_path_with_multiline_with_firstline
841
932
  File.open("#{TMP_DIR}/tail.txt", "wb") { |f| }
842
933
 
843
- d = create_driver %[
844
- path_key path
845
- format multiline
846
- format1 /^s (?<message1>[^\\n]+)(\\nf (?<message2>[^\\n]+))?(\\nf (?<message3>.*))?/
847
- format_firstline /^[s]/
848
- ]
849
- d.run do
934
+ config = config_element("", "", {
935
+ "path_key" => "path",
936
+ "format" => "multiline",
937
+ "format1" => "/^s (?<message1>[^\\n]+)(\\nf (?<message2>[^\\n]+))?(\\nf (?<message3>.*))?/",
938
+ "format_firstline" => "/^[s]/"
939
+ })
940
+ d = create_driver(config)
941
+ d.run(expect_emits: 1) do
850
942
  File.open("#{TMP_DIR}/tail.txt", "ab") { |f|
851
943
  f.puts "f test1"
852
944
  f.puts "s test2"
@@ -857,12 +949,11 @@ class TailInputTest < Test::Unit::TestCase
857
949
  f.puts "f test7"
858
950
  f.puts "s test8"
859
951
  }
860
- sleep 1
861
952
  end
862
953
 
863
- emits = d.emits
864
- assert(emits.length == 4)
865
- emits.each do |emit|
954
+ events = d.events
955
+ assert_equal(4, events.length)
956
+ events.each do |emit|
866
957
  assert_equal("#{TMP_DIR}/tail.txt", emit[2]["path"])
867
958
  end
868
959
  end
@@ -870,25 +961,25 @@ class TailInputTest < Test::Unit::TestCase
870
961
  def test_tail_path_with_multiline_without_firstline
871
962
  File.open("#{TMP_DIR}/tail.txt", "wb") { |f| }
872
963
 
873
- d = create_driver %[
874
- path_key path
875
- format multiline
876
- format1 /(?<var1>foo \\d)\\n/
877
- format2 /(?<var2>bar \\d)\\n/
878
- format3 /(?<var3>baz \\d)/
879
- ]
880
- d.run do
964
+ config = config_element("", "", {
965
+ "path_key" => "path",
966
+ "format" => "multiline",
967
+ "format1" => "/(?<var1>foo \\d)\\n/",
968
+ "format2" => "/(?<var2>bar \\d)\\n/",
969
+ "format3" => "/(?<var3>baz \\d)/",
970
+ })
971
+ d = create_driver(config)
972
+ d.run(expect_emits: 1) do
881
973
  File.open("#{TMP_DIR}/tail.txt", "ab") { |f|
882
974
  f.puts "foo 1"
883
975
  f.puts "bar 1"
884
976
  f.puts "baz 1"
885
977
  }
886
- sleep 1
887
978
  end
888
979
 
889
- emits = d.emits
890
- assert(emits.length > 0)
891
- emits.each do |emit|
980
+ events = d.events
981
+ assert(events.length > 0)
982
+ events.each do |emit|
892
983
  assert_equal("#{TMP_DIR}/tail.txt", emit[2]["path"])
893
984
  end
894
985
  end
@@ -897,15 +988,16 @@ class TailInputTest < Test::Unit::TestCase
897
988
  files = ["#{TMP_DIR}/tail1.txt", "#{TMP_DIR}/tail2.txt"]
898
989
  files.each { |file| File.open(file, "wb") { |f| } }
899
990
 
900
- d = create_driver(%[
901
- path #{files[0]},#{files[1]}
902
- path_key path
903
- tag t1
904
- format multiline
905
- format1 /^[s|f] (?<message>.*)/
906
- format_firstline /^[s]/
907
- ], false)
908
- d.run do
991
+ config = config_element("", "", {
992
+ "path" => "#{files[0]},#{files[1]}",
993
+ "path_key" => "path",
994
+ "tag" => "t1",
995
+ "format" => "multiline",
996
+ "format1" => "/^[s|f] (?<message>.*)/",
997
+ "format_firstline" => "/^[s]/"
998
+ })
999
+ d = create_driver(config, false)
1000
+ d.run(expect_emits: 2) do
909
1001
  files.each do |file|
910
1002
  File.open(file, 'ab') { |f|
911
1003
  f.puts "f #{file} line should be ignored"
@@ -915,14 +1007,13 @@ class TailInputTest < Test::Unit::TestCase
915
1007
  f.puts "s test4"
916
1008
  }
917
1009
  end
918
- sleep 1
919
1010
  end
920
1011
 
921
- emits = d.emits
922
- assert(emits.length == 4)
923
- assert_equal(files, [emits[0][2]["path"], emits[1][2]["path"]].sort)
1012
+ events = d.events
1013
+ assert_equal(4, events.length)
1014
+ assert_equal(files, [events[0][2]["path"], events[1][2]["path"]].sort)
924
1015
  # "test4" events are here because these events are flushed at shutdown phase
925
- assert_equal(files, [emits[2][2]["path"], emits[3][2]["path"]].sort)
1016
+ assert_equal(files, [events[2][2]["path"], events[3][2]["path"]].sort)
926
1017
  end
927
1018
  end
928
1019
  end