fluentd 1.14.4-x64-mingw-ucrt
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 +7 -0
- data/.deepsource.toml +13 -0
- data/.drone.yml +35 -0
- data/.github/ISSUE_TEMPLATE/bug_report.yaml +70 -0
- data/.github/ISSUE_TEMPLATE/config.yml +5 -0
- data/.github/ISSUE_TEMPLATE/feature_request.yaml +38 -0
- data/.github/ISSUE_TEMPLATE.md +17 -0
- data/.github/PULL_REQUEST_TEMPLATE.md +14 -0
- data/.github/workflows/issue-auto-closer.yml +12 -0
- data/.github/workflows/linux-test.yaml +36 -0
- data/.github/workflows/macos-test.yaml +30 -0
- data/.github/workflows/stale-actions.yml +22 -0
- data/.github/workflows/windows-test.yaml +46 -0
- data/.gitignore +30 -0
- data/.gitlab-ci.yml +103 -0
- data/ADOPTERS.md +5 -0
- data/AUTHORS +2 -0
- data/CHANGELOG.md +2409 -0
- data/CONTRIBUTING.md +45 -0
- data/GOVERNANCE.md +55 -0
- data/Gemfile +9 -0
- data/GithubWorkflow.md +78 -0
- data/LICENSE +202 -0
- data/MAINTAINERS.md +11 -0
- data/README.md +97 -0
- data/Rakefile +79 -0
- data/SECURITY.md +18 -0
- data/bin/fluent-binlog-reader +7 -0
- data/bin/fluent-ca-generate +6 -0
- data/bin/fluent-cap-ctl +7 -0
- data/bin/fluent-cat +5 -0
- data/bin/fluent-ctl +7 -0
- data/bin/fluent-debug +5 -0
- data/bin/fluent-gem +9 -0
- data/bin/fluent-plugin-config-format +5 -0
- data/bin/fluent-plugin-generate +5 -0
- data/bin/fluentd +15 -0
- data/code-of-conduct.md +3 -0
- data/docs/SECURITY_AUDIT.pdf +0 -0
- data/example/copy_roundrobin.conf +39 -0
- data/example/counter.conf +18 -0
- data/example/filter_stdout.conf +22 -0
- data/example/in_forward.conf +14 -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 +16 -0
- data/example/in_out_forward.conf +17 -0
- data/example/in_sample_blocks.conf +17 -0
- data/example/in_sample_with_compression.conf +23 -0
- data/example/in_syslog.conf +15 -0
- data/example/in_tail.conf +14 -0
- data/example/in_tcp.conf +13 -0
- data/example/in_udp.conf +13 -0
- data/example/logevents.conf +25 -0
- data/example/multi_filters.conf +61 -0
- data/example/out_copy.conf +20 -0
- data/example/out_exec_filter.conf +42 -0
- data/example/out_file.conf +13 -0
- data/example/out_forward.conf +35 -0
- 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_sd.conf +17 -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/sd.yaml +8 -0
- data/example/secondary_file.conf +42 -0
- data/example/suppress_config_dump.conf +7 -0
- data/example/v0_12_filter.conf +78 -0
- data/example/v1_literal_example.conf +36 -0
- data/example/worker_section.conf +36 -0
- data/fluent.conf +139 -0
- data/fluentd.gemspec +55 -0
- data/lib/fluent/agent.rb +168 -0
- data/lib/fluent/capability.rb +87 -0
- data/lib/fluent/clock.rb +66 -0
- data/lib/fluent/command/binlog_reader.rb +244 -0
- data/lib/fluent/command/bundler_injection.rb +45 -0
- data/lib/fluent/command/ca_generate.rb +184 -0
- data/lib/fluent/command/cap_ctl.rb +174 -0
- data/lib/fluent/command/cat.rb +365 -0
- data/lib/fluent/command/ctl.rb +177 -0
- data/lib/fluent/command/debug.rb +103 -0
- data/lib/fluent/command/fluentd.rb +374 -0
- data/lib/fluent/command/plugin_config_formatter.rb +308 -0
- data/lib/fluent/command/plugin_generator.rb +365 -0
- data/lib/fluent/compat/call_super_mixin.rb +76 -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 +721 -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/basic_parser.rb +123 -0
- data/lib/fluent/config/configure_proxy.rb +424 -0
- data/lib/fluent/config/dsl.rb +152 -0
- data/lib/fluent/config/element.rb +265 -0
- data/lib/fluent/config/error.rb +32 -0
- data/lib/fluent/config/literal_parser.rb +286 -0
- data/lib/fluent/config/parser.rb +107 -0
- data/lib/fluent/config/section.rb +272 -0
- data/lib/fluent/config/types.rb +249 -0
- data/lib/fluent/config/v1_parser.rb +192 -0
- data/lib/fluent/config.rb +76 -0
- data/lib/fluent/configurable.rb +201 -0
- data/lib/fluent/counter/base_socket.rb +44 -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/daemonizer.rb +88 -0
- data/lib/fluent/engine.rb +253 -0
- data/lib/fluent/env.rb +40 -0
- data/lib/fluent/error.rb +34 -0
- data/lib/fluent/event.rb +326 -0
- data/lib/fluent/event_router.rb +297 -0
- data/lib/fluent/ext_monitor_require.rb +28 -0
- data/lib/fluent/filter.rb +21 -0
- data/lib/fluent/fluent_log_event_router.rb +141 -0
- data/lib/fluent/formatter.rb +23 -0
- data/lib/fluent/input.rb +21 -0
- data/lib/fluent/label.rb +46 -0
- data/lib/fluent/load.rb +34 -0
- data/lib/fluent/log.rb +713 -0
- data/lib/fluent/match.rb +187 -0
- data/lib/fluent/mixin.rb +31 -0
- data/lib/fluent/msgpack_factory.rb +106 -0
- data/lib/fluent/oj_options.rb +62 -0
- data/lib/fluent/output.rb +29 -0
- data/lib/fluent/output_chain.rb +23 -0
- data/lib/fluent/parser.rb +23 -0
- data/lib/fluent/plugin/bare_output.rb +104 -0
- data/lib/fluent/plugin/base.rb +197 -0
- data/lib/fluent/plugin/buf_file.rb +213 -0
- data/lib/fluent/plugin/buf_file_single.rb +225 -0
- data/lib/fluent/plugin/buf_memory.rb +34 -0
- data/lib/fluent/plugin/buffer/chunk.rb +240 -0
- data/lib/fluent/plugin/buffer/file_chunk.rb +413 -0
- data/lib/fluent/plugin/buffer/file_single_chunk.rb +311 -0
- data/lib/fluent/plugin/buffer/memory_chunk.rb +91 -0
- data/lib/fluent/plugin/buffer.rb +918 -0
- data/lib/fluent/plugin/compressable.rb +96 -0
- data/lib/fluent/plugin/exec_util.rb +22 -0
- data/lib/fluent/plugin/file_util.rb +22 -0
- data/lib/fluent/plugin/file_wrapper.rb +187 -0
- data/lib/fluent/plugin/filter.rb +127 -0
- data/lib/fluent/plugin/filter_grep.rb +189 -0
- data/lib/fluent/plugin/filter_parser.rb +130 -0
- data/lib/fluent/plugin/filter_record_transformer.rb +324 -0
- data/lib/fluent/plugin/filter_stdout.rb +53 -0
- data/lib/fluent/plugin/formatter.rb +75 -0
- data/lib/fluent/plugin/formatter_csv.rb +78 -0
- data/lib/fluent/plugin/formatter_hash.rb +35 -0
- data/lib/fluent/plugin/formatter_json.rb +59 -0
- data/lib/fluent/plugin/formatter_ltsv.rb +44 -0
- data/lib/fluent/plugin/formatter_msgpack.rb +33 -0
- data/lib/fluent/plugin/formatter_out_file.rb +53 -0
- data/lib/fluent/plugin/formatter_single_value.rb +36 -0
- data/lib/fluent/plugin/formatter_stdout.rb +76 -0
- data/lib/fluent/plugin/formatter_tsv.rb +40 -0
- data/lib/fluent/plugin/in_debug_agent.rb +71 -0
- data/lib/fluent/plugin/in_dummy.rb +18 -0
- data/lib/fluent/plugin/in_exec.rb +110 -0
- data/lib/fluent/plugin/in_forward.rb +473 -0
- data/lib/fluent/plugin/in_gc_stat.rb +72 -0
- data/lib/fluent/plugin/in_http.rb +667 -0
- data/lib/fluent/plugin/in_monitor_agent.rb +412 -0
- data/lib/fluent/plugin/in_object_space.rb +93 -0
- data/lib/fluent/plugin/in_sample.rb +141 -0
- data/lib/fluent/plugin/in_syslog.rb +276 -0
- data/lib/fluent/plugin/in_tail/position_file.rb +269 -0
- data/lib/fluent/plugin/in_tail.rb +1228 -0
- data/lib/fluent/plugin/in_tcp.rb +181 -0
- data/lib/fluent/plugin/in_udp.rb +92 -0
- data/lib/fluent/plugin/in_unix.rb +195 -0
- data/lib/fluent/plugin/input.rb +75 -0
- data/lib/fluent/plugin/metrics.rb +119 -0
- data/lib/fluent/plugin/metrics_local.rb +96 -0
- data/lib/fluent/plugin/multi_output.rb +195 -0
- data/lib/fluent/plugin/out_copy.rb +120 -0
- data/lib/fluent/plugin/out_exec.rb +105 -0
- data/lib/fluent/plugin/out_exec_filter.rb +319 -0
- data/lib/fluent/plugin/out_file.rb +334 -0
- data/lib/fluent/plugin/out_forward/ack_handler.rb +161 -0
- data/lib/fluent/plugin/out_forward/connection_manager.rb +113 -0
- data/lib/fluent/plugin/out_forward/error.rb +28 -0
- data/lib/fluent/plugin/out_forward/failure_detector.rb +84 -0
- data/lib/fluent/plugin/out_forward/handshake_protocol.rb +125 -0
- data/lib/fluent/plugin/out_forward/load_balancer.rb +114 -0
- data/lib/fluent/plugin/out_forward/socket_cache.rb +140 -0
- data/lib/fluent/plugin/out_forward.rb +826 -0
- data/lib/fluent/plugin/out_http.rb +275 -0
- data/lib/fluent/plugin/out_null.rb +74 -0
- data/lib/fluent/plugin/out_relabel.rb +32 -0
- data/lib/fluent/plugin/out_roundrobin.rb +84 -0
- data/lib/fluent/plugin/out_secondary_file.rb +131 -0
- data/lib/fluent/plugin/out_stdout.rb +74 -0
- data/lib/fluent/plugin/out_stream.rb +130 -0
- data/lib/fluent/plugin/output.rb +1556 -0
- data/lib/fluent/plugin/owned_by_mixin.rb +42 -0
- data/lib/fluent/plugin/parser.rb +275 -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 +114 -0
- data/lib/fluent/plugin/parser_json.rb +96 -0
- data/lib/fluent/plugin/parser_ltsv.rb +51 -0
- data/lib/fluent/plugin/parser_msgpack.rb +50 -0
- data/lib/fluent/plugin/parser_multiline.rb +152 -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 +496 -0
- data/lib/fluent/plugin/parser_tsv.rb +42 -0
- data/lib/fluent/plugin/sd_file.rb +156 -0
- data/lib/fluent/plugin/sd_srv.rb +135 -0
- data/lib/fluent/plugin/sd_static.rb +58 -0
- data/lib/fluent/plugin/service_discovery.rb +65 -0
- data/lib/fluent/plugin/socket_util.rb +22 -0
- data/lib/fluent/plugin/storage.rb +84 -0
- data/lib/fluent/plugin/storage_local.rb +162 -0
- data/lib/fluent/plugin/string_util.rb +22 -0
- data/lib/fluent/plugin.rb +206 -0
- data/lib/fluent/plugin_helper/cert_option.rb +191 -0
- data/lib/fluent/plugin_helper/child_process.rb +366 -0
- data/lib/fluent/plugin_helper/compat_parameters.rb +343 -0
- data/lib/fluent/plugin_helper/counter.rb +51 -0
- data/lib/fluent/plugin_helper/event_emitter.rb +100 -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 +92 -0
- data/lib/fluent/plugin_helper/http_server/compat/ssl_context_extractor.rb +52 -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 +93 -0
- data/lib/fluent/plugin_helper/http_server/ssl_context_builder.rb +41 -0
- data/lib/fluent/plugin_helper/http_server.rb +135 -0
- data/lib/fluent/plugin_helper/inject.rb +154 -0
- data/lib/fluent/plugin_helper/metrics.rb +129 -0
- data/lib/fluent/plugin_helper/parser.rb +147 -0
- data/lib/fluent/plugin_helper/record_accessor.rb +207 -0
- data/lib/fluent/plugin_helper/retry_state.rb +209 -0
- data/lib/fluent/plugin_helper/server.rb +801 -0
- data/lib/fluent/plugin_helper/service_discovery/manager.rb +146 -0
- data/lib/fluent/plugin_helper/service_discovery/round_robin_balancer.rb +43 -0
- data/lib/fluent/plugin_helper/service_discovery.rb +125 -0
- data/lib/fluent/plugin_helper/socket.rb +277 -0
- data/lib/fluent/plugin_helper/socket_option.rb +98 -0
- data/lib/fluent/plugin_helper/storage.rb +349 -0
- data/lib/fluent/plugin_helper/thread.rb +180 -0
- data/lib/fluent/plugin_helper/timer.rb +92 -0
- data/lib/fluent/plugin_helper.rb +75 -0
- data/lib/fluent/plugin_id.rb +93 -0
- data/lib/fluent/process.rb +22 -0
- data/lib/fluent/registry.rb +116 -0
- data/lib/fluent/root_agent.rb +372 -0
- data/lib/fluent/rpc.rb +94 -0
- data/lib/fluent/static_config_analysis.rb +194 -0
- data/lib/fluent/supervisor.rb +1054 -0
- data/lib/fluent/system_config.rb +187 -0
- data/lib/fluent/test/base.rb +78 -0
- data/lib/fluent/test/driver/base.rb +225 -0
- data/lib/fluent/test/driver/base_owned.rb +83 -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/storage.rb +30 -0
- data/lib/fluent/test/driver/test_event_router.rb +45 -0
- data/lib/fluent/test/filter_test.rb +77 -0
- data/lib/fluent/test/formatter_test.rb +65 -0
- data/lib/fluent/test/helpers.rb +134 -0
- data/lib/fluent/test/input_test.rb +174 -0
- data/lib/fluent/test/log.rb +79 -0
- data/lib/fluent/test/output_test.rb +156 -0
- data/lib/fluent/test/parser_test.rb +70 -0
- data/lib/fluent/test/startup_shutdown.rb +46 -0
- data/lib/fluent/test.rb +58 -0
- data/lib/fluent/time.rb +512 -0
- data/lib/fluent/timezone.rb +171 -0
- data/lib/fluent/tls.rb +81 -0
- data/lib/fluent/unique_id.rb +39 -0
- data/lib/fluent/variable_store.rb +40 -0
- data/lib/fluent/version.rb +21 -0
- data/lib/fluent/winsvc.rb +103 -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/lib/fluent/plugin/storage.rb.erb +40 -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/new_gem/test/plugin/test_storage.rb.erb +18 -0
- data/templates/plugin_config_formatter/param.md-compact.erb +25 -0
- data/templates/plugin_config_formatter/param.md-table.erb +10 -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 +362 -0
- data/test/command/test_ca_generate.rb +70 -0
- data/test/command/test_cap_ctl.rb +100 -0
- data/test/command/test_cat.rb +128 -0
- data/test/command/test_ctl.rb +57 -0
- data/test/command/test_fluentd.rb +1106 -0
- data/test/command/test_plugin_config_formatter.rb +398 -0
- data/test/command/test_plugin_generator.rb +109 -0
- data/test/compat/test_calls_super.rb +166 -0
- data/test/compat/test_parser.rb +92 -0
- data/test/config/assertions.rb +42 -0
- data/test/config/test_config_parser.rb +551 -0
- data/test/config/test_configurable.rb +1784 -0
- data/test/config/test_configure_proxy.rb +604 -0
- data/test/config/test_dsl.rb +415 -0
- data/test/config/test_element.rb +518 -0
- data/test/config/test_literal_parser.rb +309 -0
- data/test/config/test_plugin_configuration.rb +56 -0
- data/test/config/test_section.rb +191 -0
- data/test/config/test_system_config.rb +199 -0
- data/test/config/test_types.rb +408 -0
- data/test/counter/test_client.rb +563 -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 +155 -0
- data/test/helpers/fuzzy_assert.rb +89 -0
- data/test/helpers/process_extenstion.rb +33 -0
- data/test/plugin/data/2010/01/20100102-030405.log +0 -0
- data/test/plugin/data/2010/01/20100102-030406.log +0 -0
- data/test/plugin/data/2010/01/20100102.log +0 -0
- data/test/plugin/data/log/bar +0 -0
- data/test/plugin/data/log/foo/bar.log +0 -0
- data/test/plugin/data/log/foo/bar2 +0 -0
- data/test/plugin/data/log/test.log +0 -0
- data/test/plugin/data/sd_file/config +11 -0
- data/test/plugin/data/sd_file/config.json +17 -0
- data/test/plugin/data/sd_file/config.yaml +11 -0
- data/test/plugin/data/sd_file/config.yml +11 -0
- data/test/plugin/data/sd_file/invalid_config.yml +7 -0
- data/test/plugin/in_tail/test_fifo.rb +121 -0
- data/test/plugin/in_tail/test_io_handler.rb +140 -0
- data/test/plugin/in_tail/test_position_file.rb +379 -0
- data/test/plugin/out_forward/test_ack_handler.rb +101 -0
- data/test/plugin/out_forward/test_connection_manager.rb +145 -0
- data/test/plugin/out_forward/test_handshake_protocol.rb +112 -0
- data/test/plugin/out_forward/test_load_balancer.rb +106 -0
- data/test/plugin/out_forward/test_socket_cache.rb +149 -0
- data/test/plugin/test_bare_output.rb +131 -0
- data/test/plugin/test_base.rb +115 -0
- data/test/plugin/test_buf_file.rb +1275 -0
- data/test/plugin/test_buf_file_single.rb +833 -0
- data/test/plugin/test_buf_memory.rb +42 -0
- data/test/plugin/test_buffer.rb +1383 -0
- data/test/plugin/test_buffer_chunk.rb +198 -0
- data/test/plugin/test_buffer_file_chunk.rb +871 -0
- data/test/plugin/test_buffer_file_single_chunk.rb +611 -0
- data/test/plugin/test_buffer_memory_chunk.rb +339 -0
- data/test/plugin/test_compressable.rb +87 -0
- data/test/plugin/test_file_util.rb +96 -0
- data/test/plugin/test_file_wrapper.rb +126 -0
- data/test/plugin/test_filter.rb +368 -0
- data/test/plugin/test_filter_grep.rb +697 -0
- data/test/plugin/test_filter_parser.rb +731 -0
- data/test/plugin/test_filter_record_transformer.rb +577 -0
- data/test/plugin/test_filter_stdout.rb +207 -0
- data/test/plugin/test_formatter_csv.rb +136 -0
- data/test/plugin/test_formatter_hash.rb +38 -0
- data/test/plugin/test_formatter_json.rb +61 -0
- data/test/plugin/test_formatter_ltsv.rb +70 -0
- data/test/plugin/test_formatter_msgpack.rb +28 -0
- data/test/plugin/test_formatter_out_file.rb +116 -0
- data/test/plugin/test_formatter_single_value.rb +44 -0
- data/test/plugin/test_formatter_tsv.rb +76 -0
- data/test/plugin/test_in_debug_agent.rb +49 -0
- data/test/plugin/test_in_exec.rb +261 -0
- data/test/plugin/test_in_forward.rb +1180 -0
- data/test/plugin/test_in_gc_stat.rb +62 -0
- data/test/plugin/test_in_http.rb +1080 -0
- data/test/plugin/test_in_monitor_agent.rb +923 -0
- data/test/plugin/test_in_object_space.rb +60 -0
- data/test/plugin/test_in_sample.rb +190 -0
- data/test/plugin/test_in_syslog.rb +505 -0
- data/test/plugin/test_in_tail.rb +2363 -0
- data/test/plugin/test_in_tcp.rb +243 -0
- data/test/plugin/test_in_udp.rb +268 -0
- data/test/plugin/test_in_unix.rb +181 -0
- data/test/plugin/test_input.rb +137 -0
- data/test/plugin/test_metadata.rb +89 -0
- data/test/plugin/test_metrics.rb +294 -0
- data/test/plugin/test_metrics_local.rb +96 -0
- data/test/plugin/test_multi_output.rb +204 -0
- data/test/plugin/test_out_copy.rb +308 -0
- data/test/plugin/test_out_exec.rb +312 -0
- data/test/plugin/test_out_exec_filter.rb +606 -0
- data/test/plugin/test_out_file.rb +1037 -0
- data/test/plugin/test_out_forward.rb +1348 -0
- data/test/plugin/test_out_http.rb +428 -0
- 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 +146 -0
- data/test/plugin/test_out_secondary_file.rb +458 -0
- data/test/plugin/test_out_stdout.rb +205 -0
- data/test/plugin/test_out_stream.rb +103 -0
- data/test/plugin/test_output.rb +1065 -0
- data/test/plugin/test_output_as_buffered.rb +2024 -0
- data/test/plugin/test_output_as_buffered_backup.rb +363 -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 +919 -0
- data/test/plugin/test_output_as_buffered_secondary.rb +882 -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 +399 -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 +200 -0
- data/test/plugin/test_parser_json.rb +138 -0
- data/test/plugin/test_parser_labeled_tsv.rb +160 -0
- data/test/plugin/test_parser_multiline.rb +111 -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 +650 -0
- data/test/plugin/test_parser_tsv.rb +122 -0
- data/test/plugin/test_sd_file.rb +228 -0
- data/test/plugin/test_sd_srv.rb +230 -0
- data/test/plugin/test_storage.rb +167 -0
- data/test/plugin/test_storage_local.rb +335 -0
- data/test/plugin/test_string_util.rb +26 -0
- data/test/plugin_helper/data/cert/cert-key.pem +27 -0
- data/test/plugin_helper/data/cert/cert-with-CRLF.pem +19 -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/data/cert/cert_chains/ca-cert-key.pem +27 -0
- data/test/plugin_helper/data/cert/cert_chains/ca-cert.pem +20 -0
- data/test/plugin_helper/data/cert/cert_chains/cert-key.pem +27 -0
- data/test/plugin_helper/data/cert/cert_chains/cert.pem +40 -0
- data/test/plugin_helper/data/cert/empty.pem +0 -0
- data/test/plugin_helper/data/cert/generate_cert.rb +125 -0
- data/test/plugin_helper/data/cert/with_ca/ca-cert-key-pass.pem +30 -0
- data/test/plugin_helper/data/cert/with_ca/ca-cert-key.pem +27 -0
- data/test/plugin_helper/data/cert/with_ca/ca-cert-pass.pem +20 -0
- data/test/plugin_helper/data/cert/with_ca/ca-cert.pem +20 -0
- data/test/plugin_helper/data/cert/with_ca/cert-key-pass.pem +30 -0
- data/test/plugin_helper/data/cert/with_ca/cert-key.pem +27 -0
- data/test/plugin_helper/data/cert/with_ca/cert-pass.pem +21 -0
- data/test/plugin_helper/data/cert/with_ca/cert.pem +21 -0
- data/test/plugin_helper/data/cert/without_ca/cert-key-pass.pem +30 -0
- data/test/plugin_helper/data/cert/without_ca/cert-key.pem +27 -0
- data/test/plugin_helper/data/cert/without_ca/cert-pass.pem +20 -0
- data/test/plugin_helper/data/cert/without_ca/cert.pem +20 -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/service_discovery/test_manager.rb +93 -0
- data/test/plugin_helper/service_discovery/test_round_robin_balancer.rb +21 -0
- data/test/plugin_helper/test_cert_option.rb +25 -0
- data/test/plugin_helper/test_child_process.rb +840 -0
- data/test/plugin_helper/test_compat_parameters.rb +358 -0
- data/test/plugin_helper/test_event_emitter.rb +80 -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 +372 -0
- data/test/plugin_helper/test_inject.rb +561 -0
- data/test/plugin_helper/test_metrics.rb +137 -0
- data/test/plugin_helper/test_parser.rb +264 -0
- data/test/plugin_helper/test_record_accessor.rb +238 -0
- data/test/plugin_helper/test_retry_state.rb +442 -0
- data/test/plugin_helper/test_server.rb +1823 -0
- data/test/plugin_helper/test_service_discovery.rb +165 -0
- data/test/plugin_helper/test_socket.rb +146 -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 +130 -0
- data/test/scripts/exec_script.rb +32 -0
- 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/formatter_known.rb +8 -0
- data/test/scripts/fluent/plugin/out_test.rb +81 -0
- data/test/scripts/fluent/plugin/out_test2.rb +80 -0
- data/test/scripts/fluent/plugin/parser_known.rb +4 -0
- data/test/test_capability.rb +74 -0
- data/test/test_clock.rb +164 -0
- data/test/test_config.rb +202 -0
- data/test/test_configdsl.rb +148 -0
- data/test/test_daemonizer.rb +91 -0
- data/test/test_engine.rb +203 -0
- data/test/test_event.rb +531 -0
- data/test/test_event_router.rb +331 -0
- data/test/test_event_time.rb +199 -0
- data/test/test_filter.rb +121 -0
- data/test/test_fluent_log_event_router.rb +99 -0
- data/test/test_formatter.rb +366 -0
- data/test/test_input.rb +31 -0
- data/test/test_log.rb +994 -0
- data/test/test_logger_initializer.rb +46 -0
- data/test/test_match.rb +148 -0
- data/test/test_mixin.rb +351 -0
- data/test/test_msgpack_factory.rb +18 -0
- data/test/test_oj_options.rb +55 -0
- data/test/test_output.rb +278 -0
- data/test/test_plugin.rb +251 -0
- data/test/test_plugin_classes.rb +370 -0
- data/test/test_plugin_helper.rb +81 -0
- data/test/test_plugin_id.rb +119 -0
- data/test/test_process.rb +14 -0
- data/test/test_root_agent.rb +951 -0
- data/test/test_static_config_analysis.rb +177 -0
- data/test/test_supervisor.rb +601 -0
- data/test/test_test_drivers.rb +136 -0
- data/test/test_time_formatter.rb +301 -0
- data/test/test_time_parser.rb +362 -0
- data/test/test_tls.rb +65 -0
- data/test/test_unique_id.rb +47 -0
- data/test/test_variable_store.rb +65 -0
- metadata +1261 -0
@@ -0,0 +1,1228 @@
|
|
1
|
+
#
|
2
|
+
# Fluentd
|
3
|
+
#
|
4
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
5
|
+
# you may not use this file except in compliance with the License.
|
6
|
+
# You may obtain a copy of the License at
|
7
|
+
#
|
8
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
9
|
+
#
|
10
|
+
# Unless required by applicable law or agreed to in writing, software
|
11
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
12
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
13
|
+
# See the License for the specific language governing permissions and
|
14
|
+
# limitations under the License.
|
15
|
+
#
|
16
|
+
|
17
|
+
require 'cool.io'
|
18
|
+
|
19
|
+
require 'fluent/plugin/input'
|
20
|
+
require 'fluent/config/error'
|
21
|
+
require 'fluent/event'
|
22
|
+
require 'fluent/plugin/buffer'
|
23
|
+
require 'fluent/plugin/parser_multiline'
|
24
|
+
require 'fluent/variable_store'
|
25
|
+
require 'fluent/capability'
|
26
|
+
require 'fluent/plugin/in_tail/position_file'
|
27
|
+
|
28
|
+
if Fluent.windows?
|
29
|
+
require_relative 'file_wrapper'
|
30
|
+
else
|
31
|
+
Fluent::FileWrapper = File
|
32
|
+
end
|
33
|
+
|
34
|
+
module Fluent::Plugin
|
35
|
+
class TailInput < Fluent::Plugin::Input
|
36
|
+
Fluent::Plugin.register_input('tail', self)
|
37
|
+
|
38
|
+
helpers :timer, :event_loop, :parser, :compat_parameters
|
39
|
+
|
40
|
+
RESERVED_CHARS = ['/', '*', '%'].freeze
|
41
|
+
MetricsInfo = Struct.new(:opened, :closed, :rotated)
|
42
|
+
|
43
|
+
class WatcherSetupError < StandardError
|
44
|
+
def initialize(msg)
|
45
|
+
@message = msg
|
46
|
+
end
|
47
|
+
|
48
|
+
def to_s
|
49
|
+
@message
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
def initialize
|
54
|
+
super
|
55
|
+
@paths = []
|
56
|
+
@tails = {}
|
57
|
+
@pf_file = nil
|
58
|
+
@pf = nil
|
59
|
+
@ignore_list = []
|
60
|
+
@shutdown_start_time = nil
|
61
|
+
@metrics = nil
|
62
|
+
@startup = true
|
63
|
+
end
|
64
|
+
|
65
|
+
desc 'The paths to read. Multiple paths can be specified, separated by comma.'
|
66
|
+
config_param :path, :string
|
67
|
+
desc 'path delimiter used for spliting path config'
|
68
|
+
config_param :path_delimiter, :string, default: ','
|
69
|
+
desc 'The tag of the event.'
|
70
|
+
config_param :tag, :string
|
71
|
+
desc 'The paths to exclude the files from watcher list.'
|
72
|
+
config_param :exclude_path, :array, default: []
|
73
|
+
desc 'Specify interval to keep reference to old file when rotate a file.'
|
74
|
+
config_param :rotate_wait, :time, default: 5
|
75
|
+
desc 'Fluentd will record the position it last read into this file.'
|
76
|
+
config_param :pos_file, :string, default: nil
|
77
|
+
desc 'The cleanup interval of pos file'
|
78
|
+
config_param :pos_file_compaction_interval, :time, default: nil
|
79
|
+
desc 'Start to read the logs from the head of file, not bottom.'
|
80
|
+
config_param :read_from_head, :bool, default: false
|
81
|
+
# When the program deletes log file and re-creates log file with same filename after passed refresh_interval,
|
82
|
+
# in_tail may raise a pos_file related error. This is a known issue but there is no such program on production.
|
83
|
+
# If we find such program / application, we will fix the problem.
|
84
|
+
desc 'The interval of refreshing the list of watch file.'
|
85
|
+
config_param :refresh_interval, :time, default: 60
|
86
|
+
desc 'The number of reading lines at each IO.'
|
87
|
+
config_param :read_lines_limit, :integer, default: 1000
|
88
|
+
desc 'The number of reading bytes per second'
|
89
|
+
config_param :read_bytes_limit_per_second, :size, default: -1
|
90
|
+
desc 'The interval of flushing the buffer for multiline format'
|
91
|
+
config_param :multiline_flush_interval, :time, default: nil
|
92
|
+
desc 'Enable the option to emit unmatched lines.'
|
93
|
+
config_param :emit_unmatched_lines, :bool, default: false
|
94
|
+
desc 'Enable the additional watch timer.'
|
95
|
+
config_param :enable_watch_timer, :bool, default: true
|
96
|
+
desc 'Enable the stat watcher based on inotify.'
|
97
|
+
config_param :enable_stat_watcher, :bool, default: true
|
98
|
+
desc 'The encoding after conversion of the input.'
|
99
|
+
config_param :encoding, :string, default: nil
|
100
|
+
desc 'The encoding of the input.'
|
101
|
+
config_param :from_encoding, :string, default: nil
|
102
|
+
desc 'Add the log path being tailed to records. Specify the field name to be used.'
|
103
|
+
config_param :path_key, :string, default: nil
|
104
|
+
desc 'Open and close the file on every update instead of leaving it open until it gets rotated.'
|
105
|
+
config_param :open_on_every_update, :bool, default: false
|
106
|
+
desc 'Limit the watching files that the modification time is within the specified time range (when use \'*\' in path).'
|
107
|
+
config_param :limit_recently_modified, :time, default: nil
|
108
|
+
desc 'Enable the option to skip the refresh of watching list on startup.'
|
109
|
+
config_param :skip_refresh_on_startup, :bool, default: false
|
110
|
+
desc 'Ignore repeated permission error logs'
|
111
|
+
config_param :ignore_repeated_permission_error, :bool, default: false
|
112
|
+
desc 'Format path with the specified timezone'
|
113
|
+
config_param :path_timezone, :string, default: nil
|
114
|
+
desc 'Follow inodes instead of following file names. Guarantees more stable delivery and allows to use * in path pattern with rotating files'
|
115
|
+
config_param :follow_inodes, :bool, default: false
|
116
|
+
desc 'Maximum length of line. The longer line is just skipped.'
|
117
|
+
config_param :max_line_size, :size, default: nil
|
118
|
+
|
119
|
+
config_section :parse, required: false, multi: true, init: true, param_name: :parser_configs do
|
120
|
+
config_argument :usage, :string, default: 'in_tail_parser'
|
121
|
+
end
|
122
|
+
|
123
|
+
attr_reader :paths
|
124
|
+
|
125
|
+
def configure(conf)
|
126
|
+
@variable_store = Fluent::VariableStore.fetch_or_build(:in_tail)
|
127
|
+
compat_parameters_convert(conf, :parser)
|
128
|
+
parser_config = conf.elements('parse').first
|
129
|
+
unless parser_config
|
130
|
+
raise Fluent::ConfigError, "<parse> section is required."
|
131
|
+
end
|
132
|
+
|
133
|
+
(1..Fluent::Plugin::MultilineParser::FORMAT_MAX_NUM).each do |n|
|
134
|
+
parser_config["format#{n}"] = conf["format#{n}"] if conf["format#{n}"]
|
135
|
+
end
|
136
|
+
|
137
|
+
parser_config['unmatched_lines'] = conf['emit_unmatched_lines']
|
138
|
+
|
139
|
+
super
|
140
|
+
|
141
|
+
if !@enable_watch_timer && !@enable_stat_watcher
|
142
|
+
raise Fluent::ConfigError, "either of enable_watch_timer or enable_stat_watcher must be true"
|
143
|
+
end
|
144
|
+
|
145
|
+
if RESERVED_CHARS.include?(@path_delimiter)
|
146
|
+
rc = RESERVED_CHARS.join(', ')
|
147
|
+
raise Fluent::ConfigError, "#{rc} are reserved words: #{@path_delimiter}"
|
148
|
+
end
|
149
|
+
|
150
|
+
@paths = @path.split(@path_delimiter).map(&:strip).uniq
|
151
|
+
if @paths.empty?
|
152
|
+
raise Fluent::ConfigError, "tail: 'path' parameter is required on tail input"
|
153
|
+
end
|
154
|
+
if @path_timezone
|
155
|
+
Fluent::Timezone.validate!(@path_timezone)
|
156
|
+
@path_formatters = @paths.map{|path| [path, Fluent::Timezone.formatter(@path_timezone, path)]}.to_h
|
157
|
+
@exclude_path_formatters = @exclude_path.map{|path| [path, Fluent::Timezone.formatter(@path_timezone, path)]}.to_h
|
158
|
+
end
|
159
|
+
|
160
|
+
# TODO: Use plugin_root_dir and storage plugin to store positions if available
|
161
|
+
if @pos_file
|
162
|
+
if @variable_store.key?(@pos_file) && !called_in_test?
|
163
|
+
plugin_id_using_this_path = @variable_store[@pos_file]
|
164
|
+
raise Fluent::ConfigError, "Other 'in_tail' plugin already use same pos_file path: plugin_id = #{plugin_id_using_this_path}, pos_file path = #{@pos_file}"
|
165
|
+
end
|
166
|
+
@variable_store[@pos_file] = self.plugin_id
|
167
|
+
else
|
168
|
+
if @follow_inodes
|
169
|
+
raise Fluent::ConfigError, "Can't follow inodes without pos_file configuration parameter"
|
170
|
+
end
|
171
|
+
$log.warn "'pos_file PATH' parameter is not set to a 'tail' source."
|
172
|
+
$log.warn "this parameter is highly recommended to save the position to resume tailing."
|
173
|
+
end
|
174
|
+
|
175
|
+
configure_tag
|
176
|
+
configure_encoding
|
177
|
+
|
178
|
+
@multiline_mode = parser_config["@type"] =~ /multiline/
|
179
|
+
@receive_handler = if @multiline_mode
|
180
|
+
method(:parse_multilines)
|
181
|
+
else
|
182
|
+
method(:parse_singleline)
|
183
|
+
end
|
184
|
+
@file_perm = system_config.file_permission || Fluent::DEFAULT_FILE_PERMISSION
|
185
|
+
@dir_perm = system_config.dir_permission || Fluent::DEFAULT_DIR_PERMISSION
|
186
|
+
# parser is already created by parser helper
|
187
|
+
@parser = parser_create(usage: parser_config['usage'] || @parser_configs.first.usage)
|
188
|
+
@capability = Fluent::Capability.new(:current_process)
|
189
|
+
if @read_bytes_limit_per_second > 0
|
190
|
+
if !@enable_watch_timer
|
191
|
+
raise Fluent::ConfigError, "Need to enable watch timer when using log throttling feature"
|
192
|
+
end
|
193
|
+
min_bytes = TailWatcher::IOHandler::BYTES_TO_READ
|
194
|
+
if @read_bytes_limit_per_second < min_bytes
|
195
|
+
log.warn "Should specify greater equal than #{min_bytes}. Use #{min_bytes} for read_bytes_limit_per_second"
|
196
|
+
@read_bytes_limit_per_second = min_bytes
|
197
|
+
end
|
198
|
+
end
|
199
|
+
opened_file_metrics = metrics_create(namespace: "fluentd", subsystem: "input", name: "files_opened_total", help_text: "Total number of opened files")
|
200
|
+
closed_file_metrics = metrics_create(namespace: "fluentd", subsystem: "input", name: "files_closed_total", help_text: "Total number of closed files")
|
201
|
+
rotated_file_metrics = metrics_create(namespace: "fluentd", subsystem: "input", name: "files_rotated_total", help_text: "Total number of rotated files")
|
202
|
+
@metrics = MetricsInfo.new(opened_file_metrics, closed_file_metrics, rotated_file_metrics)
|
203
|
+
end
|
204
|
+
|
205
|
+
def configure_tag
|
206
|
+
if @tag.index('*')
|
207
|
+
@tag_prefix, @tag_suffix = @tag.split('*')
|
208
|
+
@tag_prefix ||= ''
|
209
|
+
@tag_suffix ||= ''
|
210
|
+
else
|
211
|
+
@tag_prefix = nil
|
212
|
+
@tag_suffix = nil
|
213
|
+
end
|
214
|
+
end
|
215
|
+
|
216
|
+
def configure_encoding
|
217
|
+
unless @encoding
|
218
|
+
if @from_encoding
|
219
|
+
raise Fluent::ConfigError, "tail: 'from_encoding' parameter must be specified with 'encoding' parameter."
|
220
|
+
end
|
221
|
+
end
|
222
|
+
|
223
|
+
@encoding = parse_encoding_param(@encoding) if @encoding
|
224
|
+
@from_encoding = parse_encoding_param(@from_encoding) if @from_encoding
|
225
|
+
if @encoding && (@encoding == @from_encoding)
|
226
|
+
log.warn "'encoding' and 'from_encoding' are same encoding. No effect"
|
227
|
+
end
|
228
|
+
end
|
229
|
+
|
230
|
+
def parse_encoding_param(encoding_name)
|
231
|
+
begin
|
232
|
+
Encoding.find(encoding_name) if encoding_name
|
233
|
+
rescue ArgumentError => e
|
234
|
+
raise Fluent::ConfigError, e.message
|
235
|
+
end
|
236
|
+
end
|
237
|
+
|
238
|
+
def start
|
239
|
+
super
|
240
|
+
|
241
|
+
if @pos_file
|
242
|
+
pos_file_dir = File.dirname(@pos_file)
|
243
|
+
FileUtils.mkdir_p(pos_file_dir, mode: @dir_perm) unless Dir.exist?(pos_file_dir)
|
244
|
+
@pf_file = File.open(@pos_file, File::RDWR|File::CREAT|File::BINARY, @file_perm)
|
245
|
+
@pf_file.sync = true
|
246
|
+
@pf = PositionFile.load(@pf_file, @follow_inodes, expand_paths, logger: log)
|
247
|
+
|
248
|
+
if @pos_file_compaction_interval
|
249
|
+
timer_execute(:in_tail_refresh_compact_pos_file, @pos_file_compaction_interval) do
|
250
|
+
log.info('Clean up the pos file')
|
251
|
+
@pf.try_compact
|
252
|
+
end
|
253
|
+
end
|
254
|
+
end
|
255
|
+
|
256
|
+
refresh_watchers unless @skip_refresh_on_startup
|
257
|
+
timer_execute(:in_tail_refresh_watchers, @refresh_interval, &method(:refresh_watchers))
|
258
|
+
end
|
259
|
+
|
260
|
+
def stop
|
261
|
+
if @variable_store
|
262
|
+
@variable_store.delete(@pos_file)
|
263
|
+
end
|
264
|
+
|
265
|
+
super
|
266
|
+
end
|
267
|
+
|
268
|
+
def shutdown
|
269
|
+
@shutdown_start_time = Fluent::Clock.now
|
270
|
+
# during shutdown phase, don't close io. It should be done in close after all threads are stopped. See close.
|
271
|
+
stop_watchers(existence_path, immediate: true, remove_watcher: false)
|
272
|
+
@pf_file.close if @pf_file
|
273
|
+
|
274
|
+
super
|
275
|
+
end
|
276
|
+
|
277
|
+
def close
|
278
|
+
super
|
279
|
+
# close file handles after all threads stopped (in #close of thread plugin helper)
|
280
|
+
close_watcher_handles
|
281
|
+
end
|
282
|
+
|
283
|
+
def have_read_capability?
|
284
|
+
@capability.have_capability?(:effective, :dac_read_search) ||
|
285
|
+
@capability.have_capability?(:effective, :dac_override)
|
286
|
+
end
|
287
|
+
|
288
|
+
def expand_paths
|
289
|
+
date = Fluent::EventTime.now
|
290
|
+
paths = []
|
291
|
+
@paths.each { |path|
|
292
|
+
path = if @path_timezone
|
293
|
+
@path_formatters[path].call(date)
|
294
|
+
else
|
295
|
+
date.to_time.strftime(path)
|
296
|
+
end
|
297
|
+
if path.include?('*')
|
298
|
+
paths += Dir.glob(path).select { |p|
|
299
|
+
begin
|
300
|
+
is_file = !File.directory?(p)
|
301
|
+
if (File.readable?(p) || have_read_capability?) && is_file
|
302
|
+
if @limit_recently_modified && File.mtime(p) < (date.to_time - @limit_recently_modified)
|
303
|
+
false
|
304
|
+
else
|
305
|
+
true
|
306
|
+
end
|
307
|
+
else
|
308
|
+
if is_file
|
309
|
+
unless @ignore_list.include?(p)
|
310
|
+
log.warn "#{p} unreadable. It is excluded and would be examined next time."
|
311
|
+
@ignore_list << p if @ignore_repeated_permission_error
|
312
|
+
end
|
313
|
+
end
|
314
|
+
false
|
315
|
+
end
|
316
|
+
rescue Errno::ENOENT, Errno::EACCES
|
317
|
+
log.debug("#{p} is missing after refresh file list")
|
318
|
+
false
|
319
|
+
end
|
320
|
+
}
|
321
|
+
else
|
322
|
+
# When file is not created yet, Dir.glob returns an empty array. So just add when path is static.
|
323
|
+
paths << path
|
324
|
+
end
|
325
|
+
}
|
326
|
+
excluded = @exclude_path.map { |path|
|
327
|
+
path = if @path_timezone
|
328
|
+
@exclude_path_formatters[path].call(date)
|
329
|
+
else
|
330
|
+
date.to_time.strftime(path)
|
331
|
+
end
|
332
|
+
path.include?('*') ? Dir.glob(path) : path
|
333
|
+
}.flatten.uniq
|
334
|
+
# filter out non existing files, so in case pattern is without '*' we don't do unnecessary work
|
335
|
+
hash = {}
|
336
|
+
(paths - excluded).select { |path|
|
337
|
+
FileTest.exist?(path)
|
338
|
+
}.each { |path|
|
339
|
+
# Even we just checked for existence, there is a race condition here as
|
340
|
+
# of which stat() might fail with ENOENT. See #3224.
|
341
|
+
begin
|
342
|
+
target_info = TargetInfo.new(path, Fluent::FileWrapper.stat(path).ino)
|
343
|
+
if @follow_inodes
|
344
|
+
hash[target_info.ino] = target_info
|
345
|
+
else
|
346
|
+
hash[target_info.path] = target_info
|
347
|
+
end
|
348
|
+
rescue Errno::ENOENT, Errno::EACCES => e
|
349
|
+
$log.warn "expand_paths: stat() for #{path} failed with #{e.class.name}. Skip file."
|
350
|
+
end
|
351
|
+
}
|
352
|
+
hash
|
353
|
+
end
|
354
|
+
|
355
|
+
def existence_path
|
356
|
+
hash = {}
|
357
|
+
@tails.each_key {|target_info|
|
358
|
+
if @follow_inodes
|
359
|
+
hash[target_info.ino] = target_info
|
360
|
+
else
|
361
|
+
hash[target_info.path] = target_info
|
362
|
+
end
|
363
|
+
}
|
364
|
+
hash
|
365
|
+
end
|
366
|
+
|
367
|
+
# in_tail with '*' path doesn't check rotation file equality at refresh phase.
|
368
|
+
# So you should not use '*' path when your logs will be rotated by another tool.
|
369
|
+
# It will cause log duplication after updated watch files.
|
370
|
+
# In such case, you should separate log directory and specify two paths in path parameter.
|
371
|
+
# e.g. path /path/to/dir/*,/path/to/rotated_logs/target_file
|
372
|
+
def refresh_watchers
|
373
|
+
target_paths_hash = expand_paths
|
374
|
+
existence_paths_hash = existence_path
|
375
|
+
|
376
|
+
log.debug {
|
377
|
+
target_paths_str = target_paths_hash.collect { |key, target_info| target_info.path }.join(",")
|
378
|
+
existence_paths_str = existence_paths_hash.collect { |key, target_info| target_info.path }.join(",")
|
379
|
+
"tailing paths: target = #{target_paths_str} | existing = #{existence_paths_str}"
|
380
|
+
}
|
381
|
+
|
382
|
+
unwatched_hash = existence_paths_hash.reject {|key, value| target_paths_hash.key?(key)}
|
383
|
+
added_hash = target_paths_hash.reject {|key, value| existence_paths_hash.key?(key)}
|
384
|
+
|
385
|
+
stop_watchers(unwatched_hash, immediate: false, unwatched: true) unless unwatched_hash.empty?
|
386
|
+
start_watchers(added_hash) unless added_hash.empty?
|
387
|
+
@startup = false if @startup
|
388
|
+
end
|
389
|
+
|
390
|
+
def setup_watcher(target_info, pe)
|
391
|
+
line_buffer_timer_flusher = @multiline_mode ? TailWatcher::LineBufferTimerFlusher.new(log, @multiline_flush_interval, &method(:flush_buffer)) : nil
|
392
|
+
read_from_head = !@startup || @read_from_head
|
393
|
+
tw = TailWatcher.new(target_info, pe, log, read_from_head, @follow_inodes, method(:update_watcher), line_buffer_timer_flusher, method(:io_handler), @metrics)
|
394
|
+
|
395
|
+
if @enable_watch_timer
|
396
|
+
tt = TimerTrigger.new(1, log) { tw.on_notify }
|
397
|
+
tw.register_watcher(tt)
|
398
|
+
end
|
399
|
+
|
400
|
+
if @enable_stat_watcher
|
401
|
+
tt = StatWatcher.new(target_info.path, log) { tw.on_notify }
|
402
|
+
tw.register_watcher(tt)
|
403
|
+
end
|
404
|
+
|
405
|
+
tw.watchers.each do |watcher|
|
406
|
+
event_loop_attach(watcher)
|
407
|
+
end
|
408
|
+
|
409
|
+
tw
|
410
|
+
rescue => e
|
411
|
+
if tw
|
412
|
+
tw.watchers.each do |watcher|
|
413
|
+
event_loop_detach(watcher)
|
414
|
+
end
|
415
|
+
|
416
|
+
tw.detach(@shutdown_start_time)
|
417
|
+
tw.close
|
418
|
+
end
|
419
|
+
raise e
|
420
|
+
end
|
421
|
+
|
422
|
+
def construct_watcher(target_info)
|
423
|
+
pe = nil
|
424
|
+
if @pf
|
425
|
+
pe = @pf[target_info]
|
426
|
+
if @read_from_head && pe.read_inode.zero?
|
427
|
+
begin
|
428
|
+
pe.update(Fluent::FileWrapper.stat(target_info.path).ino, 0)
|
429
|
+
rescue Errno::ENOENT, Errno::EACCES
|
430
|
+
$log.warn "stat() for #{target_info.path} failed. Continuing without tailing it."
|
431
|
+
end
|
432
|
+
end
|
433
|
+
end
|
434
|
+
|
435
|
+
begin
|
436
|
+
tw = setup_watcher(target_info, pe)
|
437
|
+
rescue WatcherSetupError => e
|
438
|
+
log.warn "Skip #{target_info.path} because unexpected setup error happens: #{e}"
|
439
|
+
return
|
440
|
+
end
|
441
|
+
|
442
|
+
begin
|
443
|
+
target_info = TargetInfo.new(target_info.path, Fluent::FileWrapper.stat(target_info.path).ino)
|
444
|
+
@tails.delete(target_info)
|
445
|
+
@tails[target_info] = tw
|
446
|
+
tw.on_notify
|
447
|
+
rescue Errno::ENOENT, Errno::EACCES => e
|
448
|
+
$log.warn "stat() for #{target_info.path} failed with #{e.class.name}. Drop tail watcher for now."
|
449
|
+
# explicitly detach and unwatch watcher `tw`.
|
450
|
+
tw.unwatched = true
|
451
|
+
detach_watcher(tw, target_info.ino, false)
|
452
|
+
end
|
453
|
+
end
|
454
|
+
|
455
|
+
def start_watchers(targets_info)
|
456
|
+
targets_info.each_value {|target_info|
|
457
|
+
construct_watcher(target_info)
|
458
|
+
break if before_shutdown?
|
459
|
+
}
|
460
|
+
end
|
461
|
+
|
462
|
+
def stop_watchers(targets_info, immediate: false, unwatched: false, remove_watcher: true)
|
463
|
+
targets_info.each_value { |target_info|
|
464
|
+
if remove_watcher
|
465
|
+
tw = @tails.delete(target_info)
|
466
|
+
else
|
467
|
+
tw = @tails[target_info]
|
468
|
+
end
|
469
|
+
if tw
|
470
|
+
tw.unwatched = unwatched
|
471
|
+
if immediate
|
472
|
+
detach_watcher(tw, target_info.ino, false)
|
473
|
+
else
|
474
|
+
detach_watcher_after_rotate_wait(tw, target_info.ino)
|
475
|
+
end
|
476
|
+
end
|
477
|
+
}
|
478
|
+
end
|
479
|
+
|
480
|
+
def close_watcher_handles
|
481
|
+
@tails.keys.each do |target_info|
|
482
|
+
tw = @tails.delete(target_info)
|
483
|
+
if tw
|
484
|
+
tw.close
|
485
|
+
end
|
486
|
+
end
|
487
|
+
end
|
488
|
+
|
489
|
+
# refresh_watchers calls @tails.keys so we don't use stop_watcher -> start_watcher sequence for safety.
|
490
|
+
def update_watcher(target_info, pe)
|
491
|
+
log.info("detected rotation of #{target_info.path}; waiting #{@rotate_wait} seconds")
|
492
|
+
|
493
|
+
if @pf
|
494
|
+
pe_inode = pe.read_inode
|
495
|
+
target_info_from_position_entry = TargetInfo.new(target_info.path, pe_inode)
|
496
|
+
unless pe_inode == @pf[target_info_from_position_entry].read_inode
|
497
|
+
log.debug "Skip update_watcher because watcher has been already updated by other inotify event"
|
498
|
+
return
|
499
|
+
end
|
500
|
+
end
|
501
|
+
|
502
|
+
rotated_target_info = TargetInfo.new(target_info.path, pe.read_inode)
|
503
|
+
rotated_tw = @tails[rotated_target_info]
|
504
|
+
new_target_info = target_info.dup
|
505
|
+
|
506
|
+
if @follow_inodes
|
507
|
+
new_position_entry = @pf[target_info]
|
508
|
+
|
509
|
+
if new_position_entry.read_inode == 0
|
510
|
+
# When follow_inodes is true, it's not cleaned up by refresh_watcher.
|
511
|
+
# So it should be unwatched here explicitly.
|
512
|
+
rotated_tw.unwatched = true
|
513
|
+
# Make sure to delete old key, it has a different ino while the hash key is same.
|
514
|
+
@tails.delete(rotated_target_info)
|
515
|
+
@tails[new_target_info] = setup_watcher(new_target_info, new_position_entry)
|
516
|
+
@tails[new_target_info].on_notify
|
517
|
+
end
|
518
|
+
else
|
519
|
+
# Make sure to delete old key, it has a different ino while the hash key is same.
|
520
|
+
@tails.delete(rotated_target_info)
|
521
|
+
@tails[new_target_info] = setup_watcher(new_target_info, pe)
|
522
|
+
@tails[new_target_info].on_notify
|
523
|
+
end
|
524
|
+
detach_watcher_after_rotate_wait(rotated_tw, pe.read_inode) if rotated_tw
|
525
|
+
end
|
526
|
+
|
527
|
+
# TailWatcher#close is called by another thread at shutdown phase.
|
528
|
+
# It causes 'can't modify string; temporarily locked' error in IOHandler
|
529
|
+
# so adding close_io argument to avoid this problem.
|
530
|
+
# At shutdown, IOHandler's io will be released automatically after detached the event loop
|
531
|
+
def detach_watcher(tw, ino, close_io = true)
|
532
|
+
tw.watchers.each do |watcher|
|
533
|
+
event_loop_detach(watcher)
|
534
|
+
end
|
535
|
+
tw.detach(@shutdown_start_time)
|
536
|
+
|
537
|
+
tw.close if close_io
|
538
|
+
|
539
|
+
if tw.unwatched && @pf
|
540
|
+
target_info = TargetInfo.new(tw.path, ino)
|
541
|
+
@pf.unwatch(target_info)
|
542
|
+
end
|
543
|
+
end
|
544
|
+
|
545
|
+
def detach_watcher_after_rotate_wait(tw, ino)
|
546
|
+
# Call event_loop_attach/event_loop_detach is high-cost for short-live object.
|
547
|
+
# If this has a problem with large number of files, use @_event_loop directly instead of timer_execute.
|
548
|
+
if @open_on_every_update
|
549
|
+
# Detach now because it's already closed, waiting it doesn't make sense.
|
550
|
+
detach_watcher(tw, ino)
|
551
|
+
elsif @read_bytes_limit_per_second < 0
|
552
|
+
# throttling isn't enabled, just wait @rotate_wait
|
553
|
+
timer_execute(:in_tail_close_watcher, @rotate_wait, repeat: false) do
|
554
|
+
detach_watcher(tw, ino)
|
555
|
+
end
|
556
|
+
else
|
557
|
+
# When the throttling feature is enabled, it might not reach EOF yet.
|
558
|
+
# Should ensure to read all contents before closing it, with keeping throttling.
|
559
|
+
start_time_to_wait = Fluent::Clock.now
|
560
|
+
timer = timer_execute(:in_tail_close_watcher, 1, repeat: true) do
|
561
|
+
elapsed = Fluent::Clock.now - start_time_to_wait
|
562
|
+
if tw.eof? && elapsed >= @rotate_wait
|
563
|
+
timer.detach
|
564
|
+
detach_watcher(tw, ino)
|
565
|
+
end
|
566
|
+
end
|
567
|
+
end
|
568
|
+
end
|
569
|
+
|
570
|
+
def flush_buffer(tw, buf)
|
571
|
+
buf.chomp!
|
572
|
+
@parser.parse(buf) { |time, record|
|
573
|
+
if time && record
|
574
|
+
tag = if @tag_prefix || @tag_suffix
|
575
|
+
@tag_prefix + tw.tag + @tag_suffix
|
576
|
+
else
|
577
|
+
@tag
|
578
|
+
end
|
579
|
+
record[@path_key] ||= tw.path unless @path_key.nil?
|
580
|
+
router.emit(tag, time, record)
|
581
|
+
else
|
582
|
+
if @emit_unmatched_lines
|
583
|
+
record = { 'unmatched_line' => buf }
|
584
|
+
record[@path_key] ||= tail_watcher.path unless @path_key.nil?
|
585
|
+
tag = if @tag_prefix || @tag_suffix
|
586
|
+
@tag_prefix + tw.tag + @tag_suffix
|
587
|
+
else
|
588
|
+
@tag
|
589
|
+
end
|
590
|
+
router.emit(tag, Fluent::EventTime.now, record)
|
591
|
+
end
|
592
|
+
log.warn "got incomplete line at shutdown from #{tw.path}: #{buf.inspect}"
|
593
|
+
end
|
594
|
+
}
|
595
|
+
end
|
596
|
+
|
597
|
+
# @return true if no error or unrecoverable error happens in emit action. false if got BufferOverflowError
|
598
|
+
def receive_lines(lines, tail_watcher)
|
599
|
+
lines = lines.reject do |line|
|
600
|
+
skip_line = @max_line_size ? line.bytesize > @max_line_size : false
|
601
|
+
if skip_line
|
602
|
+
log.warn "received line length is longer than #{@max_line_size}"
|
603
|
+
log.debug "skipped line: #{line.chomp}"
|
604
|
+
end
|
605
|
+
skip_line
|
606
|
+
end
|
607
|
+
es = @receive_handler.call(lines, tail_watcher)
|
608
|
+
unless es.empty?
|
609
|
+
tag = if @tag_prefix || @tag_suffix
|
610
|
+
@tag_prefix + tail_watcher.tag + @tag_suffix
|
611
|
+
else
|
612
|
+
@tag
|
613
|
+
end
|
614
|
+
begin
|
615
|
+
router.emit_stream(tag, es)
|
616
|
+
rescue Fluent::Plugin::Buffer::BufferOverflowError
|
617
|
+
return false
|
618
|
+
rescue
|
619
|
+
# ignore non BufferQueueLimitError errors because in_tail can't recover. Engine shows logs and backtraces.
|
620
|
+
return true
|
621
|
+
end
|
622
|
+
end
|
623
|
+
|
624
|
+
return true
|
625
|
+
end
|
626
|
+
|
627
|
+
def convert_line_to_event(line, es, tail_watcher)
|
628
|
+
begin
|
629
|
+
line.chomp! # remove \n
|
630
|
+
@parser.parse(line) { |time, record|
|
631
|
+
if time && record
|
632
|
+
record[@path_key] ||= tail_watcher.path unless @path_key.nil?
|
633
|
+
es.add(time, record)
|
634
|
+
else
|
635
|
+
if @emit_unmatched_lines
|
636
|
+
record = {'unmatched_line' => line}
|
637
|
+
record[@path_key] ||= tail_watcher.path unless @path_key.nil?
|
638
|
+
es.add(Fluent::EventTime.now, record)
|
639
|
+
end
|
640
|
+
log.warn "pattern not matched: #{line.inspect}"
|
641
|
+
end
|
642
|
+
}
|
643
|
+
rescue => e
|
644
|
+
log.warn 'invalid line found', file: tail_watcher.path, line: line, error: e.to_s
|
645
|
+
log.debug_backtrace(e.backtrace)
|
646
|
+
end
|
647
|
+
end
|
648
|
+
|
649
|
+
def parse_singleline(lines, tail_watcher)
|
650
|
+
es = Fluent::MultiEventStream.new
|
651
|
+
lines.each { |line|
|
652
|
+
convert_line_to_event(line, es, tail_watcher)
|
653
|
+
}
|
654
|
+
es
|
655
|
+
end
|
656
|
+
|
657
|
+
# No need to check if line_buffer_timer_flusher is nil, since line_buffer_timer_flusher should exist
|
658
|
+
def parse_multilines(lines, tail_watcher)
|
659
|
+
lb = tail_watcher.line_buffer_timer_flusher.line_buffer
|
660
|
+
es = Fluent::MultiEventStream.new
|
661
|
+
if @parser.has_firstline?
|
662
|
+
tail_watcher.line_buffer_timer_flusher.reset_timer
|
663
|
+
lines.each { |line|
|
664
|
+
if @parser.firstline?(line)
|
665
|
+
if lb
|
666
|
+
convert_line_to_event(lb, es, tail_watcher)
|
667
|
+
end
|
668
|
+
lb = line
|
669
|
+
else
|
670
|
+
if lb.nil?
|
671
|
+
if @emit_unmatched_lines
|
672
|
+
convert_line_to_event(line, es, tail_watcher)
|
673
|
+
end
|
674
|
+
log.warn "got incomplete line before first line from #{tail_watcher.path}: #{line.inspect}"
|
675
|
+
else
|
676
|
+
lb << line
|
677
|
+
end
|
678
|
+
end
|
679
|
+
}
|
680
|
+
else
|
681
|
+
lb ||= ''
|
682
|
+
lines.each do |line|
|
683
|
+
lb << line
|
684
|
+
@parser.parse(lb) { |time, record|
|
685
|
+
if time && record
|
686
|
+
convert_line_to_event(lb, es, tail_watcher)
|
687
|
+
lb = ''
|
688
|
+
end
|
689
|
+
}
|
690
|
+
end
|
691
|
+
end
|
692
|
+
tail_watcher.line_buffer_timer_flusher.line_buffer = lb
|
693
|
+
es
|
694
|
+
end
|
695
|
+
|
696
|
+
def statistics
|
697
|
+
stats = super
|
698
|
+
|
699
|
+
stats = {
|
700
|
+
'input' => stats["input"].merge({
|
701
|
+
'opened_file_count' => @metrics.opened.get,
|
702
|
+
'closed_file_count' => @metrics.closed.get,
|
703
|
+
'rotated_file_count' => @metrics.rotated.get,
|
704
|
+
})
|
705
|
+
}
|
706
|
+
stats
|
707
|
+
end
|
708
|
+
|
709
|
+
private
|
710
|
+
|
711
|
+
def io_handler(watcher, path)
|
712
|
+
TailWatcher::IOHandler.new(
|
713
|
+
watcher,
|
714
|
+
path: path,
|
715
|
+
log: log,
|
716
|
+
read_lines_limit: @read_lines_limit,
|
717
|
+
read_bytes_limit_per_second: @read_bytes_limit_per_second,
|
718
|
+
open_on_every_update: @open_on_every_update,
|
719
|
+
from_encoding: @from_encoding,
|
720
|
+
encoding: @encoding,
|
721
|
+
metrics: @metrics,
|
722
|
+
&method(:receive_lines)
|
723
|
+
)
|
724
|
+
end
|
725
|
+
|
726
|
+
class StatWatcher < Coolio::StatWatcher
|
727
|
+
def initialize(path, log, &callback)
|
728
|
+
@callback = callback
|
729
|
+
@log = log
|
730
|
+
super(path)
|
731
|
+
end
|
732
|
+
|
733
|
+
def on_change(prev, cur)
|
734
|
+
@callback.call
|
735
|
+
rescue
|
736
|
+
@log.error $!.to_s
|
737
|
+
@log.error_backtrace
|
738
|
+
end
|
739
|
+
end
|
740
|
+
|
741
|
+
class TimerTrigger < Coolio::TimerWatcher
|
742
|
+
def initialize(interval, log, &callback)
|
743
|
+
@log = log
|
744
|
+
@callback = callback
|
745
|
+
super(interval, true)
|
746
|
+
end
|
747
|
+
|
748
|
+
def on_timer
|
749
|
+
@callback.call
|
750
|
+
rescue => e
|
751
|
+
@log.error e.to_s
|
752
|
+
@log.error_backtrace
|
753
|
+
end
|
754
|
+
end
|
755
|
+
|
756
|
+
class TailWatcher
|
757
|
+
def initialize(target_info, pe, log, read_from_head, follow_inodes, update_watcher, line_buffer_timer_flusher, io_handler_build, metrics)
|
758
|
+
@path = target_info.path
|
759
|
+
@ino = target_info.ino
|
760
|
+
@pe = pe || MemoryPositionEntry.new
|
761
|
+
@read_from_head = read_from_head
|
762
|
+
@follow_inodes = follow_inodes
|
763
|
+
@update_watcher = update_watcher
|
764
|
+
@log = log
|
765
|
+
@rotate_handler = RotateHandler.new(log, &method(:on_rotate))
|
766
|
+
@line_buffer_timer_flusher = line_buffer_timer_flusher
|
767
|
+
@io_handler = nil
|
768
|
+
@io_handler_build = io_handler_build
|
769
|
+
@metrics = metrics
|
770
|
+
@watchers = []
|
771
|
+
end
|
772
|
+
|
773
|
+
attr_reader :path, :ino
|
774
|
+
attr_reader :pe
|
775
|
+
attr_reader :line_buffer_timer_flusher
|
776
|
+
attr_accessor :unwatched # This is used for removing position entry from PositionFile
|
777
|
+
attr_reader :watchers
|
778
|
+
|
779
|
+
def tag
|
780
|
+
@parsed_tag ||= @path.tr('/', '.').gsub(/\.+/, '.').gsub(/^\./, '')
|
781
|
+
end
|
782
|
+
|
783
|
+
def register_watcher(watcher)
|
784
|
+
@watchers << watcher
|
785
|
+
end
|
786
|
+
|
787
|
+
def detach(shutdown_start_time = nil)
|
788
|
+
if @io_handler
|
789
|
+
@io_handler.ready_to_shutdown(shutdown_start_time)
|
790
|
+
@io_handler.on_notify
|
791
|
+
end
|
792
|
+
@line_buffer_timer_flusher&.close(self)
|
793
|
+
end
|
794
|
+
|
795
|
+
def close
|
796
|
+
if @io_handler
|
797
|
+
@io_handler.close
|
798
|
+
@io_handler = nil
|
799
|
+
end
|
800
|
+
end
|
801
|
+
|
802
|
+
def eof?
|
803
|
+
@io_handler.nil? || @io_handler.eof?
|
804
|
+
end
|
805
|
+
|
806
|
+
def on_notify
|
807
|
+
begin
|
808
|
+
stat = Fluent::FileWrapper.stat(@path)
|
809
|
+
rescue Errno::ENOENT, Errno::EACCES
|
810
|
+
# moved or deleted
|
811
|
+
stat = nil
|
812
|
+
end
|
813
|
+
|
814
|
+
@rotate_handler.on_notify(stat) if @rotate_handler
|
815
|
+
@line_buffer_timer_flusher.on_notify(self) if @line_buffer_timer_flusher
|
816
|
+
@io_handler.on_notify if @io_handler
|
817
|
+
end
|
818
|
+
|
819
|
+
def on_rotate(stat)
|
820
|
+
if @io_handler.nil?
|
821
|
+
if stat
|
822
|
+
# first time
|
823
|
+
fsize = stat.size
|
824
|
+
inode = stat.ino
|
825
|
+
|
826
|
+
last_inode = @pe.read_inode
|
827
|
+
if inode == last_inode
|
828
|
+
# rotated file has the same inode number with the last file.
|
829
|
+
# assuming following situation:
|
830
|
+
# a) file was once renamed and backed, or
|
831
|
+
# b) symlink or hardlink to the same file is recreated
|
832
|
+
# in either case of a and b, seek to the saved position
|
833
|
+
# c) file was once renamed, truncated and then backed
|
834
|
+
# in this case, consider it truncated
|
835
|
+
@pe.update(inode, 0) if fsize < @pe.read_pos
|
836
|
+
elsif last_inode != 0
|
837
|
+
# this is FilePositionEntry and fluentd once started.
|
838
|
+
# read data from the head of the rotated file.
|
839
|
+
# logs never duplicate because this file is a rotated new file.
|
840
|
+
@pe.update(inode, 0)
|
841
|
+
else
|
842
|
+
# this is MemoryPositionEntry or this is the first time fluentd started.
|
843
|
+
# seek to the end of the any files.
|
844
|
+
# logs may duplicate without this seek because it's not sure the file is
|
845
|
+
# existent file or rotated new file.
|
846
|
+
pos = @read_from_head ? 0 : fsize
|
847
|
+
@pe.update(inode, pos)
|
848
|
+
end
|
849
|
+
@io_handler = io_handler
|
850
|
+
else
|
851
|
+
@io_handler = NullIOHandler.new
|
852
|
+
end
|
853
|
+
else
|
854
|
+
watcher_needs_update = false
|
855
|
+
|
856
|
+
if stat
|
857
|
+
inode = stat.ino
|
858
|
+
if inode == @pe.read_inode # truncated
|
859
|
+
@pe.update_pos(0)
|
860
|
+
@io_handler.close
|
861
|
+
elsif !@io_handler.opened? # There is no previous file. Reuse TailWatcher
|
862
|
+
@pe.update(inode, 0)
|
863
|
+
else # file is rotated and new file found
|
864
|
+
watcher_needs_update = true
|
865
|
+
# Handle the old log file before renewing TailWatcher [fluentd#1055]
|
866
|
+
@io_handler.on_notify
|
867
|
+
end
|
868
|
+
else # file is rotated and new file not found
|
869
|
+
# Clear RotateHandler to avoid duplicated file watch in same path.
|
870
|
+
@rotate_handler = nil
|
871
|
+
watcher_needs_update = true
|
872
|
+
end
|
873
|
+
|
874
|
+
if watcher_needs_update
|
875
|
+
if @follow_inodes
|
876
|
+
# No need to update a watcher if stat is nil (file not present), because moving to inodes will create
|
877
|
+
# new watcher, and old watcher will be closed by stop_watcher in refresh_watchers method
|
878
|
+
# don't want to swap state because we need latest read offset in pos file even after rotate_wait
|
879
|
+
if stat
|
880
|
+
target_info = TargetInfo.new(@path, stat.ino)
|
881
|
+
@update_watcher.call(target_info, @pe)
|
882
|
+
end
|
883
|
+
else
|
884
|
+
# Permit to handle if stat is nil (file not present).
|
885
|
+
# If a file is mv-ed and a new file is created during
|
886
|
+
# calling `#refresh_watchers`s, and `#refresh_watchers` won't run `#start_watchers`
|
887
|
+
# and `#stop_watchers()` for the path because `target_paths_hash`
|
888
|
+
# always contains the path.
|
889
|
+
target_info = TargetInfo.new(@path, stat ? stat.ino : nil)
|
890
|
+
@update_watcher.call(target_info, swap_state(@pe))
|
891
|
+
end
|
892
|
+
else
|
893
|
+
@log.info "detected rotation of #{@path}"
|
894
|
+
@io_handler = io_handler
|
895
|
+
end
|
896
|
+
@metrics.rotated.inc
|
897
|
+
end
|
898
|
+
end
|
899
|
+
|
900
|
+
def io_handler
|
901
|
+
@io_handler_build.call(self, @path)
|
902
|
+
end
|
903
|
+
|
904
|
+
def swap_state(pe)
|
905
|
+
# Use MemoryPositionEntry for rotated file temporary
|
906
|
+
mpe = MemoryPositionEntry.new
|
907
|
+
mpe.update(pe.read_inode, pe.read_pos)
|
908
|
+
@pe = mpe
|
909
|
+
pe # This pe will be updated in on_rotate after TailWatcher is initialized
|
910
|
+
end
|
911
|
+
|
912
|
+
class FIFO
|
913
|
+
def initialize(from_encoding, encoding)
|
914
|
+
@from_encoding = from_encoding
|
915
|
+
@encoding = encoding
|
916
|
+
@need_enc = from_encoding != encoding
|
917
|
+
@buffer = ''.force_encoding(from_encoding)
|
918
|
+
@eol = "\n".encode(from_encoding).freeze
|
919
|
+
end
|
920
|
+
|
921
|
+
attr_reader :from_encoding, :encoding, :buffer
|
922
|
+
|
923
|
+
def <<(chunk)
|
924
|
+
# Although "chunk" is most likely transient besides String#force_encoding itself
|
925
|
+
# won't affect the actual content of it, it is also probable that "chunk" is
|
926
|
+
# a reused buffer and changing its encoding causes some problems on the caller side.
|
927
|
+
#
|
928
|
+
# Actually, the caller here is specific and "chunk" comes from IO#partial with
|
929
|
+
# the second argument, which the function always returns as a return value.
|
930
|
+
#
|
931
|
+
# Feeding a string that has its encoding attribute set to any double-byte or
|
932
|
+
# quad-byte encoding to IO#readpartial as the second arguments results in an
|
933
|
+
# assertion failure on Ruby < 2.4.0 for unknown reasons.
|
934
|
+
orig_encoding = chunk.encoding
|
935
|
+
chunk.force_encoding(from_encoding)
|
936
|
+
@buffer << chunk
|
937
|
+
# Thus the encoding needs to be reverted back here
|
938
|
+
chunk.force_encoding(orig_encoding)
|
939
|
+
end
|
940
|
+
|
941
|
+
def convert(s)
|
942
|
+
if @need_enc
|
943
|
+
s.encode!(@encoding, @from_encoding)
|
944
|
+
else
|
945
|
+
s
|
946
|
+
end
|
947
|
+
rescue
|
948
|
+
s.encode!(@encoding, @from_encoding, :invalid => :replace, :undef => :replace)
|
949
|
+
end
|
950
|
+
|
951
|
+
def read_lines(lines)
|
952
|
+
idx = @buffer.index(@eol)
|
953
|
+
|
954
|
+
until idx.nil?
|
955
|
+
# Using freeze and slice is faster than slice!
|
956
|
+
# See https://github.com/fluent/fluentd/pull/2527
|
957
|
+
@buffer.freeze
|
958
|
+
rbuf = @buffer.slice(0, idx + 1)
|
959
|
+
@buffer = @buffer.slice(idx + 1, @buffer.size)
|
960
|
+
idx = @buffer.index(@eol)
|
961
|
+
lines << convert(rbuf)
|
962
|
+
end
|
963
|
+
end
|
964
|
+
|
965
|
+
def bytesize
|
966
|
+
@buffer.bytesize
|
967
|
+
end
|
968
|
+
end
|
969
|
+
|
970
|
+
class IOHandler
|
971
|
+
BYTES_TO_READ = 8192
|
972
|
+
SHUTDOWN_TIMEOUT = 5
|
973
|
+
|
974
|
+
attr_accessor :shutdown_timeout
|
975
|
+
|
976
|
+
def initialize(watcher, path:, read_lines_limit:, read_bytes_limit_per_second:, log:, open_on_every_update:, from_encoding: nil, encoding: nil, metrics:, &receive_lines)
|
977
|
+
@watcher = watcher
|
978
|
+
@path = path
|
979
|
+
@read_lines_limit = read_lines_limit
|
980
|
+
@read_bytes_limit_per_second = read_bytes_limit_per_second
|
981
|
+
@receive_lines = receive_lines
|
982
|
+
@open_on_every_update = open_on_every_update
|
983
|
+
@fifo = FIFO.new(from_encoding || Encoding::ASCII_8BIT, encoding || Encoding::ASCII_8BIT)
|
984
|
+
@iobuf = ''.force_encoding('ASCII-8BIT')
|
985
|
+
@lines = []
|
986
|
+
@io = nil
|
987
|
+
@notify_mutex = Mutex.new
|
988
|
+
@log = log
|
989
|
+
@start_reading_time = nil
|
990
|
+
@number_bytes_read = 0
|
991
|
+
@shutdown_start_time = nil
|
992
|
+
@shutdown_timeout = SHUTDOWN_TIMEOUT
|
993
|
+
@shutdown_mutex = Mutex.new
|
994
|
+
@eof = false
|
995
|
+
@metrics = metrics
|
996
|
+
|
997
|
+
@log.info "following tail of #{@path}"
|
998
|
+
end
|
999
|
+
|
1000
|
+
def on_notify
|
1001
|
+
@notify_mutex.synchronize { handle_notify }
|
1002
|
+
end
|
1003
|
+
|
1004
|
+
def ready_to_shutdown(shutdown_start_time = nil)
|
1005
|
+
@shutdown_mutex.synchronize {
|
1006
|
+
@shutdown_start_time =
|
1007
|
+
shutdown_start_time || Fluent::Clock.now
|
1008
|
+
}
|
1009
|
+
end
|
1010
|
+
|
1011
|
+
def close
|
1012
|
+
if @io && !@io.closed?
|
1013
|
+
@io.close
|
1014
|
+
@io = nil
|
1015
|
+
@metrics.closed.inc
|
1016
|
+
end
|
1017
|
+
end
|
1018
|
+
|
1019
|
+
def opened?
|
1020
|
+
!!@io
|
1021
|
+
end
|
1022
|
+
|
1023
|
+
def eof?
|
1024
|
+
@eof
|
1025
|
+
end
|
1026
|
+
|
1027
|
+
private
|
1028
|
+
|
1029
|
+
def limit_bytes_per_second_reached?
|
1030
|
+
return false if @read_bytes_limit_per_second < 0 # not enabled by conf
|
1031
|
+
return false if @number_bytes_read < @read_bytes_limit_per_second
|
1032
|
+
|
1033
|
+
@start_reading_time ||= Fluent::Clock.now
|
1034
|
+
time_spent_reading = Fluent::Clock.now - @start_reading_time
|
1035
|
+
@log.debug("time_spent_reading: #{time_spent_reading} #{ @watcher.path}")
|
1036
|
+
|
1037
|
+
if time_spent_reading < 1
|
1038
|
+
true
|
1039
|
+
else
|
1040
|
+
@start_reading_time = nil
|
1041
|
+
@number_bytes_read = 0
|
1042
|
+
false
|
1043
|
+
end
|
1044
|
+
end
|
1045
|
+
|
1046
|
+
def should_shutdown_now?
|
1047
|
+
# Ensure to read all remaining lines, but abort immediately if it
|
1048
|
+
# seems to take too long time.
|
1049
|
+
@shutdown_mutex.synchronize {
|
1050
|
+
return false if @shutdown_start_time.nil?
|
1051
|
+
return Fluent::Clock.now - @shutdown_start_time > @shutdown_timeout
|
1052
|
+
}
|
1053
|
+
end
|
1054
|
+
|
1055
|
+
def handle_notify
|
1056
|
+
return if limit_bytes_per_second_reached?
|
1057
|
+
|
1058
|
+
with_io do |io|
|
1059
|
+
begin
|
1060
|
+
read_more = false
|
1061
|
+
|
1062
|
+
if !io.nil? && @lines.empty?
|
1063
|
+
begin
|
1064
|
+
while true
|
1065
|
+
@start_reading_time ||= Fluent::Clock.now
|
1066
|
+
data = io.readpartial(BYTES_TO_READ, @iobuf)
|
1067
|
+
@eof = false
|
1068
|
+
@number_bytes_read += data.bytesize
|
1069
|
+
@fifo << data
|
1070
|
+
@fifo.read_lines(@lines)
|
1071
|
+
|
1072
|
+
if limit_bytes_per_second_reached? || should_shutdown_now?
|
1073
|
+
# Just get out from tailing loop.
|
1074
|
+
read_more = false
|
1075
|
+
break
|
1076
|
+
end
|
1077
|
+
if @lines.size >= @read_lines_limit
|
1078
|
+
# not to use too much memory in case the file is very large
|
1079
|
+
read_more = true
|
1080
|
+
break
|
1081
|
+
end
|
1082
|
+
end
|
1083
|
+
rescue EOFError
|
1084
|
+
@eof = true
|
1085
|
+
end
|
1086
|
+
end
|
1087
|
+
|
1088
|
+
unless @lines.empty?
|
1089
|
+
if @receive_lines.call(@lines, @watcher)
|
1090
|
+
@watcher.pe.update_pos(io.pos - @fifo.bytesize)
|
1091
|
+
@lines.clear
|
1092
|
+
else
|
1093
|
+
read_more = false
|
1094
|
+
end
|
1095
|
+
end
|
1096
|
+
end while read_more
|
1097
|
+
end
|
1098
|
+
end
|
1099
|
+
|
1100
|
+
def open
|
1101
|
+
io = Fluent::FileWrapper.open(@path)
|
1102
|
+
io.seek(@watcher.pe.read_pos + @fifo.bytesize)
|
1103
|
+
@metrics.opened.inc
|
1104
|
+
io
|
1105
|
+
rescue RangeError
|
1106
|
+
io.close if io
|
1107
|
+
raise WatcherSetupError, "seek error with #{@path}: file position = #{@watcher.pe.read_pos.to_s(16)}, reading bytesize = #{@fifo.bytesize.to_s(16)}"
|
1108
|
+
rescue Errno::EACCES => e
|
1109
|
+
@log.warn "#{e}"
|
1110
|
+
nil
|
1111
|
+
rescue Errno::ENOENT
|
1112
|
+
nil
|
1113
|
+
end
|
1114
|
+
|
1115
|
+
def with_io
|
1116
|
+
if @open_on_every_update
|
1117
|
+
io = open
|
1118
|
+
begin
|
1119
|
+
yield io
|
1120
|
+
ensure
|
1121
|
+
io.close unless io.nil?
|
1122
|
+
end
|
1123
|
+
else
|
1124
|
+
@io ||= open
|
1125
|
+
yield @io
|
1126
|
+
@eof = true if @io.nil?
|
1127
|
+
end
|
1128
|
+
rescue WatcherSetupError => e
|
1129
|
+
close
|
1130
|
+
@eof = true
|
1131
|
+
raise e
|
1132
|
+
rescue
|
1133
|
+
@log.error $!.to_s
|
1134
|
+
@log.error_backtrace
|
1135
|
+
close
|
1136
|
+
@eof = true
|
1137
|
+
end
|
1138
|
+
end
|
1139
|
+
|
1140
|
+
class NullIOHandler
|
1141
|
+
def initialize
|
1142
|
+
end
|
1143
|
+
|
1144
|
+
def io
|
1145
|
+
end
|
1146
|
+
|
1147
|
+
def on_notify
|
1148
|
+
end
|
1149
|
+
|
1150
|
+
def close
|
1151
|
+
end
|
1152
|
+
|
1153
|
+
def opened?
|
1154
|
+
false
|
1155
|
+
end
|
1156
|
+
|
1157
|
+
def eof?
|
1158
|
+
true
|
1159
|
+
end
|
1160
|
+
end
|
1161
|
+
|
1162
|
+
class RotateHandler
|
1163
|
+
def initialize(log, &on_rotate)
|
1164
|
+
@log = log
|
1165
|
+
@inode = nil
|
1166
|
+
@fsize = -1 # first
|
1167
|
+
@on_rotate = on_rotate
|
1168
|
+
end
|
1169
|
+
|
1170
|
+
def on_notify(stat)
|
1171
|
+
if stat.nil?
|
1172
|
+
inode = nil
|
1173
|
+
fsize = 0
|
1174
|
+
else
|
1175
|
+
inode = stat.ino
|
1176
|
+
fsize = stat.size
|
1177
|
+
end
|
1178
|
+
|
1179
|
+
if @inode != inode || fsize < @fsize
|
1180
|
+
@on_rotate.call(stat)
|
1181
|
+
end
|
1182
|
+
@inode = inode
|
1183
|
+
@fsize = fsize
|
1184
|
+
rescue
|
1185
|
+
@log.error $!.to_s
|
1186
|
+
@log.error_backtrace
|
1187
|
+
end
|
1188
|
+
end
|
1189
|
+
|
1190
|
+
class LineBufferTimerFlusher
|
1191
|
+
attr_accessor :line_buffer
|
1192
|
+
|
1193
|
+
def initialize(log, flush_interval, &flush_method)
|
1194
|
+
@log = log
|
1195
|
+
@flush_interval = flush_interval
|
1196
|
+
@flush_method = flush_method
|
1197
|
+
@start = nil
|
1198
|
+
@line_buffer = nil
|
1199
|
+
end
|
1200
|
+
|
1201
|
+
def on_notify(tw)
|
1202
|
+
unless @start && @flush_method
|
1203
|
+
return
|
1204
|
+
end
|
1205
|
+
|
1206
|
+
if Time.now - @start >= @flush_interval
|
1207
|
+
@flush_method.call(tw, @line_buffer) if @line_buffer
|
1208
|
+
@line_buffer = nil
|
1209
|
+
@start = nil
|
1210
|
+
end
|
1211
|
+
end
|
1212
|
+
|
1213
|
+
def close(tw)
|
1214
|
+
return unless @line_buffer
|
1215
|
+
|
1216
|
+
@flush_method.call(tw, @line_buffer)
|
1217
|
+
@line_buffer = nil
|
1218
|
+
end
|
1219
|
+
|
1220
|
+
def reset_timer
|
1221
|
+
return unless @flush_interval
|
1222
|
+
|
1223
|
+
@start = Time.now
|
1224
|
+
end
|
1225
|
+
end
|
1226
|
+
end
|
1227
|
+
end
|
1228
|
+
end
|