fluentd-hubspot 0.14.14.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (396) hide show
  1. data/.github/ISSUE_TEMPLATE.md +6 -0
  2. data/.gitignore +28 -0
  3. data/.travis.yml +51 -0
  4. data/AUTHORS +2 -0
  5. data/CONTRIBUTING.md +42 -0
  6. data/COPYING +14 -0
  7. data/ChangeLog +593 -0
  8. data/Gemfile +9 -0
  9. data/README.md +76 -0
  10. data/Rakefile +74 -0
  11. data/Vagrantfile +17 -0
  12. data/appveyor.yml +43 -0
  13. data/bin/fluent-binlog-reader +7 -0
  14. data/bin/fluent-debug +5 -0
  15. data/bin/fluent-plugin-config-format +5 -0
  16. data/bin/fluent-plugin-generate +5 -0
  17. data/code-of-conduct.md +3 -0
  18. data/example/copy_roundrobin.conf +39 -0
  19. data/example/filter_stdout.conf +22 -0
  20. data/example/in_dummy_blocks.conf +17 -0
  21. data/example/in_dummy_with_compression.conf +23 -0
  22. data/example/in_forward.conf +14 -0
  23. data/example/in_forward_client.conf +37 -0
  24. data/example/in_forward_shared_key.conf +15 -0
  25. data/example/in_forward_tls.conf +14 -0
  26. data/example/in_forward_users.conf +24 -0
  27. data/example/in_forward_workers.conf +21 -0
  28. data/example/in_http.conf +14 -0
  29. data/example/in_out_forward.conf +17 -0
  30. data/example/in_syslog.conf +15 -0
  31. data/example/in_tail.conf +14 -0
  32. data/example/in_tcp.conf +13 -0
  33. data/example/in_udp.conf +13 -0
  34. data/example/logevents.conf +25 -0
  35. data/example/multi_filters.conf +61 -0
  36. data/example/out_copy.conf +20 -0
  37. data/example/out_exec_filter.conf +42 -0
  38. data/example/out_file.conf +13 -0
  39. data/example/out_forward.conf +35 -0
  40. data/example/out_forward_buf_file.conf +23 -0
  41. data/example/out_forward_client.conf +109 -0
  42. data/example/out_forward_heartbeat_none.conf +16 -0
  43. data/example/out_forward_shared_key.conf +36 -0
  44. data/example/out_forward_tls.conf +18 -0
  45. data/example/out_forward_users.conf +65 -0
  46. data/example/out_null.conf +36 -0
  47. data/example/secondary_file.conf +41 -0
  48. data/example/suppress_config_dump.conf +7 -0
  49. data/example/v0_12_filter.conf +78 -0
  50. data/example/v1_literal_example.conf +36 -0
  51. data/fluent.conf +139 -0
  52. data/fluentd.gemspec +51 -0
  53. data/lib/fluent/agent.rb +163 -0
  54. data/lib/fluent/clock.rb +62 -0
  55. data/lib/fluent/command/binlog_reader.rb +234 -0
  56. data/lib/fluent/command/bundler_injection.rb +45 -0
  57. data/lib/fluent/command/cat.rb +330 -0
  58. data/lib/fluent/command/debug.rb +102 -0
  59. data/lib/fluent/command/fluentd.rb +301 -0
  60. data/lib/fluent/command/plugin_config_formatter.rb +258 -0
  61. data/lib/fluent/command/plugin_generator.rb +301 -0
  62. data/lib/fluent/compat/call_super_mixin.rb +67 -0
  63. data/lib/fluent/compat/detach_process_mixin.rb +25 -0
  64. data/lib/fluent/compat/exec_util.rb +129 -0
  65. data/lib/fluent/compat/file_util.rb +54 -0
  66. data/lib/fluent/compat/filter.rb +68 -0
  67. data/lib/fluent/compat/formatter.rb +111 -0
  68. data/lib/fluent/compat/formatter_utils.rb +85 -0
  69. data/lib/fluent/compat/handle_tag_and_time_mixin.rb +62 -0
  70. data/lib/fluent/compat/handle_tag_name_mixin.rb +53 -0
  71. data/lib/fluent/compat/input.rb +59 -0
  72. data/lib/fluent/compat/output.rb +728 -0
  73. data/lib/fluent/compat/output_chain.rb +60 -0
  74. data/lib/fluent/compat/parser.rb +310 -0
  75. data/lib/fluent/compat/parser_utils.rb +40 -0
  76. data/lib/fluent/compat/propagate_default.rb +62 -0
  77. data/lib/fluent/compat/record_filter_mixin.rb +34 -0
  78. data/lib/fluent/compat/set_tag_key_mixin.rb +50 -0
  79. data/lib/fluent/compat/set_time_key_mixin.rb +69 -0
  80. data/lib/fluent/compat/socket_util.rb +165 -0
  81. data/lib/fluent/compat/string_util.rb +34 -0
  82. data/lib/fluent/compat/structured_format_mixin.rb +26 -0
  83. data/lib/fluent/compat/type_converter.rb +90 -0
  84. data/lib/fluent/config.rb +56 -0
  85. data/lib/fluent/config/basic_parser.rb +123 -0
  86. data/lib/fluent/config/configure_proxy.rb +418 -0
  87. data/lib/fluent/config/dsl.rb +149 -0
  88. data/lib/fluent/config/element.rb +218 -0
  89. data/lib/fluent/config/error.rb +26 -0
  90. data/lib/fluent/config/literal_parser.rb +251 -0
  91. data/lib/fluent/config/parser.rb +107 -0
  92. data/lib/fluent/config/section.rb +223 -0
  93. data/lib/fluent/config/types.rb +136 -0
  94. data/lib/fluent/config/v1_parser.rb +190 -0
  95. data/lib/fluent/configurable.rb +200 -0
  96. data/lib/fluent/daemon.rb +15 -0
  97. data/lib/fluent/engine.rb +266 -0
  98. data/lib/fluent/env.rb +28 -0
  99. data/lib/fluent/error.rb +30 -0
  100. data/lib/fluent/event.rb +334 -0
  101. data/lib/fluent/event_router.rb +269 -0
  102. data/lib/fluent/filter.rb +21 -0
  103. data/lib/fluent/formatter.rb +23 -0
  104. data/lib/fluent/input.rb +21 -0
  105. data/lib/fluent/label.rb +46 -0
  106. data/lib/fluent/load.rb +35 -0
  107. data/lib/fluent/log.rb +546 -0
  108. data/lib/fluent/match.rb +178 -0
  109. data/lib/fluent/mixin.rb +31 -0
  110. data/lib/fluent/msgpack_factory.rb +62 -0
  111. data/lib/fluent/output.rb +29 -0
  112. data/lib/fluent/output_chain.rb +23 -0
  113. data/lib/fluent/parser.rb +23 -0
  114. data/lib/fluent/plugin.rb +183 -0
  115. data/lib/fluent/plugin/bare_output.rb +63 -0
  116. data/lib/fluent/plugin/base.rb +165 -0
  117. data/lib/fluent/plugin/buf_file.rb +184 -0
  118. data/lib/fluent/plugin/buf_memory.rb +34 -0
  119. data/lib/fluent/plugin/buffer.rb +617 -0
  120. data/lib/fluent/plugin/buffer/chunk.rb +221 -0
  121. data/lib/fluent/plugin/buffer/file_chunk.rb +364 -0
  122. data/lib/fluent/plugin/buffer/memory_chunk.rb +90 -0
  123. data/lib/fluent/plugin/compressable.rb +92 -0
  124. data/lib/fluent/plugin/exec_util.rb +22 -0
  125. data/lib/fluent/plugin/file_util.rb +22 -0
  126. data/lib/fluent/plugin/file_wrapper.rb +120 -0
  127. data/lib/fluent/plugin/filter.rb +93 -0
  128. data/lib/fluent/plugin/filter_grep.rb +75 -0
  129. data/lib/fluent/plugin/filter_parser.rb +119 -0
  130. data/lib/fluent/plugin/filter_record_transformer.rb +322 -0
  131. data/lib/fluent/plugin/filter_stdout.rb +53 -0
  132. data/lib/fluent/plugin/formatter.rb +50 -0
  133. data/lib/fluent/plugin/formatter_csv.rb +52 -0
  134. data/lib/fluent/plugin/formatter_hash.rb +33 -0
  135. data/lib/fluent/plugin/formatter_json.rb +55 -0
  136. data/lib/fluent/plugin/formatter_ltsv.rb +42 -0
  137. data/lib/fluent/plugin/formatter_msgpack.rb +33 -0
  138. data/lib/fluent/plugin/formatter_out_file.rb +51 -0
  139. data/lib/fluent/plugin/formatter_single_value.rb +34 -0
  140. data/lib/fluent/plugin/formatter_stdout.rb +75 -0
  141. data/lib/fluent/plugin/formatter_tsv.rb +34 -0
  142. data/lib/fluent/plugin/in_debug_agent.rb +64 -0
  143. data/lib/fluent/plugin/in_dummy.rb +139 -0
  144. data/lib/fluent/plugin/in_exec.rb +108 -0
  145. data/lib/fluent/plugin/in_forward.rb +455 -0
  146. data/lib/fluent/plugin/in_gc_stat.rb +56 -0
  147. data/lib/fluent/plugin/in_http.rb +433 -0
  148. data/lib/fluent/plugin/in_monitor_agent.rb +448 -0
  149. data/lib/fluent/plugin/in_object_space.rb +93 -0
  150. data/lib/fluent/plugin/in_syslog.rb +209 -0
  151. data/lib/fluent/plugin/in_tail.rb +905 -0
  152. data/lib/fluent/plugin/in_tcp.rb +85 -0
  153. data/lib/fluent/plugin/in_udp.rb +81 -0
  154. data/lib/fluent/plugin/in_unix.rb +201 -0
  155. data/lib/fluent/plugin/input.rb +37 -0
  156. data/lib/fluent/plugin/multi_output.rb +157 -0
  157. data/lib/fluent/plugin/out_copy.rb +46 -0
  158. data/lib/fluent/plugin/out_exec.rb +105 -0
  159. data/lib/fluent/plugin/out_exec_filter.rb +317 -0
  160. data/lib/fluent/plugin/out_file.rb +302 -0
  161. data/lib/fluent/plugin/out_forward.rb +912 -0
  162. data/lib/fluent/plugin/out_null.rb +74 -0
  163. data/lib/fluent/plugin/out_relabel.rb +32 -0
  164. data/lib/fluent/plugin/out_roundrobin.rb +84 -0
  165. data/lib/fluent/plugin/out_secondary_file.rb +133 -0
  166. data/lib/fluent/plugin/out_stdout.rb +75 -0
  167. data/lib/fluent/plugin/out_stream.rb +130 -0
  168. data/lib/fluent/plugin/output.rb +1291 -0
  169. data/lib/fluent/plugin/owned_by_mixin.rb +42 -0
  170. data/lib/fluent/plugin/parser.rb +191 -0
  171. data/lib/fluent/plugin/parser_apache.rb +28 -0
  172. data/lib/fluent/plugin/parser_apache2.rb +84 -0
  173. data/lib/fluent/plugin/parser_apache_error.rb +26 -0
  174. data/lib/fluent/plugin/parser_csv.rb +39 -0
  175. data/lib/fluent/plugin/parser_json.rb +81 -0
  176. data/lib/fluent/plugin/parser_ltsv.rb +42 -0
  177. data/lib/fluent/plugin/parser_msgpack.rb +50 -0
  178. data/lib/fluent/plugin/parser_multiline.rb +105 -0
  179. data/lib/fluent/plugin/parser_nginx.rb +28 -0
  180. data/lib/fluent/plugin/parser_none.rb +36 -0
  181. data/lib/fluent/plugin/parser_regexp.rb +63 -0
  182. data/lib/fluent/plugin/parser_syslog.rb +121 -0
  183. data/lib/fluent/plugin/parser_tsv.rb +42 -0
  184. data/lib/fluent/plugin/socket_util.rb +22 -0
  185. data/lib/fluent/plugin/storage.rb +84 -0
  186. data/lib/fluent/plugin/storage_local.rb +159 -0
  187. data/lib/fluent/plugin/string_util.rb +22 -0
  188. data/lib/fluent/plugin_helper.rb +70 -0
  189. data/lib/fluent/plugin_helper/cert_option.rb +159 -0
  190. data/lib/fluent/plugin_helper/child_process.rb +364 -0
  191. data/lib/fluent/plugin_helper/compat_parameters.rb +331 -0
  192. data/lib/fluent/plugin_helper/event_emitter.rb +93 -0
  193. data/lib/fluent/plugin_helper/event_loop.rb +161 -0
  194. data/lib/fluent/plugin_helper/extract.rb +104 -0
  195. data/lib/fluent/plugin_helper/formatter.rb +147 -0
  196. data/lib/fluent/plugin_helper/inject.rb +151 -0
  197. data/lib/fluent/plugin_helper/parser.rb +147 -0
  198. data/lib/fluent/plugin_helper/retry_state.rb +201 -0
  199. data/lib/fluent/plugin_helper/server.rb +738 -0
  200. data/lib/fluent/plugin_helper/socket.rb +241 -0
  201. data/lib/fluent/plugin_helper/socket_option.rb +69 -0
  202. data/lib/fluent/plugin_helper/storage.rb +349 -0
  203. data/lib/fluent/plugin_helper/thread.rb +179 -0
  204. data/lib/fluent/plugin_helper/timer.rb +91 -0
  205. data/lib/fluent/plugin_id.rb +80 -0
  206. data/lib/fluent/process.rb +22 -0
  207. data/lib/fluent/registry.rb +116 -0
  208. data/lib/fluent/root_agent.rb +323 -0
  209. data/lib/fluent/rpc.rb +94 -0
  210. data/lib/fluent/supervisor.rb +741 -0
  211. data/lib/fluent/system_config.rb +159 -0
  212. data/lib/fluent/test.rb +58 -0
  213. data/lib/fluent/test/base.rb +78 -0
  214. data/lib/fluent/test/driver/base.rb +224 -0
  215. data/lib/fluent/test/driver/base_owned.rb +70 -0
  216. data/lib/fluent/test/driver/base_owner.rb +135 -0
  217. data/lib/fluent/test/driver/event_feeder.rb +98 -0
  218. data/lib/fluent/test/driver/filter.rb +57 -0
  219. data/lib/fluent/test/driver/formatter.rb +30 -0
  220. data/lib/fluent/test/driver/input.rb +31 -0
  221. data/lib/fluent/test/driver/multi_output.rb +53 -0
  222. data/lib/fluent/test/driver/output.rb +102 -0
  223. data/lib/fluent/test/driver/parser.rb +30 -0
  224. data/lib/fluent/test/driver/test_event_router.rb +45 -0
  225. data/lib/fluent/test/filter_test.rb +77 -0
  226. data/lib/fluent/test/formatter_test.rb +65 -0
  227. data/lib/fluent/test/helpers.rb +134 -0
  228. data/lib/fluent/test/input_test.rb +174 -0
  229. data/lib/fluent/test/log.rb +79 -0
  230. data/lib/fluent/test/output_test.rb +156 -0
  231. data/lib/fluent/test/parser_test.rb +70 -0
  232. data/lib/fluent/test/startup_shutdown.rb +46 -0
  233. data/lib/fluent/time.rb +412 -0
  234. data/lib/fluent/timezone.rb +133 -0
  235. data/lib/fluent/unique_id.rb +39 -0
  236. data/lib/fluent/version.rb +21 -0
  237. data/lib/fluent/winsvc.rb +71 -0
  238. data/templates/new_gem/Gemfile +3 -0
  239. data/templates/new_gem/README.md.erb +43 -0
  240. data/templates/new_gem/Rakefile +13 -0
  241. data/templates/new_gem/fluent-plugin.gemspec.erb +27 -0
  242. data/templates/new_gem/lib/fluent/plugin/filter.rb.erb +14 -0
  243. data/templates/new_gem/lib/fluent/plugin/formatter.rb.erb +14 -0
  244. data/templates/new_gem/lib/fluent/plugin/input.rb.erb +11 -0
  245. data/templates/new_gem/lib/fluent/plugin/output.rb.erb +11 -0
  246. data/templates/new_gem/lib/fluent/plugin/parser.rb.erb +15 -0
  247. data/templates/new_gem/test/helper.rb.erb +8 -0
  248. data/templates/new_gem/test/plugin/test_filter.rb.erb +18 -0
  249. data/templates/new_gem/test/plugin/test_formatter.rb.erb +18 -0
  250. data/templates/new_gem/test/plugin/test_input.rb.erb +18 -0
  251. data/templates/new_gem/test/plugin/test_output.rb.erb +18 -0
  252. data/templates/new_gem/test/plugin/test_parser.rb.erb +18 -0
  253. data/templates/plugin_config_formatter/param.md-compact.erb +25 -0
  254. data/templates/plugin_config_formatter/param.md.erb +34 -0
  255. data/templates/plugin_config_formatter/section.md.erb +12 -0
  256. data/test/command/test_binlog_reader.rb +346 -0
  257. data/test/command/test_fluentd.rb +618 -0
  258. data/test/command/test_plugin_config_formatter.rb +275 -0
  259. data/test/command/test_plugin_generator.rb +66 -0
  260. data/test/compat/test_calls_super.rb +166 -0
  261. data/test/compat/test_parser.rb +92 -0
  262. data/test/config/assertions.rb +42 -0
  263. data/test/config/test_config_parser.rb +513 -0
  264. data/test/config/test_configurable.rb +1587 -0
  265. data/test/config/test_configure_proxy.rb +566 -0
  266. data/test/config/test_dsl.rb +415 -0
  267. data/test/config/test_element.rb +403 -0
  268. data/test/config/test_literal_parser.rb +297 -0
  269. data/test/config/test_section.rb +184 -0
  270. data/test/config/test_system_config.rb +168 -0
  271. data/test/config/test_types.rb +191 -0
  272. data/test/helper.rb +153 -0
  273. data/test/plugin/data/2010/01/20100102-030405.log +0 -0
  274. data/test/plugin/data/2010/01/20100102-030406.log +0 -0
  275. data/test/plugin/data/2010/01/20100102.log +0 -0
  276. data/test/plugin/data/log/bar +0 -0
  277. data/test/plugin/data/log/foo/bar.log +0 -0
  278. data/test/plugin/data/log/foo/bar2 +0 -0
  279. data/test/plugin/data/log/test.log +0 -0
  280. data/test/plugin/test_bare_output.rb +118 -0
  281. data/test/plugin/test_base.rb +115 -0
  282. data/test/plugin/test_buf_file.rb +843 -0
  283. data/test/plugin/test_buf_memory.rb +42 -0
  284. data/test/plugin/test_buffer.rb +1220 -0
  285. data/test/plugin/test_buffer_chunk.rb +198 -0
  286. data/test/plugin/test_buffer_file_chunk.rb +844 -0
  287. data/test/plugin/test_buffer_memory_chunk.rb +338 -0
  288. data/test/plugin/test_compressable.rb +84 -0
  289. data/test/plugin/test_file_util.rb +96 -0
  290. data/test/plugin/test_filter.rb +357 -0
  291. data/test/plugin/test_filter_grep.rb +119 -0
  292. data/test/plugin/test_filter_parser.rb +700 -0
  293. data/test/plugin/test_filter_record_transformer.rb +556 -0
  294. data/test/plugin/test_filter_stdout.rb +202 -0
  295. data/test/plugin/test_formatter_csv.rb +111 -0
  296. data/test/plugin/test_formatter_hash.rb +35 -0
  297. data/test/plugin/test_formatter_json.rb +51 -0
  298. data/test/plugin/test_formatter_ltsv.rb +59 -0
  299. data/test/plugin/test_formatter_msgpack.rb +28 -0
  300. data/test/plugin/test_formatter_out_file.rb +95 -0
  301. data/test/plugin/test_formatter_single_value.rb +38 -0
  302. data/test/plugin/test_in_debug_agent.rb +28 -0
  303. data/test/plugin/test_in_dummy.rb +192 -0
  304. data/test/plugin/test_in_exec.rb +245 -0
  305. data/test/plugin/test_in_forward.rb +1120 -0
  306. data/test/plugin/test_in_gc_stat.rb +39 -0
  307. data/test/plugin/test_in_http.rb +588 -0
  308. data/test/plugin/test_in_monitor_agent.rb +516 -0
  309. data/test/plugin/test_in_object_space.rb +64 -0
  310. data/test/plugin/test_in_syslog.rb +271 -0
  311. data/test/plugin/test_in_tail.rb +1216 -0
  312. data/test/plugin/test_in_tcp.rb +118 -0
  313. data/test/plugin/test_in_udp.rb +152 -0
  314. data/test/plugin/test_in_unix.rb +126 -0
  315. data/test/plugin/test_input.rb +126 -0
  316. data/test/plugin/test_multi_output.rb +180 -0
  317. data/test/plugin/test_out_copy.rb +160 -0
  318. data/test/plugin/test_out_exec.rb +310 -0
  319. data/test/plugin/test_out_exec_filter.rb +613 -0
  320. data/test/plugin/test_out_file.rb +873 -0
  321. data/test/plugin/test_out_forward.rb +685 -0
  322. data/test/plugin/test_out_null.rb +105 -0
  323. data/test/plugin/test_out_relabel.rb +28 -0
  324. data/test/plugin/test_out_roundrobin.rb +146 -0
  325. data/test/plugin/test_out_secondary_file.rb +442 -0
  326. data/test/plugin/test_out_stdout.rb +170 -0
  327. data/test/plugin/test_out_stream.rb +93 -0
  328. data/test/plugin/test_output.rb +870 -0
  329. data/test/plugin/test_output_as_buffered.rb +1932 -0
  330. data/test/plugin/test_output_as_buffered_compress.rb +165 -0
  331. data/test/plugin/test_output_as_buffered_overflow.rb +250 -0
  332. data/test/plugin/test_output_as_buffered_retries.rb +839 -0
  333. data/test/plugin/test_output_as_buffered_secondary.rb +877 -0
  334. data/test/plugin/test_output_as_standard.rb +374 -0
  335. data/test/plugin/test_owned_by.rb +35 -0
  336. data/test/plugin/test_parser.rb +359 -0
  337. data/test/plugin/test_parser_apache.rb +42 -0
  338. data/test/plugin/test_parser_apache2.rb +46 -0
  339. data/test/plugin/test_parser_apache_error.rb +45 -0
  340. data/test/plugin/test_parser_csv.rb +103 -0
  341. data/test/plugin/test_parser_json.rb +114 -0
  342. data/test/plugin/test_parser_labeled_tsv.rb +128 -0
  343. data/test/plugin/test_parser_multiline.rb +100 -0
  344. data/test/plugin/test_parser_nginx.rb +48 -0
  345. data/test/plugin/test_parser_none.rb +52 -0
  346. data/test/plugin/test_parser_regexp.rb +281 -0
  347. data/test/plugin/test_parser_syslog.rb +242 -0
  348. data/test/plugin/test_parser_tsv.rb +122 -0
  349. data/test/plugin/test_storage.rb +167 -0
  350. data/test/plugin/test_storage_local.rb +335 -0
  351. data/test/plugin/test_string_util.rb +26 -0
  352. data/test/plugin_helper/test_child_process.rb +794 -0
  353. data/test/plugin_helper/test_compat_parameters.rb +331 -0
  354. data/test/plugin_helper/test_event_emitter.rb +51 -0
  355. data/test/plugin_helper/test_event_loop.rb +52 -0
  356. data/test/plugin_helper/test_extract.rb +194 -0
  357. data/test/plugin_helper/test_formatter.rb +255 -0
  358. data/test/plugin_helper/test_inject.rb +519 -0
  359. data/test/plugin_helper/test_parser.rb +264 -0
  360. data/test/plugin_helper/test_retry_state.rb +422 -0
  361. data/test/plugin_helper/test_server.rb +1677 -0
  362. data/test/plugin_helper/test_storage.rb +542 -0
  363. data/test/plugin_helper/test_thread.rb +164 -0
  364. data/test/plugin_helper/test_timer.rb +132 -0
  365. data/test/scripts/exec_script.rb +32 -0
  366. data/test/scripts/fluent/plugin/formatter1/formatter_test1.rb +7 -0
  367. data/test/scripts/fluent/plugin/formatter2/formatter_test2.rb +7 -0
  368. data/test/scripts/fluent/plugin/formatter_known.rb +8 -0
  369. data/test/scripts/fluent/plugin/out_test.rb +81 -0
  370. data/test/scripts/fluent/plugin/out_test2.rb +80 -0
  371. data/test/scripts/fluent/plugin/parser_known.rb +4 -0
  372. data/test/test_clock.rb +164 -0
  373. data/test/test_config.rb +179 -0
  374. data/test/test_configdsl.rb +148 -0
  375. data/test/test_event.rb +515 -0
  376. data/test/test_event_router.rb +331 -0
  377. data/test/test_event_time.rb +186 -0
  378. data/test/test_filter.rb +121 -0
  379. data/test/test_formatter.rb +312 -0
  380. data/test/test_input.rb +31 -0
  381. data/test/test_log.rb +828 -0
  382. data/test/test_match.rb +137 -0
  383. data/test/test_mixin.rb +351 -0
  384. data/test/test_output.rb +273 -0
  385. data/test/test_plugin.rb +251 -0
  386. data/test/test_plugin_classes.rb +253 -0
  387. data/test/test_plugin_helper.rb +81 -0
  388. data/test/test_plugin_id.rb +101 -0
  389. data/test/test_process.rb +14 -0
  390. data/test/test_root_agent.rb +611 -0
  391. data/test/test_supervisor.rb +373 -0
  392. data/test/test_test_drivers.rb +135 -0
  393. data/test/test_time_formatter.rb +282 -0
  394. data/test/test_time_parser.rb +211 -0
  395. data/test/test_unique_id.rb +47 -0
  396. metadata +898 -0
