fluentd 1.14.4-x64-mingw-ucrt

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of fluentd might be problematic. Click here for more details.

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