dtomasgu-fluentd 1.14.7.pre.dev

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