fluentd222 1.16.2-x86_64-linux

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