dtomasgu-fluentd 1.14.7.pre.dev

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