fluentd 0.12.43 → 0.14.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.github/ISSUE_TEMPLATE.md +6 -0
- data/.gitignore +2 -0
- data/.travis.yml +33 -21
- data/CONTRIBUTING.md +1 -0
- data/ChangeLog +1239 -0
- data/README.md +0 -25
- data/Rakefile +2 -1
- data/Vagrantfile +17 -0
- data/appveyor.yml +35 -0
- data/example/filter_stdout.conf +5 -5
- data/example/in_forward.conf +2 -2
- data/example/in_http.conf +2 -2
- data/example/in_out_forward.conf +17 -0
- data/example/in_syslog.conf +2 -2
- data/example/in_tail.conf +2 -2
- data/example/in_tcp.conf +2 -2
- data/example/in_udp.conf +2 -2
- data/example/out_copy.conf +4 -4
- data/example/out_file.conf +2 -2
- data/example/out_forward.conf +2 -2
- data/example/out_forward_buf_file.conf +23 -0
- data/example/v0_12_filter.conf +8 -8
- data/fluent.conf +29 -0
- data/fluentd.gemspec +18 -11
- data/lib/fluent/agent.rb +60 -58
- data/lib/fluent/command/cat.rb +1 -1
- data/lib/fluent/command/debug.rb +7 -5
- data/lib/fluent/command/fluentd.rb +97 -2
- data/lib/fluent/compat/call_super_mixin.rb +67 -0
- data/lib/fluent/compat/filter.rb +50 -0
- data/lib/fluent/compat/formatter.rb +109 -0
- data/lib/fluent/compat/input.rb +50 -0
- data/lib/fluent/compat/output.rb +617 -0
- data/lib/fluent/compat/output_chain.rb +60 -0
- data/lib/fluent/compat/parser.rb +163 -0
- data/lib/fluent/compat/propagate_default.rb +62 -0
- data/lib/fluent/config.rb +23 -20
- data/lib/fluent/config/configure_proxy.rb +119 -70
- data/lib/fluent/config/dsl.rb +5 -18
- data/lib/fluent/config/element.rb +72 -8
- data/lib/fluent/config/error.rb +0 -3
- data/lib/fluent/config/literal_parser.rb +0 -2
- data/lib/fluent/config/parser.rb +4 -4
- data/lib/fluent/config/section.rb +39 -28
- data/lib/fluent/config/types.rb +2 -13
- data/lib/fluent/config/v1_parser.rb +1 -3
- data/lib/fluent/configurable.rb +48 -16
- data/lib/fluent/daemon.rb +15 -0
- data/lib/fluent/engine.rb +26 -52
- data/lib/fluent/env.rb +6 -4
- data/lib/fluent/event.rb +58 -11
- data/lib/fluent/event_router.rb +5 -5
- data/lib/fluent/filter.rb +2 -50
- data/lib/fluent/formatter.rb +4 -293
- data/lib/fluent/input.rb +2 -32
- data/lib/fluent/label.rb +2 -2
- data/lib/fluent/load.rb +3 -2
- data/lib/fluent/log.rb +107 -38
- data/lib/fluent/match.rb +0 -36
- data/lib/fluent/mixin.rb +117 -7
- data/lib/fluent/msgpack_factory.rb +62 -0
- data/lib/fluent/output.rb +7 -612
- data/lib/fluent/output_chain.rb +23 -0
- data/lib/fluent/parser.rb +4 -800
- data/lib/fluent/plugin.rb +100 -121
- data/lib/fluent/plugin/bare_output.rb +63 -0
- data/lib/fluent/plugin/base.rb +121 -0
- data/lib/fluent/plugin/buf_file.rb +101 -182
- data/lib/fluent/plugin/buf_memory.rb +9 -92
- data/lib/fluent/plugin/buffer.rb +473 -0
- data/lib/fluent/plugin/buffer/chunk.rb +135 -0
- data/lib/fluent/plugin/buffer/file_chunk.rb +339 -0
- data/lib/fluent/plugin/buffer/memory_chunk.rb +100 -0
- data/lib/fluent/plugin/exec_util.rb +80 -75
- data/lib/fluent/plugin/file_util.rb +33 -28
- data/lib/fluent/plugin/file_wrapper.rb +120 -0
- data/lib/fluent/plugin/filter.rb +51 -0
- data/lib/fluent/plugin/filter_grep.rb +13 -40
- data/lib/fluent/plugin/filter_record_transformer.rb +22 -18
- data/lib/fluent/plugin/formatter.rb +93 -0
- data/lib/fluent/plugin/formatter_csv.rb +48 -0
- data/lib/fluent/plugin/formatter_hash.rb +32 -0
- data/lib/fluent/plugin/formatter_json.rb +47 -0
- data/lib/fluent/plugin/formatter_ltsv.rb +42 -0
- data/lib/fluent/plugin/formatter_msgpack.rb +32 -0
- data/lib/fluent/plugin/formatter_out_file.rb +45 -0
- data/lib/fluent/plugin/formatter_single_value.rb +34 -0
- data/lib/fluent/plugin/formatter_stdout.rb +39 -0
- data/lib/fluent/plugin/in_debug_agent.rb +4 -0
- data/lib/fluent/plugin/in_dummy.rb +22 -18
- data/lib/fluent/plugin/in_exec.rb +18 -8
- data/lib/fluent/plugin/in_forward.rb +36 -79
- data/lib/fluent/plugin/in_gc_stat.rb +4 -0
- data/lib/fluent/plugin/in_http.rb +21 -18
- data/lib/fluent/plugin/in_monitor_agent.rb +15 -48
- data/lib/fluent/plugin/in_object_space.rb +6 -1
- data/lib/fluent/plugin/in_stream.rb +7 -3
- data/lib/fluent/plugin/in_syslog.rb +46 -95
- data/lib/fluent/plugin/in_tail.rb +58 -640
- data/lib/fluent/plugin/in_tcp.rb +8 -1
- data/lib/fluent/plugin/in_udp.rb +8 -18
- data/lib/fluent/plugin/input.rb +33 -0
- data/lib/fluent/plugin/multi_output.rb +95 -0
- data/lib/fluent/plugin/out_buffered_null.rb +59 -0
- data/lib/fluent/plugin/out_copy.rb +11 -7
- data/lib/fluent/plugin/out_exec.rb +15 -11
- data/lib/fluent/plugin/out_exec_filter.rb +18 -10
- data/lib/fluent/plugin/out_file.rb +34 -5
- data/lib/fluent/plugin/out_forward.rb +25 -19
- data/lib/fluent/plugin/out_null.rb +0 -14
- data/lib/fluent/plugin/out_roundrobin.rb +11 -7
- data/lib/fluent/plugin/out_stdout.rb +5 -7
- data/lib/fluent/plugin/out_stream.rb +3 -1
- data/lib/fluent/plugin/output.rb +979 -0
- data/lib/fluent/plugin/owned_by_mixin.rb +42 -0
- data/lib/fluent/plugin/parser.rb +244 -0
- data/lib/fluent/plugin/parser_apache.rb +24 -0
- data/lib/fluent/plugin/parser_apache2.rb +84 -0
- data/lib/fluent/plugin/parser_apache_error.rb +21 -0
- data/lib/fluent/plugin/parser_csv.rb +31 -0
- data/lib/fluent/plugin/parser_json.rb +79 -0
- data/lib/fluent/plugin/parser_ltsv.rb +50 -0
- data/lib/fluent/plugin/parser_multiline.rb +102 -0
- data/lib/fluent/plugin/parser_nginx.rb +24 -0
- data/lib/fluent/plugin/parser_none.rb +36 -0
- data/lib/fluent/plugin/parser_syslog.rb +82 -0
- data/lib/fluent/plugin/parser_tsv.rb +37 -0
- data/lib/fluent/plugin/socket_util.rb +119 -117
- data/lib/fluent/plugin/storage.rb +84 -0
- data/lib/fluent/plugin/storage_local.rb +116 -0
- data/lib/fluent/plugin/string_util.rb +16 -13
- data/lib/fluent/plugin_helper.rb +39 -0
- data/lib/fluent/plugin_helper/child_process.rb +298 -0
- data/lib/fluent/plugin_helper/compat_parameters.rb +99 -0
- data/lib/fluent/plugin_helper/event_emitter.rb +80 -0
- data/lib/fluent/plugin_helper/event_loop.rb +118 -0
- data/lib/fluent/plugin_helper/retry_state.rb +177 -0
- data/lib/fluent/plugin_helper/storage.rb +308 -0
- data/lib/fluent/plugin_helper/thread.rb +147 -0
- data/lib/fluent/plugin_helper/timer.rb +85 -0
- data/lib/fluent/plugin_id.rb +63 -0
- data/lib/fluent/process.rb +21 -30
- data/lib/fluent/registry.rb +21 -9
- data/lib/fluent/root_agent.rb +115 -40
- data/lib/fluent/supervisor.rb +330 -320
- data/lib/fluent/system_config.rb +42 -18
- data/lib/fluent/test.rb +6 -1
- data/lib/fluent/test/base.rb +23 -3
- data/lib/fluent/test/driver/base.rb +247 -0
- data/lib/fluent/test/driver/event_feeder.rb +98 -0
- data/lib/fluent/test/driver/filter.rb +35 -0
- data/lib/fluent/test/driver/input.rb +31 -0
- data/lib/fluent/test/driver/output.rb +78 -0
- data/lib/fluent/test/driver/test_event_router.rb +45 -0
- data/lib/fluent/test/filter_test.rb +0 -1
- data/lib/fluent/test/formatter_test.rb +2 -1
- data/lib/fluent/test/input_test.rb +23 -17
- data/lib/fluent/test/output_test.rb +28 -39
- data/lib/fluent/test/parser_test.rb +1 -1
- data/lib/fluent/time.rb +104 -1
- data/lib/fluent/{status.rb → unique_id.rb} +15 -24
- data/lib/fluent/version.rb +1 -1
- data/lib/fluent/winsvc.rb +72 -0
- data/test/compat/test_calls_super.rb +164 -0
- data/test/config/test_config_parser.rb +83 -0
- data/test/config/test_configurable.rb +547 -274
- data/test/config/test_configure_proxy.rb +146 -29
- data/test/config/test_dsl.rb +3 -181
- data/test/config/test_element.rb +274 -0
- data/test/config/test_literal_parser.rb +1 -1
- data/test/config/test_section.rb +79 -7
- data/test/config/test_system_config.rb +21 -0
- data/test/config/test_types.rb +3 -26
- data/test/helper.rb +78 -8
- data/test/plugin/test_bare_output.rb +118 -0
- data/test/plugin/test_base.rb +75 -0
- data/test/plugin/test_buf_file.rb +420 -521
- data/test/plugin/test_buf_memory.rb +32 -194
- data/test/plugin/test_buffer.rb +981 -0
- data/test/plugin/test_buffer_chunk.rb +110 -0
- data/test/plugin/test_buffer_file_chunk.rb +770 -0
- data/test/plugin/test_buffer_memory_chunk.rb +265 -0
- data/test/plugin/test_filter.rb +255 -0
- data/test/plugin/test_filter_grep.rb +2 -73
- data/test/plugin/test_filter_record_transformer.rb +24 -68
- data/test/plugin/test_filter_stdout.rb +6 -6
- data/test/plugin/test_in_debug_agent.rb +2 -0
- data/test/plugin/test_in_dummy.rb +11 -17
- data/test/plugin/test_in_exec.rb +6 -25
- data/test/plugin/test_in_forward.rb +112 -151
- data/test/plugin/test_in_gc_stat.rb +2 -0
- data/test/plugin/test_in_http.rb +106 -157
- data/test/plugin/test_in_object_space.rb +21 -5
- data/test/plugin/test_in_stream.rb +14 -13
- data/test/plugin/test_in_syslog.rb +30 -275
- data/test/plugin/test_in_tail.rb +95 -282
- data/test/plugin/test_in_tcp.rb +14 -0
- data/test/plugin/test_in_udp.rb +21 -67
- data/test/plugin/test_input.rb +122 -0
- data/test/plugin/test_multi_output.rb +180 -0
- data/test/plugin/test_out_buffered_null.rb +79 -0
- data/test/plugin/test_out_copy.rb +15 -2
- data/test/plugin/test_out_exec.rb +75 -25
- data/test/plugin/test_out_exec_filter.rb +74 -8
- data/test/plugin/test_out_file.rb +61 -7
- data/test/plugin/test_out_forward.rb +92 -15
- data/test/plugin/test_out_roundrobin.rb +1 -0
- data/test/plugin/test_out_stdout.rb +22 -13
- data/test/plugin/test_out_stream.rb +18 -0
- data/test/plugin/test_output.rb +515 -0
- data/test/plugin/test_output_as_buffered.rb +1540 -0
- data/test/plugin/test_output_as_buffered_overflow.rb +247 -0
- data/test/plugin/test_output_as_buffered_retries.rb +808 -0
- data/test/plugin/test_output_as_buffered_secondary.rb +776 -0
- data/test/plugin/test_output_as_standard.rb +362 -0
- data/test/plugin/test_owned_by.rb +35 -0
- data/test/plugin/test_storage.rb +167 -0
- data/test/plugin/test_storage_local.rb +8 -0
- data/test/plugin_helper/test_child_process.rb +599 -0
- data/test/plugin_helper/test_compat_parameters.rb +175 -0
- data/test/plugin_helper/test_event_emitter.rb +51 -0
- data/test/plugin_helper/test_event_loop.rb +52 -0
- data/test/plugin_helper/test_retry_state.rb +399 -0
- data/test/plugin_helper/test_storage.rb +411 -0
- data/test/plugin_helper/test_thread.rb +164 -0
- data/test/plugin_helper/test_timer.rb +100 -0
- data/test/scripts/exec_script.rb +0 -6
- data/test/scripts/fluent/plugin/out_test.rb +3 -0
- data/test/test_config.rb +13 -4
- data/test/test_event.rb +24 -13
- data/test/test_event_router.rb +8 -7
- data/test/test_event_time.rb +187 -0
- data/test/test_formatter.rb +13 -51
- data/test/test_input.rb +1 -1
- data/test/test_log.rb +239 -16
- data/test/test_mixin.rb +1 -1
- data/test/test_output.rb +53 -66
- data/test/test_parser.rb +105 -323
- data/test/test_plugin_helper.rb +81 -0
- data/test/test_root_agent.rb +4 -52
- data/test/test_supervisor.rb +272 -0
- data/test/test_unique_id.rb +47 -0
- metadata +181 -55
- data/CHANGELOG.md +0 -710
- data/lib/fluent/buffer.rb +0 -365
- data/lib/fluent/plugin/filter_parser.rb +0 -107
- data/lib/fluent/plugin/in_status.rb +0 -76
- data/lib/fluent/test/helpers.rb +0 -86
- data/test/plugin/data/log/foo/bar2 +0 -0
- data/test/plugin/test_filter_parser.rb +0 -744
- data/test/plugin/test_in_status.rb +0 -38
- data/test/test_buffer.rb +0 -624
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
require_relative '../helper'
|
|
2
2
|
require 'fluent/test'
|
|
3
|
+
require 'fluent/plugin/out_file'
|
|
3
4
|
require 'fileutils'
|
|
4
5
|
require 'time'
|
|
5
6
|
|
|
@@ -62,7 +63,7 @@ class FileOutputTest < Test::Unit::TestCase
|
|
|
62
63
|
d = create_driver(%[path #{TMP_DIR}/out_file_test])
|
|
63
64
|
time = Time.parse("2011-01-02 13:14:15 UTC").to_i
|
|
64
65
|
|
|
65
|
-
with_timezone('Asia/Taipei') do
|
|
66
|
+
with_timezone(Fluent.windows? ? 'NST-8' : 'Asia/Taipei') do
|
|
66
67
|
d.emit({"a"=>1}, time)
|
|
67
68
|
d.expect_format %[2011-01-02T21:14:15+08:00\ttest\t{"a":1}\n]
|
|
68
69
|
d.run
|
|
@@ -121,7 +122,7 @@ class FileOutputTest < Test::Unit::TestCase
|
|
|
121
122
|
# Zlib::GzipReader has a bug of concatenated file: https://bugs.ruby-lang.org/issues/9790
|
|
122
123
|
# Following code from https://www.ruby-forum.com/topic/971591#979520
|
|
123
124
|
result = ''
|
|
124
|
-
File.open(path) { |io|
|
|
125
|
+
File.open(path, "rb") { |io|
|
|
125
126
|
loop do
|
|
126
127
|
gzr = Zlib::GzipReader.new(io)
|
|
127
128
|
result << gzr.read
|
|
@@ -150,6 +151,54 @@ class FileOutputTest < Test::Unit::TestCase
|
|
|
150
151
|
check_gzipped_result(paths[0], %[2011-01-02T13:14:15Z\ttest\t{"a":1}\n] + %[2011-01-02T13:14:15Z\ttest\t{"a":2}\n])
|
|
151
152
|
end
|
|
152
153
|
|
|
154
|
+
class TestWithSystem < self
|
|
155
|
+
TMP_DIR_WITH_SYSTEM = File.expand_path(File.dirname(__FILE__) + "/../tmp/out_file_system#{ENV['TEST_ENV_NUMBER']}")
|
|
156
|
+
# 0750 interprets as "488". "488".to_i(8) # => 4. So, it makes wrong permission. Umm....
|
|
157
|
+
OVERRIDE_DIR_PERMISSION = 750
|
|
158
|
+
OVERRIDE_FILE_PERMISSION = 0620
|
|
159
|
+
CONFIG_WITH_SYSTEM = %[
|
|
160
|
+
path #{TMP_DIR_WITH_SYSTEM}/out_file_test
|
|
161
|
+
compress gz
|
|
162
|
+
utc
|
|
163
|
+
<system>
|
|
164
|
+
file_permission #{OVERRIDE_FILE_PERMISSION}
|
|
165
|
+
dir_permission #{OVERRIDE_DIR_PERMISSION}
|
|
166
|
+
</system>
|
|
167
|
+
]
|
|
168
|
+
|
|
169
|
+
def setup
|
|
170
|
+
omit "NTFS doesn't support UNIX like permissions" if Fluent.windows?
|
|
171
|
+
FileUtils.rm_rf(TMP_DIR_WITH_SYSTEM)
|
|
172
|
+
end
|
|
173
|
+
|
|
174
|
+
def parse_system(text)
|
|
175
|
+
basepath = File.expand_path(File.dirname(__FILE__) + '/../../')
|
|
176
|
+
Fluent::Config.parse(text, '(test)', basepath, true).elements.find { |e| e.name == 'system' }
|
|
177
|
+
end
|
|
178
|
+
|
|
179
|
+
def test_write_with_system
|
|
180
|
+
system_conf = parse_system(CONFIG_WITH_SYSTEM)
|
|
181
|
+
sc = Fluent::SystemConfig.new(system_conf)
|
|
182
|
+
Fluent::Engine.init(sc)
|
|
183
|
+
d = create_driver CONFIG_WITH_SYSTEM
|
|
184
|
+
|
|
185
|
+
time = Time.parse("2011-01-02 13:14:15 UTC").to_i
|
|
186
|
+
d.emit({"a"=>1}, time)
|
|
187
|
+
d.emit({"a"=>2}, time)
|
|
188
|
+
|
|
189
|
+
# FileOutput#write returns path
|
|
190
|
+
paths = d.run
|
|
191
|
+
expect_paths = ["#{TMP_DIR_WITH_SYSTEM}/out_file_test.20110102_0.log.gz"]
|
|
192
|
+
assert_equal expect_paths, paths
|
|
193
|
+
|
|
194
|
+
check_gzipped_result(paths[0], %[2011-01-02T13:14:15Z\ttest\t{"a":1}\n] + %[2011-01-02T13:14:15Z\ttest\t{"a":2}\n])
|
|
195
|
+
dir_mode = "%o" % File::stat(TMP_DIR_WITH_SYSTEM).mode
|
|
196
|
+
assert_equal(OVERRIDE_DIR_PERMISSION, dir_mode[-3, 3].to_i)
|
|
197
|
+
file_mode = "%o" % File::stat(paths[0]).mode
|
|
198
|
+
assert_equal(OVERRIDE_FILE_PERMISSION, file_mode[-3, 3].to_i)
|
|
199
|
+
end
|
|
200
|
+
end
|
|
201
|
+
|
|
153
202
|
def test_write_with_format_json
|
|
154
203
|
d = create_driver [CONFIG, 'format json', 'include_time_key true', 'time_as_epoch'].join("\n")
|
|
155
204
|
|
|
@@ -245,28 +294,33 @@ class FileOutputTest < Test::Unit::TestCase
|
|
|
245
294
|
end
|
|
246
295
|
|
|
247
296
|
def test_write_with_symlink
|
|
297
|
+
omit "Windows doesn't support symlink" if Fluent.windows?
|
|
248
298
|
conf = CONFIG + %[
|
|
249
299
|
symlink_path #{SYMLINK_PATH}
|
|
250
300
|
]
|
|
251
301
|
symlink_path = "#{SYMLINK_PATH}"
|
|
252
302
|
|
|
253
|
-
Fluent::FileBuffer.clear_buffer_paths
|
|
254
303
|
d = Fluent::Test::TestDriver.new(Fluent::FileOutput).configure(conf)
|
|
255
304
|
|
|
256
305
|
begin
|
|
257
306
|
d.instance.start
|
|
258
307
|
10.times { sleep 0.05 }
|
|
308
|
+
|
|
259
309
|
time = Time.parse("2011-01-02 13:14:15 UTC").to_i
|
|
260
310
|
es = Fluent::OneEventStream.new(time, {"a"=>1})
|
|
261
|
-
d.instance.
|
|
311
|
+
d.instance.emit_events('tag', es)
|
|
262
312
|
|
|
263
|
-
assert File.
|
|
313
|
+
assert File.exist?(symlink_path)
|
|
264
314
|
assert File.symlink?(symlink_path)
|
|
265
315
|
|
|
266
|
-
|
|
316
|
+
es = Fluent::OneEventStream.new(event_time("2011-01-03 14:15:16 UTC"), {"a"=>2})
|
|
317
|
+
d.instance.emit_events('tag', es)
|
|
267
318
|
|
|
268
|
-
assert
|
|
319
|
+
assert File.exist?(symlink_path)
|
|
269
320
|
assert File.symlink?(symlink_path)
|
|
321
|
+
|
|
322
|
+
meta = d.instance.metadata('tag', event_time("2011-01-03 14:15:16 UTC"), {})
|
|
323
|
+
assert_equal d.instance.buffer.instance_eval{ @stage[meta].path }, File.readlink(symlink_path)
|
|
270
324
|
ensure
|
|
271
325
|
d.instance.shutdown
|
|
272
326
|
FileUtils.rm_rf(symlink_path)
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
require_relative '../helper'
|
|
2
2
|
require 'fluent/test'
|
|
3
|
+
require 'fluent/plugin/out_forward'
|
|
3
4
|
|
|
4
5
|
class ForwardOutputTest < Test::Unit::TestCase
|
|
5
6
|
def setup
|
|
@@ -10,6 +11,7 @@ class ForwardOutputTest < Test::Unit::TestCase
|
|
|
10
11
|
TARGET_PORT = 13999
|
|
11
12
|
CONFIG = %[
|
|
12
13
|
send_timeout 51
|
|
14
|
+
heartbeat_type udp
|
|
13
15
|
<server>
|
|
14
16
|
name test
|
|
15
17
|
host #{TARGET_HOST}
|
|
@@ -39,21 +41,20 @@ class ForwardOutputTest < Test::Unit::TestCase
|
|
|
39
41
|
@exceptions << e
|
|
40
42
|
raise e
|
|
41
43
|
end
|
|
42
|
-
|
|
43
|
-
# Fluentd v0.12 BufferedOutputTestDriver calls this method.
|
|
44
|
-
# BufferedOutput#format_stream calls format method, but ForwardOutput#format is not defined.
|
|
45
|
-
# Because ObjectBufferedOutput#emit calls es.to_msgpack_stream directly.
|
|
46
|
-
def format_stream(tag, es)
|
|
47
|
-
es.to_msgpack_stream
|
|
48
|
-
end
|
|
49
44
|
}.configure(conf)
|
|
50
45
|
end
|
|
51
46
|
|
|
52
47
|
def test_configure
|
|
53
|
-
d = create_driver
|
|
48
|
+
d = create_driver(%[
|
|
49
|
+
<server>
|
|
50
|
+
name test
|
|
51
|
+
host #{TARGET_HOST}
|
|
52
|
+
port #{TARGET_PORT}
|
|
53
|
+
</server>
|
|
54
|
+
])
|
|
54
55
|
nodes = d.instance.nodes
|
|
55
|
-
assert_equal
|
|
56
|
-
assert_equal :
|
|
56
|
+
assert_equal 60, d.instance.send_timeout
|
|
57
|
+
assert_equal :tcp, d.instance.heartbeat_type
|
|
57
58
|
assert_equal 1, nodes.length
|
|
58
59
|
node = nodes.first
|
|
59
60
|
assert_equal "test", node.name
|
|
@@ -61,9 +62,9 @@ class ForwardOutputTest < Test::Unit::TestCase
|
|
|
61
62
|
assert_equal 13999, node.port
|
|
62
63
|
end
|
|
63
64
|
|
|
64
|
-
def
|
|
65
|
-
d = create_driver(CONFIG + "\nheartbeat_type
|
|
66
|
-
assert_equal :
|
|
65
|
+
def test_configure_udp_heartbeat
|
|
66
|
+
d = create_driver(CONFIG + "\nheartbeat_type udp")
|
|
67
|
+
assert_equal :udp, d.instance.heartbeat_type
|
|
67
68
|
end
|
|
68
69
|
|
|
69
70
|
def test_configure_none_heartbeat
|
|
@@ -119,6 +120,75 @@ class ForwardOutputTest < Test::Unit::TestCase
|
|
|
119
120
|
assert_equal 2, d.instance.ack_response_timeout
|
|
120
121
|
end
|
|
121
122
|
|
|
123
|
+
def test_send_with_time_as_integer
|
|
124
|
+
target_input_driver = create_target_input_driver
|
|
125
|
+
|
|
126
|
+
d = create_driver(CONFIG + %[flush_interval 1s])
|
|
127
|
+
|
|
128
|
+
time = Fluent::EventTime.parse("2011-01-02 13:14:15 UTC")
|
|
129
|
+
|
|
130
|
+
records = [
|
|
131
|
+
{"a" => 1},
|
|
132
|
+
{"a" => 2}
|
|
133
|
+
]
|
|
134
|
+
d.register_run_post_condition do
|
|
135
|
+
d.instance.responses.length == 1
|
|
136
|
+
end
|
|
137
|
+
|
|
138
|
+
target_input_driver.run do
|
|
139
|
+
d.run do
|
|
140
|
+
records.each do |record|
|
|
141
|
+
d.emit record, time
|
|
142
|
+
end
|
|
143
|
+
end
|
|
144
|
+
end
|
|
145
|
+
|
|
146
|
+
emits = target_input_driver.emits
|
|
147
|
+
assert_equal ['test', time, records[0]], emits[0]
|
|
148
|
+
assert_equal ['test', time, records[1]], emits[1]
|
|
149
|
+
assert(emits[0][1].is_a?(Integer))
|
|
150
|
+
assert(emits[1][1].is_a?(Integer))
|
|
151
|
+
|
|
152
|
+
assert_equal [nil], d.instance.responses # not attempt to receive responses, so nil is returned
|
|
153
|
+
assert_empty d.instance.exceptions
|
|
154
|
+
end
|
|
155
|
+
|
|
156
|
+
def test_send_without_time_as_integer
|
|
157
|
+
target_input_driver = create_target_input_driver
|
|
158
|
+
|
|
159
|
+
d = create_driver(CONFIG + %[
|
|
160
|
+
flush_interval 1s
|
|
161
|
+
time_as_integer false
|
|
162
|
+
])
|
|
163
|
+
|
|
164
|
+
time = Fluent::EventTime.parse("2011-01-02 13:14:15 UTC")
|
|
165
|
+
|
|
166
|
+
records = [
|
|
167
|
+
{"a" => 1},
|
|
168
|
+
{"a" => 2}
|
|
169
|
+
]
|
|
170
|
+
d.register_run_post_condition do
|
|
171
|
+
d.instance.responses.length == 1
|
|
172
|
+
end
|
|
173
|
+
|
|
174
|
+
target_input_driver.run do
|
|
175
|
+
d.run do
|
|
176
|
+
records.each do |record|
|
|
177
|
+
d.emit record, time
|
|
178
|
+
end
|
|
179
|
+
end
|
|
180
|
+
end
|
|
181
|
+
|
|
182
|
+
emits = target_input_driver.emits
|
|
183
|
+
assert_equal ['test', time, records[0]], emits[0]
|
|
184
|
+
assert_equal ['test', time, records[1]], emits[1]
|
|
185
|
+
assert_equal_event_time(time, emits[0][1])
|
|
186
|
+
assert_equal_event_time(time, emits[1][1])
|
|
187
|
+
|
|
188
|
+
assert_equal [nil], d.instance.responses # not attempt to receive responses, so nil is returned
|
|
189
|
+
assert_empty d.instance.exceptions
|
|
190
|
+
end
|
|
191
|
+
|
|
122
192
|
def test_send_to_a_node_supporting_responses
|
|
123
193
|
target_input_driver = create_target_input_driver(true)
|
|
124
194
|
|
|
@@ -305,6 +375,7 @@ class ForwardOutputTest < Test::Unit::TestCase
|
|
|
305
375
|
def create_target_input_driver(do_respond=false, disconnect=false, conf=TARGET_CONFIG)
|
|
306
376
|
require 'fluent/plugin/in_forward'
|
|
307
377
|
|
|
378
|
+
# TODO: Support actual TCP heartbeat test
|
|
308
379
|
DummyEngineDriver.new(Fluent::ForwardInput) {
|
|
309
380
|
handler_class = Class.new(Fluent::ForwardInput::Handler) { |klass|
|
|
310
381
|
attr_reader :chunk_counter # for checking if received data is successfully deserialized
|
|
@@ -314,12 +385,14 @@ class ForwardOutputTest < Test::Unit::TestCase
|
|
|
314
385
|
@log = log
|
|
315
386
|
@chunk_counter = 0
|
|
316
387
|
@on_message = on_message
|
|
388
|
+
@source = nil
|
|
317
389
|
end
|
|
318
390
|
|
|
319
391
|
if do_respond
|
|
320
392
|
def write(data)
|
|
321
393
|
@sock.write data
|
|
322
|
-
rescue
|
|
394
|
+
rescue
|
|
395
|
+
@sock.close_write
|
|
323
396
|
@sock.close
|
|
324
397
|
end
|
|
325
398
|
else
|
|
@@ -338,7 +411,7 @@ class ForwardOutputTest < Test::Unit::TestCase
|
|
|
338
411
|
|
|
339
412
|
define_method(:start) do
|
|
340
413
|
@thread = Thread.new do
|
|
341
|
-
Socket.tcp_server_loop(@
|
|
414
|
+
Socket.tcp_server_loop(@bind, @port) do |sock, client_addrinfo|
|
|
342
415
|
begin
|
|
343
416
|
handler = handler_class.new(sock, @log, method(:on_message))
|
|
344
417
|
loop do
|
|
@@ -416,6 +489,10 @@ class ForwardOutputTest < Test::Unit::TestCase
|
|
|
416
489
|
@emit_streams ||= []
|
|
417
490
|
end
|
|
418
491
|
|
|
492
|
+
def clear!
|
|
493
|
+
@emit_streams = []
|
|
494
|
+
end
|
|
495
|
+
|
|
419
496
|
def emit_stream(tag, es)
|
|
420
497
|
@emit_streams << [tag, es.to_a]
|
|
421
498
|
end
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
require_relative '../helper'
|
|
2
|
-
require 'fluent/test'
|
|
2
|
+
require 'fluent/test/driver/output'
|
|
3
|
+
require 'fluent/plugin/out_stdout'
|
|
3
4
|
|
|
4
5
|
class StdoutOutputTest < Test::Unit::TestCase
|
|
5
6
|
def setup
|
|
@@ -10,7 +11,7 @@ class StdoutOutputTest < Test::Unit::TestCase
|
|
|
10
11
|
]
|
|
11
12
|
|
|
12
13
|
def create_driver(conf = CONFIG)
|
|
13
|
-
Fluent::Test::
|
|
14
|
+
Fluent::Test::Driver::Output.new(Fluent::Plugin::StdoutOutput).configure(conf)
|
|
14
15
|
end
|
|
15
16
|
|
|
16
17
|
def test_configure
|
|
@@ -33,28 +34,36 @@ class StdoutOutputTest < Test::Unit::TestCase
|
|
|
33
34
|
data('oj' => 'oj', 'yajl' => 'yajl')
|
|
34
35
|
def test_emit_json(data)
|
|
35
36
|
d = create_driver(CONFIG + "\noutput_type json\njson_parser #{data}")
|
|
36
|
-
time =
|
|
37
|
-
out = capture_log
|
|
38
|
-
|
|
37
|
+
time = event_time()
|
|
38
|
+
out = capture_log do
|
|
39
|
+
d.run(default_tag: 'test') do
|
|
40
|
+
d.feed(time, {'test' => 'test'})
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
assert_equal "#{Time.at(time).localtime} test: {\"test\":\"test\"}\n", out
|
|
39
44
|
|
|
40
45
|
if data == 'yajl'
|
|
41
46
|
# NOTE: Float::NAN is not jsonable
|
|
42
|
-
assert_raise(Yajl::EncodeError) { d.
|
|
47
|
+
assert_raise(Yajl::EncodeError) { d.feed('test', time, {'test' => Float::NAN}) }
|
|
43
48
|
else
|
|
44
|
-
out = capture_log { d.
|
|
45
|
-
assert_equal "#{time.localtime} test: {\"test\":NaN}\n", out
|
|
49
|
+
out = capture_log { d.feed('test', time, {'test' => Float::NAN}) }
|
|
50
|
+
assert_equal "#{Time.at(time).localtime} test: {\"test\":NaN}\n", out
|
|
46
51
|
end
|
|
47
52
|
end
|
|
48
53
|
|
|
49
54
|
def test_emit_hash
|
|
50
55
|
d = create_driver(CONFIG + "\noutput_type hash")
|
|
51
|
-
time =
|
|
52
|
-
out = capture_log
|
|
53
|
-
|
|
56
|
+
time = event_time()
|
|
57
|
+
out = capture_log do
|
|
58
|
+
d.run(default_tag: 'test') do
|
|
59
|
+
d.feed(time, {'test' => 'test'})
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
assert_equal "#{Time.at(time).localtime} test: {\"test\"=>\"test\"}\n", out
|
|
54
63
|
|
|
55
64
|
# NOTE: Float::NAN is not jsonable, but hash string can output it.
|
|
56
|
-
out = capture_log { d.
|
|
57
|
-
assert_equal "#{time.localtime} test: {\"test\"=>NaN}\n", out
|
|
65
|
+
out = capture_log { d.feed('test', time, {'test' => Float::NAN}) }
|
|
66
|
+
assert_equal "#{Time.at(time).localtime} test: {\"test\"=>NaN}\n", out
|
|
58
67
|
end
|
|
59
68
|
|
|
60
69
|
private
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
require_relative '../helper'
|
|
2
2
|
require 'fluent/test'
|
|
3
|
+
require 'fluent/plugin/out_stream'
|
|
3
4
|
|
|
4
5
|
module StreamOutputTest
|
|
5
6
|
def setup
|
|
@@ -22,6 +23,23 @@ module StreamOutputTest
|
|
|
22
23
|
assert_equal(expect, result)
|
|
23
24
|
end
|
|
24
25
|
|
|
26
|
+
def test_write_event_time
|
|
27
|
+
d = create_driver
|
|
28
|
+
|
|
29
|
+
time = Fluent::EventTime.parse("2011-01-02 13:14:15 UTC")
|
|
30
|
+
d.emit({"a"=>1}, time)
|
|
31
|
+
d.emit({"a"=>2}, time)
|
|
32
|
+
|
|
33
|
+
expect = ["test",
|
|
34
|
+
Fluent::Engine.msgpack_factory.packer.write([time,{"a"=>1}]).to_s +
|
|
35
|
+
Fluent::Engine.msgpack_factory.packer.write([time,{"a"=>2}]).to_s
|
|
36
|
+
]
|
|
37
|
+
expect = Fluent::Engine.msgpack_factory.packer.write(expect).to_s
|
|
38
|
+
|
|
39
|
+
result = d.run
|
|
40
|
+
assert_equal(expect, result)
|
|
41
|
+
end
|
|
42
|
+
|
|
25
43
|
def create_driver(klass, conf)
|
|
26
44
|
Fluent::Test::BufferedOutputTestDriver.new(klass) do
|
|
27
45
|
def write(chunk)
|
|
@@ -0,0 +1,515 @@
|
|
|
1
|
+
require_relative '../helper'
|
|
2
|
+
require 'fluent/plugin/output'
|
|
3
|
+
require 'fluent/plugin/buffer'
|
|
4
|
+
require 'fluent/event'
|
|
5
|
+
|
|
6
|
+
require 'json'
|
|
7
|
+
require 'time'
|
|
8
|
+
require 'timeout'
|
|
9
|
+
|
|
10
|
+
module FluentPluginOutputTest
|
|
11
|
+
class DummyBareOutput < Fluent::Plugin::Output
|
|
12
|
+
def register(name, &block)
|
|
13
|
+
instance_variable_set("@#{name}", block)
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
class DummySyncOutput < DummyBareOutput
|
|
17
|
+
def initialize
|
|
18
|
+
super
|
|
19
|
+
@process = nil
|
|
20
|
+
end
|
|
21
|
+
def process(tag, es)
|
|
22
|
+
@process ? @process.call(tag, es) : nil
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
class DummyAsyncOutput < DummyBareOutput
|
|
26
|
+
def initialize
|
|
27
|
+
super
|
|
28
|
+
@format = nil
|
|
29
|
+
@write = nil
|
|
30
|
+
end
|
|
31
|
+
def format(tag, time, record)
|
|
32
|
+
@format ? @format.call(tag, time, record) : [tag, time, record].to_json
|
|
33
|
+
end
|
|
34
|
+
def write(chunk)
|
|
35
|
+
@write ? @write.call(chunk) : nil
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
class DummyAsyncStandardOutput < DummyBareOutput
|
|
39
|
+
def initialize
|
|
40
|
+
super
|
|
41
|
+
@write = nil
|
|
42
|
+
end
|
|
43
|
+
def write(chunk)
|
|
44
|
+
@write ? @write.call(chunk) : nil
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
class DummyDelayedOutput < DummyBareOutput
|
|
48
|
+
def initialize
|
|
49
|
+
super
|
|
50
|
+
@format = nil
|
|
51
|
+
@try_write = nil
|
|
52
|
+
end
|
|
53
|
+
def format(tag, time, record)
|
|
54
|
+
@format ? @format.call(tag, time, record) : [tag, time, record].to_json
|
|
55
|
+
end
|
|
56
|
+
def try_write(chunk)
|
|
57
|
+
@try_write ? @try_write.call(chunk) : nil
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
class DummyDelayedStandardOutput < DummyBareOutput
|
|
61
|
+
def initialize
|
|
62
|
+
super
|
|
63
|
+
@try_write = nil
|
|
64
|
+
end
|
|
65
|
+
def try_write(chunk)
|
|
66
|
+
@try_write ? @try_write.call(chunk) : nil
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
class DummyFullFeatureOutput < DummyBareOutput
|
|
70
|
+
def initialize
|
|
71
|
+
super
|
|
72
|
+
@prefer_buffered_processing = nil
|
|
73
|
+
@prefer_delayed_commit = nil
|
|
74
|
+
@process = nil
|
|
75
|
+
@format = nil
|
|
76
|
+
@write = nil
|
|
77
|
+
@try_write = nil
|
|
78
|
+
end
|
|
79
|
+
def prefer_buffered_processing
|
|
80
|
+
@prefer_buffered_processing ? @prefer_buffered_processing.call : false
|
|
81
|
+
end
|
|
82
|
+
def prefer_delayed_commit
|
|
83
|
+
@prefer_delayed_commit ? @prefer_delayed_commit.call : false
|
|
84
|
+
end
|
|
85
|
+
def process(tag, es)
|
|
86
|
+
@process ? @process.call(tag, es) : nil
|
|
87
|
+
end
|
|
88
|
+
def format(tag, time, record)
|
|
89
|
+
@format ? @format.call(tag, time, record) : [tag, time, record].to_json
|
|
90
|
+
end
|
|
91
|
+
def write(chunk)
|
|
92
|
+
@write ? @write.call(chunk) : nil
|
|
93
|
+
end
|
|
94
|
+
def try_write(chunk)
|
|
95
|
+
@try_write ? @try_write.call(chunk) : nil
|
|
96
|
+
end
|
|
97
|
+
end
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
class OutputTest < Test::Unit::TestCase
|
|
101
|
+
def create_output(type=:full)
|
|
102
|
+
case type
|
|
103
|
+
when :bare then FluentPluginOutputTest::DummyBareOutput.new
|
|
104
|
+
when :sync then FluentPluginOutputTest::DummySyncOutput.new
|
|
105
|
+
when :buffered then FluentPluginOutputTest::DummyAsyncOutput.new
|
|
106
|
+
when :standard then FluentPluginOutputTest::DummyAsyncStandardOutput.new
|
|
107
|
+
when :delayed then FluentPluginOutputTest::DummyDelayedOutput.new
|
|
108
|
+
when :sdelayed then FluentPluginOutputTest::DummyDelayedStandardOutput.new
|
|
109
|
+
when :full then FluentPluginOutputTest::DummyFullFeatureOutput.new
|
|
110
|
+
else
|
|
111
|
+
raise ArgumentError, "unknown type: #{type}"
|
|
112
|
+
end
|
|
113
|
+
end
|
|
114
|
+
def create_metadata(timekey: nil, tag: nil, variables: nil)
|
|
115
|
+
Fluent::Plugin::Buffer::Metadata.new(timekey, tag, variables)
|
|
116
|
+
end
|
|
117
|
+
def waiting(seconds)
|
|
118
|
+
begin
|
|
119
|
+
Timeout.timeout(seconds) do
|
|
120
|
+
yield
|
|
121
|
+
end
|
|
122
|
+
rescue Timeout::Error
|
|
123
|
+
STDERR.print(*@i.log.out.logs)
|
|
124
|
+
raise
|
|
125
|
+
end
|
|
126
|
+
end
|
|
127
|
+
|
|
128
|
+
sub_test_case 'basic output feature' do
|
|
129
|
+
setup do
|
|
130
|
+
@i = create_output(:full)
|
|
131
|
+
end
|
|
132
|
+
|
|
133
|
+
test '#implement? can return features for plugin instances' do
|
|
134
|
+
i1 = FluentPluginOutputTest::DummyBareOutput.new
|
|
135
|
+
assert !i1.implement?(:synchronous)
|
|
136
|
+
assert !i1.implement?(:buffered)
|
|
137
|
+
assert !i1.implement?(:delayed_commit)
|
|
138
|
+
assert !i1.implement?(:custom_format)
|
|
139
|
+
|
|
140
|
+
i2 = FluentPluginOutputTest::DummySyncOutput.new
|
|
141
|
+
assert i2.implement?(:synchronous)
|
|
142
|
+
assert !i2.implement?(:buffered)
|
|
143
|
+
assert !i2.implement?(:delayed_commit)
|
|
144
|
+
assert !i2.implement?(:custom_format)
|
|
145
|
+
|
|
146
|
+
i3 = FluentPluginOutputTest::DummyAsyncOutput.new
|
|
147
|
+
assert !i3.implement?(:synchronous)
|
|
148
|
+
assert i3.implement?(:buffered)
|
|
149
|
+
assert !i3.implement?(:delayed_commit)
|
|
150
|
+
assert i3.implement?(:custom_format)
|
|
151
|
+
|
|
152
|
+
i4 = FluentPluginOutputTest::DummyAsyncStandardOutput.new
|
|
153
|
+
assert !i4.implement?(:synchronous)
|
|
154
|
+
assert i4.implement?(:buffered)
|
|
155
|
+
assert !i4.implement?(:delayed_commit)
|
|
156
|
+
assert !i4.implement?(:custom_format)
|
|
157
|
+
|
|
158
|
+
i5 = FluentPluginOutputTest::DummyDelayedOutput.new
|
|
159
|
+
assert !i5.implement?(:synchronous)
|
|
160
|
+
assert !i5.implement?(:buffered)
|
|
161
|
+
assert i5.implement?(:delayed_commit)
|
|
162
|
+
assert i5.implement?(:custom_format)
|
|
163
|
+
|
|
164
|
+
i6 = FluentPluginOutputTest::DummyDelayedStandardOutput.new
|
|
165
|
+
assert !i6.implement?(:synchronous)
|
|
166
|
+
assert !i6.implement?(:buffered)
|
|
167
|
+
assert i6.implement?(:delayed_commit)
|
|
168
|
+
assert !i6.implement?(:custom_format)
|
|
169
|
+
|
|
170
|
+
i6 = FluentPluginOutputTest::DummyFullFeatureOutput.new
|
|
171
|
+
assert i6.implement?(:synchronous)
|
|
172
|
+
assert i6.implement?(:buffered)
|
|
173
|
+
assert i6.implement?(:delayed_commit)
|
|
174
|
+
assert i6.implement?(:custom_format)
|
|
175
|
+
end
|
|
176
|
+
|
|
177
|
+
test 'plugin lifecycle for configure/start/stop/before_shutdown/shutdown/after_shutdown/close/terminate' do
|
|
178
|
+
assert !@i.configured?
|
|
179
|
+
@i.configure(config_element())
|
|
180
|
+
assert @i.configured?
|
|
181
|
+
assert !@i.started?
|
|
182
|
+
@i.start
|
|
183
|
+
assert @i.started?
|
|
184
|
+
assert !@i.stopped?
|
|
185
|
+
@i.stop
|
|
186
|
+
assert @i.stopped?
|
|
187
|
+
assert !@i.before_shutdown?
|
|
188
|
+
@i.before_shutdown
|
|
189
|
+
assert @i.before_shutdown?
|
|
190
|
+
assert !@i.shutdown?
|
|
191
|
+
@i.shutdown
|
|
192
|
+
assert @i.shutdown?
|
|
193
|
+
assert !@i.after_shutdown?
|
|
194
|
+
@i.after_shutdown
|
|
195
|
+
assert @i.after_shutdown?
|
|
196
|
+
assert !@i.closed?
|
|
197
|
+
@i.close
|
|
198
|
+
assert @i.closed?
|
|
199
|
+
assert !@i.terminated?
|
|
200
|
+
@i.terminate
|
|
201
|
+
assert @i.terminated?
|
|
202
|
+
end
|
|
203
|
+
|
|
204
|
+
test '#extract_placeholders does nothing if chunk key is not specified' do
|
|
205
|
+
@i.configure(config_element('ROOT', '', {}, [config_element('buffer', '')]))
|
|
206
|
+
assert !@i.chunk_key_time
|
|
207
|
+
assert !@i.chunk_key_tag
|
|
208
|
+
assert_equal [], @i.chunk_keys
|
|
209
|
+
tmpl = "/mypath/%Y/%m/%d/${tag}/${tag[1]}/${tag[2]}/${key1}/${key2}/tail"
|
|
210
|
+
t = event_time('2016-04-11 20:30:00 +0900')
|
|
211
|
+
v = {key1: "value1", key2: "value2"}
|
|
212
|
+
m = create_metadata(timekey: t, tag: 'fluentd.test.output', variables: v)
|
|
213
|
+
assert_equal tmpl, @i.extract_placeholders(tmpl, m)
|
|
214
|
+
end
|
|
215
|
+
|
|
216
|
+
test '#extract_placeholders can extract time if time key and range are configured' do
|
|
217
|
+
@i.configure(config_element('ROOT', '', {}, [config_element('buffer', 'time', {'timekey' => 60*30, 'timekey_zone' => "+0900"})]))
|
|
218
|
+
assert @i.chunk_key_time
|
|
219
|
+
assert !@i.chunk_key_tag
|
|
220
|
+
assert_equal [], @i.chunk_keys
|
|
221
|
+
tmpl = "/mypath/%Y/%m/%d/%H-%M/${tag}/${tag[1]}/${tag[2]}/${key1}/${key2}/tail"
|
|
222
|
+
t = event_time('2016-04-11 20:30:00 +0900')
|
|
223
|
+
v = {key1: "value1", key2: "value2"}
|
|
224
|
+
m = create_metadata(timekey: t, tag: 'fluentd.test.output', variables: v)
|
|
225
|
+
assert_equal "/mypath/2016/04/11/20-30/${tag}/${tag[1]}/${tag[2]}/${key1}/${key2}/tail", @i.extract_placeholders(tmpl, m)
|
|
226
|
+
end
|
|
227
|
+
|
|
228
|
+
test '#extract_placeholders can extract tag and parts of tag if tag is configured' do
|
|
229
|
+
@i.configure(config_element('ROOT', '', {}, [config_element('buffer', 'tag', {})]))
|
|
230
|
+
assert !@i.chunk_key_time
|
|
231
|
+
assert @i.chunk_key_tag
|
|
232
|
+
assert_equal [], @i.chunk_keys
|
|
233
|
+
tmpl = "/mypath/%Y/%m/%d/%H-%M/${tag}/${tag[1]}/${tag[2]}/${key1}/${key2}/tail"
|
|
234
|
+
t = event_time('2016-04-11 20:30:00 +0900')
|
|
235
|
+
v = {key1: "value1", key2: "value2"}
|
|
236
|
+
m = create_metadata(timekey: t, tag: 'fluentd.test.output', variables: v)
|
|
237
|
+
assert_equal "/mypath/%Y/%m/%d/%H-%M/fluentd.test.output/test/output/${key1}/${key2}/tail", @i.extract_placeholders(tmpl, m)
|
|
238
|
+
end
|
|
239
|
+
|
|
240
|
+
test '#extract_placeholders can extract variables if variables are configured' do
|
|
241
|
+
@i.configure(config_element('ROOT', '', {}, [config_element('buffer', 'key1,key2', {})]))
|
|
242
|
+
assert !@i.chunk_key_time
|
|
243
|
+
assert !@i.chunk_key_tag
|
|
244
|
+
assert_equal ['key1','key2'], @i.chunk_keys
|
|
245
|
+
tmpl = "/mypath/%Y/%m/%d/%H-%M/${tag}/${tag[1]}/${tag[2]}/${key1}/${key2}/tail"
|
|
246
|
+
t = event_time('2016-04-11 20:30:00 +0900')
|
|
247
|
+
v = {key1: "value1", key2: "value2"}
|
|
248
|
+
m = create_metadata(timekey: t, tag: 'fluentd.test.output', variables: v)
|
|
249
|
+
assert_equal "/mypath/%Y/%m/%d/%H-%M/${tag}/${tag[1]}/${tag[2]}/value1/value2/tail", @i.extract_placeholders(tmpl, m)
|
|
250
|
+
end
|
|
251
|
+
|
|
252
|
+
test '#extract_placeholders can extract all chunk keys if configured' do
|
|
253
|
+
@i.configure(config_element('ROOT', '', {}, [config_element('buffer', 'time,tag,key1,key2', {'timekey' => 60*30, 'timekey_zone' => "+0900"})]))
|
|
254
|
+
assert @i.chunk_key_time
|
|
255
|
+
assert @i.chunk_key_tag
|
|
256
|
+
assert_equal ['key1','key2'], @i.chunk_keys
|
|
257
|
+
tmpl = "/mypath/%Y/%m/%d/%H-%M/${tag}/${tag[1]}/${tag[2]}/${key1}/${key2}/tail"
|
|
258
|
+
t = event_time('2016-04-11 20:30:00 +0900')
|
|
259
|
+
v = {key1: "value1", key2: "value2"}
|
|
260
|
+
m = create_metadata(timekey: t, tag: 'fluentd.test.output', variables: v)
|
|
261
|
+
assert_equal "/mypath/2016/04/11/20-30/fluentd.test.output/test/output/value1/value2/tail", @i.extract_placeholders(tmpl, m)
|
|
262
|
+
end
|
|
263
|
+
|
|
264
|
+
test '#extract_placeholders removes out-of-range tag part and unknown variable placeholders' do
|
|
265
|
+
@i.configure(config_element('ROOT', '', {}, [config_element('buffer', 'time,tag,key1,key2', {'timekey' => 60*30, 'timekey_zone' => "+0900"})]))
|
|
266
|
+
assert @i.chunk_key_time
|
|
267
|
+
assert @i.chunk_key_tag
|
|
268
|
+
assert_equal ['key1','key2'], @i.chunk_keys
|
|
269
|
+
tmpl = "/mypath/%Y/%m/%d/%H-%M/${tag}/${tag[3]}/${tag[4]}/${key3}/${key4}/tail"
|
|
270
|
+
t = event_time('2016-04-11 20:30:00 +0900')
|
|
271
|
+
v = {key1: "value1", key2: "value2"}
|
|
272
|
+
m = create_metadata(timekey: t, tag: 'fluentd.test.output', variables: v)
|
|
273
|
+
assert_equal "/mypath/2016/04/11/20-30/fluentd.test.output/////tail", @i.extract_placeholders(tmpl, m)
|
|
274
|
+
end
|
|
275
|
+
|
|
276
|
+
test '#metadata returns object which contains tag/timekey/variables from records as specified in configuration' do
|
|
277
|
+
tag = 'test.output'
|
|
278
|
+
time = event_time('2016-04-12 15:31:23 -0700')
|
|
279
|
+
timekey = event_time('2016-04-12 15:00:00 -0700')
|
|
280
|
+
record = {"key1" => "value1", "num1" => 1, "message" => "my message"}
|
|
281
|
+
|
|
282
|
+
i1 = create_output(:buffered)
|
|
283
|
+
i1.configure(config_element('ROOT','',{},[config_element('buffer', '')]))
|
|
284
|
+
assert_equal create_metadata(), i1.metadata(tag, time, record)
|
|
285
|
+
|
|
286
|
+
i2 = create_output(:buffered)
|
|
287
|
+
i2.configure(config_element('ROOT','',{},[config_element('buffer', 'tag')]))
|
|
288
|
+
assert_equal create_metadata(tag: tag), i2.metadata(tag, time, record)
|
|
289
|
+
|
|
290
|
+
i3 = create_output(:buffered)
|
|
291
|
+
i3.configure(config_element('ROOT','',{},[config_element('buffer', 'time', {"timekey" => 3600, "timekey_zone" => "-0700"})]))
|
|
292
|
+
assert_equal create_metadata(timekey: timekey), i3.metadata(tag, time, record)
|
|
293
|
+
|
|
294
|
+
i4 = create_output(:buffered)
|
|
295
|
+
i4.configure(config_element('ROOT','',{},[config_element('buffer', 'key1', {})]))
|
|
296
|
+
assert_equal create_metadata(variables: {key1: "value1"}), i4.metadata(tag, time, record)
|
|
297
|
+
|
|
298
|
+
i5 = create_output(:buffered)
|
|
299
|
+
i5.configure(config_element('ROOT','',{},[config_element('buffer', 'key1,num1', {})]))
|
|
300
|
+
assert_equal create_metadata(variables: {key1: "value1", num1: 1}), i5.metadata(tag, time, record)
|
|
301
|
+
|
|
302
|
+
i6 = create_output(:buffered)
|
|
303
|
+
i6.configure(config_element('ROOT','',{},[config_element('buffer', 'tag,time', {"timekey" => 3600, "timekey_zone" => "-0700"})]))
|
|
304
|
+
assert_equal create_metadata(timekey: timekey, tag: tag), i6.metadata(tag, time, record)
|
|
305
|
+
|
|
306
|
+
i7 = create_output(:buffered)
|
|
307
|
+
i7.configure(config_element('ROOT','',{},[config_element('buffer', 'tag,num1', {"timekey" => 3600, "timekey_zone" => "-0700"})]))
|
|
308
|
+
assert_equal create_metadata(tag: tag, variables: {num1: 1}), i7.metadata(tag, time, record)
|
|
309
|
+
|
|
310
|
+
i8 = create_output(:buffered)
|
|
311
|
+
i8.configure(config_element('ROOT','',{},[config_element('buffer', 'time,tag,key1', {"timekey" => 3600, "timekey_zone" => "-0700"})]))
|
|
312
|
+
assert_equal create_metadata(timekey: timekey, tag: tag, variables: {key1: "value1"}), i8.metadata(tag, time, record)
|
|
313
|
+
end
|
|
314
|
+
|
|
315
|
+
test '#emit calls #process via #emit_sync for non-buffered output' do
|
|
316
|
+
i = create_output(:sync)
|
|
317
|
+
process_called = false
|
|
318
|
+
i.register(:process){|tag, es| process_called = true }
|
|
319
|
+
i.configure(config_element())
|
|
320
|
+
i.start
|
|
321
|
+
|
|
322
|
+
t = event_time()
|
|
323
|
+
i.emit_events('tag', Fluent::ArrayEventStream.new([ [t, {"key" => "value1"}], [t, {"key" => "value2"}] ]))
|
|
324
|
+
|
|
325
|
+
assert process_called
|
|
326
|
+
|
|
327
|
+
i.stop; i.before_shutdown; i.shutdown; i.after_shutdown; i.close; i.terminate
|
|
328
|
+
end
|
|
329
|
+
|
|
330
|
+
test '#emit calls #format for buffered output' do
|
|
331
|
+
i = create_output(:buffered)
|
|
332
|
+
format_called_times = 0
|
|
333
|
+
i.register(:format){|tag, time, record| format_called_times += 1; '' }
|
|
334
|
+
i.configure(config_element())
|
|
335
|
+
i.start
|
|
336
|
+
|
|
337
|
+
t = event_time()
|
|
338
|
+
i.emit_events('tag', Fluent::ArrayEventStream.new([ [t, {"key" => "value1"}], [t, {"key" => "value2"}] ]))
|
|
339
|
+
|
|
340
|
+
assert_equal 2, format_called_times
|
|
341
|
+
|
|
342
|
+
i.stop; i.before_shutdown; i.shutdown; i.after_shutdown; i.close; i.terminate
|
|
343
|
+
end
|
|
344
|
+
|
|
345
|
+
test '#prefer_buffered_processing (returns false) decides non-buffered without <buffer> section' do
|
|
346
|
+
i = create_output(:full)
|
|
347
|
+
|
|
348
|
+
process_called = false
|
|
349
|
+
format_called_times = 0
|
|
350
|
+
i.register(:process){|tag, es| process_called = true }
|
|
351
|
+
i.register(:format){|tag, time, record| format_called_times += 1; '' }
|
|
352
|
+
|
|
353
|
+
i.configure(config_element())
|
|
354
|
+
i.register(:prefer_buffered_processing){ false } # delayed decision is possible to change after (output's) configure
|
|
355
|
+
i.start
|
|
356
|
+
|
|
357
|
+
assert !i.prefer_buffered_processing
|
|
358
|
+
|
|
359
|
+
t = event_time()
|
|
360
|
+
i.emit_events('tag', Fluent::ArrayEventStream.new([ [t, {"key" => "value1"}], [t, {"key" => "value2"}] ]))
|
|
361
|
+
|
|
362
|
+
waiting(4){ Thread.pass until process_called }
|
|
363
|
+
|
|
364
|
+
assert process_called
|
|
365
|
+
assert_equal 0, format_called_times
|
|
366
|
+
|
|
367
|
+
i.stop; i.before_shutdown; i.shutdown; i.after_shutdown; i.close; i.terminate
|
|
368
|
+
end
|
|
369
|
+
|
|
370
|
+
test '#prefer_buffered_processing (returns true) decides buffered without <buffer> section' do
|
|
371
|
+
i = create_output(:full)
|
|
372
|
+
|
|
373
|
+
process_called = false
|
|
374
|
+
format_called_times = 0
|
|
375
|
+
i.register(:process){|tag, es| process_called = true }
|
|
376
|
+
i.register(:format){|tag, time, record| format_called_times += 1; '' }
|
|
377
|
+
|
|
378
|
+
i.configure(config_element())
|
|
379
|
+
i.register(:prefer_buffered_processing){ true } # delayed decision is possible to change after (output's) configure
|
|
380
|
+
i.start
|
|
381
|
+
|
|
382
|
+
assert i.prefer_buffered_processing
|
|
383
|
+
|
|
384
|
+
t = event_time()
|
|
385
|
+
i.emit_events('tag', Fluent::ArrayEventStream.new([ [t, {"key" => "value1"}], [t, {"key" => "value2"}] ]))
|
|
386
|
+
|
|
387
|
+
assert !process_called
|
|
388
|
+
assert_equal 2, format_called_times
|
|
389
|
+
|
|
390
|
+
i.stop; i.before_shutdown; i.shutdown; i.after_shutdown; i.close; i.terminate
|
|
391
|
+
end
|
|
392
|
+
|
|
393
|
+
test 'output plugin will call #write for normal buffered plugin to flush buffer chunks' do
|
|
394
|
+
i = create_output(:buffered)
|
|
395
|
+
write_called = false
|
|
396
|
+
i.register(:write){ |chunk| write_called = true }
|
|
397
|
+
|
|
398
|
+
i.configure(config_element('ROOT', '', {}, [config_element('buffer', '', {"flush_mode" => "immediate"})]))
|
|
399
|
+
i.start
|
|
400
|
+
|
|
401
|
+
t = event_time()
|
|
402
|
+
i.emit_events('tag', Fluent::ArrayEventStream.new([ [t, {"key" => "value1"}], [t, {"key" => "value2"}] ]))
|
|
403
|
+
i.force_flush
|
|
404
|
+
|
|
405
|
+
waiting(4){ Thread.pass until write_called }
|
|
406
|
+
|
|
407
|
+
assert write_called
|
|
408
|
+
|
|
409
|
+
i.stop; i.before_shutdown; i.shutdown; i.after_shutdown; i.close; i.terminate
|
|
410
|
+
end
|
|
411
|
+
|
|
412
|
+
test 'output plugin will call #try_write for plugin supports delayed commit only to flush buffer chunks' do
|
|
413
|
+
i = create_output(:delayed)
|
|
414
|
+
try_write_called = false
|
|
415
|
+
i.register(:try_write){|chunk| try_write_called = true; commit_write(chunk.unique_id) }
|
|
416
|
+
|
|
417
|
+
i.configure(config_element('ROOT', '', {}, [config_element('buffer', '', {"flush_mode" => "immediate"})]))
|
|
418
|
+
i.start
|
|
419
|
+
|
|
420
|
+
t = event_time()
|
|
421
|
+
i.emit_events('tag', Fluent::ArrayEventStream.new([ [t, {"key" => "value1"}], [t, {"key" => "value2"}] ]))
|
|
422
|
+
i.force_flush
|
|
423
|
+
|
|
424
|
+
waiting(4){ Thread.pass until try_write_called }
|
|
425
|
+
|
|
426
|
+
assert try_write_called
|
|
427
|
+
|
|
428
|
+
i.stop; i.before_shutdown; i.shutdown; i.after_shutdown; i.close; i.terminate
|
|
429
|
+
end
|
|
430
|
+
|
|
431
|
+
test '#prefer_delayed_commit (returns false) decides delayed commit is disabled if both are implemented' do
|
|
432
|
+
i = create_output(:full)
|
|
433
|
+
write_called = false
|
|
434
|
+
try_write_called = false
|
|
435
|
+
i.register(:write){ |chunk| write_called = true }
|
|
436
|
+
i.register(:try_write){|chunk| try_write_called = true; commit_write(chunk.unique_id) }
|
|
437
|
+
|
|
438
|
+
i.configure(config_element('ROOT', '', {}, [config_element('buffer', '', {"flush_mode" => "immediate"})]))
|
|
439
|
+
i.register(:prefer_delayed_commit){ false } # delayed decision is possible to change after (output's) configure
|
|
440
|
+
i.start
|
|
441
|
+
|
|
442
|
+
assert !i.prefer_delayed_commit
|
|
443
|
+
|
|
444
|
+
t = event_time()
|
|
445
|
+
i.emit_events('tag', Fluent::ArrayEventStream.new([ [t, {"key" => "value1"}], [t, {"key" => "value2"}] ]))
|
|
446
|
+
i.force_flush
|
|
447
|
+
|
|
448
|
+
waiting(4){ Thread.pass until write_called || try_write_called }
|
|
449
|
+
|
|
450
|
+
assert write_called
|
|
451
|
+
assert !try_write_called
|
|
452
|
+
|
|
453
|
+
i.stop; i.before_shutdown; i.shutdown; i.after_shutdown; i.close; i.terminate
|
|
454
|
+
end
|
|
455
|
+
|
|
456
|
+
test '#prefer_delayed_commit (returns true) decides delayed commit is enabled if both are implemented' do
|
|
457
|
+
i = create_output(:full)
|
|
458
|
+
write_called = false
|
|
459
|
+
try_write_called = false
|
|
460
|
+
i.register(:write){ |chunk| write_called = true }
|
|
461
|
+
i.register(:try_write){|chunk| try_write_called = true; commit_write(chunk.unique_id) }
|
|
462
|
+
|
|
463
|
+
i.configure(config_element('ROOT', '', {}, [config_element('buffer', '', {"flush_mode" => "immediate"})]))
|
|
464
|
+
i.register(:prefer_delayed_commit){ true } # delayed decision is possible to change after (output's) configure
|
|
465
|
+
i.start
|
|
466
|
+
|
|
467
|
+
assert i.prefer_delayed_commit
|
|
468
|
+
|
|
469
|
+
t = event_time()
|
|
470
|
+
i.emit_events('tag', Fluent::ArrayEventStream.new([ [t, {"key" => "value1"}], [t, {"key" => "value2"}] ]))
|
|
471
|
+
i.force_flush
|
|
472
|
+
|
|
473
|
+
waiting(4){ Thread.pass until write_called || try_write_called }
|
|
474
|
+
|
|
475
|
+
assert !write_called
|
|
476
|
+
assert try_write_called
|
|
477
|
+
|
|
478
|
+
i.stop; i.before_shutdown; i.shutdown; i.after_shutdown; i.close; i.terminate
|
|
479
|
+
end
|
|
480
|
+
end
|
|
481
|
+
|
|
482
|
+
sub_test_case 'sync output feature' do
|
|
483
|
+
setup do
|
|
484
|
+
@i = create_output(:sync)
|
|
485
|
+
end
|
|
486
|
+
|
|
487
|
+
test 'raises configuration error if <buffer> section is specified' do
|
|
488
|
+
assert_raise Fluent::ConfigError do
|
|
489
|
+
@i.configure(config_element('ROOT','',{},[config_element('buffer', '')]))
|
|
490
|
+
end
|
|
491
|
+
end
|
|
492
|
+
|
|
493
|
+
test 'raises configuration error if <secondary> section is specified' do
|
|
494
|
+
assert_raise Fluent::ConfigError do
|
|
495
|
+
@i.configure(config_element('ROOT','',{},[config_element('secondary','')]))
|
|
496
|
+
end
|
|
497
|
+
end
|
|
498
|
+
|
|
499
|
+
test '#process is called for each event streams' do
|
|
500
|
+
ary = []
|
|
501
|
+
@i.register(:process){|tag, es| ary << [tag, es] }
|
|
502
|
+
@i.configure(config_element())
|
|
503
|
+
@i.start
|
|
504
|
+
|
|
505
|
+
t = event_time()
|
|
506
|
+
es = Fluent::ArrayEventStream.new([ [t, {"key" => "value1"}], [t, {"key" => "value2"}] ])
|
|
507
|
+
5.times do
|
|
508
|
+
@i.emit_events('tag', es)
|
|
509
|
+
end
|
|
510
|
+
assert_equal 5, ary.size
|
|
511
|
+
|
|
512
|
+
@i.stop; @i.before_shutdown; @i.shutdown; @i.after_shutdown; @i.close; @i.terminate
|
|
513
|
+
end
|
|
514
|
+
end
|
|
515
|
+
end
|