fluentd222 1.16.2-x86_64-linux

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (562) hide show
  1. checksums.yaml +7 -0
  2. data/.deepsource.toml +13 -0
  3. data/.github/ISSUE_TEMPLATE/bug_report.yaml +71 -0
  4. data/.github/ISSUE_TEMPLATE/config.yml +5 -0
  5. data/.github/ISSUE_TEMPLATE/feature_request.yaml +39 -0
  6. data/.github/ISSUE_TEMPLATE.md +17 -0
  7. data/.github/PULL_REQUEST_TEMPLATE.md +14 -0
  8. data/.github/workflows/stale-actions.yml +24 -0
  9. data/.github/workflows/test-ruby-head.yaml +31 -0
  10. data/.github/workflows/test.yaml +32 -0
  11. data/.gitignore +30 -0
  12. data/ADOPTERS.md +5 -0
  13. data/AUTHORS +2 -0
  14. data/CHANGELOG.md +2720 -0
  15. data/CONTRIBUTING.md +45 -0
  16. data/GOVERNANCE.md +55 -0
  17. data/Gemfile +9 -0
  18. data/GithubWorkflow.md +78 -0
  19. data/LICENSE +202 -0
  20. data/MAINTAINERS.md +13 -0
  21. data/README.md +75 -0
  22. data/Rakefile +79 -0
  23. data/SECURITY.md +14 -0
  24. data/bin/fluent-binlog-reader +7 -0
  25. data/bin/fluent-ca-generate +6 -0
  26. data/bin/fluent-cap-ctl +7 -0
  27. data/bin/fluent-cat +5 -0
  28. data/bin/fluent-ctl +7 -0
  29. data/bin/fluent-debug +5 -0
  30. data/bin/fluent-gem +9 -0
  31. data/bin/fluent-plugin-config-format +5 -0
  32. data/bin/fluent-plugin-generate +5 -0
  33. data/bin/fluentd +15 -0
  34. data/code-of-conduct.md +3 -0
  35. data/docs/SECURITY_AUDIT.pdf +0 -0
  36. data/example/copy_roundrobin.conf +39 -0
  37. data/example/counter.conf +18 -0
  38. data/example/filter_stdout.conf +22 -0
  39. data/example/in_forward.conf +14 -0
  40. data/example/in_forward_client.conf +37 -0
  41. data/example/in_forward_shared_key.conf +15 -0
  42. data/example/in_forward_tls.conf +14 -0
  43. data/example/in_forward_users.conf +24 -0
  44. data/example/in_forward_workers.conf +21 -0
  45. data/example/in_http.conf +16 -0
  46. data/example/in_out_forward.conf +17 -0
  47. data/example/in_sample_blocks.conf +17 -0
  48. data/example/in_sample_with_compression.conf +23 -0
  49. data/example/in_syslog.conf +15 -0
  50. data/example/in_tail.conf +14 -0
  51. data/example/in_tcp.conf +13 -0
  52. data/example/in_udp.conf +13 -0
  53. data/example/logevents.conf +25 -0
  54. data/example/multi_filters.conf +61 -0
  55. data/example/out_copy.conf +20 -0
  56. data/example/out_exec_filter.conf +42 -0
  57. data/example/out_file.conf +13 -0
  58. data/example/out_forward.conf +35 -0
  59. data/example/out_forward_buf_file.conf +23 -0
  60. data/example/out_forward_client.conf +109 -0
  61. data/example/out_forward_heartbeat_none.conf +16 -0
  62. data/example/out_forward_sd.conf +17 -0
  63. data/example/out_forward_shared_key.conf +36 -0
  64. data/example/out_forward_tls.conf +18 -0
  65. data/example/out_forward_users.conf +65 -0
  66. data/example/out_null.conf +36 -0
  67. data/example/sd.yaml +8 -0
  68. data/example/secondary_file.conf +42 -0
  69. data/example/suppress_config_dump.conf +7 -0
  70. data/example/v0_12_filter.conf +78 -0
  71. data/example/v1_literal_example.conf +36 -0
  72. data/example/worker_section.conf +36 -0
  73. data/fluent.conf +139 -0
  74. data/fluentd.gemspec +54 -0
  75. data/lib/fluent/agent.rb +168 -0
  76. data/lib/fluent/capability.rb +87 -0
  77. data/lib/fluent/clock.rb +66 -0
  78. data/lib/fluent/command/binlog_reader.rb +244 -0
  79. data/lib/fluent/command/bundler_injection.rb +45 -0
  80. data/lib/fluent/command/ca_generate.rb +184 -0
  81. data/lib/fluent/command/cap_ctl.rb +174 -0
  82. data/lib/fluent/command/cat.rb +365 -0
  83. data/lib/fluent/command/ctl.rb +180 -0
  84. data/lib/fluent/command/debug.rb +103 -0
  85. data/lib/fluent/command/fluentd.rb +374 -0
  86. data/lib/fluent/command/plugin_config_formatter.rb +308 -0
  87. data/lib/fluent/command/plugin_generator.rb +365 -0
  88. data/lib/fluent/compat/call_super_mixin.rb +76 -0
  89. data/lib/fluent/compat/detach_process_mixin.rb +33 -0
  90. data/lib/fluent/compat/exec_util.rb +129 -0
  91. data/lib/fluent/compat/file_util.rb +54 -0
  92. data/lib/fluent/compat/filter.rb +68 -0
  93. data/lib/fluent/compat/formatter.rb +111 -0
  94. data/lib/fluent/compat/formatter_utils.rb +85 -0
  95. data/lib/fluent/compat/handle_tag_and_time_mixin.rb +62 -0
  96. data/lib/fluent/compat/handle_tag_name_mixin.rb +53 -0
  97. data/lib/fluent/compat/input.rb +49 -0
  98. data/lib/fluent/compat/output.rb +721 -0
  99. data/lib/fluent/compat/output_chain.rb +60 -0
  100. data/lib/fluent/compat/parser.rb +310 -0
  101. data/lib/fluent/compat/parser_utils.rb +40 -0
  102. data/lib/fluent/compat/propagate_default.rb +62 -0
  103. data/lib/fluent/compat/record_filter_mixin.rb +34 -0
  104. data/lib/fluent/compat/set_tag_key_mixin.rb +50 -0
  105. data/lib/fluent/compat/set_time_key_mixin.rb +69 -0
  106. data/lib/fluent/compat/socket_util.rb +165 -0
  107. data/lib/fluent/compat/string_util.rb +34 -0
  108. data/lib/fluent/compat/structured_format_mixin.rb +26 -0
  109. data/lib/fluent/compat/type_converter.rb +90 -0
  110. data/lib/fluent/config/basic_parser.rb +123 -0
  111. data/lib/fluent/config/configure_proxy.rb +424 -0
  112. data/lib/fluent/config/dsl.rb +152 -0
  113. data/lib/fluent/config/element.rb +265 -0
  114. data/lib/fluent/config/error.rb +44 -0
  115. data/lib/fluent/config/literal_parser.rb +286 -0
  116. data/lib/fluent/config/parser.rb +107 -0
  117. data/lib/fluent/config/section.rb +272 -0
  118. data/lib/fluent/config/types.rb +249 -0
  119. data/lib/fluent/config/v1_parser.rb +192 -0
  120. data/lib/fluent/config/yaml_parser/fluent_value.rb +47 -0
  121. data/lib/fluent/config/yaml_parser/loader.rb +108 -0
  122. data/lib/fluent/config/yaml_parser/parser.rb +166 -0
  123. data/lib/fluent/config/yaml_parser/section_builder.rb +107 -0
  124. data/lib/fluent/config/yaml_parser.rb +56 -0
  125. data/lib/fluent/config.rb +89 -0
  126. data/lib/fluent/configurable.rb +201 -0
  127. data/lib/fluent/counter/base_socket.rb +44 -0
  128. data/lib/fluent/counter/client.rb +297 -0
  129. data/lib/fluent/counter/error.rb +86 -0
  130. data/lib/fluent/counter/mutex_hash.rb +163 -0
  131. data/lib/fluent/counter/server.rb +273 -0
  132. data/lib/fluent/counter/store.rb +205 -0
  133. data/lib/fluent/counter/validator.rb +145 -0
  134. data/lib/fluent/counter.rb +23 -0
  135. data/lib/fluent/daemon.rb +13 -0
  136. data/lib/fluent/daemonizer.rb +88 -0
  137. data/lib/fluent/engine.rb +253 -0
  138. data/lib/fluent/env.rb +40 -0
  139. data/lib/fluent/error.rb +37 -0
  140. data/lib/fluent/event.rb +330 -0
  141. data/lib/fluent/event_router.rb +315 -0
  142. data/lib/fluent/ext_monitor_require.rb +28 -0
  143. data/lib/fluent/file_wrapper.rb +137 -0
  144. data/lib/fluent/filter.rb +21 -0
  145. data/lib/fluent/fluent_log_event_router.rb +139 -0
  146. data/lib/fluent/formatter.rb +23 -0
  147. data/lib/fluent/input.rb +21 -0
  148. data/lib/fluent/label.rb +46 -0
  149. data/lib/fluent/load.rb +34 -0
  150. data/lib/fluent/log/console_adapter.rb +66 -0
  151. data/lib/fluent/log.rb +752 -0
  152. data/lib/fluent/match.rb +187 -0
  153. data/lib/fluent/mixin.rb +31 -0
  154. data/lib/fluent/msgpack_factory.rb +111 -0
  155. data/lib/fluent/oj_options.rb +61 -0
  156. data/lib/fluent/output.rb +29 -0
  157. data/lib/fluent/output_chain.rb +23 -0
  158. data/lib/fluent/parser.rb +23 -0
  159. data/lib/fluent/plugin/bare_output.rb +104 -0
  160. data/lib/fluent/plugin/base.rb +214 -0
  161. data/lib/fluent/plugin/buf_file.rb +242 -0
  162. data/lib/fluent/plugin/buf_file_single.rb +254 -0
  163. data/lib/fluent/plugin/buf_memory.rb +34 -0
  164. data/lib/fluent/plugin/buffer/chunk.rb +240 -0
  165. data/lib/fluent/plugin/buffer/file_chunk.rb +413 -0
  166. data/lib/fluent/plugin/buffer/file_single_chunk.rb +310 -0
  167. data/lib/fluent/plugin/buffer/memory_chunk.rb +91 -0
  168. data/lib/fluent/plugin/buffer.rb +941 -0
  169. data/lib/fluent/plugin/compressable.rb +96 -0
  170. data/lib/fluent/plugin/exec_util.rb +22 -0
  171. data/lib/fluent/plugin/file_util.rb +22 -0
  172. data/lib/fluent/plugin/filter.rb +127 -0
  173. data/lib/fluent/plugin/filter_grep.rb +189 -0
  174. data/lib/fluent/plugin/filter_parser.rb +130 -0
  175. data/lib/fluent/plugin/filter_record_transformer.rb +324 -0
  176. data/lib/fluent/plugin/filter_stdout.rb +53 -0
  177. data/lib/fluent/plugin/formatter.rb +75 -0
  178. data/lib/fluent/plugin/formatter_csv.rb +78 -0
  179. data/lib/fluent/plugin/formatter_hash.rb +35 -0
  180. data/lib/fluent/plugin/formatter_json.rb +59 -0
  181. data/lib/fluent/plugin/formatter_ltsv.rb +44 -0
  182. data/lib/fluent/plugin/formatter_msgpack.rb +33 -0
  183. data/lib/fluent/plugin/formatter_out_file.rb +53 -0
  184. data/lib/fluent/plugin/formatter_single_value.rb +36 -0
  185. data/lib/fluent/plugin/formatter_stdout.rb +76 -0
  186. data/lib/fluent/plugin/formatter_tsv.rb +40 -0
  187. data/lib/fluent/plugin/in_debug_agent.rb +71 -0
  188. data/lib/fluent/plugin/in_dummy.rb +18 -0
  189. data/lib/fluent/plugin/in_exec.rb +110 -0
  190. data/lib/fluent/plugin/in_forward.rb +473 -0
  191. data/lib/fluent/plugin/in_gc_stat.rb +72 -0
  192. data/lib/fluent/plugin/in_http.rb +677 -0
  193. data/lib/fluent/plugin/in_monitor_agent.rb +412 -0
  194. data/lib/fluent/plugin/in_object_space.rb +93 -0
  195. data/lib/fluent/plugin/in_sample.rb +141 -0
  196. data/lib/fluent/plugin/in_syslog.rb +276 -0
  197. data/lib/fluent/plugin/in_tail/group_watch.rb +204 -0
  198. data/lib/fluent/plugin/in_tail/position_file.rb +269 -0
  199. data/lib/fluent/plugin/in_tail.rb +1299 -0
  200. data/lib/fluent/plugin/in_tcp.rb +226 -0
  201. data/lib/fluent/plugin/in_udp.rb +92 -0
  202. data/lib/fluent/plugin/in_unix.rb +195 -0
  203. data/lib/fluent/plugin/input.rb +75 -0
  204. data/lib/fluent/plugin/metrics.rb +119 -0
  205. data/lib/fluent/plugin/metrics_local.rb +96 -0
  206. data/lib/fluent/plugin/multi_output.rb +195 -0
  207. data/lib/fluent/plugin/out_copy.rb +120 -0
  208. data/lib/fluent/plugin/out_exec.rb +105 -0
  209. data/lib/fluent/plugin/out_exec_filter.rb +319 -0
  210. data/lib/fluent/plugin/out_file.rb +340 -0
  211. data/lib/fluent/plugin/out_forward/ack_handler.rb +176 -0
  212. data/lib/fluent/plugin/out_forward/connection_manager.rb +113 -0
  213. data/lib/fluent/plugin/out_forward/error.rb +28 -0
  214. data/lib/fluent/plugin/out_forward/failure_detector.rb +84 -0
  215. data/lib/fluent/plugin/out_forward/handshake_protocol.rb +125 -0
  216. data/lib/fluent/plugin/out_forward/load_balancer.rb +114 -0
  217. data/lib/fluent/plugin/out_forward/socket_cache.rb +142 -0
  218. data/lib/fluent/plugin/out_forward.rb +826 -0
  219. data/lib/fluent/plugin/out_http.rb +275 -0
  220. data/lib/fluent/plugin/out_null.rb +74 -0
  221. data/lib/fluent/plugin/out_relabel.rb +32 -0
  222. data/lib/fluent/plugin/out_roundrobin.rb +84 -0
  223. data/lib/fluent/plugin/out_secondary_file.rb +148 -0
  224. data/lib/fluent/plugin/out_stdout.rb +74 -0
  225. data/lib/fluent/plugin/out_stream.rb +130 -0
  226. data/lib/fluent/plugin/output.rb +1603 -0
  227. data/lib/fluent/plugin/owned_by_mixin.rb +41 -0
  228. data/lib/fluent/plugin/parser.rb +274 -0
  229. data/lib/fluent/plugin/parser_apache.rb +28 -0
  230. data/lib/fluent/plugin/parser_apache2.rb +88 -0
  231. data/lib/fluent/plugin/parser_apache_error.rb +26 -0
  232. data/lib/fluent/plugin/parser_csv.rb +114 -0
  233. data/lib/fluent/plugin/parser_json.rb +96 -0
  234. data/lib/fluent/plugin/parser_ltsv.rb +51 -0
  235. data/lib/fluent/plugin/parser_msgpack.rb +50 -0
  236. data/lib/fluent/plugin/parser_multiline.rb +152 -0
  237. data/lib/fluent/plugin/parser_nginx.rb +28 -0
  238. data/lib/fluent/plugin/parser_none.rb +36 -0
  239. data/lib/fluent/plugin/parser_regexp.rb +68 -0
  240. data/lib/fluent/plugin/parser_syslog.rb +496 -0
  241. data/lib/fluent/plugin/parser_tsv.rb +42 -0
  242. data/lib/fluent/plugin/sd_file.rb +156 -0
  243. data/lib/fluent/plugin/sd_srv.rb +135 -0
  244. data/lib/fluent/plugin/sd_static.rb +58 -0
  245. data/lib/fluent/plugin/service_discovery.rb +65 -0
  246. data/lib/fluent/plugin/socket_util.rb +22 -0
  247. data/lib/fluent/plugin/storage.rb +84 -0
  248. data/lib/fluent/plugin/storage_local.rb +162 -0
  249. data/lib/fluent/plugin/string_util.rb +22 -0
  250. data/lib/fluent/plugin.rb +206 -0
  251. data/lib/fluent/plugin_helper/cert_option.rb +191 -0
  252. data/lib/fluent/plugin_helper/child_process.rb +369 -0
  253. data/lib/fluent/plugin_helper/compat_parameters.rb +343 -0
  254. data/lib/fluent/plugin_helper/counter.rb +51 -0
  255. data/lib/fluent/plugin_helper/event_emitter.rb +100 -0
  256. data/lib/fluent/plugin_helper/event_loop.rb +170 -0
  257. data/lib/fluent/plugin_helper/extract.rb +104 -0
  258. data/lib/fluent/plugin_helper/formatter.rb +147 -0
  259. data/lib/fluent/plugin_helper/http_server/app.rb +79 -0
  260. data/lib/fluent/plugin_helper/http_server/compat/server.rb +92 -0
  261. data/lib/fluent/plugin_helper/http_server/compat/ssl_context_extractor.rb +52 -0
  262. data/lib/fluent/plugin_helper/http_server/compat/webrick_handler.rb +58 -0
  263. data/lib/fluent/plugin_helper/http_server/methods.rb +35 -0
  264. data/lib/fluent/plugin_helper/http_server/request.rb +42 -0
  265. data/lib/fluent/plugin_helper/http_server/router.rb +54 -0
  266. data/lib/fluent/plugin_helper/http_server/server.rb +94 -0
  267. data/lib/fluent/plugin_helper/http_server/ssl_context_builder.rb +41 -0
  268. data/lib/fluent/plugin_helper/http_server.rb +135 -0
  269. data/lib/fluent/plugin_helper/inject.rb +154 -0
  270. data/lib/fluent/plugin_helper/metrics.rb +129 -0
  271. data/lib/fluent/plugin_helper/parser.rb +147 -0
  272. data/lib/fluent/plugin_helper/record_accessor.rb +207 -0
  273. data/lib/fluent/plugin_helper/retry_state.rb +219 -0
  274. data/lib/fluent/plugin_helper/server.rb +828 -0
  275. data/lib/fluent/plugin_helper/service_discovery/manager.rb +146 -0
  276. data/lib/fluent/plugin_helper/service_discovery/round_robin_balancer.rb +43 -0
  277. data/lib/fluent/plugin_helper/service_discovery.rb +125 -0
  278. data/lib/fluent/plugin_helper/socket.rb +288 -0
  279. data/lib/fluent/plugin_helper/socket_option.rb +98 -0
  280. data/lib/fluent/plugin_helper/storage.rb +349 -0
  281. data/lib/fluent/plugin_helper/thread.rb +180 -0
  282. data/lib/fluent/plugin_helper/timer.rb +92 -0
  283. data/lib/fluent/plugin_helper.rb +75 -0
  284. data/lib/fluent/plugin_id.rb +93 -0
  285. data/lib/fluent/process.rb +22 -0
  286. data/lib/fluent/registry.rb +117 -0
  287. data/lib/fluent/root_agent.rb +372 -0
  288. data/lib/fluent/rpc.rb +95 -0
  289. data/lib/fluent/static_config_analysis.rb +194 -0
  290. data/lib/fluent/supervisor.rb +1076 -0
  291. data/lib/fluent/system_config.rb +189 -0
  292. data/lib/fluent/test/base.rb +78 -0
  293. data/lib/fluent/test/driver/base.rb +231 -0
  294. data/lib/fluent/test/driver/base_owned.rb +83 -0
  295. data/lib/fluent/test/driver/base_owner.rb +135 -0
  296. data/lib/fluent/test/driver/event_feeder.rb +98 -0
  297. data/lib/fluent/test/driver/filter.rb +61 -0
  298. data/lib/fluent/test/driver/formatter.rb +30 -0
  299. data/lib/fluent/test/driver/input.rb +31 -0
  300. data/lib/fluent/test/driver/multi_output.rb +53 -0
  301. data/lib/fluent/test/driver/output.rb +102 -0
  302. data/lib/fluent/test/driver/parser.rb +30 -0
  303. data/lib/fluent/test/driver/storage.rb +30 -0
  304. data/lib/fluent/test/driver/test_event_router.rb +45 -0
  305. data/lib/fluent/test/filter_test.rb +77 -0
  306. data/lib/fluent/test/formatter_test.rb +65 -0
  307. data/lib/fluent/test/helpers.rb +134 -0
  308. data/lib/fluent/test/input_test.rb +174 -0
  309. data/lib/fluent/test/log.rb +79 -0
  310. data/lib/fluent/test/output_test.rb +156 -0
  311. data/lib/fluent/test/parser_test.rb +70 -0
  312. data/lib/fluent/test/startup_shutdown.rb +44 -0
  313. data/lib/fluent/test.rb +58 -0
  314. data/lib/fluent/time.rb +512 -0
  315. data/lib/fluent/timezone.rb +171 -0
  316. data/lib/fluent/tls.rb +81 -0
  317. data/lib/fluent/unique_id.rb +39 -0
  318. data/lib/fluent/variable_store.rb +40 -0
  319. data/lib/fluent/version.rb +21 -0
  320. data/lib/fluent/win32api.rb +38 -0
  321. data/lib/fluent/winsvc.rb +100 -0
  322. data/templates/new_gem/Gemfile +3 -0
  323. data/templates/new_gem/README.md.erb +43 -0
  324. data/templates/new_gem/Rakefile +13 -0
  325. data/templates/new_gem/fluent-plugin.gemspec.erb +27 -0
  326. data/templates/new_gem/lib/fluent/plugin/filter.rb.erb +14 -0
  327. data/templates/new_gem/lib/fluent/plugin/formatter.rb.erb +14 -0
  328. data/templates/new_gem/lib/fluent/plugin/input.rb.erb +11 -0
  329. data/templates/new_gem/lib/fluent/plugin/output.rb.erb +11 -0
  330. data/templates/new_gem/lib/fluent/plugin/parser.rb.erb +15 -0
  331. data/templates/new_gem/lib/fluent/plugin/storage.rb.erb +40 -0
  332. data/templates/new_gem/test/helper.rb.erb +7 -0
  333. data/templates/new_gem/test/plugin/test_filter.rb.erb +18 -0
  334. data/templates/new_gem/test/plugin/test_formatter.rb.erb +18 -0
  335. data/templates/new_gem/test/plugin/test_input.rb.erb +18 -0
  336. data/templates/new_gem/test/plugin/test_output.rb.erb +18 -0
  337. data/templates/new_gem/test/plugin/test_parser.rb.erb +18 -0
  338. data/templates/new_gem/test/plugin/test_storage.rb.erb +18 -0
  339. data/templates/plugin_config_formatter/param.md-compact.erb +25 -0
  340. data/templates/plugin_config_formatter/param.md-table.erb +10 -0
  341. data/templates/plugin_config_formatter/param.md.erb +34 -0
  342. data/templates/plugin_config_formatter/section.md.erb +12 -0
  343. data/test/command/test_binlog_reader.rb +362 -0
  344. data/test/command/test_ca_generate.rb +70 -0
  345. data/test/command/test_cap_ctl.rb +100 -0
  346. data/test/command/test_cat.rb +128 -0
  347. data/test/command/test_ctl.rb +56 -0
  348. data/test/command/test_fluentd.rb +1291 -0
  349. data/test/command/test_plugin_config_formatter.rb +397 -0
  350. data/test/command/test_plugin_generator.rb +109 -0
  351. data/test/compat/test_calls_super.rb +166 -0
  352. data/test/compat/test_parser.rb +92 -0
  353. data/test/config/assertions.rb +42 -0
  354. data/test/config/test_config_parser.rb +551 -0
  355. data/test/config/test_configurable.rb +1784 -0
  356. data/test/config/test_configure_proxy.rb +604 -0
  357. data/test/config/test_dsl.rb +415 -0
  358. data/test/config/test_element.rb +518 -0
  359. data/test/config/test_literal_parser.rb +309 -0
  360. data/test/config/test_plugin_configuration.rb +56 -0
  361. data/test/config/test_section.rb +191 -0
  362. data/test/config/test_system_config.rb +195 -0
  363. data/test/config/test_types.rb +408 -0
  364. data/test/counter/test_client.rb +563 -0
  365. data/test/counter/test_error.rb +44 -0
  366. data/test/counter/test_mutex_hash.rb +179 -0
  367. data/test/counter/test_server.rb +589 -0
  368. data/test/counter/test_store.rb +258 -0
  369. data/test/counter/test_validator.rb +137 -0
  370. data/test/helper.rb +155 -0
  371. data/test/helpers/fuzzy_assert.rb +89 -0
  372. data/test/helpers/process_extenstion.rb +33 -0
  373. data/test/log/test_console_adapter.rb +110 -0
  374. data/test/plugin/data/2010/01/20100102-030405.log +0 -0
  375. data/test/plugin/data/2010/01/20100102-030406.log +0 -0
  376. data/test/plugin/data/2010/01/20100102.log +0 -0
  377. data/test/plugin/data/log/bar +0 -0
  378. data/test/plugin/data/log/foo/bar.log +0 -0
  379. data/test/plugin/data/log/foo/bar2 +0 -0
  380. data/test/plugin/data/log/test.log +0 -0
  381. data/test/plugin/data/sd_file/config +11 -0
  382. data/test/plugin/data/sd_file/config.json +17 -0
  383. data/test/plugin/data/sd_file/config.yaml +11 -0
  384. data/test/plugin/data/sd_file/config.yml +11 -0
  385. data/test/plugin/data/sd_file/invalid_config.yml +7 -0
  386. data/test/plugin/in_tail/test_fifo.rb +121 -0
  387. data/test/plugin/in_tail/test_io_handler.rb +150 -0
  388. data/test/plugin/in_tail/test_position_file.rb +346 -0
  389. data/test/plugin/out_forward/test_ack_handler.rb +140 -0
  390. data/test/plugin/out_forward/test_connection_manager.rb +145 -0
  391. data/test/plugin/out_forward/test_handshake_protocol.rb +112 -0
  392. data/test/plugin/out_forward/test_load_balancer.rb +106 -0
  393. data/test/plugin/out_forward/test_socket_cache.rb +174 -0
  394. data/test/plugin/test_bare_output.rb +131 -0
  395. data/test/plugin/test_base.rb +247 -0
  396. data/test/plugin/test_buf_file.rb +1314 -0
  397. data/test/plugin/test_buf_file_single.rb +898 -0
  398. data/test/plugin/test_buf_memory.rb +42 -0
  399. data/test/plugin/test_buffer.rb +1434 -0
  400. data/test/plugin/test_buffer_chunk.rb +209 -0
  401. data/test/plugin/test_buffer_file_chunk.rb +871 -0
  402. data/test/plugin/test_buffer_file_single_chunk.rb +611 -0
  403. data/test/plugin/test_buffer_memory_chunk.rb +339 -0
  404. data/test/plugin/test_compressable.rb +87 -0
  405. data/test/plugin/test_file_util.rb +96 -0
  406. data/test/plugin/test_filter.rb +368 -0
  407. data/test/plugin/test_filter_grep.rb +697 -0
  408. data/test/plugin/test_filter_parser.rb +731 -0
  409. data/test/plugin/test_filter_record_transformer.rb +577 -0
  410. data/test/plugin/test_filter_stdout.rb +207 -0
  411. data/test/plugin/test_formatter_csv.rb +136 -0
  412. data/test/plugin/test_formatter_hash.rb +38 -0
  413. data/test/plugin/test_formatter_json.rb +61 -0
  414. data/test/plugin/test_formatter_ltsv.rb +70 -0
  415. data/test/plugin/test_formatter_msgpack.rb +28 -0
  416. data/test/plugin/test_formatter_out_file.rb +116 -0
  417. data/test/plugin/test_formatter_single_value.rb +44 -0
  418. data/test/plugin/test_formatter_tsv.rb +76 -0
  419. data/test/plugin/test_in_debug_agent.rb +49 -0
  420. data/test/plugin/test_in_exec.rb +261 -0
  421. data/test/plugin/test_in_forward.rb +1178 -0
  422. data/test/plugin/test_in_gc_stat.rb +62 -0
  423. data/test/plugin/test_in_http.rb +1102 -0
  424. data/test/plugin/test_in_monitor_agent.rb +922 -0
  425. data/test/plugin/test_in_object_space.rb +66 -0
  426. data/test/plugin/test_in_sample.rb +190 -0
  427. data/test/plugin/test_in_syslog.rb +505 -0
  428. data/test/plugin/test_in_tail.rb +3125 -0
  429. data/test/plugin/test_in_tcp.rb +328 -0
  430. data/test/plugin/test_in_udp.rb +296 -0
  431. data/test/plugin/test_in_unix.rb +181 -0
  432. data/test/plugin/test_input.rb +137 -0
  433. data/test/plugin/test_metadata.rb +89 -0
  434. data/test/plugin/test_metrics.rb +294 -0
  435. data/test/plugin/test_metrics_local.rb +96 -0
  436. data/test/plugin/test_multi_output.rb +204 -0
  437. data/test/plugin/test_out_copy.rb +308 -0
  438. data/test/plugin/test_out_exec.rb +312 -0
  439. data/test/plugin/test_out_exec_filter.rb +606 -0
  440. data/test/plugin/test_out_file.rb +1038 -0
  441. data/test/plugin/test_out_forward.rb +1361 -0
  442. data/test/plugin/test_out_http.rb +429 -0
  443. data/test/plugin/test_out_null.rb +105 -0
  444. data/test/plugin/test_out_relabel.rb +28 -0
  445. data/test/plugin/test_out_roundrobin.rb +146 -0
  446. data/test/plugin/test_out_secondary_file.rb +458 -0
  447. data/test/plugin/test_out_stdout.rb +205 -0
  448. data/test/plugin/test_out_stream.rb +103 -0
  449. data/test/plugin/test_output.rb +1334 -0
  450. data/test/plugin/test_output_as_buffered.rb +2024 -0
  451. data/test/plugin/test_output_as_buffered_backup.rb +363 -0
  452. data/test/plugin/test_output_as_buffered_compress.rb +179 -0
  453. data/test/plugin/test_output_as_buffered_overflow.rb +250 -0
  454. data/test/plugin/test_output_as_buffered_retries.rb +966 -0
  455. data/test/plugin/test_output_as_buffered_secondary.rb +882 -0
  456. data/test/plugin/test_output_as_standard.rb +374 -0
  457. data/test/plugin/test_owned_by.rb +34 -0
  458. data/test/plugin/test_parser.rb +399 -0
  459. data/test/plugin/test_parser_apache.rb +42 -0
  460. data/test/plugin/test_parser_apache2.rb +47 -0
  461. data/test/plugin/test_parser_apache_error.rb +45 -0
  462. data/test/plugin/test_parser_csv.rb +200 -0
  463. data/test/plugin/test_parser_json.rb +138 -0
  464. data/test/plugin/test_parser_labeled_tsv.rb +160 -0
  465. data/test/plugin/test_parser_multiline.rb +111 -0
  466. data/test/plugin/test_parser_nginx.rb +88 -0
  467. data/test/plugin/test_parser_none.rb +52 -0
  468. data/test/plugin/test_parser_regexp.rb +284 -0
  469. data/test/plugin/test_parser_syslog.rb +650 -0
  470. data/test/plugin/test_parser_tsv.rb +122 -0
  471. data/test/plugin/test_sd_file.rb +228 -0
  472. data/test/plugin/test_sd_srv.rb +230 -0
  473. data/test/plugin/test_storage.rb +166 -0
  474. data/test/plugin/test_storage_local.rb +335 -0
  475. data/test/plugin/test_string_util.rb +26 -0
  476. data/test/plugin_helper/data/cert/cert-key.pem +27 -0
  477. data/test/plugin_helper/data/cert/cert-with-CRLF.pem +19 -0
  478. data/test/plugin_helper/data/cert/cert-with-no-newline.pem +19 -0
  479. data/test/plugin_helper/data/cert/cert.pem +19 -0
  480. data/test/plugin_helper/data/cert/cert_chains/ca-cert-key.pem +27 -0
  481. data/test/plugin_helper/data/cert/cert_chains/ca-cert.pem +20 -0
  482. data/test/plugin_helper/data/cert/cert_chains/cert-key.pem +27 -0
  483. data/test/plugin_helper/data/cert/cert_chains/cert.pem +40 -0
  484. data/test/plugin_helper/data/cert/empty.pem +0 -0
  485. data/test/plugin_helper/data/cert/generate_cert.rb +125 -0
  486. data/test/plugin_helper/data/cert/with_ca/ca-cert-key-pass.pem +30 -0
  487. data/test/plugin_helper/data/cert/with_ca/ca-cert-key.pem +27 -0
  488. data/test/plugin_helper/data/cert/with_ca/ca-cert-pass.pem +20 -0
  489. data/test/plugin_helper/data/cert/with_ca/ca-cert.pem +20 -0
  490. data/test/plugin_helper/data/cert/with_ca/cert-key-pass.pem +30 -0
  491. data/test/plugin_helper/data/cert/with_ca/cert-key.pem +27 -0
  492. data/test/plugin_helper/data/cert/with_ca/cert-pass.pem +21 -0
  493. data/test/plugin_helper/data/cert/with_ca/cert.pem +21 -0
  494. data/test/plugin_helper/data/cert/without_ca/cert-key-pass.pem +30 -0
  495. data/test/plugin_helper/data/cert/without_ca/cert-key.pem +27 -0
  496. data/test/plugin_helper/data/cert/without_ca/cert-pass.pem +20 -0
  497. data/test/plugin_helper/data/cert/without_ca/cert.pem +20 -0
  498. data/test/plugin_helper/http_server/test_app.rb +65 -0
  499. data/test/plugin_helper/http_server/test_route.rb +32 -0
  500. data/test/plugin_helper/service_discovery/test_manager.rb +93 -0
  501. data/test/plugin_helper/service_discovery/test_round_robin_balancer.rb +21 -0
  502. data/test/plugin_helper/test_cert_option.rb +25 -0
  503. data/test/plugin_helper/test_child_process.rb +862 -0
  504. data/test/plugin_helper/test_compat_parameters.rb +358 -0
  505. data/test/plugin_helper/test_event_emitter.rb +80 -0
  506. data/test/plugin_helper/test_event_loop.rb +52 -0
  507. data/test/plugin_helper/test_extract.rb +194 -0
  508. data/test/plugin_helper/test_formatter.rb +255 -0
  509. data/test/plugin_helper/test_http_server_helper.rb +372 -0
  510. data/test/plugin_helper/test_inject.rb +561 -0
  511. data/test/plugin_helper/test_metrics.rb +137 -0
  512. data/test/plugin_helper/test_parser.rb +264 -0
  513. data/test/plugin_helper/test_record_accessor.rb +238 -0
  514. data/test/plugin_helper/test_retry_state.rb +1006 -0
  515. data/test/plugin_helper/test_server.rb +1895 -0
  516. data/test/plugin_helper/test_service_discovery.rb +165 -0
  517. data/test/plugin_helper/test_socket.rb +146 -0
  518. data/test/plugin_helper/test_storage.rb +542 -0
  519. data/test/plugin_helper/test_thread.rb +164 -0
  520. data/test/plugin_helper/test_timer.rb +130 -0
  521. data/test/scripts/exec_script.rb +32 -0
  522. data/test/scripts/fluent/plugin/formatter1/formatter_test1.rb +7 -0
  523. data/test/scripts/fluent/plugin/formatter2/formatter_test2.rb +7 -0
  524. data/test/scripts/fluent/plugin/formatter_known.rb +8 -0
  525. data/test/scripts/fluent/plugin/out_test.rb +81 -0
  526. data/test/scripts/fluent/plugin/out_test2.rb +80 -0
  527. data/test/scripts/fluent/plugin/parser_known.rb +4 -0
  528. data/test/test_capability.rb +74 -0
  529. data/test/test_clock.rb +164 -0
  530. data/test/test_config.rb +369 -0
  531. data/test/test_configdsl.rb +148 -0
  532. data/test/test_daemonizer.rb +91 -0
  533. data/test/test_engine.rb +203 -0
  534. data/test/test_event.rb +531 -0
  535. data/test/test_event_router.rb +348 -0
  536. data/test/test_event_time.rb +199 -0
  537. data/test/test_file_wrapper.rb +53 -0
  538. data/test/test_filter.rb +121 -0
  539. data/test/test_fluent_log_event_router.rb +99 -0
  540. data/test/test_formatter.rb +369 -0
  541. data/test/test_input.rb +31 -0
  542. data/test/test_log.rb +1076 -0
  543. data/test/test_match.rb +148 -0
  544. data/test/test_mixin.rb +351 -0
  545. data/test/test_msgpack_factory.rb +50 -0
  546. data/test/test_oj_options.rb +55 -0
  547. data/test/test_output.rb +278 -0
  548. data/test/test_plugin.rb +251 -0
  549. data/test/test_plugin_classes.rb +370 -0
  550. data/test/test_plugin_helper.rb +81 -0
  551. data/test/test_plugin_id.rb +119 -0
  552. data/test/test_process.rb +14 -0
  553. data/test/test_root_agent.rb +951 -0
  554. data/test/test_static_config_analysis.rb +177 -0
  555. data/test/test_supervisor.rb +821 -0
  556. data/test/test_test_drivers.rb +136 -0
  557. data/test/test_time_formatter.rb +301 -0
  558. data/test/test_time_parser.rb +362 -0
  559. data/test/test_tls.rb +65 -0
  560. data/test/test_unique_id.rb +47 -0
  561. data/test/test_variable_store.rb +65 -0
  562. metadata +1183 -0