@@ -0,0 +1,1291 @@
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/plugin/base'
18
+ require 'fluent/log'
19
+ require 'fluent/plugin_id'
20
+ require 'fluent/plugin_helper'
21
+ require 'fluent/timezone'
22
+ require 'fluent/unique_id'
23
+ require 'fluent/clock'
24
+
25
+ require 'time'
26
+ require 'monitor'
27
+
28
+ module Fluent
29
+ module Plugin
30
+ class Output < Base
31
+ include PluginId
32
+ include PluginLoggerMixin
33
+ include PluginHelper::Mixin
34
+ include UniqueId::Mixin
35
+
36
+ helpers_internal :thread, :retry_state
37
+
38
+ CHUNK_KEY_PATTERN = /^[-_.@a-zA-Z0-9]+$/
39
+ CHUNK_KEY_PLACEHOLDER_PATTERN = /\$\{[-_.@a-zA-Z0-9]+\}/
40
+ CHUNK_TAG_PLACEHOLDER_PATTERN = /\$\{(tag(?:\[\d+\])?)\}/
41
+
42
+ CHUNKING_FIELD_WARN_NUM = 4
43
+
44
+ config_param :time_as_integer, :bool, default: false
45
+ desc 'The threshold to show slow flush logs'
46
+ config_param :slow_flush_log_threshold, :float, default: 20.0
47
+
48
+ # `<buffer>` and `<secondary>` sections are available only when '#format' and '#write' are implemented
49
+ config_section :buffer, param_name: :buffer_config, init: true, required: false, multi: false, final: true do
50
+ config_argument :chunk_keys, :array, value_type: :string, default: []
51
+ config_param :@type, :string, default: 'memory', alias: :type
52
+
53
+ config_param :timekey, :time, default: nil # range size to be used: `time.to_i / @timekey`
54
+ config_param :timekey_wait, :time, default: 600
55
+ # These are for #extract_placeholders
56
+ config_param :timekey_use_utc, :bool, default: false # default is localtime
57
+ config_param :timekey_zone, :string, default: Time.now.strftime('%z') # e.g., "-0700" or "Asia/Tokyo"
58
+
59
+ desc 'If true, plugin will try to flush buffer just before shutdown.'
60
+ config_param :flush_at_shutdown, :bool, default: nil # change default by buffer_plugin.persistent?
61
+
62
+ desc 'How to enqueue chunks to be flushed. "interval" flushes per flush_interval, "immediate" flushes just after event arrival.'
63
+ config_param :flush_mode, :enum, list: [:default, :lazy, :interval, :immediate], default: :default
64
+ config_param :flush_interval, :time, default: 60, desc: 'The interval between buffer chunk flushes.'
65
+
66
+ config_param :flush_thread_count, :integer, default: 1, desc: 'The number of threads to flush the buffer.'
67
+
68
+ config_param :flush_thread_interval, :float, default: 1.0, desc: 'Seconds to sleep between checks for buffer flushes in flush threads.'
69
+ config_param :flush_thread_burst_interval, :float, default: 1.0, desc: 'Seconds to sleep between flushes when many buffer chunks are queued.'
70
+
71
+ config_param :delayed_commit_timeout, :time, default: 60, desc: 'Seconds of timeout for buffer chunks to be committed by plugins later.'
72
+
73
+ 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.'
74
+
75
+ config_param :retry_forever, :bool, default: false, desc: 'If true, plugin will ignore retry_timeout and retry_max_times options and retry flushing forever.'
76
+ config_param :retry_timeout, :time, default: 72 * 60 * 60, desc: 'The maximum seconds to retry to flush while failing, until plugin discards buffer chunks.'
77
+ # 72hours == 17 times with exponential backoff (not to change default behavior)
78
+ config_param :retry_max_times, :integer, default: nil, desc: 'The maximum number of times to retry to flush while failing.'
79
+
80
+ config_param :retry_secondary_threshold, :float, default: 0.8, desc: 'ratio of retry_timeout to switch to use secondary while failing.'
81
+ # exponential backoff sequence will be initialized at the time of this threshold
82
+
83
+ desc 'How to wait next retry to flush buffer.'
84
+ config_param :retry_type, :enum, list: [:exponential_backoff, :periodic], default: :exponential_backoff
85
+ ### Periodic -> fixed :retry_wait
86
+ ### Exponential backoff: k is number of retry times
87
+ # c: constant factor, @retry_wait
88
+ # b: base factor, @retry_exponential_backoff_base
89
+ # k: times
90
+ # total retry time: c + c * b^1 + (...) + c*b^k = c*b^(k+1) - 1
91
+ config_param :retry_wait, :time, default: 1, desc: 'Seconds to wait before next retry to flush, or constant factor of exponential backoff.'
92
+ config_param :retry_exponential_backoff_base, :float, default: 2, desc: 'The base number of exponencial backoff for retries.'
93
+ config_param :retry_max_interval, :time, default: nil, desc: 'The maximum interval seconds for exponencial backoff between retries while failing.'
94
+
95
+ config_param :retry_randomize, :bool, default: true, desc: 'If true, output plugin will retry after randomized interval not to do burst retries.'
96
+ end
97
+
98
+ config_section :secondary, param_name: :secondary_config, required: false, multi: false, final: true do
99
+ config_param :@type, :string, default: nil, alias: :type
100
+ config_section :buffer, required: false, multi: false do
101
+ # dummy to detect invalid specification for here
102
+ end
103
+ config_section :secondary, required: false, multi: false do
104
+ # dummy to detect invalid specification for here
105
+ end
106
+ end
107
+
108
+ def process(tag, es)
109
+ raise NotImplementedError, "BUG: output plugins MUST implement this method"
110
+ end
111
+
112
+ def write(chunk)
113
+ raise NotImplementedError, "BUG: output plugins MUST implement this method"
114
+ end
115
+
116
+ def try_write(chunk)
117
+ raise NotImplementedError, "BUG: output plugins MUST implement this method"
118
+ end
119
+
120
+ def format(tag, time, record)
121
+ # standard msgpack_event_stream chunk will be used if this method is not implemented in plugin subclass
122
+ raise NotImplementedError, "BUG: output plugins MUST implement this method"
123
+ end
124
+
125
+ def formatted_to_msgpack_binary
126
+ # To indicate custom format method (#format) returns msgpack binary or not.
127
+ # If #format returns msgpack binary, override this method to return true.
128
+ false
129
+ end
130
+
131
+ def prefer_buffered_processing
132
+ # override this method to return false only when all of these are true:
133
+ # * plugin has both implementation for buffered and non-buffered methods
134
+ # * plugin is expected to work as non-buffered plugin if no `<buffer>` sections specified
135
+ true
136
+ end
137
+
138
+ def prefer_delayed_commit
139
+ # override this method to decide which is used of `write` or `try_write` if both are implemented
140
+ true
141
+ end
142
+
143
+ def multi_workers_ready?
144
+ false
145
+ end
146
+
147
+ # Internal states
148
+ FlushThreadState = Struct.new(:thread, :next_clock)
149
+ DequeuedChunkInfo = Struct.new(:chunk_id, :time, :timeout) do
150
+ def expired?
151
+ time + timeout < Time.now
152
+ end
153
+ end
154
+
155
+ attr_reader :as_secondary, :delayed_commit, :delayed_commit_timeout, :timekey_zone
156
+ attr_reader :num_errors, :emit_count, :emit_records, :write_count, :rollback_count
157
+
158
+ # for tests
159
+ attr_reader :buffer, :retry, :secondary, :chunk_keys, :chunk_key_time, :chunk_key_tag
160
+ attr_accessor :output_enqueue_thread_waiting, :dequeued_chunks, :dequeued_chunks_mutex
161
+ # output_enqueue_thread_waiting: for test of output.rb itself
162
+ attr_accessor :retry_for_error_chunk # if true, error flush will be retried even if under_plugin_development is true
163
+
164
+ def initialize
165
+ super
166
+ @counters_monitor = Monitor.new
167
+ @buffering = false
168
+ @delayed_commit = false
169
+ @as_secondary = false
170
+ @primary_instance = nil
171
+
172
+ # TODO: well organized counters
173
+ @num_errors = 0
174
+ @emit_count = 0
175
+ @emit_records = 0
176
+ @write_count = 0
177
+ @rollback_count = 0
178
+
179
+ # How to process events is decided here at once, but it will be decided in delayed way on #configure & #start
180
+ if implement?(:synchronous)
181
+ if implement?(:buffered) || implement?(:delayed_commit)
182
+ @buffering = nil # do #configure or #start to determine this for full-featured plugins
183
+ else
184
+ @buffering = false
185
+ end
186
+ else
187
+ @buffering = true
188
+ end
189
+ @custom_format = implement?(:custom_format)
190
+ @enable_msgpack_streamer = false # decided later
191
+
192
+ @buffer = nil
193
+ @secondary = nil
194
+ @retry = nil
195
+ @dequeued_chunks = nil
196
+ @dequeued_chunks_mutex = nil
197
+ @output_enqueue_thread = nil
198
+ @output_flush_threads = nil
199
+
200
+ @simple_chunking = nil
201
+ @chunk_keys = @chunk_key_time = @chunk_key_tag = nil
202
+ @flush_mode = nil
203
+ @timekey_zone = nil
204
+
205
+ @retry_for_error_chunk = false
206
+ end
207
+
208
+ def acts_as_secondary(primary)
209
+ @as_secondary = true
210
+ @primary_instance = primary
211
+ @chunk_keys = @primary_instance.chunk_keys || []
212
+ @chunk_key_tag = @primary_instance.chunk_key_tag || false
213
+ if @primary_instance.chunk_key_time
214
+ @chunk_key_time = @primary_instance.chunk_key_time
215
+ @timekey_zone = @primary_instance.timekey_zone
216
+ @output_time_formatter_cache = {}
217
+ end
218
+ self.context_router = primary.context_router
219
+
220
+ (class << self; self; end).module_eval do
221
+ define_method(:commit_write){ |chunk_id| @primary_instance.commit_write(chunk_id, delayed: delayed_commit, secondary: true) }
222
+ define_method(:rollback_write){ |chunk_id| @primary_instance.rollback_write(chunk_id) }
223
+ end
224
+ end
225
+
226
+ def configure(conf)
227
+ unless implement?(:synchronous) || implement?(:buffered) || implement?(:delayed_commit)
228
+ raise "BUG: output plugin must implement some methods. see developer documents."
229
+ end
230
+
231
+ has_buffer_section = (conf.elements(name: 'buffer').size > 0)
232
+ has_flush_interval = conf.has_key?('flush_interval')
233
+
234
+ super
235
+
236
+ if has_buffer_section
237
+ unless implement?(:buffered) || implement?(:delayed_commit)
238
+ raise Fluent::ConfigError, "<buffer> section is configured, but plugin '#{self.class}' doesn't support buffering"
239
+ end
240
+ @buffering = true
241
+ else # no buffer sections
242
+ if implement?(:synchronous)
243
+ if !implement?(:buffered) && !implement?(:delayed_commit)
244
+ if @as_secondary
245
+ raise Fluent::ConfigError, "secondary plugin '#{self.class}' must support buffering, but doesn't."
246
+ end
247
+ @buffering = false
248
+ else
249
+ if @as_secondary
250
+ # secondary plugin always works as buffered plugin without buffer instance
251
+ @buffering = true
252
+ else
253
+ # @buffering.nil? shows that enabling buffering or not will be decided in lazy way in #start
254
+ @buffering = nil
255
+ end
256
+ end
257
+ else # buffered or delayed_commit is supported by `unless` of first line in this method
258
+ @buffering = true
259
+ end
260
+ end
261
+
262
+ if @as_secondary
263
+ if !@buffering && !@buffering.nil?
264
+ raise Fluent::ConfigError, "secondary plugin '#{self.class}' must support buffering, but doesn't"
265
+ end
266
+ end
267
+
268
+ if (@buffering || @buffering.nil?) && !@as_secondary
269
+ # When @buffering.nil?, @buffer_config was initialized with default value for all parameters.
270
+ # If so, this configuration MUST success.
271
+ @chunk_keys = @buffer_config.chunk_keys.dup
272
+ @chunk_key_time = !!@chunk_keys.delete('time')
273
+ @chunk_key_tag = !!@chunk_keys.delete('tag')
274
+ if @chunk_keys.any?{ |key| key !~ CHUNK_KEY_PATTERN }
275
+ raise Fluent::ConfigError, "chunk_keys specification includes invalid char"
276
+ end
277
+
278
+ if @chunk_key_time
279
+ raise Fluent::ConfigError, "<buffer ...> argument includes 'time', but timekey is not configured" unless @buffer_config.timekey
280
+ Fluent::Timezone.validate!(@buffer_config.timekey_zone)
281
+ @timekey_zone = @buffer_config.timekey_use_utc ? '+0000' : @buffer_config.timekey_zone
282
+ @output_time_formatter_cache = {}
283
+ end
284
+
285
+ if (@chunk_key_tag ? 1 : 0) + @chunk_keys.size >= CHUNKING_FIELD_WARN_NUM
286
+ log.warn "many chunk keys specified, and it may cause too many chunks on your system."
287
+ end
288
+
289
+ # no chunk keys or only tags (chunking can be done without iterating event stream)
290
+ @simple_chunking = !@chunk_key_time && @chunk_keys.empty?
291
+
292
+ @flush_mode = @buffer_config.flush_mode
293
+ if @flush_mode == :default
294
+ if has_flush_interval
295
+ log.info "'flush_interval' is configured at out side of <buffer>. 'flush_mode' is set to 'interval' to keep existing behaviour"
296
+ @flush_mode = :interval
297
+ else
298
+ @flush_mode = (@chunk_key_time ? :lazy : :interval)
299
+ end
300
+ end
301
+
302
+ buffer_type = @buffer_config[:@type]
303
+ buffer_conf = conf.elements(name: 'buffer').first || Fluent::Config::Element.new('buffer', '', {}, [])
304
+ @buffer = Plugin.new_buffer(buffer_type, parent: self)
305
+ @buffer.configure(buffer_conf)
306
+
307
+ @flush_at_shutdown = @buffer_config.flush_at_shutdown
308
+ if @flush_at_shutdown.nil?
309
+ @flush_at_shutdown = if @buffer.persistent?
310
+ false
311
+ else
312
+ true # flush_at_shutdown is true in default for on-memory buffer
313
+ end
314
+ elsif !@flush_at_shutdown && !@buffer.persistent?
315
+ buf_type = Plugin.lookup_type_from_class(@buffer.class)
316
+ log.warn "'flush_at_shutdown' is false, and buffer plugin '#{buf_type}' is not persistent buffer."
317
+ log.warn "your configuration will lose buffered data at shutdown. please confirm your configuration again."
318
+ end
319
+
320
+ if (@flush_mode != :interval) && buffer_conf.has_key?('flush_interval')
321
+ if buffer_conf.has_key?('flush_mode')
322
+ raise Fluent::ConfigError, "'flush_interval' can't be specified when 'flush_mode' is not 'interval' explicitly: '#{@flush_mode}'"
323
+ else
324
+ log.warn "'flush_interval' is ignored because default 'flush_mode' is not 'interval': '#{@flush_mode}'"
325
+ end
326
+ end
327
+ end
328
+
329
+ if @secondary_config
330
+ raise Fluent::ConfigError, "Invalid <secondary> section for non-buffered plugin" unless @buffering
331
+ raise Fluent::ConfigError, "<secondary> section cannot have <buffer> section" if @secondary_config.buffer
332
+ raise Fluent::ConfigError, "<secondary> section cannot have <secondary> section" if @secondary_config.secondary
333
+ raise Fluent::ConfigError, "<secondary> section and 'retry_forever' are exclusive" if @buffer_config.retry_forever
334
+
335
+ secondary_type = @secondary_config[:@type]
336
+ unless secondary_type
337
+ secondary_type = conf['@type'] # primary plugin type
338
+ end
339
+ secondary_conf = conf.elements(name: 'secondary').first
340
+ @secondary = Plugin.new_output(secondary_type)
341
+ @secondary.acts_as_secondary(self)
342
+ @secondary.configure(secondary_conf)
343
+ if (self.class != @secondary.class) && (@custom_format || @secondary.implement?(:custom_format))
344
+ log.warn "secondary type should be same with primary one", primary: self.class.to_s, secondary: @secondary.class.to_s
345
+ end
346
+ else
347
+ @secondary = nil
348
+ end
349
+
350
+ self
351
+ end
352
+
353
+ def start
354
+ super
355
+
356
+ if @buffering.nil?
357
+ @buffering = prefer_buffered_processing
358
+ if !@buffering && @buffer
359
+ @buffer.terminate # it's not started, so terminate will be enough
360
+ # At here, this plugin works as non-buffered plugin.
361
+ # Un-assign @buffer not to show buffering metrics (e.g., in_monitor_agent)
362
+ @buffer = nil
363
+ end
364
+ end
365
+
366
+ if @buffering
367
+ m = method(:emit_buffered)
368
+ (class << self; self; end).module_eval do
369
+ define_method(:emit_events, m)
370
+ end
371
+
372
+ @custom_format = implement?(:custom_format)
373
+ @enable_msgpack_streamer = @custom_format ? formatted_to_msgpack_binary : true
374
+ @delayed_commit = if implement?(:buffered) && implement?(:delayed_commit)
375
+ prefer_delayed_commit
376
+ else
377
+ implement?(:delayed_commit)
378
+ end
379
+ @delayed_commit_timeout = @buffer_config.delayed_commit_timeout
380
+ else # !@buffering
381
+ m = method(:emit_sync)
382
+ (class << self; self; end).module_eval do
383
+ define_method(:emit_events, m)
384
+ end
385
+ end
386
+
387
+ if @buffering && !@as_secondary
388
+ @retry = nil
389
+ @retry_mutex = Mutex.new
390
+
391
+ @buffer.start
392
+
393
+ @output_enqueue_thread = nil
394
+ @output_enqueue_thread_running = true
395
+
396
+ @output_flush_threads = []
397
+ @output_flush_threads_mutex = Mutex.new
398
+ @output_flush_threads_running = true
399
+
400
+ # mainly for test: detect enqueue works as code below:
401
+ # @output.interrupt_flushes
402
+ # # emits
403
+ # @output.enqueue_thread_wait
404
+ @output_flush_interrupted = false
405
+ @output_enqueue_thread_mutex = Mutex.new
406
+ @output_enqueue_thread_waiting = false
407
+
408
+ @dequeued_chunks = []
409
+ @dequeued_chunks_mutex = Mutex.new
410
+
411
+ @buffer_config.flush_thread_count.times do |i|
412
+ thread_title = "flush_thread_#{i}".to_sym
413
+ thread_state = FlushThreadState.new(nil, nil)
414
+ thread = thread_create(thread_title) do
415
+ flush_thread_run(thread_state)
416
+ end
417
+ thread_state.thread = thread
418
+ @output_flush_threads_mutex.synchronize do
419
+ @output_flush_threads << thread_state
420
+ end
421
+ end
422
+ @output_flush_thread_current_position = 0
423
+
424
+ if !@under_plugin_development && (@flush_mode == :interval || @chunk_key_time)
425
+ @output_enqueue_thread = thread_create(:enqueue_thread, &method(:enqueue_thread_run))
426
+ end
427
+ end
428
+ @secondary.start if @secondary
429
+ end
430
+
431
+ def after_start
432
+ super
433
+ @secondary.after_start if @secondary
434
+ end
435
+
436
+ def stop
437
+ @secondary.stop if @secondary
438
+ @buffer.stop if @buffering && @buffer
439
+
440
+ super
441
+ end
442
+
443
+ def before_shutdown
444
+ @secondary.before_shutdown if @secondary
445
+
446
+ if @buffering && @buffer
447
+ if @flush_at_shutdown
448
+ force_flush
449
+ end
450
+ @buffer.before_shutdown
451
+ # Need to ensure to stop enqueueing ... after #shutdown, we cannot write any data
452
+ @output_enqueue_thread_running = false
453
+ if @output_enqueue_thread && @output_enqueue_thread.alive?
454
+ @output_enqueue_thread.wakeup
455
+ @output_enqueue_thread.join
456
+ end
457
+ end
458
+
459
+ super
460
+ end
461
+
462
+ def shutdown
463
+ @secondary.shutdown if @secondary
464
+ @buffer.shutdown if @buffering && @buffer
465
+
466
+ super
467
+ end
468
+
469
+ def after_shutdown
470
+ try_rollback_all if @buffering && !@as_secondary # rollback regardless with @delayed_commit, because secondary may do it
471
+ @secondary.after_shutdown if @secondary
472
+
473
+ if @buffering && @buffer
474
+ @buffer.after_shutdown
475
+
476
+ @output_flush_threads_running = false
477
+ if @output_flush_threads && !@output_flush_threads.empty?
478
+ @output_flush_threads.each do |state|
479
+ state.thread.run if state.thread.alive? # to wakeup thread and make it to stop by itself
480
+ end
481
+ @output_flush_threads.each do |state|
482
+ state.thread.join
483
+ end
484
+ end
485
+ end
486
+
487
+ super
488
+ end
489
+
490
+ def close
491
+ @buffer.close if @buffering && @buffer
492
+ @secondary.close if @secondary
493
+
494
+ super
495
+ end
496
+
497
+ def terminate
498
+ @buffer.terminate if @buffering && @buffer
499
+ @secondary.terminate if @secondary
500
+
501
+ super
502
+ end
503
+
504
+ def support_in_v12_style?(feature)
505
+ # for plugins written in v0.12 styles
506
+ case feature
507
+ when :synchronous then false
508
+ when :buffered then false
509
+ when :delayed_commit then false
510
+ when :custom_format then false
511
+ else
512
+ raise ArgumentError, "unknown feature: #{feature}"
513
+ end
514
+ end
515
+
516
+ def implement?(feature)
517
+ methods_of_plugin = self.class.instance_methods(false)
518
+ case feature
519
+ when :synchronous then methods_of_plugin.include?(:process) || support_in_v12_style?(:synchronous)
520
+ when :buffered then methods_of_plugin.include?(:write) || support_in_v12_style?(:buffered)
521
+ when :delayed_commit then methods_of_plugin.include?(:try_write)
522
+ when :custom_format then methods_of_plugin.include?(:format) || support_in_v12_style?(:custom_format)
523
+ else
524
+ raise ArgumentError, "Unknown feature for output plugin: #{feature}"
525
+ end
526
+ end
527
+
528
+ def placeholder_validate!(name, str)
529
+ placeholder_validators(name, str).each do |v|
530
+ v.validate!
531
+ end
532
+ end
533
+
534
+ def placeholder_validators(name, str, time_key = (@chunk_key_time && @buffer_config.timekey), tag_key = @chunk_key_tag, chunk_keys = @chunk_keys)
535
+ validators = []
536
+
537
+ sec, title, example = get_placeholders_time(str)
538
+ if sec || time_key
539
+ validators << PlaceholderValidator.new(name, str, :time, {sec: sec, title: title, example: example, timekey: time_key})
540
+ end
541
+
542
+ parts = get_placeholders_tag(str)
543
+ if tag_key || !parts.empty?
544
+ validators << PlaceholderValidator.new(name, str, :tag, {parts: parts, tagkey: tag_key})
545
+ end
546
+
547
+ keys = get_placeholders_keys(str)
548
+ if chunk_keys && !chunk_keys.empty? || !keys.empty?
549
+ validators << PlaceholderValidator.new(name, str, :keys, {keys: keys, chunkkeys: chunk_keys})
550
+ end
551
+
552
+ validators
553
+ end
554
+
555
+ class PlaceholderValidator
556
+ attr_reader :name, :string, :type, :argument
557
+
558
+ def initialize(name, str, type, arg)
559
+ @name = name
560
+ @string = str
561
+ @type = type
562
+ raise ArgumentError, "invalid type:#{type}" if @type != :time && @type != :tag && @type != :keys
563
+ @argument = arg
564
+ end
565
+
566
+ def time?
567
+ @type == :time
568
+ end
569
+
570
+ def tag?
571
+ @type == :tag
572
+ end
573
+
574
+ def keys?
575
+ @type == :keys
576
+ end
577
+
578
+ def validate!
579
+ case @type
580
+ when :time then validate_time!
581
+ when :tag then validate_tag!
582
+ when :keys then validate_keys!
583
+ end
584
+ end
585
+
586
+ def validate_time!
587
+ sec = @argument[:sec]
588
+ title = @argument[:title]
589
+ example = @argument[:example]
590
+ timekey = @argument[:timekey]
591
+ if !sec && timekey
592
+ raise Fluent::ConfigError, "Parameter '#{name}: #{string}' doesn't have timestamp placeholders for timekey #{timekey.to_i}"
593
+ end
594
+ if sec && !timekey
595
+ raise Fluent::ConfigError, "Parameter '#{name}: #{string}' has timestamp placeholders, but chunk key 'time' is not configured"
596
+ end
597
+ if sec && timekey && timekey < sec
598
+ raise Fluent::ConfigError, "Parameter '#{name}: #{string}' doesn't have timestamp placeholder for #{title}('#{example}') for timekey #{timekey.to_i}"
599
+ end
600
+ end
601
+
602
+ def validate_tag!
603
+ parts = @argument[:parts]
604
+ tagkey = @argument[:tagkey]
605
+ if tagkey && parts.empty?
606
+ raise Fluent::ConfigError, "Parameter '#{name}: #{string}' doesn't have tag placeholder"
607
+ end
608
+ if !tagkey && !parts.empty?
609
+ raise Fluent::ConfigError, "Parameter '#{name}: #{string}' has tag placeholders, but chunk key 'tag' is not configured"
610
+ end
611
+ end
612
+
613
+ def validate_keys!
614
+ keys = @argument[:keys]
615
+ chunk_keys = @argument[:chunkkeys]
616
+ if (chunk_keys - keys).size > 0
617
+ not_specified = (chunk_keys - keys).sort
618
+ raise Fluent::ConfigError, "Parameter '#{name}: #{string}' doesn't have enough placeholders for keys #{not_specified.join(',')}"
619
+ end
620
+ if (keys - chunk_keys).size > 0
621
+ not_satisfied = (keys - chunk_keys).sort
622
+ raise Fluent::ConfigError, "Parameter '#{name}: #{string}' has placeholders, but chunk keys doesn't have keys #{not_satisfied.join(',')}"
623
+ end
624
+ end
625
+ end
626
+
627
+ TIME_KEY_PLACEHOLDER_THRESHOLDS = [
628
+ [1, :second, '%S'],
629
+ [60, :minute, '%M'],
630
+ [3600, :hour, '%H'],
631
+ [86400, :day, '%d'],
632
+ ]
633
+ TIMESTAMP_CHECK_BASE_TIME = Time.parse("2016-01-01 00:00:00 UTC")
634
+ # it's not validated to use timekey larger than 1 day
635
+ def get_placeholders_time(str)
636
+ base_str = TIMESTAMP_CHECK_BASE_TIME.strftime(str)
637
+ TIME_KEY_PLACEHOLDER_THRESHOLDS.each do |triple|
638
+ sec = triple.first
639
+ return triple if (TIMESTAMP_CHECK_BASE_TIME + sec).strftime(str) != base_str
640
+ end
641
+ nil
642
+ end
643
+
644
+ # -1 means whole tag
645
+ def get_placeholders_tag(str)
646
+ # [["tag"],["tag[0]"]]
647
+ parts = []
648
+ str.scan(CHUNK_TAG_PLACEHOLDER_PATTERN).map(&:first).each do |ph|
649
+ if ph == "tag"
650
+ parts << -1
651
+ elsif ph =~ /^tag\[(\d+)\]$/
652
+ parts << $1.to_i
653
+ end
654
+ end
655
+ parts.sort
656
+ end
657
+
658
+ def get_placeholders_keys(str)
659
+ str.scan(CHUNK_KEY_PLACEHOLDER_PATTERN).map{|ph| ph[2..-2]}.reject{|s| s == "tag"}.sort
660
+ end
661
+
662
+ # TODO: optimize this code
663
+ def extract_placeholders(str, metadata)
664
+ if metadata.empty?
665
+ str
666
+ else
667
+ rvalue = str.dup
668
+ # strftime formatting
669
+ if @chunk_key_time # this section MUST be earlier than rest to use raw 'str'
670
+ @output_time_formatter_cache[str] ||= Fluent::Timezone.formatter(@timekey_zone, str)
671
+ rvalue = @output_time_formatter_cache[str].call(metadata.timekey)
672
+ end
673
+ # ${tag}, ${tag[0]}, ${tag[1]}, ...
674
+ if @chunk_key_tag
675
+ if str.include?('${tag}')
676
+ rvalue = rvalue.gsub('${tag}', metadata.tag)
677
+ end
678
+ if str =~ CHUNK_TAG_PLACEHOLDER_PATTERN
679
+ hash = {}
680
+ metadata.tag.split('.').each_with_index do |part, i|
681
+ hash["${tag[#{i}]}"] = part
682
+ end
683
+ rvalue = rvalue.gsub(CHUNK_TAG_PLACEHOLDER_PATTERN, hash)
684
+ end
685
+ if rvalue =~ CHUNK_TAG_PLACEHOLDER_PATTERN
686
+ log.warn "tag placeholder '#{$1}' not replaced. tag:#{metadata.tag}, template:#{str}"
687
+ end
688
+ end
689
+ # ${a_chunk_key}, ...
690
+ if !@chunk_keys.empty? && metadata.variables
691
+ hash = {'${tag}' => '${tag}'} # not to erase this wrongly
692
+ @chunk_keys.each do |key|
693
+ hash["${#{key}}"] = metadata.variables[key.to_sym]
694
+ end
695
+ rvalue = rvalue.gsub(CHUNK_KEY_PLACEHOLDER_PATTERN, hash)
696
+ end
697
+ if rvalue =~ CHUNK_KEY_PLACEHOLDER_PATTERN
698
+ log.warn "chunk key placeholder '#{$1}' not replaced. templace:#{str}"
699
+ end
700
+ rvalue
701
+ end
702
+ end
703
+
704
+ def emit_events(tag, es)
705
+ # actually this method will be overwritten by #configure
706
+ if @buffering
707
+ emit_buffered(tag, es)
708
+ else
709
+ emit_sync(tag, es)
710
+ end
711
+ end
712
+
713
+ def emit_sync(tag, es)
714
+ @counters_monitor.synchronize{ @emit_count += 1 }
715
+ begin
716
+ process(tag, es)
717
+ @counters_monitor.synchronize{ @emit_records += es.size }
718
+ rescue
719
+ @counters_monitor.synchronize{ @num_errors += 1 }
720
+ raise
721
+ end
722
+ end
723
+
724
+ def emit_buffered(tag, es)
725
+ @counters_monitor.synchronize{ @emit_count += 1 }
726
+ begin
727
+ execute_chunking(tag, es, enqueue: (@flush_mode == :immediate))
728
+ if !@retry && @buffer.queued?
729
+ submit_flush_once
730
+ end
731
+ rescue
732
+ # TODO: separate number of errors into emit errors and write/flush errors
733
+ @counters_monitor.synchronize{ @num_errors += 1 }
734
+ raise
735
+ end
736
+ end
737
+
738
+ # TODO: optimize this code
739
+ def metadata(tag, time, record)
740
+ # this arguments are ordered in output plugin's rule
741
+ # Metadata 's argument order is different from this one (timekey, tag, variables)
742
+
743
+ raise ArgumentError, "tag must be a String: #{tag.class}" unless tag.nil? || tag.is_a?(String)
744
+ raise ArgumentError, "time must be a Fluent::EventTime (or Integer): #{time.class}" unless time.nil? || time.is_a?(Fluent::EventTime) || time.is_a?(Integer)
745
+ raise ArgumentError, "record must be a Hash: #{record.class}" unless record.nil? || record.is_a?(Hash)
746
+
747
+ if @chunk_keys.nil? && @chunk_key_time.nil? && @chunk_key_tag.nil?
748
+ # for tests
749
+ return Struct.new(:timekey, :tag, :variables).new
750
+ end
751
+
752
+ # timekey is int from epoch, and `timekey - timekey % 60` is assumed to mach with 0s of each minutes.
753
+ # it's wrong if timezone is configured as one which supports leap second, but it's very rare and
754
+ # we can ignore it (especially in production systems).
755
+ if @chunk_keys.empty?
756
+ if !@chunk_key_time && !@chunk_key_tag
757
+ @buffer.metadata()
758
+ elsif @chunk_key_time && @chunk_key_tag
759
+ time_int = time.to_i
760
+ timekey = (time_int - (time_int % @buffer_config.timekey)).to_i
761
+ @buffer.metadata(timekey: timekey, tag: tag)
762
+ elsif @chunk_key_time
763
+ time_int = time.to_i
764
+ timekey = (time_int - (time_int % @buffer_config.timekey)).to_i
765
+ @buffer.metadata(timekey: timekey)
766
+ else
767
+ @buffer.metadata(tag: tag)
768
+ end
769
+ else
770
+ timekey = if @chunk_key_time
771
+ time_int = time.to_i
772
+ (time_int - (time_int % @buffer_config.timekey)).to_i
773
+ else
774
+ nil
775
+ end
776
+ pairs = Hash[@chunk_keys.map{|k| [k.to_sym, record[k]]}]
777
+ @buffer.metadata(timekey: timekey, tag: (@chunk_key_tag ? tag : nil), variables: pairs)
778
+ end
779
+ end
780
+
781
+ def metadata_for_test(tag, time, record)
782
+ raise "BUG: #metadata_for_test is available only when no actual metadata exists" unless @buffer.metadata_list.empty?
783
+ m = metadata(tag, time, record)
784
+ @buffer.metadata_list_clear!
785
+ m
786
+ end
787
+
788
+ def execute_chunking(tag, es, enqueue: false)
789
+ if @simple_chunking
790
+ handle_stream_simple(tag, es, enqueue: enqueue)
791
+ elsif @custom_format
792
+ handle_stream_with_custom_format(tag, es, enqueue: enqueue)
793
+ else
794
+ handle_stream_with_standard_format(tag, es, enqueue: enqueue)
795
+ end
796
+ end
797
+
798
+ def write_guard(&block)
799
+ begin
800
+ block.call
801
+ rescue Fluent::Plugin::Buffer::BufferOverflowError
802
+ log.warn "failed to write data into buffer by buffer overflow", action: @buffer_config.overflow_action
803
+ case @buffer_config.overflow_action
804
+ when :throw_exception
805
+ raise
806
+ when :block
807
+ log.debug "buffer.write is now blocking"
808
+ until @buffer.storable?
809
+ if self.stopped?
810
+ log.error "breaking block behavior to shutdown Fluentd"
811
+ # to break infinite loop to exit Fluentd process
812
+ raise
813
+ end
814
+ log.trace "sleeping until buffer can store more data"
815
+ sleep 1
816
+ end
817
+ log.debug "retrying buffer.write after blocked operation"
818
+ retry
819
+ when :drop_oldest_chunk
820
+ begin
821
+ oldest = @buffer.dequeue_chunk
822
+ if oldest
823
+ log.warn "dropping oldest chunk to make space after buffer overflow", chunk_id: oldest.unique_id
824
+ @buffer.purge_chunk(oldest.unique_id)
825
+ else
826
+ log.error "no queued chunks to be dropped for drop_oldest_chunk"
827
+ end
828
+ rescue
829
+ # ignore any errors
830
+ end
831
+ raise unless @buffer.storable?
832
+ retry
833
+ else
834
+ raise "BUG: unknown overflow_action '#{@buffer_config.overflow_action}'"
835
+ end
836
+ end
837
+ end
838
+
839
+ FORMAT_MSGPACK_STREAM = ->(e){ e.to_msgpack_stream }
840
+ FORMAT_COMPRESSED_MSGPACK_STREAM = ->(e){ e.to_compressed_msgpack_stream }
841
+ FORMAT_MSGPACK_STREAM_TIME_INT = ->(e){ e.to_msgpack_stream(time_int: true) }
842
+ FORMAT_COMPRESSED_MSGPACK_STREAM_TIME_INT = ->(e){ e.to_compressed_msgpack_stream(time_int: true) }
843
+
844
+ def generate_format_proc
845
+ if @buffer && @buffer.compress == :gzip
846
+ @time_as_integer ? FORMAT_COMPRESSED_MSGPACK_STREAM_TIME_INT : FORMAT_COMPRESSED_MSGPACK_STREAM
847
+ else
848
+ @time_as_integer ? FORMAT_MSGPACK_STREAM_TIME_INT : FORMAT_MSGPACK_STREAM
849
+ end
850
+ end
851
+
852
+ # metadata_and_data is a Hash of:
853
+ # (standard format) metadata => event stream
854
+ # (custom format) metadata => array of formatted event
855
+ # For standard format, formatting should be done for whole event stream, but
856
+ # "whole event stream" may be a split of "es" here when it's bigger than chunk_limit_size.
857
+ # `@buffer.write` will do this splitting.
858
+ # For custom format, formatting will be done here. Custom formatting always requires
859
+ # iteration of event stream, and it should be done just once even if total event stream size
860
+ # is bigger than chunk_limit_size because of performance.
861
+ def handle_stream_with_custom_format(tag, es, enqueue: false)
862
+ meta_and_data = {}
863
+ records = 0
864
+ es.each do |time, record|
865
+ meta = metadata(tag, time, record)
866
+ meta_and_data[meta] ||= []
867
+ res = format(tag, time, record)
868
+ if res
869
+ meta_and_data[meta] << res
870
+ records += 1
871
+ end
872
+ end
873
+ write_guard do
874
+ @buffer.write(meta_and_data, enqueue: enqueue)
875
+ end
876
+ @counters_monitor.synchronize{ @emit_records += records }
877
+ true
878
+ end
879
+
880
+ def handle_stream_with_standard_format(tag, es, enqueue: false)
881
+ format_proc = generate_format_proc
882
+ meta_and_data = {}
883
+ records = 0
884
+ es.each do |time, record|
885
+ meta = metadata(tag, time, record)
886
+ meta_and_data[meta] ||= MultiEventStream.new
887
+ meta_and_data[meta].add(time, record)
888
+ records += 1
889
+ end
890
+ write_guard do
891
+ @buffer.write(meta_and_data, format: format_proc, enqueue: enqueue)
892
+ end
893
+ @counters_monitor.synchronize{ @emit_records += records }
894
+ true
895
+ end
896
+
897
+ def handle_stream_simple(tag, es, enqueue: false)
898
+ format_proc = nil
899
+ meta = metadata((@chunk_key_tag ? tag : nil), nil, nil)
900
+ records = es.size
901
+ if @custom_format
902
+ records = 0
903
+ data = []
904
+ es.each do |time, record|
905
+ res = format(tag, time, record)
906
+ if res
907
+ data << res
908
+ records += 1
909
+ end
910
+ end
911
+ else
912
+ format_proc = generate_format_proc
913
+ data = es
914
+ end
915
+ write_guard do
916
+ @buffer.write({meta => data}, format: format_proc, enqueue: enqueue)
917
+ end
918
+ @counters_monitor.synchronize{ @emit_records += records }
919
+ true
920
+ end
921
+
922
+ def commit_write(chunk_id, delayed: @delayed_commit, secondary: false)
923
+ log.trace "committing write operation to a chunk", chunk: dump_unique_id_hex(chunk_id), delayed: delayed
924
+ if delayed
925
+ @dequeued_chunks_mutex.synchronize do
926
+ @dequeued_chunks.delete_if{ |info| info.chunk_id == chunk_id }
927
+ end
928
+ end
929
+ @buffer.purge_chunk(chunk_id)
930
+
931
+ @retry_mutex.synchronize do
932
+ if @retry # success to flush chunks in retries
933
+ if secondary
934
+ log.warn "retry succeeded by secondary.", chunk_id: dump_unique_id_hex(chunk_id)
935
+ else
936
+ log.warn "retry succeeded.", chunk_id: dump_unique_id_hex(chunk_id)
937
+ end
938
+ @retry = nil
939
+ end
940
+ end
941
+ end
942
+
943
+ def rollback_write(chunk_id)
944
+ # This API is to rollback chunks explicitly from plugins.
945
+ # 3rd party plugins can depend it on automatic rollback of #try_rollback_write
946
+ @dequeued_chunks_mutex.synchronize do
947
+ @dequeued_chunks.delete_if{ |info| info.chunk_id == chunk_id }
948
+ end
949
+ # returns true if chunk was rollbacked as expected
950
+ # false if chunk was already flushed and couldn't be rollbacked unexpectedly
951
+ # in many cases, false can be just ignored
952
+ if @buffer.takeback_chunk(chunk_id)
953
+ @counters_monitor.synchronize{ @rollback_count += 1 }
954
+ primary = @as_secondary ? @primary_instance : self
955
+ primary.update_retry_state(chunk_id, @as_secondary)
956
+ true
957
+ else
958
+ false
959
+ end
960
+ end
961
+
962
+ def try_rollback_write
963
+ @dequeued_chunks_mutex.synchronize do
964
+ while @dequeued_chunks.first && @dequeued_chunks.first.expired?
965
+ info = @dequeued_chunks.shift
966
+ if @buffer.takeback_chunk(info.chunk_id)
967
+ @counters_monitor.synchronize{ @rollback_count += 1 }
968
+ log.warn "failed to flush the buffer chunk, timeout to commit.", chunk_id: dump_unique_id_hex(info.chunk_id), flushed_at: info.time
969
+ primary = @as_secondary ? @primary_instance : self
970
+ primary.update_retry_state(info.chunk_id, @as_secondary)
971
+ end
972
+ end
973
+ end
974
+ end
975
+
976
+ def try_rollback_all
977
+ return unless @dequeued_chunks
978
+ @dequeued_chunks_mutex.synchronize do
979
+ until @dequeued_chunks.empty?
980
+ info = @dequeued_chunks.shift
981
+ if @buffer.takeback_chunk(info.chunk_id)
982
+ @counters_monitor.synchronize{ @rollback_count += 1 }
983
+ log.info "delayed commit for buffer chunks was cancelled in shutdown", chunk_id: dump_unique_id_hex(info.chunk_id)
984
+ primary = @as_secondary ? @primary_instance : self
985
+ primary.update_retry_state(info.chunk_id, @as_secondary)
986
+ end
987
+ end
988
+ end
989
+ end
990
+
991
+ def next_flush_time
992
+ if @buffer.queued?
993
+ @retry_mutex.synchronize do
994
+ @retry ? @retry.next_time : Time.now + @buffer_config.flush_thread_burst_interval
995
+ end
996
+ else
997
+ Time.now + @buffer_config.flush_thread_interval
998
+ end
999
+ end
1000
+
1001
+ def try_flush
1002
+ chunk = @buffer.dequeue_chunk
1003
+ return unless chunk
1004
+
1005
+ log.trace "trying flush for a chunk", chunk: dump_unique_id_hex(chunk.unique_id)
1006
+
1007
+ output = self
1008
+ using_secondary = false
1009
+ if @retry_mutex.synchronize{ @retry && @retry.secondary? }
1010
+ output = @secondary
1011
+ using_secondary = true
1012
+ end
1013
+
1014
+ if @enable_msgpack_streamer
1015
+ chunk.extend ChunkMessagePackEventStreamer
1016
+ end
1017
+
1018
+ begin
1019
+ chunk_write_start = Fluent::Clock.now
1020
+
1021
+ if output.delayed_commit
1022
+ log.trace "executing delayed write and commit", chunk: dump_unique_id_hex(chunk.unique_id)
1023
+ @counters_monitor.synchronize{ @write_count += 1 }
1024
+ @dequeued_chunks_mutex.synchronize do
1025
+ # delayed_commit_timeout for secondary is configured in <buffer> of primary (<secondary> don't get <buffer>)
1026
+ @dequeued_chunks << DequeuedChunkInfo.new(chunk.unique_id, Time.now, self.delayed_commit_timeout)
1027
+ end
1028
+
1029
+ output.try_write(chunk)
1030
+ check_slow_flush(chunk_write_start)
1031
+ else # output plugin without delayed purge
1032
+ chunk_id = chunk.unique_id
1033
+ dump_chunk_id = dump_unique_id_hex(chunk_id)
1034
+ log.trace "adding write count", instance: self.object_id
1035
+ @counters_monitor.synchronize{ @write_count += 1 }
1036
+ log.trace "executing sync write", chunk: dump_chunk_id
1037
+
1038
+ output.write(chunk)
1039
+ check_slow_flush(chunk_write_start)
1040
+
1041
+ log.trace "write operation done, committing", chunk: dump_chunk_id
1042
+ commit_write(chunk_id, delayed: false, secondary: using_secondary)
1043
+ log.trace "done to commit a chunk", chunk: dump_chunk_id
1044
+ end
1045
+ rescue => e
1046
+ log.debug "taking back chunk for errors.", chunk: dump_unique_id_hex(chunk.unique_id)
1047
+ if output.delayed_commit
1048
+ @dequeued_chunks_mutex.synchronize do
1049
+ @dequeued_chunks.delete_if{|d| d.chunk_id == chunk.unique_id }
1050
+ end
1051
+ end
1052
+ @buffer.takeback_chunk(chunk.unique_id)
1053
+
1054
+ update_retry_state(chunk.unique_id, using_secondary, e)
1055
+
1056
+ raise if @under_plugin_development && !@retry_for_error_chunk
1057
+ end
1058
+ end
1059
+
1060
+ def check_slow_flush(start)
1061
+ elapsed_time = Fluent::Clock.now - start
1062
+ if elapsed_time > @slow_flush_log_threshold
1063
+ log.warn "buffer flush took longer time than slow_flush_log_threshold:",
1064
+ elapsed_time: elapsed_time, slow_flush_log_threshold: @slow_flush_log_threshold, plugin_id: self.plugin_id
1065
+ end
1066
+ end
1067
+
1068
+ def update_retry_state(chunk_id, using_secondary, error = nil)
1069
+ @retry_mutex.synchronize do
1070
+ @counters_monitor.synchronize{ @num_errors += 1 }
1071
+ chunk_id_hex = dump_unique_id_hex(chunk_id)
1072
+
1073
+ unless @retry
1074
+ @retry = retry_state(@buffer_config.retry_randomize)
1075
+ if error
1076
+ log.warn "failed to flush the buffer.", retry_time: @retry.steps, next_retry_seconds: @retry.next_time, chunk: chunk_id_hex, error: error
1077
+ log.warn_backtrace error.backtrace
1078
+ end
1079
+ return
1080
+ end
1081
+
1082
+ # @retry exists
1083
+
1084
+ if error
1085
+ if @retry.limit?
1086
+ records = @buffer.queued_records
1087
+ msg = "failed to flush the buffer, and hit limit for retries. dropping all chunks in the buffer queue."
1088
+ log.error msg, retry_times: @retry.steps, records: records, error: error
1089
+ log.error_backtrace error.backtrace
1090
+ elsif using_secondary
1091
+ msg = "failed to flush the buffer with secondary output."
1092
+ log.warn msg, retry_time: @retry.steps, next_retry_seconds: @retry.next_time, chunk: chunk_id_hex, error: error
1093
+ log.warn_backtrace error.backtrace
1094
+ else
1095
+ msg = "failed to flush the buffer."
1096
+ log.warn msg, retry_time: @retry.steps, next_retry_seconds: @retry.next_time, chunk: chunk_id_hex, error: error
1097
+ log.warn_backtrace error.backtrace
1098
+ end
1099
+ end
1100
+
1101
+ if @retry.limit?
1102
+ @buffer.clear_queue!
1103
+ log.debug "buffer queue cleared"
1104
+ @retry = nil
1105
+ else
1106
+ @retry.step
1107
+ end
1108
+ end
1109
+ end
1110
+
1111
+ def retry_state(randomize)
1112
+ if @secondary
1113
+ retry_state_create(
1114
+ :output_retries, @buffer_config.retry_type, @buffer_config.retry_wait, @buffer_config.retry_timeout,
1115
+ forever: @buffer_config.retry_forever, max_steps: @buffer_config.retry_max_times, backoff_base: @buffer_config.retry_exponential_backoff_base,
1116
+ max_interval: @buffer_config.retry_max_interval,
1117
+ secondary: true, secondary_threshold: @buffer_config.retry_secondary_threshold,
1118
+ randomize: randomize
1119
+ )
1120
+ else
1121
+ retry_state_create(
1122
+ :output_retries, @buffer_config.retry_type, @buffer_config.retry_wait, @buffer_config.retry_timeout,
1123
+ forever: @buffer_config.retry_forever, max_steps: @buffer_config.retry_max_times, backoff_base: @buffer_config.retry_exponential_backoff_base,
1124
+ max_interval: @buffer_config.retry_max_interval,
1125
+ randomize: randomize
1126
+ )
1127
+ end
1128
+ end
1129
+
1130
+ def submit_flush_once
1131
+ # Without locks: it is rough but enough to select "next" writer selection
1132
+ @output_flush_thread_current_position = (@output_flush_thread_current_position + 1) % @buffer_config.flush_thread_count
1133
+ state = @output_flush_threads[@output_flush_thread_current_position]
1134
+ state.next_clock = 0
1135
+ if state.thread && state.thread.status # "run"/"sleep"/"aborting" or false(successfully stop) or nil(killed by exception)
1136
+ state.thread.run
1137
+ else
1138
+ log.warn "thread is already dead"
1139
+ end
1140
+ end
1141
+
1142
+ def force_flush
1143
+ if @buffering
1144
+ @buffer.enqueue_all
1145
+ submit_flush_all
1146
+ end
1147
+ end
1148
+
1149
+ def submit_flush_all
1150
+ while !@retry && @buffer.queued?
1151
+ submit_flush_once
1152
+ sleep @buffer_config.flush_thread_burst_interval
1153
+ end
1154
+ end
1155
+
1156
+ # only for tests of output plugin
1157
+ def interrupt_flushes
1158
+ @output_flush_interrupted = true
1159
+ end
1160
+
1161
+ # only for tests of output plugin
1162
+ def enqueue_thread_wait
1163
+ @output_enqueue_thread_mutex.synchronize do
1164
+ @output_flush_interrupted = false
1165
+ @output_enqueue_thread_waiting = true
1166
+ end
1167
+ require 'timeout'
1168
+ Timeout.timeout(10) do
1169
+ Thread.pass while @output_enqueue_thread_waiting
1170
+ end
1171
+ end
1172
+
1173
+ # only for tests of output plugin
1174
+ def flush_thread_wakeup
1175
+ @output_flush_threads.each do |state|
1176
+ state.next_clock = 0
1177
+ state.thread.run
1178
+ end
1179
+ end
1180
+
1181
+ def enqueue_thread_run
1182
+ value_for_interval = nil
1183
+ if @flush_mode == :interval
1184
+ value_for_interval = @buffer_config.flush_interval
1185
+ end
1186
+ if @chunk_key_time
1187
+ if !value_for_interval || @buffer_config.timekey < value_for_interval
1188
+ value_for_interval = @buffer_config.timekey
1189
+ end
1190
+ end
1191
+ unless value_for_interval
1192
+ raise "BUG: both of flush_interval and timekey are disabled"
1193
+ end
1194
+ interval = value_for_interval / 11.0
1195
+ if interval < @buffer_config.flush_thread_interval
1196
+ interval = @buffer_config.flush_thread_interval
1197
+ end
1198
+
1199
+ while !self.after_started? && !self.stopped?
1200
+ sleep 0.5
1201
+ end
1202
+ log.debug "enqueue_thread actually running"
1203
+
1204
+ begin
1205
+ while @output_enqueue_thread_running
1206
+ now_int = Time.now.to_i
1207
+ if @output_flush_interrupted
1208
+ sleep interval
1209
+ next
1210
+ end
1211
+
1212
+ @output_enqueue_thread_mutex.lock
1213
+ begin
1214
+ if @flush_mode == :interval
1215
+ flush_interval = @buffer_config.flush_interval.to_i
1216
+ # This block should be done by integer values.
1217
+ # If both of flush_interval & flush_thread_interval are 1s, expected actual flush timing is 1.5s.
1218
+ # If we use integered values for this comparison, expected actual flush timing is 1.0s.
1219
+ @buffer.enqueue_all{ |metadata, chunk| chunk.created_at.to_i + flush_interval <= now_int }
1220
+ end
1221
+
1222
+ if @chunk_key_time
1223
+ timekey_unit = @buffer_config.timekey
1224
+ timekey_wait = @buffer_config.timekey_wait
1225
+ current_timekey = now_int - now_int % timekey_unit
1226
+ @buffer.enqueue_all{ |metadata, chunk| metadata.timekey < current_timekey && metadata.timekey + timekey_unit + timekey_wait <= now_int }
1227
+ end
1228
+ rescue => e
1229
+ raise if @under_plugin_development
1230
+ log.error "unexpected error while checking flushed chunks. ignored.", error: e
1231
+ log.error_backtrace
1232
+ ensure
1233
+ @output_enqueue_thread_waiting = false
1234
+ @output_enqueue_thread_mutex.unlock
1235
+ end
1236
+ sleep interval
1237
+ end
1238
+ rescue => e
1239
+ # normal errors are rescued by inner begin-rescue clause.
1240
+ log.error "error on enqueue thread", error: e
1241
+ log.error_backtrace
1242
+ raise
1243
+ end
1244
+ end
1245
+
1246
+ def flush_thread_run(state)
1247
+ flush_thread_interval = @buffer_config.flush_thread_interval
1248
+
1249
+ state.next_clock = Fluent::Clock.now + flush_thread_interval
1250
+
1251
+ while !self.after_started? && !self.stopped?
1252
+ sleep 0.5
1253
+ end
1254
+ log.debug "flush_thread actually running"
1255
+
1256
+ begin
1257
+ # This thread don't use `thread_current_running?` because this thread should run in `before_shutdown` phase
1258
+ while @output_flush_threads_running
1259
+ current_clock = Fluent::Clock.now
1260
+ interval = state.next_clock - current_clock
1261
+
1262
+ if state.next_clock <= current_clock && (!@retry || @retry_mutex.synchronize{ @retry.next_time } <= Time.now)
1263
+ try_flush
1264
+
1265
+ # next_flush_time uses flush_thread_interval or flush_thread_burst_interval (or retrying)
1266
+ interval = next_flush_time.to_f - Time.now.to_f
1267
+ # TODO: if secondary && delayed-commit, next_flush_time will be much longer than expected
1268
+ # because @retry still exists (#commit_write is not called yet in #try_flush)
1269
+ # @retry should be cleared if delayed commit is enabled? Or any other solution?
1270
+ state.next_clock = Fluent::Clock.now + interval
1271
+ end
1272
+
1273
+ if @dequeued_chunks_mutex.synchronize{ !@dequeued_chunks.empty? && @dequeued_chunks.first.expired? }
1274
+ unless @output_flush_interrupted
1275
+ try_rollback_write
1276
+ end
1277
+ end
1278
+
1279
+ sleep interval if interval > 0
1280
+ end
1281
+ rescue => e
1282
+ # normal errors are rescued by output plugins in #try_flush
1283
+ # so this rescue section is for critical & unrecoverable errors
1284
+ log.error "error on output thread", error: e
1285
+ log.error_backtrace
1286
+ raise
1287
+ end
1288
+ end
1289
+ end
1290
+ end
1291
+ end