fluentd 0.12.40 → 1.6.2
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of fluentd might be problematic. Click here for more details.
- checksums.yaml +5 -5
- data/.github/ISSUE_TEMPLATE/bug_report.md +39 -0
- data/.github/ISSUE_TEMPLATE/feature_request.md +23 -0
- data/.github/ISSUE_TEMPLATE.md +17 -0
- data/.github/PULL_REQUEST_TEMPLATE.md +13 -0
- data/.gitignore +5 -0
- data/.gitlab/cicd-template.yaml +10 -0
- data/.gitlab-ci.yml +147 -0
- data/.travis.yml +56 -20
- data/ADOPTERS.md +5 -0
- data/CHANGELOG.md +1369 -0
- data/CONTRIBUTING.md +16 -5
- data/GOVERNANCE.md +55 -0
- data/Gemfile +5 -0
- data/GithubWorkflow.md +78 -0
- data/LICENSE +202 -0
- data/MAINTAINERS.md +7 -0
- data/README.md +23 -11
- data/Rakefile +48 -2
- data/Vagrantfile +17 -0
- data/appveyor.yml +37 -0
- data/bin/fluent-binlog-reader +7 -0
- data/bin/fluent-ca-generate +6 -0
- data/bin/fluent-plugin-config-format +5 -0
- data/bin/fluent-plugin-generate +5 -0
- data/bin/fluentd +3 -0
- data/code-of-conduct.md +3 -0
- data/example/copy_roundrobin.conf +39 -0
- data/example/counter.conf +18 -0
- data/example/in_dummy_blocks.conf +17 -0
- data/example/in_dummy_with_compression.conf +23 -0
- data/example/in_forward.conf +7 -0
- data/example/in_forward_client.conf +37 -0
- data/example/in_forward_shared_key.conf +15 -0
- data/example/in_forward_tls.conf +14 -0
- data/example/in_forward_users.conf +24 -0
- data/example/in_forward_workers.conf +21 -0
- data/example/in_http.conf +3 -1
- data/example/in_out_forward.conf +17 -0
- data/example/logevents.conf +25 -0
- data/example/multi_filters.conf +61 -0
- data/example/out_exec_filter.conf +42 -0
- data/example/out_forward.conf +13 -13
- data/example/out_forward_buf_file.conf +23 -0
- data/example/out_forward_client.conf +109 -0
- data/example/out_forward_heartbeat_none.conf +16 -0
- data/example/out_forward_shared_key.conf +36 -0
- data/example/out_forward_tls.conf +18 -0
- data/example/out_forward_users.conf +65 -0
- data/example/out_null.conf +36 -0
- data/example/secondary_file.conf +42 -0
- data/example/suppress_config_dump.conf +7 -0
- data/example/worker_section.conf +36 -0
- data/fluent.conf +29 -0
- data/fluentd.gemspec +21 -11
- data/lib/fluent/agent.rb +67 -90
- data/lib/fluent/clock.rb +62 -0
- data/lib/fluent/command/binlog_reader.rb +244 -0
- data/lib/fluent/command/ca_generate.rb +181 -0
- data/lib/fluent/command/cat.rb +42 -18
- data/lib/fluent/command/debug.rb +12 -10
- data/lib/fluent/command/fluentd.rb +153 -5
- data/lib/fluent/command/plugin_config_formatter.rb +292 -0
- data/lib/fluent/command/plugin_generator.rb +324 -0
- data/lib/fluent/compat/call_super_mixin.rb +67 -0
- data/lib/fluent/compat/detach_process_mixin.rb +33 -0
- data/lib/fluent/compat/exec_util.rb +129 -0
- data/lib/fluent/compat/file_util.rb +54 -0
- data/lib/fluent/compat/filter.rb +68 -0
- data/lib/fluent/compat/formatter.rb +111 -0
- data/lib/fluent/compat/formatter_utils.rb +85 -0
- data/lib/fluent/compat/handle_tag_and_time_mixin.rb +62 -0
- data/lib/fluent/compat/handle_tag_name_mixin.rb +53 -0
- data/lib/fluent/compat/input.rb +49 -0
- data/lib/fluent/compat/output.rb +718 -0
- data/lib/fluent/compat/output_chain.rb +60 -0
- data/lib/fluent/compat/parser.rb +310 -0
- data/lib/fluent/compat/parser_utils.rb +40 -0
- data/lib/fluent/compat/propagate_default.rb +62 -0
- data/lib/fluent/compat/record_filter_mixin.rb +34 -0
- data/lib/fluent/compat/set_tag_key_mixin.rb +50 -0
- data/lib/fluent/compat/set_time_key_mixin.rb +69 -0
- data/lib/fluent/compat/socket_util.rb +165 -0
- data/lib/fluent/compat/string_util.rb +34 -0
- data/lib/fluent/compat/structured_format_mixin.rb +26 -0
- data/lib/fluent/compat/type_converter.rb +90 -0
- data/lib/fluent/config/configure_proxy.rb +210 -62
- data/lib/fluent/config/dsl.rb +12 -5
- data/lib/fluent/config/element.rb +107 -9
- data/lib/fluent/config/literal_parser.rb +9 -3
- data/lib/fluent/config/parser.rb +4 -4
- data/lib/fluent/config/section.rb +51 -14
- data/lib/fluent/config/types.rb +28 -13
- data/lib/fluent/config/v1_parser.rb +3 -5
- data/lib/fluent/config.rb +23 -20
- data/lib/fluent/configurable.rb +79 -21
- data/lib/fluent/counter/base_socket.rb +46 -0
- data/lib/fluent/counter/client.rb +297 -0
- data/lib/fluent/counter/error.rb +86 -0
- data/lib/fluent/counter/mutex_hash.rb +163 -0
- data/lib/fluent/counter/server.rb +273 -0
- data/lib/fluent/counter/store.rb +205 -0
- data/lib/fluent/counter/validator.rb +145 -0
- data/lib/fluent/counter.rb +23 -0
- data/lib/fluent/daemon.rb +15 -0
- data/lib/fluent/engine.rb +102 -65
- data/lib/fluent/env.rb +7 -3
- data/lib/fluent/error.rb +30 -0
- data/lib/fluent/event.rb +197 -21
- data/lib/fluent/event_router.rb +93 -10
- 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 +10 -2
- data/lib/fluent/load.rb +3 -3
- data/lib/fluent/log.rb +348 -81
- data/lib/fluent/match.rb +37 -36
- data/lib/fluent/mixin.rb +12 -176
- data/lib/fluent/msgpack_factory.rb +62 -0
- data/lib/fluent/output.rb +10 -612
- data/lib/fluent/output_chain.rb +23 -0
- data/lib/fluent/parser.rb +4 -800
- data/lib/fluent/plugin/bare_output.rb +63 -0
- data/lib/fluent/plugin/base.rb +192 -0
- data/lib/fluent/plugin/buf_file.rb +128 -174
- data/lib/fluent/plugin/buf_memory.rb +9 -92
- data/lib/fluent/plugin/buffer/chunk.rb +221 -0
- data/lib/fluent/plugin/buffer/file_chunk.rb +383 -0
- data/lib/fluent/plugin/buffer/memory_chunk.rb +90 -0
- data/lib/fluent/plugin/buffer.rb +779 -0
- data/lib/fluent/plugin/compressable.rb +92 -0
- data/lib/fluent/plugin/exec_util.rb +3 -108
- data/lib/fluent/plugin/file_util.rb +4 -34
- data/lib/fluent/plugin/file_wrapper.rb +120 -0
- data/lib/fluent/plugin/filter.rb +93 -0
- data/lib/fluent/plugin/filter_grep.rb +117 -34
- data/lib/fluent/plugin/filter_parser.rb +85 -62
- data/lib/fluent/plugin/filter_record_transformer.rb +27 -39
- data/lib/fluent/plugin/filter_stdout.rb +15 -12
- data/lib/fluent/plugin/formatter.rb +50 -0
- data/lib/fluent/plugin/formatter_csv.rb +52 -0
- data/lib/fluent/plugin/formatter_hash.rb +33 -0
- data/lib/fluent/plugin/formatter_json.rb +55 -0
- data/lib/fluent/plugin/formatter_ltsv.rb +42 -0
- data/lib/fluent/plugin/formatter_msgpack.rb +33 -0
- data/lib/fluent/plugin/formatter_out_file.rb +51 -0
- data/lib/fluent/plugin/formatter_single_value.rb +34 -0
- data/lib/fluent/plugin/formatter_stdout.rb +76 -0
- data/lib/fluent/plugin/formatter_tsv.rb +38 -0
- data/lib/fluent/plugin/in_debug_agent.rb +17 -6
- data/lib/fluent/plugin/in_dummy.rb +47 -20
- data/lib/fluent/plugin/in_exec.rb +55 -123
- data/lib/fluent/plugin/in_forward.rb +299 -216
- data/lib/fluent/plugin/in_gc_stat.rb +14 -36
- data/lib/fluent/plugin/in_http.rb +204 -91
- data/lib/fluent/plugin/in_monitor_agent.rb +186 -258
- data/lib/fluent/plugin/in_object_space.rb +13 -41
- data/lib/fluent/plugin/in_syslog.rb +112 -134
- data/lib/fluent/plugin/in_tail.rb +408 -745
- data/lib/fluent/plugin/in_tcp.rb +66 -9
- data/lib/fluent/plugin/in_udp.rb +60 -11
- data/lib/fluent/plugin/{in_stream.rb → in_unix.rb} +8 -4
- data/lib/fluent/plugin/input.rb +37 -0
- data/lib/fluent/plugin/multi_output.rb +158 -0
- data/lib/fluent/plugin/out_copy.rb +23 -35
- data/lib/fluent/plugin/out_exec.rb +67 -70
- data/lib/fluent/plugin/out_exec_filter.rb +204 -271
- data/lib/fluent/plugin/out_file.rb +267 -73
- data/lib/fluent/plugin/out_forward.rb +854 -325
- data/lib/fluent/plugin/out_null.rb +42 -9
- data/lib/fluent/plugin/out_relabel.rb +9 -5
- data/lib/fluent/plugin/out_roundrobin.rb +18 -37
- data/lib/fluent/plugin/out_secondary_file.rb +133 -0
- data/lib/fluent/plugin/out_stdout.rb +43 -10
- data/lib/fluent/plugin/out_stream.rb +7 -2
- data/lib/fluent/plugin/output.rb +1498 -0
- data/lib/fluent/plugin/owned_by_mixin.rb +42 -0
- data/lib/fluent/plugin/parser.rb +191 -0
- data/lib/fluent/plugin/parser_apache.rb +28 -0
- data/lib/fluent/plugin/parser_apache2.rb +88 -0
- data/lib/fluent/plugin/parser_apache_error.rb +26 -0
- data/lib/fluent/plugin/parser_csv.rb +39 -0
- data/lib/fluent/plugin/parser_json.rb +94 -0
- data/lib/fluent/plugin/parser_ltsv.rb +49 -0
- data/lib/fluent/plugin/parser_msgpack.rb +50 -0
- data/lib/fluent/plugin/parser_multiline.rb +106 -0
- data/lib/fluent/plugin/parser_nginx.rb +28 -0
- data/lib/fluent/plugin/parser_none.rb +36 -0
- data/lib/fluent/plugin/parser_regexp.rb +68 -0
- data/lib/fluent/plugin/parser_syslog.rb +142 -0
- data/lib/fluent/plugin/parser_tsv.rb +42 -0
- data/lib/fluent/plugin/socket_util.rb +3 -143
- data/lib/fluent/plugin/storage.rb +84 -0
- data/lib/fluent/plugin/storage_local.rb +164 -0
- data/lib/fluent/plugin/string_util.rb +3 -15
- data/lib/fluent/plugin.rb +122 -121
- data/lib/fluent/plugin_helper/cert_option.rb +178 -0
- data/lib/fluent/plugin_helper/child_process.rb +364 -0
- data/lib/fluent/plugin_helper/compat_parameters.rb +333 -0
- data/lib/fluent/plugin_helper/counter.rb +51 -0
- data/lib/fluent/plugin_helper/event_emitter.rb +93 -0
- data/lib/fluent/plugin_helper/event_loop.rb +170 -0
- data/lib/fluent/plugin_helper/extract.rb +104 -0
- data/lib/fluent/plugin_helper/formatter.rb +147 -0
- data/lib/fluent/plugin_helper/http_server/app.rb +79 -0
- data/lib/fluent/plugin_helper/http_server/compat/server.rb +81 -0
- data/lib/fluent/plugin_helper/http_server/compat/webrick_handler.rb +58 -0
- data/lib/fluent/plugin_helper/http_server/methods.rb +35 -0
- data/lib/fluent/plugin_helper/http_server/request.rb +42 -0
- data/lib/fluent/plugin_helper/http_server/router.rb +54 -0
- data/lib/fluent/plugin_helper/http_server/server.rb +87 -0
- data/lib/fluent/plugin_helper/http_server.rb +76 -0
- data/lib/fluent/plugin_helper/inject.rb +151 -0
- data/lib/fluent/plugin_helper/parser.rb +147 -0
- data/lib/fluent/plugin_helper/record_accessor.rb +210 -0
- data/lib/fluent/plugin_helper/retry_state.rb +205 -0
- data/lib/fluent/plugin_helper/server.rb +807 -0
- data/lib/fluent/plugin_helper/socket.rb +250 -0
- data/lib/fluent/plugin_helper/socket_option.rb +80 -0
- data/lib/fluent/plugin_helper/storage.rb +349 -0
- data/lib/fluent/plugin_helper/thread.rb +179 -0
- data/lib/fluent/plugin_helper/timer.rb +92 -0
- data/lib/fluent/plugin_helper.rb +73 -0
- data/lib/fluent/plugin_id.rb +80 -0
- data/lib/fluent/process.rb +3 -489
- data/lib/fluent/registry.rb +52 -10
- data/lib/fluent/root_agent.rb +204 -42
- data/lib/fluent/supervisor.rb +597 -359
- data/lib/fluent/system_config.rb +131 -42
- data/lib/fluent/test/base.rb +6 -54
- data/lib/fluent/test/driver/base.rb +224 -0
- data/lib/fluent/test/driver/base_owned.rb +70 -0
- data/lib/fluent/test/driver/base_owner.rb +135 -0
- data/lib/fluent/test/driver/event_feeder.rb +98 -0
- data/lib/fluent/test/driver/filter.rb +57 -0
- data/lib/fluent/test/driver/formatter.rb +30 -0
- data/lib/fluent/test/driver/input.rb +31 -0
- data/lib/fluent/test/driver/multi_output.rb +53 -0
- data/lib/fluent/test/driver/output.rb +102 -0
- data/lib/fluent/test/driver/parser.rb +30 -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 +4 -1
- data/lib/fluent/test/helpers.rb +58 -10
- data/lib/fluent/test/input_test.rb +27 -19
- data/lib/fluent/test/log.rb +79 -0
- data/lib/fluent/test/output_test.rb +28 -39
- data/lib/fluent/test/parser_test.rb +3 -1
- data/lib/fluent/test/startup_shutdown.rb +46 -0
- data/lib/fluent/test.rb +33 -1
- data/lib/fluent/time.rb +450 -1
- data/lib/fluent/timezone.rb +27 -3
- data/lib/fluent/{status.rb → unique_id.rb} +15 -24
- data/lib/fluent/version.rb +1 -1
- data/lib/fluent/winsvc.rb +85 -0
- data/templates/new_gem/Gemfile +3 -0
- data/templates/new_gem/README.md.erb +43 -0
- data/templates/new_gem/Rakefile +13 -0
- data/templates/new_gem/fluent-plugin.gemspec.erb +27 -0
- data/templates/new_gem/lib/fluent/plugin/filter.rb.erb +14 -0
- data/templates/new_gem/lib/fluent/plugin/formatter.rb.erb +14 -0
- data/templates/new_gem/lib/fluent/plugin/input.rb.erb +11 -0
- data/templates/new_gem/lib/fluent/plugin/output.rb.erb +11 -0
- data/templates/new_gem/lib/fluent/plugin/parser.rb.erb +15 -0
- data/templates/new_gem/test/helper.rb.erb +8 -0
- data/templates/new_gem/test/plugin/test_filter.rb.erb +18 -0
- data/templates/new_gem/test/plugin/test_formatter.rb.erb +18 -0
- data/templates/new_gem/test/plugin/test_input.rb.erb +18 -0
- data/templates/new_gem/test/plugin/test_output.rb.erb +18 -0
- data/templates/new_gem/test/plugin/test_parser.rb.erb +18 -0
- data/templates/plugin_config_formatter/param.md-compact.erb +25 -0
- data/templates/plugin_config_formatter/param.md.erb +34 -0
- data/templates/plugin_config_formatter/section.md.erb +12 -0
- data/test/command/test_binlog_reader.rb +346 -0
- data/test/command/test_ca_generate.rb +70 -0
- data/test/command/test_fluentd.rb +901 -0
- data/test/command/test_plugin_config_formatter.rb +276 -0
- data/test/command/test_plugin_generator.rb +92 -0
- data/test/compat/test_calls_super.rb +166 -0
- data/test/compat/test_parser.rb +92 -0
- data/test/config/test_config_parser.rb +126 -2
- data/test/config/test_configurable.rb +946 -187
- data/test/config/test_configure_proxy.rb +424 -74
- data/test/config/test_dsl.rb +11 -11
- data/test/config/test_element.rb +500 -0
- data/test/config/test_literal_parser.rb +8 -0
- data/test/config/test_plugin_configuration.rb +56 -0
- data/test/config/test_section.rb +79 -7
- data/test/config/test_system_config.rb +122 -35
- data/test/config/test_types.rb +38 -0
- data/test/counter/test_client.rb +559 -0
- data/test/counter/test_error.rb +44 -0
- data/test/counter/test_mutex_hash.rb +179 -0
- data/test/counter/test_server.rb +589 -0
- data/test/counter/test_store.rb +258 -0
- data/test/counter/test_validator.rb +137 -0
- data/test/helper.rb +89 -6
- data/test/helpers/fuzzy_assert.rb +89 -0
- data/test/plugin/test_bare_output.rb +118 -0
- data/test/plugin/test_base.rb +115 -0
- data/test/plugin/test_buf_file.rb +823 -460
- data/test/plugin/test_buf_memory.rb +32 -194
- data/test/plugin/test_buffer.rb +1233 -0
- data/test/plugin/test_buffer_chunk.rb +198 -0
- data/test/plugin/test_buffer_file_chunk.rb +844 -0
- data/test/plugin/test_buffer_memory_chunk.rb +338 -0
- data/test/plugin/test_compressable.rb +84 -0
- data/test/plugin/test_filter.rb +357 -0
- data/test/plugin/test_filter_grep.rb +540 -29
- data/test/plugin/test_filter_parser.rb +439 -452
- data/test/plugin/test_filter_record_transformer.rb +123 -166
- data/test/plugin/test_filter_stdout.rb +160 -72
- data/test/plugin/test_formatter_csv.rb +111 -0
- data/test/plugin/test_formatter_hash.rb +35 -0
- data/test/plugin/test_formatter_json.rb +51 -0
- data/test/plugin/test_formatter_ltsv.rb +62 -0
- data/test/plugin/test_formatter_msgpack.rb +28 -0
- data/test/plugin/test_formatter_out_file.rb +95 -0
- data/test/plugin/test_formatter_single_value.rb +38 -0
- data/test/plugin/test_formatter_tsv.rb +68 -0
- data/test/plugin/test_in_debug_agent.rb +24 -1
- data/test/plugin/test_in_dummy.rb +111 -18
- data/test/plugin/test_in_exec.rb +200 -113
- data/test/plugin/test_in_forward.rb +990 -387
- data/test/plugin/test_in_gc_stat.rb +10 -8
- data/test/plugin/test_in_http.rb +600 -224
- data/test/plugin/test_in_monitor_agent.rb +690 -0
- data/test/plugin/test_in_object_space.rb +24 -8
- data/test/plugin/test_in_syslog.rb +154 -215
- data/test/plugin/test_in_tail.rb +1006 -707
- data/test/plugin/test_in_tcp.rb +125 -48
- data/test/plugin/test_in_udp.rb +204 -63
- data/test/plugin/{test_in_stream.rb → test_in_unix.rb} +14 -13
- data/test/plugin/test_input.rb +126 -0
- data/test/plugin/test_metadata.rb +89 -0
- data/test/plugin/test_multi_output.rb +180 -0
- data/test/plugin/test_out_copy.rb +117 -112
- data/test/plugin/test_out_exec.rb +258 -53
- data/test/plugin/test_out_exec_filter.rb +538 -115
- data/test/plugin/test_out_file.rb +865 -178
- data/test/plugin/test_out_forward.rb +998 -210
- data/test/plugin/test_out_null.rb +105 -0
- data/test/plugin/test_out_relabel.rb +28 -0
- data/test/plugin/test_out_roundrobin.rb +36 -29
- data/test/plugin/test_out_secondary_file.rb +458 -0
- data/test/plugin/test_out_stdout.rb +135 -37
- data/test/plugin/test_out_stream.rb +18 -0
- data/test/plugin/test_output.rb +984 -0
- data/test/plugin/test_output_as_buffered.rb +2021 -0
- data/test/plugin/test_output_as_buffered_backup.rb +312 -0
- data/test/plugin/test_output_as_buffered_compress.rb +165 -0
- data/test/plugin/test_output_as_buffered_overflow.rb +250 -0
- data/test/plugin/test_output_as_buffered_retries.rb +911 -0
- data/test/plugin/test_output_as_buffered_secondary.rb +874 -0
- data/test/plugin/test_output_as_standard.rb +374 -0
- data/test/plugin/test_owned_by.rb +35 -0
- data/test/plugin/test_parser.rb +359 -0
- data/test/plugin/test_parser_apache.rb +42 -0
- data/test/plugin/test_parser_apache2.rb +47 -0
- data/test/plugin/test_parser_apache_error.rb +45 -0
- data/test/plugin/test_parser_csv.rb +103 -0
- data/test/plugin/test_parser_json.rb +138 -0
- data/test/plugin/test_parser_labeled_tsv.rb +145 -0
- data/test/plugin/test_parser_multiline.rb +100 -0
- data/test/plugin/test_parser_nginx.rb +88 -0
- data/test/plugin/test_parser_none.rb +52 -0
- data/test/plugin/test_parser_regexp.rb +289 -0
- data/test/plugin/test_parser_syslog.rb +441 -0
- data/test/plugin/test_parser_tsv.rb +122 -0
- data/test/plugin/test_storage.rb +167 -0
- data/test/plugin/test_storage_local.rb +335 -0
- data/test/plugin_helper/data/cert/cert-key.pem +27 -0
- data/test/plugin_helper/data/cert/cert-with-no-newline.pem +19 -0
- data/test/plugin_helper/data/cert/cert.pem +19 -0
- data/test/plugin_helper/http_server/test_app.rb +65 -0
- data/test/plugin_helper/http_server/test_route.rb +32 -0
- data/test/plugin_helper/test_cert_option.rb +16 -0
- data/test/plugin_helper/test_child_process.rb +794 -0
- data/test/plugin_helper/test_compat_parameters.rb +353 -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_extract.rb +194 -0
- data/test/plugin_helper/test_formatter.rb +255 -0
- data/test/plugin_helper/test_http_server_helper.rb +205 -0
- data/test/plugin_helper/test_inject.rb +519 -0
- data/test/plugin_helper/test_parser.rb +264 -0
- data/test/plugin_helper/test_record_accessor.rb +197 -0
- data/test/plugin_helper/test_retry_state.rb +442 -0
- data/test/plugin_helper/test_server.rb +1714 -0
- data/test/plugin_helper/test_storage.rb +542 -0
- data/test/plugin_helper/test_thread.rb +164 -0
- data/test/plugin_helper/test_timer.rb +132 -0
- data/test/scripts/exec_script.rb +0 -6
- data/test/scripts/fluent/plugin/formatter1/formatter_test1.rb +7 -0
- data/test/scripts/fluent/plugin/formatter2/formatter_test2.rb +7 -0
- data/test/scripts/fluent/plugin/out_test.rb +23 -15
- data/test/scripts/fluent/plugin/out_test2.rb +80 -0
- data/test/test_clock.rb +164 -0
- data/test/test_config.rb +16 -7
- data/test/test_configdsl.rb +2 -2
- data/test/test_event.rb +360 -13
- data/test/test_event_router.rb +108 -11
- data/test/test_event_time.rb +199 -0
- data/test/test_filter.rb +48 -6
- data/test/test_formatter.rb +11 -391
- data/test/test_input.rb +1 -1
- data/test/test_log.rb +591 -31
- data/test/test_mixin.rb +1 -1
- data/test/test_output.rb +121 -185
- data/test/test_plugin.rb +251 -0
- data/test/test_plugin_classes.rb +177 -10
- data/test/test_plugin_helper.rb +81 -0
- data/test/test_plugin_id.rb +101 -0
- data/test/test_process.rb +8 -42
- data/test/test_root_agent.rb +766 -21
- data/test/test_supervisor.rb +481 -0
- data/test/test_test_drivers.rb +135 -0
- data/test/test_time_formatter.rb +282 -0
- data/test/test_time_parser.rb +231 -0
- data/test/test_unique_id.rb +47 -0
- metadata +454 -60
- data/COPYING +0 -14
- data/ChangeLog +0 -666
- data/lib/fluent/buffer.rb +0 -365
- data/lib/fluent/plugin/in_status.rb +0 -76
- data/test/plugin/test_in_status.rb +0 -38
- data/test/test_buffer.rb +0 -624
- data/test/test_parser.rb +0 -1305
@@ -14,215 +14,393 @@
|
|
14
14
|
# limitations under the License.
|
15
15
|
#
|
16
16
|
|
17
|
-
require 'base64'
|
18
|
-
require 'socket'
|
19
|
-
require 'fileutils'
|
20
|
-
|
21
|
-
require 'cool.io'
|
22
|
-
|
23
17
|
require 'fluent/output'
|
24
18
|
require 'fluent/config/error'
|
19
|
+
require 'fluent/clock'
|
20
|
+
require 'base64'
|
25
21
|
|
26
|
-
|
27
|
-
class ForwardOutputError < StandardError
|
28
|
-
end
|
22
|
+
require 'fluent/compat/socket_util'
|
29
23
|
|
30
|
-
|
31
|
-
|
24
|
+
module Fluent::Plugin
|
25
|
+
class ForwardOutput < Output
|
26
|
+
class Error < StandardError; end
|
27
|
+
class NoNodesAvailable < Error; end
|
28
|
+
class ConnectionClosedError < Error; end
|
32
29
|
|
33
|
-
|
34
|
-
end
|
30
|
+
Fluent::Plugin.register_output('forward', self)
|
35
31
|
|
36
|
-
|
37
|
-
end
|
32
|
+
helpers :socket, :server, :timer, :thread, :compat_parameters
|
38
33
|
|
39
|
-
|
40
|
-
Plugin.register_output('forward', self)
|
34
|
+
LISTEN_PORT = 24224
|
41
35
|
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
@nodes = [] #=> [Node]
|
46
|
-
end
|
36
|
+
desc 'The transport protocol.'
|
37
|
+
config_param :transport, :enum, list: [:tcp, :tls], default: :tcp
|
38
|
+
# TODO: TLS session cache/tickets
|
47
39
|
|
48
40
|
desc 'The timeout time when sending event logs.'
|
49
41
|
config_param :send_timeout, :time, default: 60
|
50
|
-
desc 'The
|
51
|
-
config_param :
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
:udp
|
57
|
-
when 'none'
|
58
|
-
:none
|
59
|
-
else
|
60
|
-
raise ConfigError, "forward output heartbeat type should be 'tcp', 'udp', or 'none'"
|
61
|
-
end
|
62
|
-
end
|
42
|
+
desc 'The timeout time for socket connect'
|
43
|
+
config_param :connect_timeout, :time, default: nil
|
44
|
+
# TODO: add linger_timeout, recv_timeout
|
45
|
+
|
46
|
+
desc 'The protocol to use for heartbeats (default is the same with "transport").'
|
47
|
+
config_param :heartbeat_type, :enum, list: [:transport, :tcp, :udp, :none], default: :transport
|
63
48
|
desc 'The interval of the heartbeat packer.'
|
64
49
|
config_param :heartbeat_interval, :time, default: 1
|
65
50
|
desc 'The wait time before accepting a server fault recovery.'
|
66
51
|
config_param :recover_wait, :time, default: 10
|
67
52
|
desc 'The hard timeout used to detect server failure.'
|
68
53
|
config_param :hard_timeout, :time, default: 60
|
69
|
-
desc 'Set TTL to expire DNS cache in seconds.'
|
70
|
-
config_param :expire_dns_cache, :time, default: nil # 0 means disable cache
|
71
54
|
desc 'The threshold parameter used to detect server faults.'
|
72
55
|
config_param :phi_threshold, :integer, default: 16
|
73
56
|
desc 'Use the "Phi accrual failure detector" to detect server failure.'
|
74
57
|
config_param :phi_failure_detector, :bool, default: true
|
75
58
|
|
76
|
-
# if any options added that requires extended forward api, fix @extend_internal_protocol
|
77
|
-
|
78
59
|
desc 'Change the protocol to at-least-once.'
|
79
60
|
config_param :require_ack_response, :bool, default: false # require in_forward to respond with ack
|
80
|
-
|
81
|
-
|
61
|
+
|
62
|
+
## The reason of default value of :ack_response_timeout:
|
82
63
|
# Linux default tcp_syn_retries is 5 (in many environment)
|
83
64
|
# 3 + 6 + 12 + 24 + 48 + 96 -> 189 (sec)
|
65
|
+
desc 'This option is used when require_ack_response is true.'
|
66
|
+
config_param :ack_response_timeout, :time, default: 190
|
67
|
+
|
68
|
+
desc 'The interval while reading data from server'
|
69
|
+
config_param :read_interval_msec, :integer, default: 50 # 50ms
|
70
|
+
desc 'Reading data size from server'
|
71
|
+
config_param :read_length, :size, default: 512 # 512bytes
|
72
|
+
|
73
|
+
desc 'Set TTL to expire DNS cache in seconds.'
|
74
|
+
config_param :expire_dns_cache, :time, default: nil # 0 means disable cache
|
84
75
|
desc 'Enable client-side DNS round robin.'
|
85
76
|
config_param :dns_round_robin, :bool, default: false # heartbeat_type 'udp' is not available for this
|
86
77
|
|
78
|
+
desc 'Ignore DNS resolution and errors at startup time.'
|
79
|
+
config_param :ignore_network_errors_at_startup, :bool, default: false
|
80
|
+
|
81
|
+
desc 'Verify that a connection can be made with one of out_forward nodes at the time of startup.'
|
82
|
+
config_param :verify_connection_at_startup, :bool, default: false
|
83
|
+
|
84
|
+
desc 'Compress buffered data.'
|
85
|
+
config_param :compress, :enum, list: [:text, :gzip], default: :text
|
86
|
+
|
87
|
+
desc 'The default version of TLS transport.'
|
88
|
+
config_param :tls_version, :enum, list: Fluent::PluginHelper::Socket::TLS_SUPPORTED_VERSIONS, default: Fluent::PluginHelper::Socket::TLS_DEFAULT_VERSION
|
89
|
+
desc 'The cipher configuration of TLS transport.'
|
90
|
+
config_param :tls_ciphers, :string, default: Fluent::PluginHelper::Socket::CIPHERS_DEFAULT
|
91
|
+
desc 'Skip all verification of certificates or not.'
|
92
|
+
config_param :tls_insecure_mode, :bool, default: false
|
93
|
+
desc 'Allow self signed certificates or not.'
|
94
|
+
config_param :tls_allow_self_signed_cert, :bool, default: false
|
95
|
+
desc 'Verify hostname of servers and certificates or not in TLS transport.'
|
96
|
+
config_param :tls_verify_hostname, :bool, default: true
|
97
|
+
desc 'The additional CA certificate path for TLS.'
|
98
|
+
config_param :tls_ca_cert_path, :array, value_type: :string, default: nil
|
99
|
+
desc 'The additional certificate path for TLS.'
|
100
|
+
config_param :tls_cert_path, :array, value_type: :string, default: nil
|
101
|
+
desc 'The client certificate path for TLS.'
|
102
|
+
config_param :tls_client_cert_path, :string, default: nil
|
103
|
+
desc 'The client private key path for TLS.'
|
104
|
+
config_param :tls_client_private_key_path, :string, default: nil
|
105
|
+
desc 'The client private key passphrase for TLS.'
|
106
|
+
config_param :tls_client_private_key_passphrase, :string, default: nil, secret: true
|
107
|
+
desc "Enable keepalive connection."
|
108
|
+
config_param :keepalive, :bool, default: false
|
109
|
+
desc "Expired time of keepalive. Default value is nil, which means to keep connection as long as possible"
|
110
|
+
config_param :keepalive_timeout, :time, default: nil
|
111
|
+
|
112
|
+
config_section :security, required: false, multi: false do
|
113
|
+
desc 'The hostname'
|
114
|
+
config_param :self_hostname, :string
|
115
|
+
desc 'Shared key for authentication'
|
116
|
+
config_param :shared_key, :string, secret: true
|
117
|
+
end
|
118
|
+
|
119
|
+
config_section :server, param_name: :servers do
|
120
|
+
desc "The IP address or host name of the server."
|
121
|
+
config_param :host, :string
|
122
|
+
desc "The name of the server. Used for logging and certificate verification in TLS transport (when host is address)."
|
123
|
+
config_param :name, :string, default: nil
|
124
|
+
desc "The port number of the host."
|
125
|
+
config_param :port, :integer, default: LISTEN_PORT
|
126
|
+
desc "The shared key per server."
|
127
|
+
config_param :shared_key, :string, default: nil, secret: true
|
128
|
+
desc "The username for authentication."
|
129
|
+
config_param :username, :string, default: ''
|
130
|
+
desc "The password for authentication."
|
131
|
+
config_param :password, :string, default: '', secret: true
|
132
|
+
desc "Marks a node as the standby node for an Active-Standby model between Fluentd nodes."
|
133
|
+
config_param :standby, :bool, default: false
|
134
|
+
desc "The load balancing weight."
|
135
|
+
config_param :weight, :integer, default: 60
|
136
|
+
end
|
137
|
+
|
87
138
|
attr_reader :nodes
|
88
139
|
|
89
|
-
config_param :port, :integer, default:
|
90
|
-
config_param :host, :string, default: nil,
|
140
|
+
config_param :port, :integer, default: LISTEN_PORT, obsoleted: "User <server> section instead."
|
141
|
+
config_param :host, :string, default: nil, obsoleted: "Use <server> section instead."
|
91
142
|
|
92
|
-
|
143
|
+
config_section :buffer do
|
144
|
+
config_set_default :chunk_keys, ["tag"]
|
145
|
+
end
|
146
|
+
|
147
|
+
attr_reader :read_interval, :recover_sample_size
|
148
|
+
|
149
|
+
def initialize
|
150
|
+
super
|
151
|
+
|
152
|
+
@nodes = [] #=> [Node]
|
153
|
+
@loop = nil
|
154
|
+
@thread = nil
|
155
|
+
|
156
|
+
@usock = nil
|
157
|
+
@sock_ack_waiting = nil
|
158
|
+
@sock_ack_waiting_mutex = nil
|
159
|
+
@keep_alive_watcher_interval = 5 # TODO
|
160
|
+
end
|
93
161
|
|
94
162
|
def configure(conf)
|
163
|
+
compat_parameters_convert(conf, :buffer, default_chunk_key: 'tag')
|
164
|
+
|
95
165
|
super
|
96
166
|
|
97
|
-
|
98
|
-
|
99
|
-
port = conf['port']
|
100
|
-
port = port ? port.to_i : DEFAULT_LISTEN_PORT
|
101
|
-
e = conf.add_element('server')
|
102
|
-
e['host'] = host
|
103
|
-
e['port'] = port.to_s
|
167
|
+
unless @chunk_key_tag
|
168
|
+
raise Fluent::ConfigError, "buffer chunk key must include 'tag' for forward output"
|
104
169
|
end
|
105
170
|
|
106
|
-
|
171
|
+
@read_interval = @read_interval_msec / 1000.0
|
172
|
+
@recover_sample_size = @recover_wait / @heartbeat_interval
|
107
173
|
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
false
|
113
|
-
end
|
174
|
+
if @heartbeat_type == :tcp
|
175
|
+
log.warn "'heartbeat_type tcp' is deprecated. use 'transport' instead."
|
176
|
+
@heartbeat_type = :transport
|
177
|
+
end
|
114
178
|
|
115
179
|
if @dns_round_robin
|
116
180
|
if @heartbeat_type == :udp
|
117
|
-
raise ConfigError, "forward output heartbeat type must be '
|
181
|
+
raise Fluent::ConfigError, "forward output heartbeat type must be 'transport' or 'none' to use dns_round_robin option"
|
118
182
|
end
|
119
183
|
end
|
120
184
|
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
185
|
+
if @transport == :tls
|
186
|
+
# socket helper adds CA cert or signed certificate to same cert store internally so unify it in this place.
|
187
|
+
if @tls_cert_path && !@tls_cert_path.empty?
|
188
|
+
@tls_ca_cert_path = @tls_cert_path
|
189
|
+
end
|
190
|
+
if @tls_ca_cert_path && !@tls_ca_cert_path.empty?
|
191
|
+
@tls_ca_cert_path.each do |path|
|
192
|
+
raise Fluent::ConfigError, "specified cert path does not exist:#{path}" unless File.exist?(path)
|
193
|
+
raise Fluent::ConfigError, "specified cert path is not readable:#{path}" unless File.readable?(path)
|
194
|
+
end
|
195
|
+
end
|
132
196
|
|
133
|
-
|
134
|
-
|
135
|
-
|
197
|
+
if @tls_insecure_mode
|
198
|
+
log.warn "TLS transport is configured in insecure way"
|
199
|
+
@tls_verify_hostname = false
|
200
|
+
@tls_allow_self_signed_cert = true
|
136
201
|
end
|
202
|
+
end
|
137
203
|
|
204
|
+
@servers.each do |server|
|
138
205
|
failure = FailureDetector.new(@heartbeat_interval, @hard_timeout, Time.now.to_i.to_f)
|
206
|
+
name = server.name || "#{server.host}:#{server.port}"
|
139
207
|
|
140
|
-
|
141
|
-
@phi_threshold, recover_sample_size, @expire_dns_cache, @phi_failure_detector, @dns_round_robin)
|
142
|
-
|
208
|
+
log.info "adding forwarding server '#{name}'", host: server.host, port: server.port, weight: server.weight, plugin_id: plugin_id
|
143
209
|
if @heartbeat_type == :none
|
144
|
-
@nodes << NoneHeartbeatNode.new(
|
210
|
+
@nodes << NoneHeartbeatNode.new(self, server, failure: failure, keepalive: @keepalive, keepalive_timeout: @keepalive_timeout)
|
145
211
|
else
|
146
|
-
|
212
|
+
node = Node.new(self, server, failure: failure, keepalive: @keepalive, keepalive_timeout: @keepalive_timeout)
|
213
|
+
begin
|
214
|
+
node.validate_host_resolution!
|
215
|
+
rescue => e
|
216
|
+
raise unless @ignore_network_errors_at_startup
|
217
|
+
log.warn "failed to resolve node name when configured", server: (server.name || server.host), error: e
|
218
|
+
node.disable!
|
219
|
+
end
|
220
|
+
@nodes << node
|
147
221
|
end
|
148
|
-
|
149
|
-
|
222
|
+
end
|
223
|
+
|
224
|
+
unless @as_secondary
|
225
|
+
if @compress == :gzip && @buffer.compress == :text
|
226
|
+
@buffer.compress = :gzip
|
227
|
+
elsif @compress == :text && @buffer.compress == :gzip
|
228
|
+
log.info "buffer is compressed. If you also want to save the bandwidth of a network, Add `compress` configuration in <match>"
|
229
|
+
end
|
230
|
+
end
|
150
231
|
|
151
232
|
if @nodes.empty?
|
152
|
-
raise ConfigError, "forward output plugin requires at least one <server> is required"
|
233
|
+
raise Fluent::ConfigError, "forward output plugin requires at least one <server> is required"
|
234
|
+
end
|
235
|
+
|
236
|
+
if !@keepalive && @keepalive_timeout
|
237
|
+
log.warn('The value of keepalive_timeout is ignored. if you want to use keepalive, please add `keepalive true` to your conf.')
|
153
238
|
end
|
239
|
+
|
240
|
+
raise Fluent::ConfigError, "ack_response_timeout must be a positive integer" if @ack_response_timeout < 1
|
241
|
+
end
|
242
|
+
|
243
|
+
def multi_workers_ready?
|
244
|
+
true
|
245
|
+
end
|
246
|
+
|
247
|
+
def prefer_delayed_commit
|
248
|
+
@require_ack_response
|
154
249
|
end
|
155
250
|
|
156
251
|
def start
|
157
252
|
super
|
158
253
|
|
254
|
+
# Output#start sets @delayed_commit_timeout by @buffer_config.delayed_commit_timeout
|
255
|
+
# But it should be overwritten by ack_response_timeout to rollback chunks after timeout
|
256
|
+
if @ack_response_timeout && @delayed_commit_timeout != @ack_response_timeout
|
257
|
+
log.info "delayed_commit_timeout is overwritten by ack_response_timeout"
|
258
|
+
@delayed_commit_timeout = @ack_response_timeout + 2 # minimum ack_reader IO.select interval is 1s
|
259
|
+
end
|
260
|
+
|
159
261
|
@rand_seed = Random.new.seed
|
160
262
|
rebuild_weight_array
|
161
263
|
@rr = 0
|
162
264
|
|
163
265
|
unless @heartbeat_type == :none
|
164
|
-
@loop = Coolio::Loop.new
|
165
|
-
|
166
266
|
if @heartbeat_type == :udp
|
167
|
-
|
168
|
-
@usock
|
169
|
-
|
170
|
-
|
171
|
-
|
267
|
+
@usock = socket_create_udp(@nodes.first.host, @nodes.first.port, nonblock: true)
|
268
|
+
server_create_udp(:out_forward_heartbeat_receiver, 0, socket: @usock, max_bytes: @read_length) do |data, sock|
|
269
|
+
sockaddr = Socket.pack_sockaddr_in(sock.remote_port, sock.remote_host)
|
270
|
+
on_heartbeat(sockaddr, data)
|
271
|
+
end
|
172
272
|
end
|
273
|
+
timer_execute(:out_forward_heartbeat_request, @heartbeat_interval, &method(:on_timer))
|
274
|
+
end
|
173
275
|
|
174
|
-
|
175
|
-
@
|
276
|
+
if @require_ack_response
|
277
|
+
@sock_ack_waiting_mutex = Mutex.new
|
278
|
+
@sock_ack_waiting = []
|
279
|
+
thread_create(:out_forward_receiving_ack, &method(:ack_reader))
|
280
|
+
end
|
176
281
|
|
177
|
-
|
282
|
+
if @verify_connection_at_startup
|
283
|
+
@nodes.each do |node|
|
284
|
+
begin
|
285
|
+
node.verify_connection
|
286
|
+
rescue StandardError => e
|
287
|
+
log.fatal "forward's connection setting error: #{e.message}"
|
288
|
+
raise Fluent::UnrecoverableError, e.message
|
289
|
+
end
|
290
|
+
end
|
178
291
|
end
|
179
|
-
end
|
180
292
|
|
181
|
-
|
182
|
-
|
183
|
-
if @loop
|
184
|
-
@loop.watchers.each {|w| w.detach }
|
185
|
-
@loop.stop
|
293
|
+
if @keepalive && @keepalive_timeout
|
294
|
+
timer_execute(:out_forward_keep_alived_socket_watcher, @keep_alive_watcher_interval, &method(:on_purge_obsolete_socks))
|
186
295
|
end
|
187
|
-
@thread.join if @thread
|
188
|
-
@usock.close if @usock
|
189
296
|
end
|
190
297
|
|
191
|
-
def
|
192
|
-
|
193
|
-
|
194
|
-
|
195
|
-
|
298
|
+
def close
|
299
|
+
if @usock
|
300
|
+
# close socket and ignore errors: this socket will not be used anyway.
|
301
|
+
@usock.close rescue nil
|
302
|
+
end
|
303
|
+
|
304
|
+
if @keepalive && @keepalive_timeout
|
305
|
+
@nodes.each(&:clear)
|
306
|
+
end
|
307
|
+
super
|
196
308
|
end
|
197
309
|
|
198
|
-
def
|
310
|
+
def write(chunk)
|
199
311
|
return if chunk.empty?
|
312
|
+
tag = chunk.metadata.tag
|
313
|
+
select_a_healthy_node{|node| node.send_data(tag, chunk) }
|
314
|
+
end
|
315
|
+
|
316
|
+
ACKWaitingSockInfo = Struct.new(:sock, :chunk_id, :chunk_id_base64, :node, :time, :timeout) do
|
317
|
+
def expired?(now)
|
318
|
+
time + timeout < now
|
319
|
+
end
|
320
|
+
end
|
200
321
|
|
322
|
+
def try_write(chunk)
|
323
|
+
log.trace "writing a chunk to destination", chunk_id: dump_unique_id_hex(chunk.unique_id)
|
324
|
+
if chunk.empty?
|
325
|
+
commit_write(chunk.unique_id)
|
326
|
+
return
|
327
|
+
end
|
328
|
+
tag = chunk.metadata.tag
|
329
|
+
sock, node = select_a_healthy_node{|n| n.send_data(tag, chunk) }
|
330
|
+
chunk_id_base64 = Base64.encode64(chunk.unique_id)
|
331
|
+
current_time = Fluent::Clock.now
|
332
|
+
info = ACKWaitingSockInfo.new(sock, chunk.unique_id, chunk_id_base64, node, current_time, @ack_response_timeout)
|
333
|
+
@sock_ack_waiting_mutex.synchronize do
|
334
|
+
@sock_ack_waiting << info
|
335
|
+
end
|
336
|
+
end
|
337
|
+
|
338
|
+
def select_a_healthy_node
|
201
339
|
error = nil
|
202
340
|
|
203
341
|
wlen = @weight_array.length
|
204
342
|
wlen.times do
|
205
343
|
@rr = (@rr + 1) % wlen
|
206
344
|
node = @weight_array[@rr]
|
345
|
+
next unless node.available?
|
207
346
|
|
208
|
-
|
209
|
-
|
210
|
-
|
211
|
-
|
212
|
-
|
213
|
-
|
214
|
-
error = $! # use the latest error
|
215
|
-
end
|
347
|
+
begin
|
348
|
+
ret = yield node
|
349
|
+
return ret, node
|
350
|
+
rescue
|
351
|
+
# for load balancing during detecting crashed servers
|
352
|
+
error = $! # use the latest error
|
216
353
|
end
|
217
354
|
end
|
218
355
|
|
219
|
-
if error
|
220
|
-
|
356
|
+
raise error if error
|
357
|
+
raise NoNodesAvailable, "no nodes are available"
|
358
|
+
end
|
359
|
+
|
360
|
+
def create_transfer_socket(host, port, hostname, &block)
|
361
|
+
case @transport
|
362
|
+
when :tls
|
363
|
+
socket_create_tls(
|
364
|
+
host, port,
|
365
|
+
version: @tls_version,
|
366
|
+
ciphers: @tls_ciphers,
|
367
|
+
insecure: @tls_insecure_mode,
|
368
|
+
verify_fqdn: @tls_verify_hostname,
|
369
|
+
fqdn: hostname,
|
370
|
+
allow_self_signed_cert: @tls_allow_self_signed_cert,
|
371
|
+
cert_paths: @tls_ca_cert_path,
|
372
|
+
cert_path: @tls_client_cert_path,
|
373
|
+
private_key_path: @tls_client_private_key_path,
|
374
|
+
private_key_passphrase: @tls_client_private_key_passphrase,
|
375
|
+
|
376
|
+
# Enabling SO_LINGER causes data loss on Windows
|
377
|
+
# https://github.com/fluent/fluentd/issues/1968
|
378
|
+
linger_timeout: Fluent.windows? ? nil : @send_timeout,
|
379
|
+
send_timeout: @send_timeout,
|
380
|
+
recv_timeout: @ack_response_timeout,
|
381
|
+
connect_timeout: @connect_timeout,
|
382
|
+
&block
|
383
|
+
)
|
384
|
+
when :tcp
|
385
|
+
socket_create_tcp(
|
386
|
+
host, port,
|
387
|
+
linger_timeout: @send_timeout,
|
388
|
+
send_timeout: @send_timeout,
|
389
|
+
recv_timeout: @ack_response_timeout,
|
390
|
+
connect_timeout: @connect_timeout,
|
391
|
+
&block
|
392
|
+
)
|
221
393
|
else
|
222
|
-
raise "
|
394
|
+
raise "BUG: unknown transport protocol #{@transport}"
|
223
395
|
end
|
224
396
|
end
|
225
397
|
|
398
|
+
# MessagePack FixArray length is 3
|
399
|
+
FORWARD_HEADER = [0x93].pack('C').freeze
|
400
|
+
def forward_header
|
401
|
+
FORWARD_HEADER
|
402
|
+
end
|
403
|
+
|
226
404
|
private
|
227
405
|
|
228
406
|
def rebuild_weight_array
|
@@ -250,6 +428,12 @@ module Fluent
|
|
250
428
|
end
|
251
429
|
|
252
430
|
weight_array = []
|
431
|
+
if regular_nodes.empty?
|
432
|
+
log.warn('No nodes are available')
|
433
|
+
@weight_array = weight_array
|
434
|
+
return @weight_array
|
435
|
+
end
|
436
|
+
|
253
437
|
gcd = regular_nodes.map {|n| n.weight }.inject(0) {|r,w| r.gcd(w) }
|
254
438
|
regular_nodes.each {|n|
|
255
439
|
(n.weight / gcd).times {
|
@@ -267,205 +451,321 @@ module Fluent
|
|
267
451
|
@weight_array = weight_array
|
268
452
|
end
|
269
453
|
|
270
|
-
|
271
|
-
|
272
|
-
|
273
|
-
|
274
|
-
|
275
|
-
|
276
|
-
|
277
|
-
|
278
|
-
|
454
|
+
def on_timer
|
455
|
+
@nodes.each {|n|
|
456
|
+
begin
|
457
|
+
log.trace "sending heartbeat", host: n.host, port: n.port, heartbeat_type: @heartbeat_type
|
458
|
+
n.usock = @usock if @usock
|
459
|
+
if n.send_heartbeat
|
460
|
+
rebuild_weight_array
|
461
|
+
end
|
462
|
+
rescue Errno::EAGAIN, Errno::EWOULDBLOCK, Errno::EINTR, Errno::ECONNREFUSED, Errno::ETIMEDOUT => e
|
463
|
+
log.debug "failed to send heartbeat packet", host: n.host, port: n.port, heartbeat_type: @heartbeat_type, error: e
|
464
|
+
rescue => e
|
465
|
+
log.debug "unexpected error happen during heartbeat", host: n.host, port: n.port, heartbeat_type: @heartbeat_type, error: e
|
466
|
+
end
|
467
|
+
if n.tick
|
468
|
+
rebuild_weight_array
|
469
|
+
end
|
470
|
+
}
|
471
|
+
end
|
472
|
+
|
473
|
+
def on_heartbeat(sockaddr, msg)
|
474
|
+
if node = @nodes.find {|n| n.sockaddr == sockaddr }
|
475
|
+
# log.trace "heartbeat arrived", name: node.name, host: node.host, port: node.port
|
476
|
+
if node.heartbeat
|
477
|
+
rebuild_weight_array
|
478
|
+
end
|
279
479
|
end
|
280
480
|
end
|
281
481
|
|
282
|
-
|
283
|
-
|
284
|
-
|
482
|
+
def on_purge_obsolete_socks
|
483
|
+
@nodes.each(&:purge_obsolete_socks)
|
484
|
+
end
|
485
|
+
|
486
|
+
# return chunk id to be committed
|
487
|
+
def read_ack_from_sock(sock, unpacker)
|
285
488
|
begin
|
286
|
-
|
287
|
-
|
288
|
-
|
289
|
-
|
290
|
-
|
291
|
-
|
292
|
-
|
293
|
-
|
294
|
-
|
489
|
+
raw_data = sock.instance_of?(Fluent::PluginHelper::Socket::WrappedSocket::TLS) ? sock.readpartial(@read_length) : sock.recv(@read_length)
|
490
|
+
rescue Errno::ECONNRESET, EOFError # ECONNRESET for #recv, #EOFError for #readpartial
|
491
|
+
raw_data = ""
|
492
|
+
end
|
493
|
+
info = @sock_ack_waiting_mutex.synchronize{ @sock_ack_waiting.find{|i| i.sock == sock } }
|
494
|
+
|
495
|
+
# When connection is closed by remote host, socket is ready to read and #recv returns an empty string that means EOF.
|
496
|
+
# If this happens we assume the data wasn't delivered and retry it.
|
497
|
+
if raw_data.empty?
|
498
|
+
log.warn "destination node closed the connection. regard it as unavailable.", host: info.node.host, port: info.node.port
|
499
|
+
info.node.disable!
|
500
|
+
rollback_write(info.chunk_id, update_retry: false)
|
501
|
+
return nil
|
502
|
+
else
|
503
|
+
unpacker.feed(raw_data)
|
504
|
+
res = unpacker.read
|
505
|
+
log.trace "getting response from destination", host: info.node.host, port: info.node.port, chunk_id: dump_unique_id_hex(info.chunk_id), response: res
|
506
|
+
if res['ack'] != info.chunk_id_base64
|
507
|
+
# Some errors may have occurred when ack and chunk id is different, so send the chunk again.
|
508
|
+
log.warn "ack in response and chunk id in sent data are different", chunk_id: dump_unique_id_hex(info.chunk_id), ack: res['ack']
|
509
|
+
rollback_write(info.chunk_id, update_retry: false)
|
510
|
+
return nil
|
511
|
+
else
|
512
|
+
log.trace "got a correct ack response", chunk_id: dump_unique_id_hex(info.chunk_id)
|
513
|
+
end
|
514
|
+
return info.chunk_id
|
515
|
+
end
|
516
|
+
rescue => e
|
517
|
+
log.error "unexpected error while receiving ack message", error: e
|
518
|
+
log.error_backtrace
|
519
|
+
ensure
|
520
|
+
if @keepalive
|
521
|
+
info.node.socket_cache.dec_ref_by_value(info.sock)
|
522
|
+
else
|
523
|
+
info.sock.close_write rescue nil
|
524
|
+
info.sock.close rescue nil
|
525
|
+
end
|
526
|
+
|
527
|
+
@sock_ack_waiting_mutex.synchronize do
|
528
|
+
@sock_ack_waiting.delete(info)
|
295
529
|
end
|
296
530
|
end
|
297
531
|
|
298
|
-
def
|
299
|
-
|
300
|
-
|
301
|
-
|
302
|
-
|
303
|
-
|
304
|
-
opt = [@send_timeout.to_i, 0].pack('L!L!') # struct timeval
|
305
|
-
sock.setsockopt(Socket::SOL_SOCKET, Socket::SO_SNDTIMEO, opt)
|
306
|
-
|
307
|
-
# beginArray(2)
|
308
|
-
sock.write forward_header
|
309
|
-
|
310
|
-
# writeRaw(tag)
|
311
|
-
sock.write tag.to_msgpack # tag
|
312
|
-
|
313
|
-
# beginRaw(size)
|
314
|
-
sz = chunk.size
|
315
|
-
#if sz < 32
|
316
|
-
# # FixRaw
|
317
|
-
# sock.write [0xa0 | sz].pack('C')
|
318
|
-
#elsif sz < 65536
|
319
|
-
# # raw 16
|
320
|
-
# sock.write [0xda, sz].pack('Cn')
|
321
|
-
#else
|
322
|
-
# raw 32
|
323
|
-
sock.write [0xdb, sz].pack('CN')
|
324
|
-
#end
|
325
|
-
|
326
|
-
# writeRawBody(packed_es)
|
327
|
-
chunk.write_to(sock)
|
328
|
-
|
329
|
-
if @extend_internal_protocol
|
330
|
-
option = {}
|
331
|
-
option['chunk'] = Base64.encode64(chunk.unique_id) if @require_ack_response
|
332
|
-
sock.write option.to_msgpack
|
333
|
-
|
334
|
-
if @require_ack_response && @ack_response_timeout > 0
|
335
|
-
# Waiting for a response here results in a decrease of throughput because a chunk queue is locked.
|
336
|
-
# To avoid a decrease of troughput, it is necessary to prepare a list of chunks that wait for responses
|
337
|
-
# and process them asynchronously.
|
338
|
-
if IO.select([sock], nil, nil, @ack_response_timeout)
|
339
|
-
raw_data = sock.recv(1024)
|
340
|
-
|
341
|
-
# When connection is closed by remote host, socket is ready to read and #recv returns an empty string that means EOF.
|
342
|
-
# If this happens we assume the data wasn't delivered and retry it.
|
343
|
-
if raw_data.empty?
|
344
|
-
@log.warn "node #{node.host}:#{node.port} closed the connection. regard it as unavailable."
|
345
|
-
node.disable!
|
346
|
-
raise ForwardOutputConnectionClosedError, "node #{node.host}:#{node.port} closed connection"
|
347
|
-
else
|
348
|
-
# Serialization type of the response is same as sent data.
|
349
|
-
res = MessagePack.unpack(raw_data)
|
532
|
+
def ack_reader
|
533
|
+
select_interval = if @delayed_commit_timeout > 3
|
534
|
+
1
|
535
|
+
else
|
536
|
+
@delayed_commit_timeout / 3.0
|
537
|
+
end
|
350
538
|
|
351
|
-
|
352
|
-
|
353
|
-
|
539
|
+
unpacker = Fluent::Engine.msgpack_unpacker
|
540
|
+
|
541
|
+
while thread_current_running?
|
542
|
+
now = Fluent::Clock.now
|
543
|
+
sockets = []
|
544
|
+
begin
|
545
|
+
@sock_ack_waiting_mutex.synchronize do
|
546
|
+
new_list = []
|
547
|
+
@sock_ack_waiting.each do |info|
|
548
|
+
if info.expired?(now)
|
549
|
+
# There are 2 types of cases when no response has been received from socket:
|
550
|
+
# (1) the node does not support sending responses
|
551
|
+
# (2) the node does support sending response but responses have not arrived for some reasons.
|
552
|
+
log.warn "no response from node. regard it as unavailable.", host: info.node.host, port: info.node.port
|
553
|
+
info.node.disable!
|
554
|
+
if @keepalive
|
555
|
+
info.node.socket_cache.revoke_by_value(info.sock)
|
354
556
|
end
|
557
|
+
info.sock.close rescue nil
|
558
|
+
rollback_write(info.chunk_id, update_retry: false)
|
559
|
+
else
|
560
|
+
sockets << info.sock
|
561
|
+
new_list << info
|
355
562
|
end
|
356
|
-
|
357
|
-
else
|
358
|
-
# IO.select returns nil on timeout.
|
359
|
-
# There are 2 types of cases when no response has been received:
|
360
|
-
# (1) the node does not support sending responses
|
361
|
-
# (2) the node does support sending response but responses have not arrived for some reasons.
|
362
|
-
@log.warn "no response from #{node.host}:#{node.port}. regard it as unavailable."
|
363
|
-
node.disable!
|
364
|
-
raise ForwardOutputACKTimeoutError, "node #{node.host}:#{node.port} does not return ACK"
|
365
563
|
end
|
564
|
+
@sock_ack_waiting = new_list
|
366
565
|
end
|
367
|
-
end
|
368
566
|
|
369
|
-
|
370
|
-
|
371
|
-
|
372
|
-
|
567
|
+
readable_sockets, _, _ = IO.select(sockets, nil, nil, select_interval)
|
568
|
+
next unless readable_sockets
|
569
|
+
|
570
|
+
readable_sockets.each do |sock|
|
571
|
+
chunk_id = read_ack_from_sock(sock, unpacker)
|
572
|
+
commit_write(chunk_id) if chunk_id
|
573
|
+
end
|
574
|
+
rescue => e
|
575
|
+
log.error "unexpected error while receiving ack", error: e
|
576
|
+
log.error_backtrace
|
577
|
+
end
|
373
578
|
end
|
374
579
|
end
|
375
580
|
|
376
|
-
|
377
|
-
|
378
|
-
|
379
|
-
|
581
|
+
class Node
|
582
|
+
class SocketCache
|
583
|
+
TimedSocket = Struct.new(:timeout, :sock, :ref)
|
584
|
+
|
585
|
+
def initialize(timeout, log)
|
586
|
+
@log = log
|
587
|
+
@timeout = timeout
|
588
|
+
@active_socks = {}
|
589
|
+
@inactive_socks = {}
|
590
|
+
@mutex = Mutex.new
|
591
|
+
end
|
380
592
|
|
381
|
-
|
382
|
-
|
383
|
-
|
384
|
-
|
385
|
-
|
593
|
+
def revoke(key = Thread.current.object_id)
|
594
|
+
@mutex.synchronize do
|
595
|
+
if @active_socks[key]
|
596
|
+
@inactive_socks[key] = @active_socks.delete(key)
|
597
|
+
@inactive_socks[key].ref = 0
|
598
|
+
end
|
599
|
+
end
|
600
|
+
end
|
386
601
|
|
387
|
-
|
388
|
-
|
389
|
-
|
390
|
-
|
391
|
-
|
392
|
-
|
602
|
+
def clear
|
603
|
+
@mutex.synchronize do
|
604
|
+
@inactive_socks.values.each do |s|
|
605
|
+
s.sock.close rescue nil
|
606
|
+
end
|
607
|
+
@inactive_socks.clear
|
393
608
|
|
394
|
-
|
395
|
-
|
396
|
-
|
397
|
-
|
398
|
-
|
609
|
+
@active_socks.values.each do |s|
|
610
|
+
s.sock.close rescue nil
|
611
|
+
end
|
612
|
+
@active_socks.clear
|
613
|
+
end
|
399
614
|
end
|
400
|
-
|
401
|
-
|
402
|
-
|
403
|
-
|
404
|
-
|
405
|
-
|
615
|
+
|
616
|
+
def purge_obsolete_socks
|
617
|
+
@mutex.synchronize do
|
618
|
+
@inactive_socks.keys.each do |k|
|
619
|
+
# 0 means sockets stored in this class received all acks
|
620
|
+
if @inactive_socks[k].ref <= 0
|
621
|
+
s = @inactive_socks.delete(k)
|
622
|
+
s.sock.close rescue nil
|
623
|
+
@log.debug("purged obsolete socket #{s.sock}")
|
624
|
+
end
|
625
|
+
end
|
626
|
+
|
627
|
+
@active_socks.keys.each do |k|
|
628
|
+
if expired?(k) && @active_socks[k].ref <= 0
|
629
|
+
@inactive_socks[k] = @active_socks.delete(k)
|
630
|
+
end
|
631
|
+
end
|
406
632
|
end
|
407
|
-
rescue Errno::EAGAIN, Errno::EWOULDBLOCK, Errno::EINTR, Errno::ECONNREFUSED
|
408
|
-
# TODO log
|
409
|
-
log.debug "failed to send heartbeat packet to #{n.host}:#{n.port}", error: $!.to_s
|
410
633
|
end
|
411
|
-
}
|
412
|
-
end
|
413
634
|
|
414
|
-
|
415
|
-
|
416
|
-
|
417
|
-
|
418
|
-
|
419
|
-
|
635
|
+
# We expect that `yield` returns a unique object in this class
|
636
|
+
def fetch_or(key = Thread.current.object_id)
|
637
|
+
@mutex.synchronize do
|
638
|
+
unless @active_socks[key]
|
639
|
+
@active_socks[key] = TimedSocket.new(timeout, yield, 1)
|
640
|
+
@log.debug("connect new socket #{@active_socks[key]}")
|
641
|
+
return @active_socks[key].sock
|
642
|
+
end
|
420
643
|
|
421
|
-
|
422
|
-
|
423
|
-
|
424
|
-
|
425
|
-
|
644
|
+
if expired?(key)
|
645
|
+
# Do not close this socket here in case of it will be used by other place (e.g. wait for receiving ack)
|
646
|
+
@inactive_socks[key] = @active_socks.delete(key)
|
647
|
+
@log.debug("connection #{@inactive_socks[key]} is expired. reconnecting...")
|
648
|
+
@active_socks[key] = TimedSocket.new(timeout, yield, 0)
|
649
|
+
end
|
650
|
+
|
651
|
+
@active_socks[key].ref += 1
|
652
|
+
@active_socks[key].sock
|
653
|
+
end
|
426
654
|
end
|
427
|
-
host = addr[3]
|
428
|
-
port = addr[1]
|
429
|
-
sockaddr = Socket.pack_sockaddr_in(port, host)
|
430
|
-
@callback.call(sockaddr, msg)
|
431
|
-
rescue
|
432
|
-
# TODO log?
|
433
|
-
end
|
434
|
-
end
|
435
655
|
|
436
|
-
|
437
|
-
|
438
|
-
|
439
|
-
|
440
|
-
|
441
|
-
|
656
|
+
def dec_ref(key = Thread.current.object_id)
|
657
|
+
@mutex.synchronize do
|
658
|
+
if @active_socks[key]
|
659
|
+
@active_socks[key].ref -= 1
|
660
|
+
elsif @inactive_socks[key]
|
661
|
+
@inactive_socks[key].ref -= 1
|
662
|
+
else
|
663
|
+
@log.warn("Not found key for dec_ref: #{key}")
|
664
|
+
end
|
665
|
+
end
|
442
666
|
end
|
443
|
-
end
|
444
|
-
end
|
445
667
|
|
446
|
-
|
447
|
-
|
668
|
+
# This method is expected to be called in class which doesn't call #inc_ref
|
669
|
+
def dec_ref_by_value(val)
|
670
|
+
@mutex.synchronize do
|
671
|
+
sock = @active_socks.detect { |_, v| v.sock == val }
|
672
|
+
if sock
|
673
|
+
key = sock.first
|
674
|
+
@active_socks[key].ref -= 1
|
675
|
+
return
|
676
|
+
end
|
448
677
|
|
449
|
-
|
450
|
-
|
451
|
-
|
452
|
-
|
453
|
-
|
454
|
-
|
455
|
-
|
456
|
-
|
457
|
-
|
678
|
+
sock = @inactive_socks.detect { |_, v| v.sock == val }
|
679
|
+
if sock
|
680
|
+
key = sock.first
|
681
|
+
@inactive_socks[key].ref -= 1
|
682
|
+
return
|
683
|
+
else
|
684
|
+
@log.warn("Not found key for dec_ref_by_value: #{key}")
|
685
|
+
end
|
686
|
+
end
|
687
|
+
end
|
688
|
+
|
689
|
+
# This method is expected to be called in class which doesn't call #fetch_or
|
690
|
+
def revoke_by_value(val)
|
691
|
+
@mutex.synchronize do
|
692
|
+
sock = @active_socks.detect { |_, v| v.sock == val }
|
693
|
+
if sock
|
694
|
+
key = sock.first
|
695
|
+
@inactive_socks[key] = @active_socks.delete(key)
|
696
|
+
@inactive_socks[key].ref = 0
|
697
|
+
else
|
698
|
+
@log.debug("Not found for revoke_by_value :#{val}")
|
699
|
+
end
|
700
|
+
end
|
701
|
+
end
|
702
|
+
|
703
|
+
private
|
704
|
+
|
705
|
+
def timeout
|
706
|
+
@timeout && Time.now + @timeout
|
707
|
+
end
|
708
|
+
|
709
|
+
# This method is thread unsafe
|
710
|
+
def expired?(key = Thread.current.object_id)
|
711
|
+
@active_socks[key].timeout ? @active_socks[key].timeout < Time.now : false
|
712
|
+
end
|
713
|
+
end
|
714
|
+
|
715
|
+
# @param keepalive [Bool]
|
716
|
+
# @param keepalive_timeout [Integer | nil]
|
717
|
+
def initialize(sender, server, failure:, keepalive: false, keepalive_timeout: nil)
|
718
|
+
@sender = sender
|
719
|
+
@log = sender.log
|
720
|
+
@compress = sender.compress
|
721
|
+
|
722
|
+
@name = server.name
|
723
|
+
@host = server.host
|
724
|
+
@port = server.port
|
725
|
+
@weight = server.weight
|
726
|
+
@standby = server.standby
|
727
|
+
@failure = failure
|
458
728
|
@available = true
|
459
729
|
|
730
|
+
# @hostname is used for certificate verification & TLS SNI
|
731
|
+
host_is_hostname = !(IPAddr.new(@host) rescue false)
|
732
|
+
@hostname = case
|
733
|
+
when host_is_hostname then @host
|
734
|
+
when @name then @name
|
735
|
+
else nil
|
736
|
+
end
|
737
|
+
|
738
|
+
@usock = nil
|
739
|
+
|
740
|
+
@username = server.username
|
741
|
+
@password = server.password
|
742
|
+
@shared_key = server.shared_key || (sender.security && sender.security.shared_key) || ""
|
743
|
+
@shared_key_salt = generate_salt
|
744
|
+
|
745
|
+
@unpacker = Fluent::Engine.msgpack_unpacker
|
746
|
+
|
460
747
|
@resolved_host = nil
|
461
748
|
@resolved_time = 0
|
462
|
-
|
749
|
+
@resolved_once = false
|
750
|
+
|
751
|
+
@keepalive = keepalive
|
752
|
+
if @keepalive
|
753
|
+
@socket_cache = SocketCache.new(keepalive_timeout, @log)
|
754
|
+
end
|
463
755
|
end
|
464
756
|
|
465
|
-
|
466
|
-
|
757
|
+
attr_accessor :usock
|
758
|
+
|
759
|
+
attr_reader :name, :host, :port, :weight, :standby, :state
|
467
760
|
attr_reader :sockaddr # used by on_heartbeat
|
468
761
|
attr_reader :failure, :available # for test
|
762
|
+
attr_reader :socket_cache # for ack
|
763
|
+
|
764
|
+
RequestInfo = Struct.new(:state, :shared_key_nonce, :auth)
|
765
|
+
|
766
|
+
def validate_host_resolution!
|
767
|
+
resolved_host
|
768
|
+
end
|
469
769
|
|
470
770
|
def available?
|
471
771
|
@available
|
@@ -476,33 +776,173 @@ module Fluent
|
|
476
776
|
end
|
477
777
|
|
478
778
|
def standby?
|
479
|
-
@
|
779
|
+
@standby
|
780
|
+
end
|
781
|
+
|
782
|
+
def verify_connection
|
783
|
+
connect do |sock, ri|
|
784
|
+
if ri.state != :established
|
785
|
+
establish_connection(sock, ri)
|
786
|
+
raise if ri.state != :established
|
787
|
+
end
|
788
|
+
end
|
789
|
+
end
|
790
|
+
|
791
|
+
def establish_connection(sock, ri)
|
792
|
+
while available? && ri.state != :established
|
793
|
+
begin
|
794
|
+
# TODO: On Ruby 2.2 or earlier, read_nonblock doesn't work expectedly.
|
795
|
+
# We need rewrite around here using new socket/server plugin helper.
|
796
|
+
buf = sock.read_nonblock(@sender.read_length)
|
797
|
+
if buf.empty?
|
798
|
+
sleep @sender.read_interval
|
799
|
+
next
|
800
|
+
end
|
801
|
+
@unpacker.feed_each(buf) do |data|
|
802
|
+
on_read(sock, ri, data)
|
803
|
+
end
|
804
|
+
rescue IO::WaitReadable
|
805
|
+
# If the exception is Errno::EWOULDBLOCK or Errno::EAGAIN, it is extended by IO::WaitReadable.
|
806
|
+
# So IO::WaitReadable can be used to rescue the exceptions for retrying read_nonblock.
|
807
|
+
# https//docs.ruby-lang.org/en/2.3.0/IO.html#method-i-read_nonblock
|
808
|
+
sleep @sender.read_interval unless ri.state == :established
|
809
|
+
rescue SystemCallError => e
|
810
|
+
@log.warn "disconnected by error", host: @host, port: @port, error: e
|
811
|
+
disable!
|
812
|
+
break
|
813
|
+
rescue EOFError
|
814
|
+
@log.warn "disconnected", host: @host, port: @port
|
815
|
+
disable!
|
816
|
+
break
|
817
|
+
end
|
818
|
+
end
|
819
|
+
end
|
820
|
+
|
821
|
+
def send_data_actual(sock, tag, chunk)
|
822
|
+
unless available?
|
823
|
+
raise ConnectionClosedError, "failed to establish connection with node #{@name}"
|
824
|
+
end
|
825
|
+
|
826
|
+
option = { 'size' => chunk.size, 'compressed' => @compress }
|
827
|
+
option['chunk'] = Base64.encode64(chunk.unique_id) if @sender.require_ack_response
|
828
|
+
|
829
|
+
# https://github.com/fluent/fluentd/wiki/Forward-Protocol-Specification-v1#packedforward-mode
|
830
|
+
# out_forward always uses str32 type for entries.
|
831
|
+
# str16 can store only 64kbytes, and it should be much smaller than buffer chunk size.
|
832
|
+
|
833
|
+
tag = tag.dup.force_encoding(Encoding::UTF_8)
|
834
|
+
|
835
|
+
sock.write @sender.forward_header # array, size=3
|
836
|
+
sock.write tag.to_msgpack # 1. tag: String (str)
|
837
|
+
chunk.open(compressed: @compress) do |chunk_io|
|
838
|
+
entries = [0xdb, chunk_io.size].pack('CN')
|
839
|
+
sock.write entries.force_encoding(Encoding::UTF_8) # 2. entries: String (str32)
|
840
|
+
IO.copy_stream(chunk_io, sock) # writeRawBody(packed_es)
|
841
|
+
end
|
842
|
+
sock.write option.to_msgpack # 3. option: Hash(map)
|
843
|
+
|
844
|
+
# TODO: use bin32 for non-utf8 content(entries) when old msgpack-ruby (0.5.x or earlier) not supported
|
845
|
+
end
|
846
|
+
|
847
|
+
def send_data(tag, chunk)
|
848
|
+
sock, ri = connect
|
849
|
+
if ri.state != :established
|
850
|
+
establish_connection(sock, ri)
|
851
|
+
end
|
852
|
+
|
853
|
+
begin
|
854
|
+
send_data_actual(sock, tag, chunk)
|
855
|
+
rescue
|
856
|
+
if @keepalive
|
857
|
+
@socket_cache.revoke
|
858
|
+
else
|
859
|
+
sock.close rescue nil
|
860
|
+
end
|
861
|
+
raise
|
862
|
+
end
|
863
|
+
|
864
|
+
if @sender.require_ack_response
|
865
|
+
return sock # to read ACK from socket
|
866
|
+
end
|
867
|
+
|
868
|
+
if @keepalive
|
869
|
+
@socket_cache.dec_ref
|
870
|
+
else
|
871
|
+
sock.close_write rescue nil
|
872
|
+
sock.close rescue nil
|
873
|
+
end
|
874
|
+
heartbeat(false)
|
875
|
+
nil
|
876
|
+
end
|
877
|
+
|
878
|
+
def clear
|
879
|
+
@keepalive && @socket_cache.clear
|
880
|
+
end
|
881
|
+
|
882
|
+
def purge_obsolete_socks
|
883
|
+
unless @keepalive
|
884
|
+
raise "Don not call this method without keepalive option"
|
885
|
+
end
|
886
|
+
@socket_cache.purge_obsolete_socks
|
887
|
+
end
|
888
|
+
|
889
|
+
# FORWARD_TCP_HEARTBEAT_DATA = FORWARD_HEADER + ''.to_msgpack + [].to_msgpack
|
890
|
+
def send_heartbeat
|
891
|
+
begin
|
892
|
+
dest_addr = resolved_host
|
893
|
+
@resolved_once = true
|
894
|
+
rescue ::SocketError => e
|
895
|
+
if !@resolved_once && @sender.ignore_network_errors_at_startup
|
896
|
+
@log.warn "failed to resolve node name in heartbeating", server: @name || @host, error: e
|
897
|
+
return
|
898
|
+
end
|
899
|
+
raise
|
900
|
+
end
|
901
|
+
|
902
|
+
case @sender.heartbeat_type
|
903
|
+
when :transport
|
904
|
+
connect(dest_addr) do |sock|
|
905
|
+
## don't send any data to not cause a compatibility problem
|
906
|
+
# sock.write FORWARD_TCP_HEARTBEAT_DATA
|
907
|
+
|
908
|
+
# successful tcp connection establishment is considered as valid heartbeat.
|
909
|
+
# When heartbeat is succeeded after detached, return true. It rebuilds weight array.
|
910
|
+
heartbeat(true)
|
911
|
+
end
|
912
|
+
when :udp
|
913
|
+
@usock.send "\0", 0, Socket.pack_sockaddr_in(@port, resolved_host)
|
914
|
+
nil
|
915
|
+
when :none # :none doesn't use this class
|
916
|
+
raise "BUG: heartbeat_type none must not use Node"
|
917
|
+
else
|
918
|
+
raise "BUG: unknown heartbeat_type '#{@sender.heartbeat_type}'"
|
919
|
+
end
|
480
920
|
end
|
481
921
|
|
482
922
|
def resolved_host
|
483
|
-
case @
|
923
|
+
case @sender.expire_dns_cache
|
484
924
|
when 0
|
485
925
|
# cache is disabled
|
486
|
-
|
926
|
+
resolve_dns!
|
487
927
|
|
488
928
|
when nil
|
489
929
|
# persistent cache
|
490
|
-
|
930
|
+
@resolved_host ||= resolve_dns!
|
491
931
|
|
492
932
|
else
|
493
|
-
now = Engine.now
|
933
|
+
now = Fluent::Engine.now
|
494
934
|
rh = @resolved_host
|
495
|
-
if !rh || now - @resolved_time >= @
|
935
|
+
if !rh || now - @resolved_time >= @sender.expire_dns_cache
|
496
936
|
rh = @resolved_host = resolve_dns!
|
497
937
|
@resolved_time = now
|
498
938
|
end
|
499
|
-
|
939
|
+
rh
|
500
940
|
end
|
501
941
|
end
|
502
942
|
|
503
943
|
def resolve_dns!
|
504
944
|
addrinfo_list = Socket.getaddrinfo(@host, @port, nil, Socket::SOCK_STREAM)
|
505
|
-
addrinfo = @
|
945
|
+
addrinfo = @sender.dns_round_robin ? addrinfo_list.sample : addrinfo_list.first
|
506
946
|
@sockaddr = Socket.pack_sockaddr_in(addrinfo[1], addrinfo[3]) # used by on_heartbeat
|
507
947
|
addrinfo[3]
|
508
948
|
end
|
@@ -525,35 +965,152 @@ module Fluent
|
|
525
965
|
return true
|
526
966
|
end
|
527
967
|
|
528
|
-
if @
|
968
|
+
if @sender.phi_failure_detector
|
529
969
|
phi = @failure.phi(now)
|
530
|
-
|
531
|
-
|
532
|
-
@log.warn "detached forwarding server '#{@name}'", host: @host, port: @port, phi: phi
|
970
|
+
if phi > @sender.phi_threshold
|
971
|
+
@log.warn "detached forwarding server '#{@name}'", host: @host, port: @port, phi: phi, phi_threshold: @sender.phi_threshold
|
533
972
|
@available = false
|
534
973
|
@resolved_host = nil # expire cached host
|
535
974
|
@failure.clear
|
536
975
|
return true
|
537
976
|
end
|
538
977
|
end
|
539
|
-
|
978
|
+
false
|
540
979
|
end
|
541
980
|
|
542
981
|
def heartbeat(detect=true)
|
543
982
|
now = Time.now.to_f
|
544
983
|
@failure.add(now)
|
545
|
-
|
546
|
-
if detect && !@available && @failure.sample_size > @conf.recover_sample_size
|
984
|
+
if detect && !@available && @failure.sample_size > @sender.recover_sample_size
|
547
985
|
@available = true
|
548
986
|
@log.warn "recovered forwarding server '#{@name}'", host: @host, port: @port
|
549
|
-
|
987
|
+
true
|
550
988
|
else
|
551
|
-
|
989
|
+
nil
|
552
990
|
end
|
553
991
|
end
|
554
992
|
|
555
|
-
def
|
556
|
-
|
993
|
+
def generate_salt
|
994
|
+
SecureRandom.hex(16)
|
995
|
+
end
|
996
|
+
|
997
|
+
def check_helo(ri, message)
|
998
|
+
@log.debug "checking helo"
|
999
|
+
# ['HELO', options(hash)]
|
1000
|
+
unless message.size == 2 && message[0] == 'HELO'
|
1001
|
+
return false
|
1002
|
+
end
|
1003
|
+
opts = message[1] || {}
|
1004
|
+
# make shared_key_check failed (instead of error) if protocol version mismatch exist
|
1005
|
+
ri.shared_key_nonce = opts['nonce'] || ''
|
1006
|
+
ri.auth = opts['auth'] || ''
|
1007
|
+
true
|
1008
|
+
end
|
1009
|
+
|
1010
|
+
def generate_ping(ri)
|
1011
|
+
@log.debug "generating ping"
|
1012
|
+
# ['PING', self_hostname, sharedkey\_salt, sha512\_hex(sharedkey\_salt + self_hostname + nonce + shared_key),
|
1013
|
+
# username || '', sha512\_hex(auth\_salt + username + password) || '']
|
1014
|
+
shared_key_hexdigest = Digest::SHA512.new.update(@shared_key_salt)
|
1015
|
+
.update(@sender.security.self_hostname)
|
1016
|
+
.update(ri.shared_key_nonce)
|
1017
|
+
.update(@shared_key)
|
1018
|
+
.hexdigest
|
1019
|
+
ping = ['PING', @sender.security.self_hostname, @shared_key_salt, shared_key_hexdigest]
|
1020
|
+
if !ri.auth.empty?
|
1021
|
+
password_hexdigest = Digest::SHA512.new.update(ri.auth).update(@username).update(@password).hexdigest
|
1022
|
+
ping.push(@username, password_hexdigest)
|
1023
|
+
else
|
1024
|
+
ping.push('','')
|
1025
|
+
end
|
1026
|
+
ping
|
1027
|
+
end
|
1028
|
+
|
1029
|
+
def check_pong(ri, message)
|
1030
|
+
@log.debug "checking pong"
|
1031
|
+
# ['PONG', bool(authentication result), 'reason if authentication failed',
|
1032
|
+
# self_hostname, sha512\_hex(salt + self_hostname + nonce + sharedkey)]
|
1033
|
+
unless message.size == 5 && message[0] == 'PONG'
|
1034
|
+
return false, 'invalid format for PONG message'
|
1035
|
+
end
|
1036
|
+
_pong, auth_result, reason, hostname, shared_key_hexdigest = message
|
1037
|
+
|
1038
|
+
unless auth_result
|
1039
|
+
return false, 'authentication failed: ' + reason
|
1040
|
+
end
|
1041
|
+
|
1042
|
+
if hostname == @sender.security.self_hostname
|
1043
|
+
return false, 'same hostname between input and output: invalid configuration'
|
1044
|
+
end
|
1045
|
+
|
1046
|
+
clientside = Digest::SHA512.new.update(@shared_key_salt).update(hostname).update(ri.shared_key_nonce).update(@shared_key).hexdigest
|
1047
|
+
unless shared_key_hexdigest == clientside
|
1048
|
+
return false, 'shared key mismatch'
|
1049
|
+
end
|
1050
|
+
|
1051
|
+
return true, nil
|
1052
|
+
end
|
1053
|
+
|
1054
|
+
def on_read(sock, ri, data)
|
1055
|
+
@log.trace __callee__
|
1056
|
+
|
1057
|
+
case ri.state
|
1058
|
+
when :helo
|
1059
|
+
unless check_helo(ri, data)
|
1060
|
+
@log.warn "received invalid helo message from #{@name}"
|
1061
|
+
disable! # shutdown
|
1062
|
+
return
|
1063
|
+
end
|
1064
|
+
sock.write(generate_ping(ri).to_msgpack)
|
1065
|
+
ri.state = :pingpong
|
1066
|
+
when :pingpong
|
1067
|
+
succeeded, reason = check_pong(ri, data)
|
1068
|
+
unless succeeded
|
1069
|
+
@log.warn "connection refused to #{@name || @host}: #{reason}"
|
1070
|
+
disable! # shutdown
|
1071
|
+
return
|
1072
|
+
end
|
1073
|
+
ri.state = :established
|
1074
|
+
@log.debug "connection established", host: @host, port: @port
|
1075
|
+
else
|
1076
|
+
raise "BUG: unknown session state: #{ri.state}"
|
1077
|
+
end
|
1078
|
+
end
|
1079
|
+
|
1080
|
+
private
|
1081
|
+
|
1082
|
+
def connect(host = nil)
|
1083
|
+
socket, request_info =
|
1084
|
+
if @keepalive
|
1085
|
+
ri = RequestInfo.new(:established)
|
1086
|
+
sock = @socket_cache.fetch_or do
|
1087
|
+
s = @sender.create_transfer_socket(host || resolved_host, port, @hostname)
|
1088
|
+
ri = RequestInfo.new(@sender.security ? :helo : :established) # overwrite if new connection
|
1089
|
+
s
|
1090
|
+
end
|
1091
|
+
[sock, ri]
|
1092
|
+
else
|
1093
|
+
@log.debug('connect new socket')
|
1094
|
+
[@sender.create_transfer_socket(host || resolved_host, port, @hostname), RequestInfo.new(@sender.security ? :helo : :established)]
|
1095
|
+
end
|
1096
|
+
|
1097
|
+
if block_given?
|
1098
|
+
ret = nil
|
1099
|
+
begin
|
1100
|
+
ret = yield(socket, request_info)
|
1101
|
+
rescue
|
1102
|
+
@socket_cache.revoke if @keepalive
|
1103
|
+
raise
|
1104
|
+
else
|
1105
|
+
@socket_cache.dec_ref if @keepalive
|
1106
|
+
ensure
|
1107
|
+
socket.close unless @keepalive
|
1108
|
+
end
|
1109
|
+
|
1110
|
+
ret
|
1111
|
+
else
|
1112
|
+
[socket, request_info]
|
1113
|
+
end
|
557
1114
|
end
|
558
1115
|
end
|
559
1116
|
|
@@ -634,33 +1191,5 @@ module Fluent
|
|
634
1191
|
@last = 0
|
635
1192
|
end
|
636
1193
|
end
|
637
|
-
|
638
|
-
## TODO
|
639
|
-
#class RPC
|
640
|
-
# def initialize(this)
|
641
|
-
# @this = this
|
642
|
-
# end
|
643
|
-
#
|
644
|
-
# def list_nodes
|
645
|
-
# @this.nodes
|
646
|
-
# end
|
647
|
-
#
|
648
|
-
# def list_fault_nodes
|
649
|
-
# list_nodes.select {|n| !n.available? }
|
650
|
-
# end
|
651
|
-
#
|
652
|
-
# def list_available_nodes
|
653
|
-
# list_nodes.select {|n| n.available? }
|
654
|
-
# end
|
655
|
-
#
|
656
|
-
# def add_node(name, host, port, weight)
|
657
|
-
# end
|
658
|
-
#
|
659
|
-
# def recover_node(host, port)
|
660
|
-
# end
|
661
|
-
#
|
662
|
-
# def remove_node(host, port)
|
663
|
-
# end
|
664
|
-
#end
|
665
1194
|
end
|
666
1195
|
end
|