dtomasgu-fluentd 1.14.7.pre.dev

Sign up to get free protection for your applications and to get access to all the features.
Files changed (564) hide show
  1. checksums.yaml +7 -0
  2. data/.deepsource.toml +13 -0
  3. data/.drone.yml +35 -0
  4. data/.github/ISSUE_TEMPLATE/bug_report.yaml +70 -0
  5. data/.github/ISSUE_TEMPLATE/config.yml +5 -0
  6. data/.github/ISSUE_TEMPLATE/feature_request.yaml +38 -0
  7. data/.github/ISSUE_TEMPLATE.md +17 -0
  8. data/.github/PULL_REQUEST_TEMPLATE.md +14 -0
  9. data/.github/workflows/issue-auto-closer.yml +12 -0
  10. data/.github/workflows/linux-test.yaml +36 -0
  11. data/.github/workflows/macos-test.yaml +34 -0
  12. data/.github/workflows/stale-actions.yml +22 -0
  13. data/.github/workflows/windows-test.yaml +49 -0
  14. data/.gitignore +30 -0
  15. data/.gitlab-ci.yml +103 -0
  16. data/ADOPTERS.md +5 -0
  17. data/AUTHORS +2 -0
  18. data/CHANGELOG.md +2453 -0
  19. data/CONTRIBUTING.md +45 -0
  20. data/GOVERNANCE.md +55 -0
  21. data/Gemfile +9 -0
  22. data/GithubWorkflow.md +78 -0
  23. data/LICENSE +202 -0
  24. data/MAINTAINERS.md +11 -0
  25. data/README.md +76 -0
  26. data/Rakefile +79 -0
  27. data/SECURITY.md +18 -0
  28. data/bin/fluent-binlog-reader +7 -0
  29. data/bin/fluent-ca-generate +6 -0
  30. data/bin/fluent-cap-ctl +7 -0
  31. data/bin/fluent-cat +5 -0
  32. data/bin/fluent-ctl +7 -0
  33. data/bin/fluent-debug +5 -0
  34. data/bin/fluent-gem +9 -0
  35. data/bin/fluent-plugin-config-format +5 -0
  36. data/bin/fluent-plugin-generate +5 -0
  37. data/bin/fluentd +15 -0
  38. data/code-of-conduct.md +3 -0
  39. data/docs/SECURITY_AUDIT.pdf +0 -0
  40. data/example/copy_roundrobin.conf +39 -0
  41. data/example/counter.conf +18 -0
  42. data/example/filter_stdout.conf +22 -0
  43. data/example/in_forward.conf +14 -0
  44. data/example/in_forward_client.conf +37 -0
  45. data/example/in_forward_shared_key.conf +15 -0
  46. data/example/in_forward_tls.conf +14 -0
  47. data/example/in_forward_users.conf +24 -0
  48. data/example/in_forward_workers.conf +21 -0
  49. data/example/in_http.conf +16 -0
  50. data/example/in_out_forward.conf +17 -0
  51. data/example/in_sample_blocks.conf +17 -0
  52. data/example/in_sample_with_compression.conf +23 -0
  53. data/example/in_syslog.conf +15 -0
  54. data/example/in_tail.conf +14 -0
  55. data/example/in_tcp.conf +13 -0
  56. data/example/in_udp.conf +13 -0
  57. data/example/logevents.conf +25 -0
  58. data/example/multi_filters.conf +61 -0
  59. data/example/out_copy.conf +20 -0
  60. data/example/out_exec_filter.conf +42 -0
  61. data/example/out_file.conf +13 -0
  62. data/example/out_forward.conf +35 -0
  63. data/example/out_forward_buf_file.conf +23 -0
  64. data/example/out_forward_client.conf +109 -0
  65. data/example/out_forward_heartbeat_none.conf +16 -0
  66. data/example/out_forward_sd.conf +17 -0
  67. data/example/out_forward_shared_key.conf +36 -0
  68. data/example/out_forward_tls.conf +18 -0
  69. data/example/out_forward_users.conf +65 -0
  70. data/example/out_null.conf +36 -0
  71. data/example/sd.yaml +8 -0
  72. data/example/secondary_file.conf +42 -0
  73. data/example/suppress_config_dump.conf +7 -0
  74. data/example/v0_12_filter.conf +78 -0
  75. data/example/v1_literal_example.conf +36 -0
  76. data/example/worker_section.conf +36 -0
  77. data/fluent.conf +139 -0
  78. data/fluentd.gemspec +57 -0
  79. data/lib/fluent/agent.rb +168 -0
  80. data/lib/fluent/capability.rb +87 -0
  81. data/lib/fluent/clock.rb +66 -0
  82. data/lib/fluent/command/binlog_reader.rb +244 -0
  83. data/lib/fluent/command/bundler_injection.rb +45 -0
  84. data/lib/fluent/command/ca_generate.rb +184 -0
  85. data/lib/fluent/command/cap_ctl.rb +174 -0
  86. data/lib/fluent/command/cat.rb +365 -0
  87. data/lib/fluent/command/ctl.rb +180 -0
  88. data/lib/fluent/command/debug.rb +103 -0
  89. data/lib/fluent/command/fluentd.rb +388 -0
  90. data/lib/fluent/command/plugin_config_formatter.rb +308 -0
  91. data/lib/fluent/command/plugin_generator.rb +365 -0
  92. data/lib/fluent/compat/call_super_mixin.rb +76 -0
  93. data/lib/fluent/compat/detach_process_mixin.rb +33 -0
  94. data/lib/fluent/compat/exec_util.rb +129 -0
  95. data/lib/fluent/compat/file_util.rb +54 -0
  96. data/lib/fluent/compat/filter.rb +68 -0
  97. data/lib/fluent/compat/formatter.rb +111 -0
  98. data/lib/fluent/compat/formatter_utils.rb +85 -0
  99. data/lib/fluent/compat/handle_tag_and_time_mixin.rb +62 -0
  100. data/lib/fluent/compat/handle_tag_name_mixin.rb +53 -0
  101. data/lib/fluent/compat/input.rb +49 -0
  102. data/lib/fluent/compat/output.rb +721 -0
  103. data/lib/fluent/compat/output_chain.rb +60 -0
  104. data/lib/fluent/compat/parser.rb +310 -0
  105. data/lib/fluent/compat/parser_utils.rb +40 -0
  106. data/lib/fluent/compat/propagate_default.rb +62 -0
  107. data/lib/fluent/compat/record_filter_mixin.rb +34 -0
  108. data/lib/fluent/compat/set_tag_key_mixin.rb +50 -0
  109. data/lib/fluent/compat/set_time_key_mixin.rb +69 -0
  110. data/lib/fluent/compat/socket_util.rb +165 -0
  111. data/lib/fluent/compat/string_util.rb +34 -0
  112. data/lib/fluent/compat/structured_format_mixin.rb +26 -0
  113. data/lib/fluent/compat/type_converter.rb +90 -0
  114. data/lib/fluent/config/basic_parser.rb +123 -0
  115. data/lib/fluent/config/configure_proxy.rb +424 -0
  116. data/lib/fluent/config/dsl.rb +152 -0
  117. data/lib/fluent/config/element.rb +265 -0
  118. data/lib/fluent/config/error.rb +44 -0
  119. data/lib/fluent/config/literal_parser.rb +286 -0
  120. data/lib/fluent/config/parser.rb +107 -0
  121. data/lib/fluent/config/section.rb +272 -0
  122. data/lib/fluent/config/types.rb +249 -0
  123. data/lib/fluent/config/v1_parser.rb +192 -0
  124. data/lib/fluent/config/yaml_parser/fluent_value.rb +47 -0
  125. data/lib/fluent/config/yaml_parser/loader.rb +91 -0
  126. data/lib/fluent/config/yaml_parser/parser.rb +166 -0
  127. data/lib/fluent/config/yaml_parser/section_builder.rb +107 -0
  128. data/lib/fluent/config/yaml_parser.rb +56 -0
  129. data/lib/fluent/config.rb +89 -0
  130. data/lib/fluent/configurable.rb +201 -0
  131. data/lib/fluent/counter/base_socket.rb +44 -0
  132. data/lib/fluent/counter/client.rb +297 -0
  133. data/lib/fluent/counter/error.rb +86 -0
  134. data/lib/fluent/counter/mutex_hash.rb +163 -0
  135. data/lib/fluent/counter/server.rb +273 -0
  136. data/lib/fluent/counter/store.rb +205 -0
  137. data/lib/fluent/counter/validator.rb +145 -0
  138. data/lib/fluent/counter.rb +23 -0
  139. data/lib/fluent/daemon.rb +15 -0
  140. data/lib/fluent/daemonizer.rb +88 -0
  141. data/lib/fluent/engine.rb +253 -0
  142. data/lib/fluent/env.rb +40 -0
  143. data/lib/fluent/error.rb +34 -0
  144. data/lib/fluent/event.rb +326 -0
  145. data/lib/fluent/event_router.rb +315 -0
  146. data/lib/fluent/ext_monitor_require.rb +28 -0
  147. data/lib/fluent/filter.rb +21 -0
  148. data/lib/fluent/fluent_log_event_router.rb +141 -0
  149. data/lib/fluent/formatter.rb +23 -0
  150. data/lib/fluent/input.rb +21 -0
  151. data/lib/fluent/label.rb +46 -0
  152. data/lib/fluent/load.rb +34 -0
  153. data/lib/fluent/log.rb +713 -0
  154. data/lib/fluent/match.rb +187 -0
  155. data/lib/fluent/mixin.rb +31 -0
  156. data/lib/fluent/msgpack_factory.rb +106 -0
  157. data/lib/fluent/oj_options.rb +62 -0
  158. data/lib/fluent/output.rb +29 -0
  159. data/lib/fluent/output_chain.rb +23 -0
  160. data/lib/fluent/parser.rb +23 -0
  161. data/lib/fluent/plugin/bare_output.rb +104 -0
  162. data/lib/fluent/plugin/base.rb +197 -0
  163. data/lib/fluent/plugin/buf_file.rb +213 -0
  164. data/lib/fluent/plugin/buf_file_single.rb +225 -0
  165. data/lib/fluent/plugin/buf_memory.rb +34 -0
  166. data/lib/fluent/plugin/buffer/chunk.rb +240 -0
  167. data/lib/fluent/plugin/buffer/file_chunk.rb +413 -0
  168. data/lib/fluent/plugin/buffer/file_single_chunk.rb +311 -0
  169. data/lib/fluent/plugin/buffer/memory_chunk.rb +91 -0
  170. data/lib/fluent/plugin/buffer.rb +918 -0
  171. data/lib/fluent/plugin/compressable.rb +96 -0
  172. data/lib/fluent/plugin/exec_util.rb +22 -0
  173. data/lib/fluent/plugin/file_util.rb +22 -0
  174. data/lib/fluent/plugin/file_wrapper.rb +132 -0
  175. data/lib/fluent/plugin/filter.rb +127 -0
  176. data/lib/fluent/plugin/filter_grep.rb +189 -0
  177. data/lib/fluent/plugin/filter_parser.rb +130 -0
  178. data/lib/fluent/plugin/filter_record_transformer.rb +324 -0
  179. data/lib/fluent/plugin/filter_stdout.rb +53 -0
  180. data/lib/fluent/plugin/formatter.rb +75 -0
  181. data/lib/fluent/plugin/formatter_csv.rb +78 -0
  182. data/lib/fluent/plugin/formatter_hash.rb +35 -0
  183. data/lib/fluent/plugin/formatter_json.rb +59 -0
  184. data/lib/fluent/plugin/formatter_ltsv.rb +44 -0
  185. data/lib/fluent/plugin/formatter_msgpack.rb +33 -0
  186. data/lib/fluent/plugin/formatter_out_file.rb +53 -0
  187. data/lib/fluent/plugin/formatter_single_value.rb +36 -0
  188. data/lib/fluent/plugin/formatter_stdout.rb +76 -0
  189. data/lib/fluent/plugin/formatter_tsv.rb +40 -0
  190. data/lib/fluent/plugin/in_debug_agent.rb +71 -0
  191. data/lib/fluent/plugin/in_dummy.rb +18 -0
  192. data/lib/fluent/plugin/in_exec.rb +110 -0
  193. data/lib/fluent/plugin/in_forward.rb +473 -0
  194. data/lib/fluent/plugin/in_gc_stat.rb +72 -0
  195. data/lib/fluent/plugin/in_http.rb +677 -0
  196. data/lib/fluent/plugin/in_monitor_agent.rb +412 -0
  197. data/lib/fluent/plugin/in_object_space.rb +93 -0
  198. data/lib/fluent/plugin/in_sample.rb +141 -0
  199. data/lib/fluent/plugin/in_syslog.rb +276 -0
  200. data/lib/fluent/plugin/in_tail/group_watch.rb +204 -0
  201. data/lib/fluent/plugin/in_tail/position_file.rb +255 -0
  202. data/lib/fluent/plugin/in_tail.rb +1247 -0
  203. data/lib/fluent/plugin/in_tcp.rb +181 -0
  204. data/lib/fluent/plugin/in_udp.rb +92 -0
  205. data/lib/fluent/plugin/in_unix.rb +195 -0
  206. data/lib/fluent/plugin/input.rb +75 -0
  207. data/lib/fluent/plugin/metrics.rb +119 -0
  208. data/lib/fluent/plugin/metrics_local.rb +96 -0
  209. data/lib/fluent/plugin/multi_output.rb +195 -0
  210. data/lib/fluent/plugin/out_copy.rb +120 -0
  211. data/lib/fluent/plugin/out_exec.rb +105 -0
  212. data/lib/fluent/plugin/out_exec_filter.rb +319 -0
  213. data/lib/fluent/plugin/out_file.rb +334 -0
  214. data/lib/fluent/plugin/out_forward/ack_handler.rb +161 -0
  215. data/lib/fluent/plugin/out_forward/connection_manager.rb +113 -0
  216. data/lib/fluent/plugin/out_forward/error.rb +28 -0
  217. data/lib/fluent/plugin/out_forward/failure_detector.rb +84 -0
  218. data/lib/fluent/plugin/out_forward/handshake_protocol.rb +125 -0
  219. data/lib/fluent/plugin/out_forward/load_balancer.rb +114 -0
  220. data/lib/fluent/plugin/out_forward/socket_cache.rb +142 -0
  221. data/lib/fluent/plugin/out_forward.rb +826 -0
  222. data/lib/fluent/plugin/out_http.rb +280 -0
  223. data/lib/fluent/plugin/out_null.rb +74 -0
  224. data/lib/fluent/plugin/out_relabel.rb +32 -0
  225. data/lib/fluent/plugin/out_roundrobin.rb +84 -0
  226. data/lib/fluent/plugin/out_secondary_file.rb +131 -0
  227. data/lib/fluent/plugin/out_stdout.rb +74 -0
  228. data/lib/fluent/plugin/out_stream.rb +130 -0
  229. data/lib/fluent/plugin/output.rb +1566 -0
  230. data/lib/fluent/plugin/owned_by_mixin.rb +42 -0
  231. data/lib/fluent/plugin/parser.rb +274 -0
  232. data/lib/fluent/plugin/parser_apache.rb +28 -0
  233. data/lib/fluent/plugin/parser_apache2.rb +88 -0
  234. data/lib/fluent/plugin/parser_apache_error.rb +26 -0
  235. data/lib/fluent/plugin/parser_csv.rb +114 -0
  236. data/lib/fluent/plugin/parser_json.rb +96 -0
  237. data/lib/fluent/plugin/parser_ltsv.rb +51 -0
  238. data/lib/fluent/plugin/parser_msgpack.rb +50 -0
  239. data/lib/fluent/plugin/parser_multiline.rb +152 -0
  240. data/lib/fluent/plugin/parser_nginx.rb +28 -0
  241. data/lib/fluent/plugin/parser_none.rb +36 -0
  242. data/lib/fluent/plugin/parser_regexp.rb +68 -0
  243. data/lib/fluent/plugin/parser_syslog.rb +496 -0
  244. data/lib/fluent/plugin/parser_tsv.rb +42 -0
  245. data/lib/fluent/plugin/sd_file.rb +156 -0
  246. data/lib/fluent/plugin/sd_srv.rb +135 -0
  247. data/lib/fluent/plugin/sd_static.rb +58 -0
  248. data/lib/fluent/plugin/service_discovery.rb +65 -0
  249. data/lib/fluent/plugin/socket_util.rb +22 -0
  250. data/lib/fluent/plugin/storage.rb +84 -0
  251. data/lib/fluent/plugin/storage_local.rb +162 -0
  252. data/lib/fluent/plugin/string_util.rb +22 -0
  253. data/lib/fluent/plugin.rb +206 -0
  254. data/lib/fluent/plugin_helper/cert_option.rb +191 -0
  255. data/lib/fluent/plugin_helper/child_process.rb +366 -0
  256. data/lib/fluent/plugin_helper/compat_parameters.rb +343 -0
  257. data/lib/fluent/plugin_helper/counter.rb +51 -0
  258. data/lib/fluent/plugin_helper/event_emitter.rb +100 -0
  259. data/lib/fluent/plugin_helper/event_loop.rb +170 -0
  260. data/lib/fluent/plugin_helper/extract.rb +104 -0
  261. data/lib/fluent/plugin_helper/formatter.rb +147 -0
  262. data/lib/fluent/plugin_helper/http_server/app.rb +79 -0
  263. data/lib/fluent/plugin_helper/http_server/compat/server.rb +92 -0
  264. data/lib/fluent/plugin_helper/http_server/compat/ssl_context_extractor.rb +52 -0
  265. data/lib/fluent/plugin_helper/http_server/compat/webrick_handler.rb +58 -0
  266. data/lib/fluent/plugin_helper/http_server/methods.rb +35 -0
  267. data/lib/fluent/plugin_helper/http_server/request.rb +42 -0
  268. data/lib/fluent/plugin_helper/http_server/router.rb +54 -0
  269. data/lib/fluent/plugin_helper/http_server/server.rb +93 -0
  270. data/lib/fluent/plugin_helper/http_server/ssl_context_builder.rb +41 -0
  271. data/lib/fluent/plugin_helper/http_server.rb +135 -0
  272. data/lib/fluent/plugin_helper/inject.rb +154 -0
  273. data/lib/fluent/plugin_helper/metrics.rb +129 -0
  274. data/lib/fluent/plugin_helper/parser.rb +147 -0
  275. data/lib/fluent/plugin_helper/record_accessor.rb +207 -0
  276. data/lib/fluent/plugin_helper/retry_state.rb +219 -0
  277. data/lib/fluent/plugin_helper/server.rb +820 -0
  278. data/lib/fluent/plugin_helper/service_discovery/manager.rb +146 -0
  279. data/lib/fluent/plugin_helper/service_discovery/round_robin_balancer.rb +43 -0
  280. data/lib/fluent/plugin_helper/service_discovery.rb +125 -0
  281. data/lib/fluent/plugin_helper/socket.rb +288 -0
  282. data/lib/fluent/plugin_helper/socket_option.rb +98 -0
  283. data/lib/fluent/plugin_helper/storage.rb +349 -0
  284. data/lib/fluent/plugin_helper/thread.rb +180 -0
  285. data/lib/fluent/plugin_helper/timer.rb +92 -0
  286. data/lib/fluent/plugin_helper.rb +75 -0
  287. data/lib/fluent/plugin_id.rb +93 -0
  288. data/lib/fluent/process.rb +22 -0
  289. data/lib/fluent/registry.rb +117 -0
  290. data/lib/fluent/root_agent.rb +372 -0
  291. data/lib/fluent/rpc.rb +95 -0
  292. data/lib/fluent/static_config_analysis.rb +194 -0
  293. data/lib/fluent/supervisor.rb +1141 -0
  294. data/lib/fluent/system_config.rb +188 -0
  295. data/lib/fluent/test/base.rb +78 -0
  296. data/lib/fluent/test/driver/base.rb +225 -0
  297. data/lib/fluent/test/driver/base_owned.rb +83 -0
  298. data/lib/fluent/test/driver/base_owner.rb +135 -0
  299. data/lib/fluent/test/driver/event_feeder.rb +98 -0
  300. data/lib/fluent/test/driver/filter.rb +57 -0
  301. data/lib/fluent/test/driver/formatter.rb +30 -0
  302. data/lib/fluent/test/driver/input.rb +31 -0
  303. data/lib/fluent/test/driver/multi_output.rb +53 -0
  304. data/lib/fluent/test/driver/output.rb +102 -0
  305. data/lib/fluent/test/driver/parser.rb +30 -0
  306. data/lib/fluent/test/driver/storage.rb +30 -0
  307. data/lib/fluent/test/driver/test_event_router.rb +45 -0
  308. data/lib/fluent/test/filter_test.rb +77 -0
  309. data/lib/fluent/test/formatter_test.rb +65 -0
  310. data/lib/fluent/test/helpers.rb +134 -0
  311. data/lib/fluent/test/input_test.rb +174 -0
  312. data/lib/fluent/test/log.rb +79 -0
  313. data/lib/fluent/test/output_test.rb +156 -0
  314. data/lib/fluent/test/parser_test.rb +70 -0
  315. data/lib/fluent/test/startup_shutdown.rb +46 -0
  316. data/lib/fluent/test.rb +58 -0
  317. data/lib/fluent/time.rb +512 -0
  318. data/lib/fluent/timezone.rb +171 -0
  319. data/lib/fluent/tls.rb +81 -0
  320. data/lib/fluent/unique_id.rb +39 -0
  321. data/lib/fluent/variable_store.rb +40 -0
  322. data/lib/fluent/version.rb +21 -0
  323. data/lib/fluent/winsvc.rb +105 -0
  324. data/templates/new_gem/Gemfile +3 -0
  325. data/templates/new_gem/README.md.erb +43 -0
  326. data/templates/new_gem/Rakefile +13 -0
  327. data/templates/new_gem/fluent-plugin.gemspec.erb +27 -0
  328. data/templates/new_gem/lib/fluent/plugin/filter.rb.erb +14 -0
  329. data/templates/new_gem/lib/fluent/plugin/formatter.rb.erb +14 -0
  330. data/templates/new_gem/lib/fluent/plugin/input.rb.erb +11 -0
  331. data/templates/new_gem/lib/fluent/plugin/output.rb.erb +11 -0
  332. data/templates/new_gem/lib/fluent/plugin/parser.rb.erb +15 -0
  333. data/templates/new_gem/lib/fluent/plugin/storage.rb.erb +40 -0
  334. data/templates/new_gem/test/helper.rb.erb +8 -0
  335. data/templates/new_gem/test/plugin/test_filter.rb.erb +18 -0
  336. data/templates/new_gem/test/plugin/test_formatter.rb.erb +18 -0
  337. data/templates/new_gem/test/plugin/test_input.rb.erb +18 -0
  338. data/templates/new_gem/test/plugin/test_output.rb.erb +18 -0
  339. data/templates/new_gem/test/plugin/test_parser.rb.erb +18 -0
  340. data/templates/new_gem/test/plugin/test_storage.rb.erb +18 -0
  341. data/templates/plugin_config_formatter/param.md-compact.erb +25 -0
  342. data/templates/plugin_config_formatter/param.md-table.erb +10 -0
  343. data/templates/plugin_config_formatter/param.md.erb +34 -0
  344. data/templates/plugin_config_formatter/section.md.erb +12 -0
  345. data/test/command/test_binlog_reader.rb +362 -0
  346. data/test/command/test_ca_generate.rb +70 -0
  347. data/test/command/test_cap_ctl.rb +100 -0
  348. data/test/command/test_cat.rb +128 -0
  349. data/test/command/test_ctl.rb +56 -0
  350. data/test/command/test_fluentd.rb +1139 -0
  351. data/test/command/test_plugin_config_formatter.rb +398 -0
  352. data/test/command/test_plugin_generator.rb +109 -0
  353. data/test/compat/test_calls_super.rb +166 -0
  354. data/test/compat/test_parser.rb +92 -0
  355. data/test/config/assertions.rb +42 -0
  356. data/test/config/test_config_parser.rb +551 -0
  357. data/test/config/test_configurable.rb +1784 -0
  358. data/test/config/test_configure_proxy.rb +604 -0
  359. data/test/config/test_dsl.rb +415 -0
  360. data/test/config/test_element.rb +518 -0
  361. data/test/config/test_literal_parser.rb +309 -0
  362. data/test/config/test_plugin_configuration.rb +56 -0
  363. data/test/config/test_section.rb +191 -0
  364. data/test/config/test_system_config.rb +201 -0
  365. data/test/config/test_types.rb +408 -0
  366. data/test/counter/test_client.rb +563 -0
  367. data/test/counter/test_error.rb +44 -0
  368. data/test/counter/test_mutex_hash.rb +179 -0
  369. data/test/counter/test_server.rb +589 -0
  370. data/test/counter/test_store.rb +258 -0
  371. data/test/counter/test_validator.rb +137 -0
  372. data/test/helper.rb +155 -0
  373. data/test/helpers/fuzzy_assert.rb +89 -0
  374. data/test/helpers/process_extenstion.rb +33 -0
  375. data/test/plugin/data/2010/01/20100102-030405.log +0 -0
  376. data/test/plugin/data/2010/01/20100102-030406.log +0 -0
  377. data/test/plugin/data/2010/01/20100102.log +0 -0
  378. data/test/plugin/data/log/bar +0 -0
  379. data/test/plugin/data/log/foo/bar.log +0 -0
  380. data/test/plugin/data/log/foo/bar2 +0 -0
  381. data/test/plugin/data/log/test.log +0 -0
  382. data/test/plugin/data/sd_file/config +11 -0
  383. data/test/plugin/data/sd_file/config.json +17 -0
  384. data/test/plugin/data/sd_file/config.yaml +11 -0
  385. data/test/plugin/data/sd_file/config.yml +11 -0
  386. data/test/plugin/data/sd_file/invalid_config.yml +7 -0
  387. data/test/plugin/in_tail/test_fifo.rb +121 -0
  388. data/test/plugin/in_tail/test_io_handler.rb +150 -0
  389. data/test/plugin/in_tail/test_position_file.rb +316 -0
  390. data/test/plugin/out_forward/test_ack_handler.rb +101 -0
  391. data/test/plugin/out_forward/test_connection_manager.rb +145 -0
  392. data/test/plugin/out_forward/test_handshake_protocol.rb +112 -0
  393. data/test/plugin/out_forward/test_load_balancer.rb +106 -0
  394. data/test/plugin/out_forward/test_socket_cache.rb +174 -0
  395. data/test/plugin/test_bare_output.rb +131 -0
  396. data/test/plugin/test_base.rb +115 -0
  397. data/test/plugin/test_buf_file.rb +1275 -0
  398. data/test/plugin/test_buf_file_single.rb +833 -0
  399. data/test/plugin/test_buf_memory.rb +42 -0
  400. data/test/plugin/test_buffer.rb +1383 -0
  401. data/test/plugin/test_buffer_chunk.rb +198 -0
  402. data/test/plugin/test_buffer_file_chunk.rb +871 -0
  403. data/test/plugin/test_buffer_file_single_chunk.rb +611 -0
  404. data/test/plugin/test_buffer_memory_chunk.rb +339 -0
  405. data/test/plugin/test_compressable.rb +87 -0
  406. data/test/plugin/test_file_util.rb +96 -0
  407. data/test/plugin/test_file_wrapper.rb +58 -0
  408. data/test/plugin/test_filter.rb +368 -0
  409. data/test/plugin/test_filter_grep.rb +697 -0
  410. data/test/plugin/test_filter_parser.rb +731 -0
  411. data/test/plugin/test_filter_record_transformer.rb +577 -0
  412. data/test/plugin/test_filter_stdout.rb +207 -0
  413. data/test/plugin/test_formatter_csv.rb +136 -0
  414. data/test/plugin/test_formatter_hash.rb +38 -0
  415. data/test/plugin/test_formatter_json.rb +61 -0
  416. data/test/plugin/test_formatter_ltsv.rb +70 -0
  417. data/test/plugin/test_formatter_msgpack.rb +28 -0
  418. data/test/plugin/test_formatter_out_file.rb +116 -0
  419. data/test/plugin/test_formatter_single_value.rb +44 -0
  420. data/test/plugin/test_formatter_tsv.rb +76 -0
  421. data/test/plugin/test_in_debug_agent.rb +49 -0
  422. data/test/plugin/test_in_exec.rb +261 -0
  423. data/test/plugin/test_in_forward.rb +1178 -0
  424. data/test/plugin/test_in_gc_stat.rb +62 -0
  425. data/test/plugin/test_in_http.rb +1103 -0
  426. data/test/plugin/test_in_monitor_agent.rb +923 -0
  427. data/test/plugin/test_in_object_space.rb +66 -0
  428. data/test/plugin/test_in_sample.rb +190 -0
  429. data/test/plugin/test_in_syslog.rb +505 -0
  430. data/test/plugin/test_in_tail.rb +2639 -0
  431. data/test/plugin/test_in_tcp.rb +243 -0
  432. data/test/plugin/test_in_udp.rb +268 -0
  433. data/test/plugin/test_in_unix.rb +181 -0
  434. data/test/plugin/test_input.rb +137 -0
  435. data/test/plugin/test_metadata.rb +89 -0
  436. data/test/plugin/test_metrics.rb +294 -0
  437. data/test/plugin/test_metrics_local.rb +96 -0
  438. data/test/plugin/test_multi_output.rb +204 -0
  439. data/test/plugin/test_out_copy.rb +308 -0
  440. data/test/plugin/test_out_exec.rb +312 -0
  441. data/test/plugin/test_out_exec_filter.rb +606 -0
  442. data/test/plugin/test_out_file.rb +1037 -0
  443. data/test/plugin/test_out_forward.rb +1358 -0
  444. data/test/plugin/test_out_http.rb +428 -0
  445. data/test/plugin/test_out_null.rb +105 -0
  446. data/test/plugin/test_out_relabel.rb +28 -0
  447. data/test/plugin/test_out_roundrobin.rb +146 -0
  448. data/test/plugin/test_out_secondary_file.rb +458 -0
  449. data/test/plugin/test_out_stdout.rb +205 -0
  450. data/test/plugin/test_out_stream.rb +103 -0
  451. data/test/plugin/test_output.rb +1065 -0
  452. data/test/plugin/test_output_as_buffered.rb +2024 -0
  453. data/test/plugin/test_output_as_buffered_backup.rb +363 -0
  454. data/test/plugin/test_output_as_buffered_compress.rb +165 -0
  455. data/test/plugin/test_output_as_buffered_overflow.rb +250 -0
  456. data/test/plugin/test_output_as_buffered_retries.rb +966 -0
  457. data/test/plugin/test_output_as_buffered_secondary.rb +882 -0
  458. data/test/plugin/test_output_as_standard.rb +374 -0
  459. data/test/plugin/test_owned_by.rb +35 -0
  460. data/test/plugin/test_parser.rb +399 -0
  461. data/test/plugin/test_parser_apache.rb +42 -0
  462. data/test/plugin/test_parser_apache2.rb +47 -0
  463. data/test/plugin/test_parser_apache_error.rb +45 -0
  464. data/test/plugin/test_parser_csv.rb +200 -0
  465. data/test/plugin/test_parser_json.rb +138 -0
  466. data/test/plugin/test_parser_labeled_tsv.rb +160 -0
  467. data/test/plugin/test_parser_multiline.rb +111 -0
  468. data/test/plugin/test_parser_nginx.rb +88 -0
  469. data/test/plugin/test_parser_none.rb +52 -0
  470. data/test/plugin/test_parser_regexp.rb +289 -0
  471. data/test/plugin/test_parser_syslog.rb +650 -0
  472. data/test/plugin/test_parser_tsv.rb +122 -0
  473. data/test/plugin/test_sd_file.rb +228 -0
  474. data/test/plugin/test_sd_srv.rb +230 -0
  475. data/test/plugin/test_storage.rb +167 -0
  476. data/test/plugin/test_storage_local.rb +335 -0
  477. data/test/plugin/test_string_util.rb +26 -0
  478. data/test/plugin_helper/data/cert/cert-key.pem +27 -0
  479. data/test/plugin_helper/data/cert/cert-with-CRLF.pem +19 -0
  480. data/test/plugin_helper/data/cert/cert-with-no-newline.pem +19 -0
  481. data/test/plugin_helper/data/cert/cert.pem +19 -0
  482. data/test/plugin_helper/data/cert/cert_chains/ca-cert-key.pem +27 -0
  483. data/test/plugin_helper/data/cert/cert_chains/ca-cert.pem +20 -0
  484. data/test/plugin_helper/data/cert/cert_chains/cert-key.pem +27 -0
  485. data/test/plugin_helper/data/cert/cert_chains/cert.pem +40 -0
  486. data/test/plugin_helper/data/cert/empty.pem +0 -0
  487. data/test/plugin_helper/data/cert/generate_cert.rb +125 -0
  488. data/test/plugin_helper/data/cert/with_ca/ca-cert-key-pass.pem +30 -0
  489. data/test/plugin_helper/data/cert/with_ca/ca-cert-key.pem +27 -0
  490. data/test/plugin_helper/data/cert/with_ca/ca-cert-pass.pem +20 -0
  491. data/test/plugin_helper/data/cert/with_ca/ca-cert.pem +20 -0
  492. data/test/plugin_helper/data/cert/with_ca/cert-key-pass.pem +30 -0
  493. data/test/plugin_helper/data/cert/with_ca/cert-key.pem +27 -0
  494. data/test/plugin_helper/data/cert/with_ca/cert-pass.pem +21 -0
  495. data/test/plugin_helper/data/cert/with_ca/cert.pem +21 -0
  496. data/test/plugin_helper/data/cert/without_ca/cert-key-pass.pem +30 -0
  497. data/test/plugin_helper/data/cert/without_ca/cert-key.pem +27 -0
  498. data/test/plugin_helper/data/cert/without_ca/cert-pass.pem +20 -0
  499. data/test/plugin_helper/data/cert/without_ca/cert.pem +20 -0
  500. data/test/plugin_helper/http_server/test_app.rb +65 -0
  501. data/test/plugin_helper/http_server/test_route.rb +32 -0
  502. data/test/plugin_helper/service_discovery/test_manager.rb +93 -0
  503. data/test/plugin_helper/service_discovery/test_round_robin_balancer.rb +21 -0
  504. data/test/plugin_helper/test_cert_option.rb +25 -0
  505. data/test/plugin_helper/test_child_process.rb +852 -0
  506. data/test/plugin_helper/test_compat_parameters.rb +358 -0
  507. data/test/plugin_helper/test_event_emitter.rb +80 -0
  508. data/test/plugin_helper/test_event_loop.rb +52 -0
  509. data/test/plugin_helper/test_extract.rb +194 -0
  510. data/test/plugin_helper/test_formatter.rb +255 -0
  511. data/test/plugin_helper/test_http_server_helper.rb +372 -0
  512. data/test/plugin_helper/test_inject.rb +561 -0
  513. data/test/plugin_helper/test_metrics.rb +137 -0
  514. data/test/plugin_helper/test_parser.rb +264 -0
  515. data/test/plugin_helper/test_record_accessor.rb +238 -0
  516. data/test/plugin_helper/test_retry_state.rb +1006 -0
  517. data/test/plugin_helper/test_server.rb +1841 -0
  518. data/test/plugin_helper/test_service_discovery.rb +165 -0
  519. data/test/plugin_helper/test_socket.rb +146 -0
  520. data/test/plugin_helper/test_storage.rb +542 -0
  521. data/test/plugin_helper/test_thread.rb +164 -0
  522. data/test/plugin_helper/test_timer.rb +130 -0
  523. data/test/scripts/exec_script.rb +32 -0
  524. data/test/scripts/fluent/plugin/formatter1/formatter_test1.rb +7 -0
  525. data/test/scripts/fluent/plugin/formatter2/formatter_test2.rb +7 -0
  526. data/test/scripts/fluent/plugin/formatter_known.rb +8 -0
  527. data/test/scripts/fluent/plugin/out_test.rb +81 -0
  528. data/test/scripts/fluent/plugin/out_test2.rb +80 -0
  529. data/test/scripts/fluent/plugin/parser_known.rb +4 -0
  530. data/test/test_capability.rb +74 -0
  531. data/test/test_clock.rb +164 -0
  532. data/test/test_config.rb +333 -0
  533. data/test/test_configdsl.rb +148 -0
  534. data/test/test_daemonizer.rb +91 -0
  535. data/test/test_engine.rb +203 -0
  536. data/test/test_event.rb +531 -0
  537. data/test/test_event_router.rb +348 -0
  538. data/test/test_event_time.rb +199 -0
  539. data/test/test_filter.rb +121 -0
  540. data/test/test_fluent_log_event_router.rb +99 -0
  541. data/test/test_formatter.rb +366 -0
  542. data/test/test_input.rb +31 -0
  543. data/test/test_log.rb +994 -0
  544. data/test/test_logger_initializer.rb +46 -0
  545. data/test/test_match.rb +148 -0
  546. data/test/test_mixin.rb +351 -0
  547. data/test/test_msgpack_factory.rb +18 -0
  548. data/test/test_oj_options.rb +55 -0
  549. data/test/test_output.rb +278 -0
  550. data/test/test_plugin.rb +251 -0
  551. data/test/test_plugin_classes.rb +370 -0
  552. data/test/test_plugin_helper.rb +81 -0
  553. data/test/test_plugin_id.rb +119 -0
  554. data/test/test_process.rb +14 -0
  555. data/test/test_root_agent.rb +951 -0
  556. data/test/test_static_config_analysis.rb +177 -0
  557. data/test/test_supervisor.rb +791 -0
  558. data/test/test_test_drivers.rb +136 -0
  559. data/test/test_time_formatter.rb +301 -0
  560. data/test/test_time_parser.rb +362 -0
  561. data/test/test_tls.rb +65 -0
  562. data/test/test_unique_id.rb +47 -0
  563. data/test/test_variable_store.rb +65 -0
  564. metadata +1191 -0
@@ -0,0 +1,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