dtomasgu-fluentd 1.14.7.pre.dev

Sign up to get free protection for your applications and to get access to all the features.
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