@@ -0,0 +1,1603 @@
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 'fluent/env'
18
+ require 'fluent/error'
19
+ require 'fluent/plugin/base'
20
+ require 'fluent/plugin/buffer'
21
+ require 'fluent/plugin_helper/record_accessor'
22
+ require 'fluent/msgpack_factory'
23
+ require 'fluent/log'
24
+ require 'fluent/plugin_id'
25
+ require 'fluent/plugin_helper'
26
+ require 'fluent/timezone'
27
+ require 'fluent/unique_id'
28
+ require 'fluent/clock'
29
+ require 'fluent/ext_monitor_require'
30
+
31
+ require 'time'
32
+
33
+ module Fluent
34
+ module Plugin
35
+ class Output < Base
36
+ include PluginId
37
+ include PluginLoggerMixin
38
+ include PluginHelper::Mixin
39
+ include UniqueId::Mixin
40
+
41
+ helpers_internal :thread, :retry_state, :metrics
42
+
43
+ CHUNK_KEY_PATTERN = /^[-_.@a-zA-Z0-9]+$/
44
+ CHUNK_KEY_PLACEHOLDER_PATTERN = /\$\{([-_.@$a-zA-Z0-9]+)\}/
45
+ CHUNK_TAG_PLACEHOLDER_PATTERN = /\$\{(tag(?:\[-?\d+\])?)\}/
46
+ CHUNK_ID_PLACEHOLDER_PATTERN = /\$\{chunk_id\}/
47
+
48
+ CHUNKING_FIELD_WARN_NUM = 4
49
+
50
+ config_param :time_as_integer, :bool, default: false
51
+ desc 'The threshold to show slow flush logs'
52
+ config_param :slow_flush_log_threshold, :float, default: 20.0
53
+
54
+ # `<buffer>` and `<secondary>` sections are available only when '#format' and '#write' are implemented
55
+ config_section :buffer, param_name: :buffer_config, init: true, required: false, multi: false, final: true do
56
+ config_argument :chunk_keys, :array, value_type: :string, default: []
57
+ config_param :@type, :string, default: 'memory', alias: :type
58
+
59
+ config_param :timekey, :time, default: nil # range size to be used: `time.to_i / @timekey`
60
+ config_param :timekey_wait, :time, default: 600
61
+ # These are for #extract_placeholders
62
+ config_param :timekey_use_utc, :bool, default: false # default is localtime
63
+ config_param :timekey_zone, :string, default: Time.now.strftime('%z') # e.g., "-0700" or "Asia/Tokyo"
64
+
65
+ desc 'If true, plugin will try to flush buffer just before shutdown.'
66
+ config_param :flush_at_shutdown, :bool, default: nil # change default by buffer_plugin.persistent?
67
+
68
+ desc 'How to enqueue chunks to be flushed. "interval" flushes per flush_interval, "immediate" flushes just after event arrival.'
69
+ config_param :flush_mode, :enum, list: [:default, :lazy, :interval, :immediate], default: :default
70
+ config_param :flush_interval, :time, default: 60, desc: 'The interval between buffer chunk flushes.'
71
+
72
+ config_param :flush_thread_count, :integer, default: 1, desc: 'The number of threads to flush the buffer.'
73
+
74
+ config_param :flush_thread_interval, :float, default: 1.0, desc: 'Seconds to sleep between checks for buffer flushes in flush threads.'
75
+ config_param :flush_thread_burst_interval, :float, default: 1.0, desc: 'Seconds to sleep between flushes when many buffer chunks are queued.'
76
+
77
+ config_param :delayed_commit_timeout, :time, default: 60, desc: 'Seconds of timeout for buffer chunks to be committed by plugins later.'
78
+
79
+ config_param :overflow_action, :enum, list: [:throw_exception, :block, :drop_oldest_chunk], default: :throw_exception, desc: 'The action when the size of buffer exceeds the limit.'
80
+
81
+ config_param :retry_forever, :bool, default: false, desc: 'If true, plugin will ignore retry_timeout and retry_max_times options and retry flushing forever.'
82
+ config_param :retry_timeout, :time, default: 72 * 60 * 60, desc: 'The maximum seconds to retry to flush while failing, until plugin discards buffer chunks.'
83
+ # 72hours == 17 times with exponential backoff (not to change default behavior)
84
+ config_param :retry_max_times, :integer, default: nil, desc: 'The maximum number of times to retry to flush while failing.'
85
+
86
+ config_param :retry_secondary_threshold, :float, default: 0.8, desc: 'ratio of retry_timeout to switch to use secondary while failing.'
87
+ # exponential backoff sequence will be initialized at the time of this threshold
88
+
89
+ desc 'How to wait next retry to flush buffer.'
90
+ config_param :retry_type, :enum, list: [:exponential_backoff, :periodic], default: :exponential_backoff
91
+ ### Periodic -> fixed :retry_wait
92
+ ### Exponential backoff: k is number of retry times
93
+ # c: constant factor, @retry_wait
94
+ # b: base factor, @retry_exponential_backoff_base
95
+ # k: times
96
+ # total retry time: c + c * b^1 + (...) + c*b^k = c*b^(k+1) - 1
97
+ config_param :retry_wait, :time, default: 1, desc: 'Seconds to wait before next retry to flush, or constant factor of exponential backoff.'
98
+ config_param :retry_exponential_backoff_base, :float, default: 2, desc: 'The base number of exponential backoff for retries.'
99
+ config_param :retry_max_interval, :time, default: nil, desc: 'The maximum interval seconds for exponential backoff between retries while failing.'
100
+
101
+ config_param :retry_randomize, :bool, default: true, desc: 'If true, output plugin will retry after randomized interval not to do burst retries.'
102
+ end
103
+
104
+ config_section :secondary, param_name: :secondary_config, required: false, multi: false, final: true do
105
+ config_param :@type, :string, default: nil, alias: :type
106
+ config_section :buffer, required: false, multi: false do
107
+ # dummy to detect invalid specification for here
108
+ end
109
+ config_section :secondary, required: false, multi: false do
110
+ # dummy to detect invalid specification for here
111
+ end
112
+ end
113
+
114
+ def process(tag, es)
115
+ raise NotImplementedError, "BUG: output plugins MUST implement this method"
116
+ end
117
+
118
+ def write(chunk)
119
+ raise NotImplementedError, "BUG: output plugins MUST implement this method"
120
+ end
121
+
122
+ def try_write(chunk)
123
+ raise NotImplementedError, "BUG: output plugins MUST implement this method"
124
+ end
125
+
126
+ def format(tag, time, record)
127
+ # standard msgpack_event_stream chunk will be used if this method is not implemented in plugin subclass
128
+ raise NotImplementedError, "BUG: output plugins MUST implement this method"
129
+ end
130
+
131
+ def formatted_to_msgpack_binary?
132
+ # To indicate custom format method (#format) returns msgpack binary or not.
133
+ # If #format returns msgpack binary, override this method to return true.
134
+ false
135
+ end
136
+
137
+ # Compatibility for existing plugins
138
+ def formatted_to_msgpack_binary
139
+ formatted_to_msgpack_binary?
140
+ end
141
+
142
+ def prefer_buffered_processing
143
+ # override this method to return false only when all of these are true:
144
+ # * plugin has both implementation for buffered and non-buffered methods
145
+ # * plugin is expected to work as non-buffered plugin if no `<buffer>` sections specified
146
+ true
147
+ end
148
+
149
+ def prefer_delayed_commit
150
+ # override this method to decide which is used of `write` or `try_write` if both are implemented
151
+ true
152
+ end
153
+
154
+ def multi_workers_ready?
155
+ false
156
+ end
157
+
158
+ # Internal states
159
+ FlushThreadState = Struct.new(:thread, :next_clock, :mutex, :cond_var)
160
+ DequeuedChunkInfo = Struct.new(:chunk_id, :time, :timeout) do
161
+ def expired?
162
+ time + timeout < Time.now
163
+ end
164
+ end
165
+
166
+ attr_reader :as_secondary, :delayed_commit, :delayed_commit_timeout, :timekey_zone
167
+
168
+ # for tests
169
+ attr_reader :buffer, :retry, :secondary, :chunk_keys, :chunk_key_accessors, :chunk_key_time, :chunk_key_tag
170
+ attr_accessor :output_enqueue_thread_waiting, :dequeued_chunks, :dequeued_chunks_mutex
171
+ # output_enqueue_thread_waiting: for test of output.rb itself
172
+ attr_accessor :retry_for_error_chunk # if true, error flush will be retried even if under_plugin_development is true
173
+
174
+ def num_errors
175
+ @num_errors_metrics.get
176
+ end
177
+
178
+ def emit_count
179
+ @emit_count_metrics.get
180
+ end
181
+
182
+ def emit_size
183
+ @emit_size_metrics.get
184
+ end
185
+
186
+ def emit_records
187
+ @emit_records_metrics.get
188
+ end
189
+
190
+ def write_count
191
+ @write_count_metrics.get
192
+ end
193
+
194
+ def rollback_count
195
+ @rollback_count_metrics.get
196
+ end
197
+
198
+ def initialize
199
+ super
200
+ @counter_mutex = Mutex.new
201
+ @flush_thread_mutex = Mutex.new
202
+ @buffering = false
203
+ @delayed_commit = false
204
+ @as_secondary = false
205
+ @primary_instance = nil
206
+
207
+ # TODO: well organized counters
208
+ @num_errors_metrics = nil
209
+ @emit_count_metrics = nil
210
+ @emit_records_metrics = nil
211
+ @emit_size_metrics = nil
212
+ @write_count_metrics = nil
213
+ @rollback_count_metrics = nil
214
+ @flush_time_count_metrics = nil
215
+ @slow_flush_count_metrics = nil
216
+ @enable_size_metrics = false
217
+
218
+ # How to process events is decided here at once, but it will be decided in delayed way on #configure & #start
219
+ if implement?(:synchronous)
220
+ if implement?(:buffered) || implement?(:delayed_commit)
221
+ @buffering = nil # do #configure or #start to determine this for full-featured plugins
222
+ else
223
+ @buffering = false
224
+ end
225
+ else
226
+ @buffering = true
227
+ end
228
+ @custom_format = implement?(:custom_format)
229
+ @enable_msgpack_streamer = false # decided later
230
+
231
+ @buffer = nil
232
+ @secondary = nil
233
+ @retry = nil
234
+ @dequeued_chunks = nil
235
+ @dequeued_chunks_mutex = nil
236
+ @output_enqueue_thread = nil
237
+ @output_flush_threads = nil
238
+ @output_flush_thread_current_position = 0
239
+
240
+ @simple_chunking = nil
241
+ @chunk_keys = @chunk_key_accessors = @chunk_key_time = @chunk_key_tag = nil
242
+ @flush_mode = nil
243
+ @timekey_zone = nil
244
+
245
+ @retry_for_error_chunk = false
246
+ end
247
+
248
+ def acts_as_secondary(primary)
249
+ @as_secondary = true
250
+ @primary_instance = primary
251
+ @chunk_keys = @primary_instance.chunk_keys || []
252
+ @chunk_key_tag = @primary_instance.chunk_key_tag || false
253
+ if @primary_instance.chunk_key_time
254
+ @chunk_key_time = @primary_instance.chunk_key_time
255
+ @timekey_zone = @primary_instance.timekey_zone
256
+ @output_time_formatter_cache = {}
257
+ end
258
+ self.context_router = primary.context_router
259
+
260
+ singleton_class.module_eval do
261
+ define_method(:commit_write){ |chunk_id| @primary_instance.commit_write(chunk_id, delayed: delayed_commit, secondary: true) }
262
+ define_method(:rollback_write){ |chunk_id, update_retry: true| @primary_instance.rollback_write(chunk_id, update_retry) }
263
+ end
264
+ end
265
+
266
+ def configure(conf)
267
+ unless implement?(:synchronous) || implement?(:buffered) || implement?(:delayed_commit)
268
+ raise "BUG: output plugin must implement some methods. see developer documents."
269
+ end
270
+
271
+ has_buffer_section = (conf.elements(name: 'buffer').size > 0)
272
+ has_flush_interval = conf.has_key?('flush_interval')
273
+
274
+ super
275
+
276
+ @num_errors_metrics = metrics_create(namespace: "fluentd", subsystem: "output", name: "num_errors", help_text: "Number of count num errors")
277
+ @emit_count_metrics = metrics_create(namespace: "fluentd", subsystem: "output", name: "emit_count", help_text: "Number of count emits")
278
+ @emit_records_metrics = metrics_create(namespace: "fluentd", subsystem: "output", name: "emit_records", help_text: "Number of emit records")
279
+ @emit_size_metrics = metrics_create(namespace: "fluentd", subsystem: "output", name: "emit_size", help_text: "Total size of emit events")
280
+ @write_count_metrics = metrics_create(namespace: "fluentd", subsystem: "output", name: "write_count", help_text: "Number of writing events")
281
+ @rollback_count_metrics = metrics_create(namespace: "fluentd", subsystem: "output", name: "rollback_count", help_text: "Number of rollbacking operations")
282
+ @flush_time_count_metrics = metrics_create(namespace: "fluentd", subsystem: "output", name: "flush_time_count", help_text: "Count of flush time")
283
+ @slow_flush_count_metrics = metrics_create(namespace: "fluentd", subsystem: "output", name: "slow_flush_count", help_text: "Count of slow flush occurred time(s)")
284
+
285
+ if has_buffer_section
286
+ unless implement?(:buffered) || implement?(:delayed_commit)
287
+ raise Fluent::ConfigError, "<buffer> section is configured, but plugin '#{self.class}' doesn't support buffering"
288
+ end
289
+ @buffering = true
290
+ else # no buffer sections
291
+ if implement?(:synchronous)
292
+ if !implement?(:buffered) && !implement?(:delayed_commit)
293
+ if @as_secondary
294
+ raise Fluent::ConfigError, "secondary plugin '#{self.class}' must support buffering, but doesn't."
295
+ end
296
+ @buffering = false
297
+ else
298
+ if @as_secondary
299
+ # secondary plugin always works as buffered plugin without buffer instance
300
+ @buffering = true
301
+ else
302
+ # @buffering.nil? shows that enabling buffering or not will be decided in lazy way in #start
303
+ @buffering = nil
304
+ end
305
+ end
306
+ else # buffered or delayed_commit is supported by `unless` of first line in this method
307
+ @buffering = true
308
+ end
309
+ end
310
+ # Enable to update record size metrics or not
311
+ @enable_size_metrics = !!system_config.enable_size_metrics
312
+
313
+ if @as_secondary
314
+ if !@buffering && !@buffering.nil?
315
+ raise Fluent::ConfigError, "secondary plugin '#{self.class}' must support buffering, but doesn't"
316
+ end
317
+ end
318
+
319
+ if (@buffering || @buffering.nil?) && !@as_secondary
320
+ # When @buffering.nil?, @buffer_config was initialized with default value for all parameters.
321
+ # If so, this configuration MUST success.
322
+ @chunk_keys = @buffer_config.chunk_keys.dup
323
+ @chunk_key_time = !!@chunk_keys.delete('time')
324
+ @chunk_key_tag = !!@chunk_keys.delete('tag')
325
+ if @chunk_keys.any? { |key|
326
+ begin
327
+ k = Fluent::PluginHelper::RecordAccessor::Accessor.parse_parameter(key)
328
+ if k.is_a?(String)
329
+ k !~ CHUNK_KEY_PATTERN
330
+ else
331
+ if key.start_with?('$[')
332
+ raise Fluent::ConfigError, "in chunk_keys: bracket notation is not allowed"
333
+ else
334
+ false
335
+ end
336
+ end
337
+ rescue => e
338
+ raise Fluent::ConfigError, "in chunk_keys: #{e.message}"
339
+ end
340
+ }
341
+ raise Fluent::ConfigError, "chunk_keys specification includes invalid char"
342
+ else
343
+ @chunk_key_accessors = Hash[@chunk_keys.map { |key| [key.to_sym, Fluent::PluginHelper::RecordAccessor::Accessor.new(key)] }]
344
+ end
345
+
346
+ if @chunk_key_time
347
+ raise Fluent::ConfigError, "<buffer ...> argument includes 'time', but timekey is not configured" unless @buffer_config.timekey
348
+ Fluent::Timezone.validate!(@buffer_config.timekey_zone)
349
+ @timekey_zone = @buffer_config.timekey_use_utc ? '+0000' : @buffer_config.timekey_zone
350
+ @timekey = @buffer_config.timekey
351
+ if @timekey <= 0
352
+ raise Fluent::ConfigError, "timekey should be greater than 0. current timekey: #{@timekey}"
353
+ end
354
+ @timekey_use_utc = @buffer_config.timekey_use_utc
355
+ @offset = Fluent::Timezone.utc_offset(@timekey_zone)
356
+ @calculate_offset = @offset.respond_to?(:call) ? @offset : nil
357
+ @output_time_formatter_cache = {}
358
+ end
359
+
360
+ if (@chunk_key_tag ? 1 : 0) + @chunk_keys.size >= CHUNKING_FIELD_WARN_NUM
361
+ log.warn "many chunk keys specified, and it may cause too many chunks on your system."
362
+ end
363
+
364
+ # no chunk keys or only tags (chunking can be done without iterating event stream)
365
+ @simple_chunking = !@chunk_key_time && @chunk_keys.empty?
366
+
367
+ @flush_mode = @buffer_config.flush_mode
368
+ if @flush_mode == :default
369
+ if has_flush_interval
370
+ log.info "'flush_interval' is configured at out side of <buffer>. 'flush_mode' is set to 'interval' to keep existing behaviour"
371
+ @flush_mode = :interval
372
+ else
373
+ @flush_mode = (@chunk_key_time ? :lazy : :interval)
374
+ end
375
+ end
376
+
377
+ buffer_type = @buffer_config[:@type]
378
+ buffer_conf = conf.elements(name: 'buffer').first || Fluent::Config::Element.new('buffer', '', {}, [])
379
+ @buffer = Plugin.new_buffer(buffer_type, parent: self)
380
+ @buffer.configure(buffer_conf)
381
+ keep_buffer_config_compat
382
+ @buffer.enable_update_timekeys if @chunk_key_time
383
+
384
+ @flush_at_shutdown = @buffer_config.flush_at_shutdown
385
+ if @flush_at_shutdown.nil?
386
+ @flush_at_shutdown = if @buffer.persistent?
387
+ false
388
+ else
389
+ true # flush_at_shutdown is true in default for on-memory buffer
390
+ end
391
+ elsif !@flush_at_shutdown && !@buffer.persistent?
392
+ buf_type = Plugin.lookup_type_from_class(@buffer.class)
393
+ log.warn "'flush_at_shutdown' is false, and buffer plugin '#{buf_type}' is not persistent buffer."
394
+ log.warn "your configuration will lose buffered data at shutdown. please confirm your configuration again."
395
+ end
396
+
397
+ if (@flush_mode != :interval) && buffer_conf.has_key?('flush_interval')
398
+ if buffer_conf.has_key?('flush_mode')
399
+ raise Fluent::ConfigError, "'flush_interval' can't be specified when 'flush_mode' is not 'interval' explicitly: '#{@flush_mode}'"
400
+ else
401
+ log.warn "'flush_interval' is ignored because default 'flush_mode' is not 'interval': '#{@flush_mode}'"
402
+ end
403
+ end
404
+
405
+ if @buffer.queued_chunks_limit_size.nil?
406
+ @buffer.queued_chunks_limit_size = @buffer_config.flush_thread_count
407
+ end
408
+ end
409
+
410
+ if @secondary_config
411
+ raise Fluent::ConfigError, "Invalid <secondary> section for non-buffered plugin" unless @buffering
412
+ raise Fluent::ConfigError, "<secondary> section cannot have <buffer> section" if @secondary_config.buffer
413
+ raise Fluent::ConfigError, "<secondary> section cannot have <secondary> section" if @secondary_config.secondary
414
+ if @buffer_config.retry_forever
415
+ log.warn "<secondary> with 'retry_forever', only unrecoverable errors are moved to secondary"
416
+ end
417
+
418
+ secondary_type = @secondary_config[:@type]
419
+ unless secondary_type
420
+ secondary_type = conf['@type'] # primary plugin type
421
+ end
422
+ secondary_conf = conf.elements(name: 'secondary').first
423
+ @secondary = Plugin.new_output(secondary_type)
424
+ unless @secondary.respond_to?(:acts_as_secondary)
425
+ raise Fluent::ConfigError, "Failed to setup secondary plugin in '#{conf['@type']}'. '#{secondary_type}' plugin in not allowed due to non buffered output"
426
+ end
427
+ @secondary.acts_as_secondary(self)
428
+ @secondary.configure(secondary_conf)
429
+ if (@secondary.class.to_s != "Fluent::Plugin::SecondaryFileOutput") &&
430
+ (self.class != @secondary.class) &&
431
+ (@custom_format || @secondary.implement?(:custom_format))
432
+ log.warn "Use different plugin for secondary. Check the plugin works with primary like secondary_file", primary: self.class.to_s, secondary: @secondary.class.to_s
433
+ end
434
+ else
435
+ @secondary = nil
436
+ end
437
+
438
+ self
439
+ end
440
+
441
+ def keep_buffer_config_compat
442
+ # Need this to call `@buffer_config.disable_chunk_backup` just as before,
443
+ # since some plugins may use this option in this way.
444
+ @buffer_config[:disable_chunk_backup] = @buffer.disable_chunk_backup
445
+ end
446
+
447
+ def start
448
+ super
449
+
450
+ if @buffering.nil?
451
+ @buffering = prefer_buffered_processing
452
+ if !@buffering && @buffer
453
+ @buffer.terminate # it's not started, so terminate will be enough
454
+ # At here, this plugin works as non-buffered plugin.
455
+ # Un-assign @buffer not to show buffering metrics (e.g., in_monitor_agent)
456
+ @buffer = nil
457
+ end
458
+ end
459
+
460
+ if @buffering
461
+ m = method(:emit_buffered)
462
+ singleton_class.module_eval do
463
+ define_method(:emit_events, m)
464
+ end
465
+
466
+ @custom_format = implement?(:custom_format)
467
+ @enable_msgpack_streamer = @custom_format ? formatted_to_msgpack_binary : true
468
+ @delayed_commit = if implement?(:buffered) && implement?(:delayed_commit)
469
+ prefer_delayed_commit
470
+ else
471
+ implement?(:delayed_commit)
472
+ end
473
+ @delayed_commit_timeout = @buffer_config.delayed_commit_timeout
474
+ else # !@buffering
475
+ m = method(:emit_sync)
476
+ singleton_class.module_eval do
477
+ define_method(:emit_events, m)
478
+ end
479
+ end
480
+
481
+ if @buffering && !@as_secondary
482
+ @retry = nil
483
+ @retry_mutex = Mutex.new
484
+
485
+ @buffer.start
486
+
487
+ @output_enqueue_thread = nil
488
+ @output_enqueue_thread_running = true
489
+
490
+ @output_flush_threads = []
491
+ @output_flush_threads_mutex = Mutex.new
492
+ @output_flush_threads_running = true
493
+
494
+ # mainly for test: detect enqueue works as code below:
495
+ # @output.interrupt_flushes
496
+ # # emits
497
+ # @output.enqueue_thread_wait
498
+ @output_flush_interrupted = false
499
+ @output_enqueue_thread_mutex = Mutex.new
500
+ @output_enqueue_thread_waiting = false
501
+
502
+ @dequeued_chunks = []
503
+ @dequeued_chunks_mutex = Mutex.new
504
+
505
+ @output_flush_thread_current_position = 0
506
+ @buffer_config.flush_thread_count.times do |i|
507
+ thread_title = "flush_thread_#{i}".to_sym
508
+ thread_state = FlushThreadState.new(nil, nil, Mutex.new, ConditionVariable.new)
509
+ thread = thread_create(thread_title) do
510
+ flush_thread_run(thread_state)
511
+ end
512
+ thread_state.thread = thread
513
+ @output_flush_threads_mutex.synchronize do
514
+ @output_flush_threads << thread_state
515
+ end
516
+ end
517
+
518
+ if !@under_plugin_development && (@flush_mode == :interval || @chunk_key_time)
519
+ @output_enqueue_thread = thread_create(:enqueue_thread, &method(:enqueue_thread_run))
520
+ end
521
+ end
522
+ @secondary.start if @secondary
523
+ end
524
+
525
+ def after_start
526
+ super
527
+ @secondary.after_start if @secondary
528
+ end
529
+
530
+ def stop
531
+ @secondary.stop if @secondary
532
+ @buffer.stop if @buffering && @buffer
533
+
534
+ super
535
+ end
536
+
537
+ def before_shutdown
538
+ @secondary.before_shutdown if @secondary
539
+
540
+ if @buffering && @buffer
541
+ if @flush_at_shutdown
542
+ force_flush
543
+ end
544
+ @buffer.before_shutdown
545
+ # Need to ensure to stop enqueueing ... after #shutdown, we cannot write any data
546
+ @output_enqueue_thread_running = false
547
+ if @output_enqueue_thread && @output_enqueue_thread.alive?
548
+ @output_enqueue_thread.wakeup
549
+ @output_enqueue_thread.join
550
+ end
551
+ end
552
+
553
+ super
554
+ end
555
+
556
+ def shutdown
557
+ @secondary.shutdown if @secondary
558
+ @buffer.shutdown if @buffering && @buffer
559
+
560
+ super
561
+ end
562
+
563
+ def after_shutdown
564
+ try_rollback_all if @buffering && !@as_secondary # rollback regardless with @delayed_commit, because secondary may do it
565
+ @secondary.after_shutdown if @secondary
566
+
567
+ if @buffering && @buffer
568
+ @buffer.after_shutdown
569
+
570
+ @output_flush_threads_running = false
571
+ if @output_flush_threads && !@output_flush_threads.empty?
572
+ @output_flush_threads.each do |state|
573
+ # to wakeup thread and make it to stop by itself
574
+ state.mutex.synchronize {
575
+ if state.thread && state.thread.status
576
+ state.next_clock = 0
577
+ state.cond_var.signal
578
+ end
579
+ }
580
+ Thread.pass
581
+ state.thread.join
582
+ end
583
+ end
584
+ end
585
+
586
+ super
587
+ end
588
+
589
+ def close
590
+ @buffer.close if @buffering && @buffer
591
+ @secondary.close if @secondary
592
+
593
+ super
594
+ end
595
+
596
+ def terminate
597
+ @buffer.terminate if @buffering && @buffer
598
+ @secondary.terminate if @secondary
599
+
600
+ super
601
+ end
602
+
603
+ def actual_flush_thread_count
604
+ return 0 unless @buffering
605
+ return @buffer_config.flush_thread_count unless @as_secondary
606
+ @primary_instance.buffer_config.flush_thread_count
607
+ end
608
+
609
+ # Ensures `path` (filename or filepath) processable
610
+ # only by the current thread in the current process.
611
+ # For multiple workers, the lock is shared if `path` is the same value.
612
+ # For multiple threads, the lock is shared by all threads in the same process.
613
+ def synchronize_path(path)
614
+ synchronize_path_in_workers(path) do
615
+ synchronize_in_threads do
616
+ yield
617
+ end
618
+ end
619
+ end
620
+
621
+ def synchronize_path_in_workers(path)
622
+ need_worker_lock = system_config.workers > 1
623
+ if need_worker_lock
624
+ acquire_worker_lock(path) { yield }
625
+ else
626
+ yield
627
+ end
628
+ end
629
+
630
+ def synchronize_in_threads
631
+ need_thread_lock = actual_flush_thread_count > 1
632
+ if need_thread_lock
633
+ @flush_thread_mutex.synchronize { yield }
634
+ else
635
+ yield
636
+ end
637
+ end
638
+
639
+ def support_in_v12_style?(feature)
640
+ # for plugins written in v0.12 styles
641
+ case feature
642
+ when :synchronous then false
643
+ when :buffered then false
644
+ when :delayed_commit then false
645
+ when :custom_format then false
646
+ else
647
+ raise ArgumentError, "unknown feature: #{feature}"
648
+ end
649
+ end
650
+
651
+ def implement?(feature)
652
+ methods_of_plugin = self.class.instance_methods(false)
653
+ case feature
654
+ when :synchronous then methods_of_plugin.include?(:process) || support_in_v12_style?(:synchronous)
655
+ when :buffered then methods_of_plugin.include?(:write) || support_in_v12_style?(:buffered)
656
+ when :delayed_commit then methods_of_plugin.include?(:try_write)
657
+ when :custom_format then methods_of_plugin.include?(:format) || support_in_v12_style?(:custom_format)
658
+ else
659
+ raise ArgumentError, "Unknown feature for output plugin: #{feature}"
660
+ end
661
+ end
662
+
663
+ def placeholder_validate!(name, str)
664
+ placeholder_validators(name, str).each do |v|
665
+ v.validate!
666
+ end
667
+ end
668
+
669
+ def placeholder_validators(name, str, time_key = (@chunk_key_time && @buffer_config.timekey), tag_key = @chunk_key_tag, chunk_keys = @chunk_keys)
670
+ validators = []
671
+
672
+ sec, title, example = get_placeholders_time(str)
673
+ if sec || time_key
674
+ validators << PlaceholderValidator.new(name, str, :time, {sec: sec, title: title, example: example, timekey: time_key})
675
+ end
676
+
677
+ parts = get_placeholders_tag(str)
678
+ if tag_key || !parts.empty?
679
+ validators << PlaceholderValidator.new(name, str, :tag, {parts: parts, tagkey: tag_key})
680
+ end
681
+
682
+ keys = get_placeholders_keys(str)
683
+ if chunk_keys && !chunk_keys.empty? || !keys.empty?
684
+ validators << PlaceholderValidator.new(name, str, :keys, {keys: keys, chunkkeys: chunk_keys})
685
+ end
686
+
687
+ validators
688
+ end
689
+
690
+ class PlaceholderValidator
691
+ attr_reader :name, :string, :type, :argument
692
+
693
+ def initialize(name, str, type, arg)
694
+ @name = name
695
+ @string = str
696
+ @type = type
697
+ raise ArgumentError, "invalid type:#{type}" if @type != :time && @type != :tag && @type != :keys
698
+ @argument = arg
699
+ end
700
+
701
+ def time?
702
+ @type == :time
703
+ end
704
+
705
+ def tag?
706
+ @type == :tag
707
+ end
708
+
709
+ def keys?
710
+ @type == :keys
711
+ end
712
+
713
+ def validate!
714
+ case @type
715
+ when :time then validate_time!
716
+ when :tag then validate_tag!
717
+ when :keys then validate_keys!
718
+ end
719
+ end
720
+
721
+ def validate_time!
722
+ sec = @argument[:sec]
723
+ title = @argument[:title]
724
+ example = @argument[:example]
725
+ timekey = @argument[:timekey]
726
+ if !sec && timekey
727
+ raise Fluent::ConfigError, "Parameter '#{name}: #{string}' doesn't have timestamp placeholders for timekey #{timekey.to_i}"
728
+ end
729
+ if sec && !timekey
730
+ raise Fluent::ConfigError, "Parameter '#{name}: #{string}' has timestamp placeholders, but chunk key 'time' is not configured"
731
+ end
732
+ if sec && timekey && timekey < sec
733
+ raise Fluent::ConfigError, "Parameter '#{name}: #{string}' doesn't have timestamp placeholder for #{title}('#{example}') for timekey #{timekey.to_i}"
734
+ end
735
+ end
736
+
737
+ def validate_tag!
738
+ parts = @argument[:parts]
739
+ tagkey = @argument[:tagkey]
740
+ if tagkey && parts.empty?
741
+ raise Fluent::ConfigError, "Parameter '#{name}: #{string}' doesn't have tag placeholder"
742
+ end
743
+ if !tagkey && !parts.empty?
744
+ raise Fluent::ConfigError, "Parameter '#{name}: #{string}' has tag placeholders, but chunk key 'tag' is not configured"
745
+ end
746
+ end
747
+
748
+ def validate_keys!
749
+ keys = @argument[:keys]
750
+ chunk_keys = @argument[:chunkkeys]
751
+ if (chunk_keys - keys).size > 0
752
+ not_specified = (chunk_keys - keys).sort
753
+ raise Fluent::ConfigError, "Parameter '#{name}: #{string}' doesn't have enough placeholders for keys #{not_specified.join(',')}"
754
+ end
755
+ if (keys - chunk_keys).size > 0
756
+ not_satisfied = (keys - chunk_keys).sort
757
+ raise Fluent::ConfigError, "Parameter '#{name}: #{string}' has placeholders, but chunk keys doesn't have keys #{not_satisfied.join(',')}"
758
+ end
759
+ end
760
+ end
761
+
762
+ TIME_KEY_PLACEHOLDER_THRESHOLDS = [
763
+ [1, :second, '%S'],
764
+ [60, :minute, '%M'],
765
+ [3600, :hour, '%H'],
766
+ [86400, :day, '%d'],
767
+ ]
768
+ TIMESTAMP_CHECK_BASE_TIME = Time.parse("2016-01-01 00:00:00 UTC")
769
+ # it's not validated to use timekey larger than 1 day
770
+ def get_placeholders_time(str)
771
+ base_str = TIMESTAMP_CHECK_BASE_TIME.strftime(str)
772
+ TIME_KEY_PLACEHOLDER_THRESHOLDS.each do |triple|
773
+ sec = triple.first
774
+ return triple if (TIMESTAMP_CHECK_BASE_TIME + sec).strftime(str) != base_str
775
+ end
776
+ nil
777
+ end
778
+
779
+ # -1 means whole tag
780
+ def get_placeholders_tag(str)
781
+ # [["tag"],["tag[0]"]]
782
+ parts = []
783
+ str.scan(CHUNK_TAG_PLACEHOLDER_PATTERN).map(&:first).each do |ph|
784
+ if ph == "tag"
785
+ parts << -1
786
+ elsif ph =~ /^tag\[(-?\d+)\]$/
787
+ parts << $1.to_i
788
+ end
789
+ end
790
+ parts.sort
791
+ end
792
+
793
+ def get_placeholders_keys(str)
794
+ str.scan(CHUNK_KEY_PLACEHOLDER_PATTERN).map(&:first).reject{|s| (s == "tag") || (s == 'chunk_id') }.sort
795
+ end
796
+
797
+ # TODO: optimize this code
798
+ def extract_placeholders(str, chunk)
799
+ metadata = if chunk.is_a?(Fluent::Plugin::Buffer::Chunk)
800
+ chunk_passed = true
801
+ chunk.metadata
802
+ else
803
+ chunk_passed = false
804
+ # For existing plugins. Old plugin passes Chunk.metadata instead of Chunk
805
+ chunk
806
+ end
807
+ if metadata.empty?
808
+ str.sub(CHUNK_ID_PLACEHOLDER_PATTERN) {
809
+ if chunk_passed
810
+ dump_unique_id_hex(chunk.unique_id)
811
+ else
812
+ log.warn "${chunk_id} is not allowed in this plugin. Pass Chunk instead of metadata in extract_placeholders's 2nd argument"
813
+ end
814
+ }
815
+ else
816
+ rvalue = str.dup
817
+ # strftime formatting
818
+ if @chunk_key_time # this section MUST be earlier than rest to use raw 'str'
819
+ @output_time_formatter_cache[str] ||= Fluent::Timezone.formatter(@timekey_zone, str)
820
+ rvalue = @output_time_formatter_cache[str].call(metadata.timekey)
821
+ end
822
+ # ${tag}, ${tag[0]}, ${tag[1]}, ... , ${tag[-2]}, ${tag[-1]}
823
+ if @chunk_key_tag
824
+ if str.include?('${tag}')
825
+ rvalue = rvalue.gsub('${tag}', metadata.tag)
826
+ end
827
+ if CHUNK_TAG_PLACEHOLDER_PATTERN.match?(str)
828
+ hash = {}
829
+ tag_parts = metadata.tag.split('.')
830
+ tag_parts.each_with_index do |part, i|
831
+ hash["${tag[#{i}]}"] = part
832
+ hash["${tag[#{i-tag_parts.size}]}"] = part
833
+ end
834
+ rvalue = rvalue.gsub(CHUNK_TAG_PLACEHOLDER_PATTERN, hash)
835
+ end
836
+ if rvalue =~ CHUNK_TAG_PLACEHOLDER_PATTERN
837
+ log.warn "tag placeholder '#{$1}' not replaced. tag:#{metadata.tag}, template:#{str}"
838
+ end
839
+ end
840
+
841
+ # First we replace ${chunk_id} with chunk.unique_id (hexlified).
842
+ rvalue = rvalue.sub(CHUNK_ID_PLACEHOLDER_PATTERN) {
843
+ if chunk_passed
844
+ dump_unique_id_hex(chunk.unique_id)
845
+ else
846
+ log.warn "${chunk_id} is not allowed in this plugin. Pass Chunk instead of metadata in extract_placeholders's 2nd argument"
847
+ end
848
+ }
849
+
850
+ # Then, replace other ${chunk_key}s.
851
+ if !@chunk_keys.empty? && metadata.variables
852
+ hash = {'${tag}' => '${tag}'} # not to erase this wrongly
853
+ @chunk_keys.each do |key|
854
+ hash["${#{key}}"] = metadata.variables[key.to_sym]
855
+ end
856
+
857
+ rvalue = rvalue.gsub(CHUNK_KEY_PLACEHOLDER_PATTERN) do |matched|
858
+ hash.fetch(matched) do
859
+ log.warn "chunk key placeholder '#{matched[2..-2]}' not replaced. template:#{str}"
860
+ ''
861
+ end
862
+ end
863
+ end
864
+
865
+ if rvalue =~ CHUNK_KEY_PLACEHOLDER_PATTERN
866
+ log.warn "chunk key placeholder '#{$1}' not replaced. template:#{str}"
867
+ end
868
+
869
+ rvalue
870
+ end
871
+ end
872
+
873
+ def emit_events(tag, es)
874
+ # actually this method will be overwritten by #configure
875
+ if @buffering
876
+ emit_buffered(tag, es)
877
+ else
878
+ emit_sync(tag, es)
879
+ end
880
+ end
881
+
882
+ def emit_sync(tag, es)
883
+ @emit_count_metrics.inc
884
+ begin
885
+ process(tag, es)
886
+ @emit_records_metrics.add(es.size)
887
+ @emit_size_metrics.add(es.to_msgpack_stream.bytesize) if @enable_size_metrics
888
+ rescue
889
+ @num_errors_metrics.inc
890
+ raise
891
+ end
892
+ end
893
+
894
+ def emit_buffered(tag, es)
895
+ @emit_count_metrics.inc
896
+ begin
897
+ execute_chunking(tag, es, enqueue: (@flush_mode == :immediate))
898
+ if !@retry && @buffer.queued?(nil, optimistic: true)
899
+ submit_flush_once
900
+ end
901
+ rescue
902
+ # TODO: separate number of errors into emit errors and write/flush errors
903
+ @num_errors_metrics.inc
904
+ raise
905
+ end
906
+ end
907
+
908
+ # TODO: optimize this code
909
+ def metadata(tag, time, record)
910
+ # this arguments are ordered in output plugin's rule
911
+ # Metadata 's argument order is different from this one (timekey, tag, variables)
912
+
913
+ raise ArgumentError, "tag must be a String: #{tag.class}" unless tag.nil? || tag.is_a?(String)
914
+ raise ArgumentError, "time must be a Fluent::EventTime (or Integer): #{time.class}" unless time.nil? || time.is_a?(Fluent::EventTime) || time.is_a?(Integer)
915
+ raise ArgumentError, "record must be a Hash: #{record.class}" unless record.nil? || record.is_a?(Hash)
916
+
917
+ if @chunk_keys.nil? && @chunk_key_time.nil? && @chunk_key_tag.nil?
918
+ # for tests
919
+ return Struct.new(:timekey, :tag, :variables).new
920
+ end
921
+
922
+ # timekey is int from epoch, and `timekey - timekey % 60` is assumed to mach with 0s of each minutes.
923
+ # it's wrong if timezone is configured as one which supports leap second, but it's very rare and
924
+ # we can ignore it (especially in production systems).
925
+ if @chunk_keys.empty?
926
+ if !@chunk_key_time && !@chunk_key_tag
927
+ @buffer.metadata()
928
+ elsif @chunk_key_time && @chunk_key_tag
929
+ timekey = calculate_timekey(time)
930
+ @buffer.metadata(timekey: timekey, tag: tag)
931
+ elsif @chunk_key_time
932
+ timekey = calculate_timekey(time)
933
+ @buffer.metadata(timekey: timekey)
934
+ else
935
+ @buffer.metadata(tag: tag)
936
+ end
937
+ else
938
+ timekey = if @chunk_key_time
939
+ calculate_timekey(time)
940
+ else
941
+ nil
942
+ end
943
+ pairs = Hash[@chunk_key_accessors.map { |k, a| [k, a.call(record)] }]
944
+ @buffer.metadata(timekey: timekey, tag: (@chunk_key_tag ? tag : nil), variables: pairs)
945
+ end
946
+ end
947
+
948
+ def calculate_timekey(time)
949
+ time_int = time.to_i
950
+ if @timekey_use_utc
951
+ (time_int - (time_int % @timekey)).to_i
952
+ else
953
+ offset = @calculate_offset ? @calculate_offset.call(time) : @offset
954
+ (time_int - ((time_int + offset)% @timekey)).to_i
955
+ end
956
+ end
957
+
958
+ def chunk_for_test(tag, time, record)
959
+ require 'fluent/plugin/buffer/memory_chunk'
960
+
961
+ m = metadata(tag, time, record)
962
+ Fluent::Plugin::Buffer::MemoryChunk.new(m)
963
+ end
964
+
965
+ def execute_chunking(tag, es, enqueue: false)
966
+ if @simple_chunking
967
+ handle_stream_simple(tag, es, enqueue: enqueue)
968
+ elsif @custom_format
969
+ handle_stream_with_custom_format(tag, es, enqueue: enqueue)
970
+ else
971
+ handle_stream_with_standard_format(tag, es, enqueue: enqueue)
972
+ end
973
+ end
974
+
975
+ def write_guard(&block)
976
+ begin
977
+ block.call
978
+ rescue Fluent::Plugin::Buffer::BufferOverflowError
979
+ log.warn "failed to write data into buffer by buffer overflow", action: @buffer_config.overflow_action
980
+ case @buffer_config.overflow_action
981
+ when :throw_exception
982
+ raise
983
+ when :block
984
+ log.debug "buffer.write is now blocking"
985
+ until @buffer.storable?
986
+ if self.stopped?
987
+ log.error "breaking block behavior to shutdown Fluentd"
988
+ # to break infinite loop to exit Fluentd process
989
+ raise
990
+ end
991
+ log.trace "sleeping until buffer can store more data"
992
+ sleep 1
993
+ end
994
+ log.debug "retrying buffer.write after blocked operation"
995
+ retry
996
+ when :drop_oldest_chunk
997
+ begin
998
+ oldest = @buffer.dequeue_chunk
999
+ if oldest
1000
+ log.warn "dropping oldest chunk to make space after buffer overflow", chunk_id: dump_unique_id_hex(oldest.unique_id)
1001
+ @buffer.purge_chunk(oldest.unique_id)
1002
+ else
1003
+ log.error "no queued chunks to be dropped for drop_oldest_chunk"
1004
+ end
1005
+ rescue
1006
+ # ignore any errors
1007
+ end
1008
+ raise unless @buffer.storable?
1009
+ retry
1010
+ else
1011
+ raise "BUG: unknown overflow_action '#{@buffer_config.overflow_action}'"
1012
+ end
1013
+ end
1014
+ end
1015
+
1016
+ FORMAT_MSGPACK_STREAM = ->(e){ e.to_msgpack_stream(packer: Fluent::MessagePackFactory.thread_local_msgpack_packer) }
1017
+ FORMAT_COMPRESSED_MSGPACK_STREAM = ->(e){ e.to_compressed_msgpack_stream(packer: Fluent::MessagePackFactory.thread_local_msgpack_packer) }
1018
+ FORMAT_MSGPACK_STREAM_TIME_INT = ->(e){ e.to_msgpack_stream(time_int: true, packer: Fluent::MessagePackFactory.thread_local_msgpack_packer) }
1019
+ FORMAT_COMPRESSED_MSGPACK_STREAM_TIME_INT = ->(e){ e.to_compressed_msgpack_stream(time_int: true, packer: Fluent::MessagePackFactory.thread_local_msgpack_packer) }
1020
+
1021
+ def generate_format_proc
1022
+ if @buffer && @buffer.compress == :gzip
1023
+ @time_as_integer ? FORMAT_COMPRESSED_MSGPACK_STREAM_TIME_INT : FORMAT_COMPRESSED_MSGPACK_STREAM
1024
+ else
1025
+ @time_as_integer ? FORMAT_MSGPACK_STREAM_TIME_INT : FORMAT_MSGPACK_STREAM
1026
+ end
1027
+ end
1028
+
1029
+ # metadata_and_data is a Hash of:
1030
+ # (standard format) metadata => event stream
1031
+ # (custom format) metadata => array of formatted event
1032
+ # For standard format, formatting should be done for whole event stream, but
1033
+ # "whole event stream" may be a split of "es" here when it's bigger than chunk_limit_size.
1034
+ # `@buffer.write` will do this splitting.
1035
+ # For custom format, formatting will be done here. Custom formatting always requires
1036
+ # iteration of event stream, and it should be done just once even if total event stream size
1037
+ # is bigger than chunk_limit_size because of performance.
1038
+ def handle_stream_with_custom_format(tag, es, enqueue: false)
1039
+ meta_and_data = {}
1040
+ records = 0
1041
+ es.each(unpacker: Fluent::MessagePackFactory.thread_local_msgpack_unpacker) do |time, record|
1042
+ meta = metadata(tag, time, record)
1043
+ meta_and_data[meta] ||= []
1044
+ res = format(tag, time, record)
1045
+ if res
1046
+ meta_and_data[meta] << res
1047
+ records += 1
1048
+ end
1049
+ end
1050
+ write_guard do
1051
+ @buffer.write(meta_and_data, enqueue: enqueue)
1052
+ end
1053
+ @emit_records_metrics.add(es.size)
1054
+ @emit_size_metrics.add(es.to_msgpack_stream.bytesize) if @enable_size_metrics
1055
+ true
1056
+ end
1057
+
1058
+ def handle_stream_with_standard_format(tag, es, enqueue: false)
1059
+ format_proc = generate_format_proc
1060
+ meta_and_data = {}
1061
+ records = 0
1062
+ es.each(unpacker: Fluent::MessagePackFactory.thread_local_msgpack_unpacker) do |time, record|
1063
+ meta = metadata(tag, time, record)
1064
+ meta_and_data[meta] ||= MultiEventStream.new
1065
+ meta_and_data[meta].add(time, record)
1066
+ records += 1
1067
+ end
1068
+ write_guard do
1069
+ @buffer.write(meta_and_data, format: format_proc, enqueue: enqueue)
1070
+ end
1071
+ @emit_records_metrics.add(es.size)
1072
+ @emit_size_metrics.add(es.to_msgpack_stream.bytesize) if @enable_size_metrics
1073
+ true
1074
+ end
1075
+
1076
+ def handle_stream_simple(tag, es, enqueue: false)
1077
+ format_proc = nil
1078
+ meta = metadata((@chunk_key_tag ? tag : nil), nil, nil)
1079
+ records = es.size
1080
+ if @custom_format
1081
+ records = 0
1082
+ data = []
1083
+ es.each(unpacker: Fluent::MessagePackFactory.thread_local_msgpack_unpacker) do |time, record|
1084
+ res = format(tag, time, record)
1085
+ if res
1086
+ data << res
1087
+ records += 1
1088
+ end
1089
+ end
1090
+ else
1091
+ format_proc = generate_format_proc
1092
+ data = es
1093
+ end
1094
+ write_guard do
1095
+ @buffer.write({meta => data}, format: format_proc, enqueue: enqueue)
1096
+ end
1097
+ @emit_records_metrics.add(es.size)
1098
+ @emit_size_metrics.add(es.to_msgpack_stream.bytesize) if @enable_size_metrics
1099
+ true
1100
+ end
1101
+
1102
+ def commit_write(chunk_id, delayed: @delayed_commit, secondary: false)
1103
+ log.on_trace { log.trace "committing write operation to a chunk", chunk: dump_unique_id_hex(chunk_id), delayed: delayed }
1104
+
1105
+ if delayed
1106
+ @dequeued_chunks_mutex.synchronize do
1107
+ @dequeued_chunks.delete_if{ |info| info.chunk_id == chunk_id }
1108
+ end
1109
+ end
1110
+ @buffer.purge_chunk(chunk_id)
1111
+
1112
+ @retry_mutex.synchronize do
1113
+ if @retry # success to flush chunks in retries
1114
+ if secondary
1115
+ log.warn "retry succeeded by secondary.", chunk_id: dump_unique_id_hex(chunk_id)
1116
+ else
1117
+ log.warn "retry succeeded.", chunk_id: dump_unique_id_hex(chunk_id)
1118
+ end
1119
+ @retry = nil
1120
+ end
1121
+ end
1122
+ end
1123
+
1124
+ # update_retry parameter is for preventing busy loop by async write
1125
+ # We will remove this parameter by re-design retry_state management between threads.
1126
+ def rollback_write(chunk_id, update_retry: true)
1127
+ # This API is to rollback chunks explicitly from plugins.
1128
+ # 3rd party plugins can depend it on automatic rollback of #try_rollback_write
1129
+ @dequeued_chunks_mutex.synchronize do
1130
+ @dequeued_chunks.delete_if{ |info| info.chunk_id == chunk_id }
1131
+ end
1132
+ # returns true if chunk was rollbacked as expected
1133
+ # false if chunk was already flushed and couldn't be rollbacked unexpectedly
1134
+ # in many cases, false can be just ignored
1135
+ if @buffer.takeback_chunk(chunk_id)
1136
+ @rollback_count_metrics.inc
1137
+ if update_retry
1138
+ primary = @as_secondary ? @primary_instance : self
1139
+ primary.update_retry_state(chunk_id, @as_secondary)
1140
+ end
1141
+ true
1142
+ else
1143
+ false
1144
+ end
1145
+ end
1146
+
1147
+ def try_rollback_write
1148
+ @dequeued_chunks_mutex.synchronize do
1149
+ while @dequeued_chunks.first && @dequeued_chunks.first.expired?
1150
+ info = @dequeued_chunks.shift
1151
+ if @buffer.takeback_chunk(info.chunk_id)
1152
+ @rollback_count_metrics.inc
1153
+ log.warn "failed to flush the buffer chunk, timeout to commit.", chunk_id: dump_unique_id_hex(info.chunk_id), flushed_at: info.time
1154
+ primary = @as_secondary ? @primary_instance : self
1155
+ primary.update_retry_state(info.chunk_id, @as_secondary)
1156
+ end
1157
+ end
1158
+ end
1159
+ end
1160
+
1161
+ def try_rollback_all
1162
+ return unless @dequeued_chunks
1163
+ @dequeued_chunks_mutex.synchronize do
1164
+ until @dequeued_chunks.empty?
1165
+ info = @dequeued_chunks.shift
1166
+ if @buffer.takeback_chunk(info.chunk_id)
1167
+ @rollback_count_metrics.inc
1168
+ log.info "delayed commit for buffer chunks was cancelled in shutdown", chunk_id: dump_unique_id_hex(info.chunk_id)
1169
+ primary = @as_secondary ? @primary_instance : self
1170
+ primary.update_retry_state(info.chunk_id, @as_secondary)
1171
+ end
1172
+ end
1173
+ end
1174
+ end
1175
+
1176
+ def next_flush_time
1177
+ if @buffer.queued?
1178
+ @retry_mutex.synchronize do
1179
+ @retry ? @retry.next_time : Time.now + @buffer_config.flush_thread_burst_interval
1180
+ end
1181
+ else
1182
+ Time.now + @buffer_config.flush_thread_interval
1183
+ end
1184
+ end
1185
+
1186
+ UNRECOVERABLE_ERRORS = [Fluent::UnrecoverableError, TypeError, ArgumentError, NoMethodError, MessagePack::UnpackError, EncodingError]
1187
+
1188
+ def try_flush
1189
+ chunk = @buffer.dequeue_chunk
1190
+ return unless chunk
1191
+
1192
+ log.on_trace { log.trace "trying flush for a chunk", chunk: dump_unique_id_hex(chunk.unique_id) }
1193
+
1194
+ output = self
1195
+ using_secondary = false
1196
+ if @retry_mutex.synchronize{ @retry && @retry.secondary? }
1197
+ output = @secondary
1198
+ using_secondary = true
1199
+ end
1200
+
1201
+ if @enable_msgpack_streamer
1202
+ chunk.extend ChunkMessagePackEventStreamer
1203
+ end
1204
+
1205
+ begin
1206
+ chunk_write_start = Fluent::Clock.now
1207
+
1208
+ if output.delayed_commit
1209
+ log.trace "executing delayed write and commit", chunk: dump_unique_id_hex(chunk.unique_id)
1210
+ @write_count_metrics.inc
1211
+ @dequeued_chunks_mutex.synchronize do
1212
+ # delayed_commit_timeout for secondary is configured in <buffer> of primary (<secondary> don't get <buffer>)
1213
+ @dequeued_chunks << DequeuedChunkInfo.new(chunk.unique_id, Time.now, self.delayed_commit_timeout)
1214
+ end
1215
+
1216
+ output.try_write(chunk)
1217
+ check_slow_flush(chunk_write_start)
1218
+ else # output plugin without delayed purge
1219
+ chunk_id = chunk.unique_id
1220
+ dump_chunk_id = dump_unique_id_hex(chunk_id)
1221
+ log.trace "adding write count", instance: self.object_id
1222
+ @write_count_metrics.inc
1223
+ log.trace "executing sync write", chunk: dump_chunk_id
1224
+
1225
+ output.write(chunk)
1226
+ check_slow_flush(chunk_write_start)
1227
+
1228
+ log.trace "write operation done, committing", chunk: dump_chunk_id
1229
+ commit_write(chunk_id, delayed: false, secondary: using_secondary)
1230
+ log.trace "done to commit a chunk", chunk: dump_chunk_id
1231
+ end
1232
+ rescue *UNRECOVERABLE_ERRORS => e
1233
+ if @secondary
1234
+ if using_secondary
1235
+ log.warn "got unrecoverable error in secondary.", error: e
1236
+ log.warn_backtrace
1237
+ backup_chunk(chunk, using_secondary, output.delayed_commit)
1238
+ else
1239
+ if (self.class == @secondary.class)
1240
+ log.warn "got unrecoverable error in primary and secondary type is same as primary. Skip secondary", error: e
1241
+ log.warn_backtrace
1242
+ backup_chunk(chunk, using_secondary, output.delayed_commit)
1243
+ else
1244
+ # Call secondary output directly without retry update.
1245
+ # In this case, delayed commit causes inconsistent state in dequeued chunks so async output in secondary is not allowed for now.
1246
+ if @secondary.delayed_commit
1247
+ log.warn "got unrecoverable error in primary and secondary is async output. Skip secondary for backup", error: e
1248
+ log.warn_backtrace
1249
+ backup_chunk(chunk, using_secondary, output.delayed_commit)
1250
+ else
1251
+ log.warn "got unrecoverable error in primary. Skip retry and flush chunk to secondary", error: e
1252
+ log.warn_backtrace
1253
+ begin
1254
+ @secondary.write(chunk)
1255
+ commit_write(chunk_id, delayed: output.delayed_commit, secondary: true)
1256
+ rescue => e
1257
+ log.warn "got an error in secondary for unrecoverable error", error: e
1258
+ log.warn_backtrace
1259
+ backup_chunk(chunk, using_secondary, output.delayed_commit)
1260
+ end
1261
+ end
1262
+ end
1263
+ end
1264
+ else
1265
+ log.warn "got unrecoverable error in primary and no secondary", error: e
1266
+ log.warn_backtrace
1267
+ backup_chunk(chunk, using_secondary, output.delayed_commit)
1268
+ end
1269
+ rescue => e
1270
+ log.debug "taking back chunk for errors.", chunk: dump_unique_id_hex(chunk.unique_id)
1271
+ if output.delayed_commit
1272
+ @dequeued_chunks_mutex.synchronize do
1273
+ @dequeued_chunks.delete_if{|d| d.chunk_id == chunk.unique_id }
1274
+ end
1275
+ end
1276
+
1277
+ if @buffer.takeback_chunk(chunk.unique_id)
1278
+ @rollback_count_metrics.inc
1279
+ end
1280
+
1281
+ update_retry_state(chunk.unique_id, using_secondary, e)
1282
+
1283
+ raise if @under_plugin_development && !@retry_for_error_chunk
1284
+ end
1285
+ end
1286
+
1287
+ def backup_chunk(chunk, using_secondary, delayed_commit)
1288
+ if @buffer.disable_chunk_backup
1289
+ log.warn "disable_chunk_backup is true. #{dump_unique_id_hex(chunk.unique_id)} chunk is thrown away"
1290
+ else
1291
+ @buffer.backup(chunk.unique_id) { |f|
1292
+ chunk.write_to(f)
1293
+ }
1294
+ end
1295
+ commit_write(chunk.unique_id, secondary: using_secondary, delayed: delayed_commit)
1296
+ end
1297
+
1298
+ def check_slow_flush(start)
1299
+ elapsed_time = Fluent::Clock.now - start
1300
+ elapsed_millsec = (elapsed_time * 1000).to_i
1301
+ @flush_time_count_metrics.add(elapsed_millsec)
1302
+ if elapsed_time > @slow_flush_log_threshold
1303
+ @slow_flush_count_metrics.inc
1304
+ log.warn "buffer flush took longer time than slow_flush_log_threshold:",
1305
+ elapsed_time: elapsed_time, slow_flush_log_threshold: @slow_flush_log_threshold, plugin_id: self.plugin_id
1306
+ end
1307
+ end
1308
+
1309
+ def update_retry_state(chunk_id, using_secondary, error = nil)
1310
+ @retry_mutex.synchronize do
1311
+ @num_errors_metrics.inc
1312
+ chunk_id_hex = dump_unique_id_hex(chunk_id)
1313
+
1314
+ unless @retry
1315
+ @retry = retry_state(@buffer_config.retry_randomize)
1316
+
1317
+ if @retry.limit?
1318
+ handle_limit_reached(error)
1319
+ elsif error
1320
+ log_retry_error(error, chunk_id_hex, using_secondary)
1321
+ end
1322
+
1323
+ return
1324
+ end
1325
+
1326
+ # @retry exists
1327
+
1328
+ # Ensure that the current time is greater than or equal to @retry.next_time to avoid the situation when
1329
+ # @retry.step is called almost as many times as the number of flush threads in a short time.
1330
+ if Time.now >= @retry.next_time
1331
+ @retry.step
1332
+ else
1333
+ @retry.recalc_next_time # to prevent all flush threads from retrying at the same time
1334
+ end
1335
+
1336
+ if @retry.limit?
1337
+ handle_limit_reached(error)
1338
+ elsif error
1339
+ log_retry_error(error, chunk_id_hex, using_secondary)
1340
+ end
1341
+ end
1342
+ end
1343
+
1344
+ def log_retry_error(error, chunk_id_hex, using_secondary)
1345
+ return unless error
1346
+ if using_secondary
1347
+ msg = "failed to flush the buffer with secondary output."
1348
+ else
1349
+ msg = "failed to flush the buffer."
1350
+ end
1351
+ log.warn(msg, retry_times: @retry.steps, next_retry_time: @retry.next_time.round, chunk: chunk_id_hex, error: error)
1352
+ log.warn_backtrace(error.backtrace)
1353
+ end
1354
+
1355
+ def handle_limit_reached(error)
1356
+ if error
1357
+ records = @buffer.queued_records
1358
+ msg = "Hit limit for retries. dropping all chunks in the buffer queue."
1359
+ log.error msg, retry_times: @retry.steps, records: records, error: error
1360
+ log.error_backtrace error.backtrace
1361
+ end
1362
+ @buffer.clear_queue!
1363
+ log.debug "buffer queue cleared"
1364
+ @retry = nil
1365
+ end
1366
+
1367
+ def retry_state(randomize)
1368
+ if @secondary
1369
+ retry_state_create(
1370
+ :output_retries, @buffer_config.retry_type, @buffer_config.retry_wait, @buffer_config.retry_timeout,
1371
+ forever: @buffer_config.retry_forever, max_steps: @buffer_config.retry_max_times, backoff_base: @buffer_config.retry_exponential_backoff_base,
1372
+ max_interval: @buffer_config.retry_max_interval,
1373
+ secondary: true, secondary_threshold: @buffer_config.retry_secondary_threshold,
1374
+ randomize: randomize
1375
+ )
1376
+ else
1377
+ retry_state_create(
1378
+ :output_retries, @buffer_config.retry_type, @buffer_config.retry_wait, @buffer_config.retry_timeout,
1379
+ forever: @buffer_config.retry_forever, max_steps: @buffer_config.retry_max_times, backoff_base: @buffer_config.retry_exponential_backoff_base,
1380
+ max_interval: @buffer_config.retry_max_interval,
1381
+ randomize: randomize
1382
+ )
1383
+ end
1384
+ end
1385
+
1386
+ def submit_flush_once
1387
+ # Without locks: it is rough but enough to select "next" writer selection
1388
+ @output_flush_thread_current_position = (@output_flush_thread_current_position + 1) % @buffer_config.flush_thread_count
1389
+ state = @output_flush_threads[@output_flush_thread_current_position]
1390
+ state.mutex.synchronize {
1391
+ if state.thread && state.thread.status # "run"/"sleep"/"aborting" or false(successfully stop) or nil(killed by exception)
1392
+ state.next_clock = 0
1393
+ state.cond_var.signal
1394
+ else
1395
+ log.warn "thread is already dead"
1396
+ end
1397
+ }
1398
+ Thread.pass
1399
+ end
1400
+
1401
+ def force_flush
1402
+ if @buffering
1403
+ @buffer.enqueue_all(true)
1404
+ submit_flush_all
1405
+ end
1406
+ end
1407
+
1408
+ def submit_flush_all
1409
+ while !@retry && @buffer.queued?
1410
+ submit_flush_once
1411
+ sleep @buffer_config.flush_thread_burst_interval
1412
+ end
1413
+ end
1414
+
1415
+ # only for tests of output plugin
1416
+ def interrupt_flushes
1417
+ @output_flush_interrupted = true
1418
+ end
1419
+
1420
+ # only for tests of output plugin
1421
+ def enqueue_thread_wait
1422
+ @output_enqueue_thread_mutex.synchronize do
1423
+ @output_flush_interrupted = false
1424
+ @output_enqueue_thread_waiting = true
1425
+ end
1426
+ require 'timeout'
1427
+ Timeout.timeout(10) do
1428
+ Thread.pass while @output_enqueue_thread_waiting
1429
+ end
1430
+ end
1431
+
1432
+ # only for tests of output plugin
1433
+ def flush_thread_wakeup
1434
+ @output_flush_threads.each do |state|
1435
+ state.mutex.synchronize {
1436
+ if state.thread && state.thread.status
1437
+ state.next_clock = 0
1438
+ state.cond_var.signal
1439
+ end
1440
+ }
1441
+ Thread.pass
1442
+ end
1443
+ end
1444
+
1445
+ def enqueue_thread_run
1446
+ value_for_interval = nil
1447
+ if @flush_mode == :interval
1448
+ value_for_interval = @buffer_config.flush_interval
1449
+ end
1450
+ if @chunk_key_time
1451
+ if !value_for_interval || @buffer_config.timekey < value_for_interval
1452
+ value_for_interval = [@buffer_config.timekey, @buffer_config.timekey_wait].min
1453
+ end
1454
+ end
1455
+ unless value_for_interval
1456
+ raise "BUG: both of flush_interval and timekey are disabled"
1457
+ end
1458
+ interval = value_for_interval / 11.0
1459
+ if interval < @buffer_config.flush_thread_interval
1460
+ interval = @buffer_config.flush_thread_interval
1461
+ end
1462
+
1463
+ while !self.after_started? && !self.stopped?
1464
+ sleep 0.5
1465
+ end
1466
+ log.debug "enqueue_thread actually running"
1467
+
1468
+ begin
1469
+ while @output_enqueue_thread_running
1470
+ now_int = Time.now.to_i
1471
+ if @output_flush_interrupted
1472
+ sleep interval
1473
+ next
1474
+ end
1475
+
1476
+ @output_enqueue_thread_mutex.lock
1477
+ begin
1478
+ if @flush_mode == :interval
1479
+ flush_interval = @buffer_config.flush_interval.to_i
1480
+ # This block should be done by integer values.
1481
+ # If both of flush_interval & flush_thread_interval are 1s, expected actual flush timing is 1.5s.
1482
+ # If we use integered values for this comparison, expected actual flush timing is 1.0s.
1483
+ @buffer.enqueue_all{ |metadata, chunk| chunk.raw_create_at + flush_interval <= now_int }
1484
+ end
1485
+
1486
+ if @chunk_key_time
1487
+ timekey_unit = @buffer_config.timekey
1488
+ timekey_wait = @buffer_config.timekey_wait
1489
+ current_timekey = now_int - now_int % timekey_unit
1490
+ @buffer.enqueue_all{ |metadata, chunk| metadata.timekey < current_timekey && metadata.timekey + timekey_unit + timekey_wait <= now_int }
1491
+ end
1492
+ rescue => e
1493
+ raise if @under_plugin_development
1494
+ log.error "unexpected error while checking flushed chunks. ignored.", error: e
1495
+ log.error_backtrace
1496
+ ensure
1497
+ @output_enqueue_thread_waiting = false
1498
+ @output_enqueue_thread_mutex.unlock
1499
+ end
1500
+ sleep interval
1501
+ end
1502
+ rescue => e
1503
+ # normal errors are rescued by inner begin-rescue clause.
1504
+ log.error "error on enqueue thread", error: e
1505
+ log.error_backtrace
1506
+ raise
1507
+ end
1508
+ end
1509
+
1510
+ def flush_thread_run(state)
1511
+ flush_thread_interval = @buffer_config.flush_thread_interval
1512
+
1513
+ state.next_clock = Fluent::Clock.now + flush_thread_interval
1514
+
1515
+ while !self.after_started? && !self.stopped?
1516
+ sleep 0.5
1517
+ end
1518
+ log.debug "flush_thread actually running"
1519
+
1520
+ state.mutex.lock
1521
+ begin
1522
+ # This thread don't use `thread_current_running?` because this thread should run in `before_shutdown` phase
1523
+ while @output_flush_threads_running
1524
+ current_clock = Fluent::Clock.now
1525
+ next_retry_time = nil
1526
+
1527
+ @retry_mutex.synchronize do
1528
+ next_retry_time = @retry ? @retry.next_time : nil
1529
+ end
1530
+
1531
+ if state.next_clock > current_clock
1532
+ interval = state.next_clock - current_clock
1533
+ elsif next_retry_time && next_retry_time > Time.now
1534
+ interval = next_retry_time.to_f - Time.now.to_f
1535
+ else
1536
+ state.mutex.unlock
1537
+ begin
1538
+ try_flush
1539
+ # next_flush_time uses flush_thread_interval or flush_thread_burst_interval (or retrying)
1540
+ interval = next_flush_time.to_f - Time.now.to_f
1541
+ # TODO: if secondary && delayed-commit, next_flush_time will be much longer than expected
1542
+ # because @retry still exists (#commit_write is not called yet in #try_flush)
1543
+ # @retry should be cleared if delayed commit is enabled? Or any other solution?
1544
+ state.next_clock = Fluent::Clock.now + interval
1545
+ ensure
1546
+ state.mutex.lock
1547
+ end
1548
+ end
1549
+
1550
+ if @dequeued_chunks_mutex.synchronize{ !@dequeued_chunks.empty? && @dequeued_chunks.first.expired? }
1551
+ unless @output_flush_interrupted
1552
+ state.mutex.unlock
1553
+ begin
1554
+ try_rollback_write
1555
+ ensure
1556
+ state.mutex.lock
1557
+ end
1558
+ end
1559
+ end
1560
+
1561
+ state.cond_var.wait(state.mutex, interval) if interval > 0
1562
+ end
1563
+ rescue => e
1564
+ # normal errors are rescued by output plugins in #try_flush
1565
+ # so this rescue section is for critical & unrecoverable errors
1566
+ log.error "error on output thread", error: e
1567
+ log.error_backtrace
1568
+ raise
1569
+ ensure
1570
+ state.mutex.unlock
1571
+ end
1572
+ end
1573
+
1574
+ BUFFER_STATS_KEYS = {}
1575
+ Fluent::Plugin::Buffer::STATS_KEYS.each { |key|
1576
+ BUFFER_STATS_KEYS[key] = "buffer_#{key}"
1577
+ }
1578
+
1579
+ def statistics
1580
+ stats = {
1581
+ 'emit_records' => @emit_records_metrics.get,
1582
+ 'emit_size' => @emit_size_metrics.get,
1583
+ # Respect original name
1584
+ # https://github.com/fluent/fluentd/blob/45c7b75ba77763eaf87136864d4942c4e0c5bfcd/lib/fluent/plugin/in_monitor_agent.rb#L284
1585
+ 'retry_count' => @num_errors_metrics.get,
1586
+ 'emit_count' => @emit_count_metrics.get,
1587
+ 'write_count' => @write_count_metrics.get,
1588
+ 'rollback_count' => @rollback_count_metrics.get,
1589
+ 'slow_flush_count' => @slow_flush_count_metrics.get,
1590
+ 'flush_time_count' => @flush_time_count_metrics.get,
1591
+ }
1592
+
1593
+ if @buffer && @buffer.respond_to?(:statistics)
1594
+ (@buffer.statistics['buffer'] || {}).each do |k, v|
1595
+ stats[BUFFER_STATS_KEYS[k]] = v
1596
+ end
1597
+ end
1598
+
1599
+ { 'output' => stats }
1600
+ end
1601
+ end
1602
+ end
1603
+ end