fluentd222 1.16.2-x86_64-linux

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