fluentd 0.14.4-x86-mingw32

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of fluentd might be problematic. Click here for more details.

Files changed (328) hide show
  1. checksums.yaml +7 -0
  2. data/.github/ISSUE_TEMPLATE.md +6 -0
  3. data/.gitignore +26 -0
  4. data/.travis.yml +45 -0
  5. data/AUTHORS +2 -0
  6. data/CONTRIBUTING.md +35 -0
  7. data/COPYING +14 -0
  8. data/ChangeLog +276 -0
  9. data/Gemfile +9 -0
  10. data/README.md +51 -0
  11. data/Rakefile +53 -0
  12. data/Vagrantfile +17 -0
  13. data/appveyor.yml +41 -0
  14. data/bin/fluent-debug +5 -0
  15. data/example/copy_roundrobin.conf +39 -0
  16. data/example/filter_stdout.conf +22 -0
  17. data/example/in_forward.conf +11 -0
  18. data/example/in_http.conf +14 -0
  19. data/example/in_out_forward.conf +17 -0
  20. data/example/in_syslog.conf +15 -0
  21. data/example/in_tail.conf +14 -0
  22. data/example/in_tcp.conf +13 -0
  23. data/example/in_udp.conf +13 -0
  24. data/example/multi_filters.conf +61 -0
  25. data/example/out_buffered_null.conf +32 -0
  26. data/example/out_copy.conf +20 -0
  27. data/example/out_file.conf +13 -0
  28. data/example/out_forward.conf +35 -0
  29. data/example/out_forward_buf_file.conf +23 -0
  30. data/example/v0_12_filter.conf +78 -0
  31. data/example/v1_literal_example.conf +36 -0
  32. data/fluent.conf +139 -0
  33. data/fluentd.gemspec +51 -0
  34. data/lib/fluent/agent.rb +194 -0
  35. data/lib/fluent/command/bundler_injection.rb +45 -0
  36. data/lib/fluent/command/cat.rb +319 -0
  37. data/lib/fluent/command/debug.rb +102 -0
  38. data/lib/fluent/command/fluentd.rb +273 -0
  39. data/lib/fluent/compat/call_super_mixin.rb +67 -0
  40. data/lib/fluent/compat/exec_util.rb +129 -0
  41. data/lib/fluent/compat/file_util.rb +54 -0
  42. data/lib/fluent/compat/filter.rb +68 -0
  43. data/lib/fluent/compat/formatter.rb +111 -0
  44. data/lib/fluent/compat/formatter_utils.rb +85 -0
  45. data/lib/fluent/compat/handle_tag_and_time_mixin.rb +62 -0
  46. data/lib/fluent/compat/handle_tag_name_mixin.rb +53 -0
  47. data/lib/fluent/compat/input.rb +49 -0
  48. data/lib/fluent/compat/output.rb +677 -0
  49. data/lib/fluent/compat/output_chain.rb +60 -0
  50. data/lib/fluent/compat/parser.rb +180 -0
  51. data/lib/fluent/compat/parser_utils.rb +40 -0
  52. data/lib/fluent/compat/propagate_default.rb +62 -0
  53. data/lib/fluent/compat/record_filter_mixin.rb +34 -0
  54. data/lib/fluent/compat/set_tag_key_mixin.rb +50 -0
  55. data/lib/fluent/compat/set_time_key_mixin.rb +69 -0
  56. data/lib/fluent/compat/socket_util.rb +165 -0
  57. data/lib/fluent/compat/string_util.rb +34 -0
  58. data/lib/fluent/compat/structured_format_mixin.rb +26 -0
  59. data/lib/fluent/compat/type_converter.rb +90 -0
  60. data/lib/fluent/config.rb +56 -0
  61. data/lib/fluent/config/basic_parser.rb +123 -0
  62. data/lib/fluent/config/configure_proxy.rb +366 -0
  63. data/lib/fluent/config/dsl.rb +149 -0
  64. data/lib/fluent/config/element.rb +218 -0
  65. data/lib/fluent/config/error.rb +26 -0
  66. data/lib/fluent/config/literal_parser.rb +251 -0
  67. data/lib/fluent/config/parser.rb +107 -0
  68. data/lib/fluent/config/section.rb +212 -0
  69. data/lib/fluent/config/types.rb +136 -0
  70. data/lib/fluent/config/v1_parser.rb +190 -0
  71. data/lib/fluent/configurable.rb +176 -0
  72. data/lib/fluent/daemon.rb +15 -0
  73. data/lib/fluent/engine.rb +220 -0
  74. data/lib/fluent/env.rb +27 -0
  75. data/lib/fluent/event.rb +287 -0
  76. data/lib/fluent/event_router.rb +259 -0
  77. data/lib/fluent/filter.rb +21 -0
  78. data/lib/fluent/formatter.rb +23 -0
  79. data/lib/fluent/input.rb +21 -0
  80. data/lib/fluent/label.rb +38 -0
  81. data/lib/fluent/load.rb +36 -0
  82. data/lib/fluent/log.rb +445 -0
  83. data/lib/fluent/match.rb +141 -0
  84. data/lib/fluent/mixin.rb +31 -0
  85. data/lib/fluent/msgpack_factory.rb +62 -0
  86. data/lib/fluent/output.rb +26 -0
  87. data/lib/fluent/output_chain.rb +23 -0
  88. data/lib/fluent/parser.rb +23 -0
  89. data/lib/fluent/plugin.rb +161 -0
  90. data/lib/fluent/plugin/bare_output.rb +63 -0
  91. data/lib/fluent/plugin/base.rb +130 -0
  92. data/lib/fluent/plugin/buf_file.rb +154 -0
  93. data/lib/fluent/plugin/buf_memory.rb +34 -0
  94. data/lib/fluent/plugin/buffer.rb +603 -0
  95. data/lib/fluent/plugin/buffer/chunk.rb +160 -0
  96. data/lib/fluent/plugin/buffer/file_chunk.rb +323 -0
  97. data/lib/fluent/plugin/buffer/memory_chunk.rb +90 -0
  98. data/lib/fluent/plugin/exec_util.rb +22 -0
  99. data/lib/fluent/plugin/file_util.rb +22 -0
  100. data/lib/fluent/plugin/file_wrapper.rb +120 -0
  101. data/lib/fluent/plugin/filter.rb +93 -0
  102. data/lib/fluent/plugin/filter_grep.rb +75 -0
  103. data/lib/fluent/plugin/filter_record_transformer.rb +342 -0
  104. data/lib/fluent/plugin/filter_stdout.rb +53 -0
  105. data/lib/fluent/plugin/formatter.rb +45 -0
  106. data/lib/fluent/plugin/formatter_csv.rb +47 -0
  107. data/lib/fluent/plugin/formatter_hash.rb +29 -0
  108. data/lib/fluent/plugin/formatter_json.rb +44 -0
  109. data/lib/fluent/plugin/formatter_ltsv.rb +41 -0
  110. data/lib/fluent/plugin/formatter_msgpack.rb +29 -0
  111. data/lib/fluent/plugin/formatter_out_file.rb +78 -0
  112. data/lib/fluent/plugin/formatter_single_value.rb +34 -0
  113. data/lib/fluent/plugin/formatter_stdout.rb +74 -0
  114. data/lib/fluent/plugin/in_debug_agent.rb +64 -0
  115. data/lib/fluent/plugin/in_dummy.rb +135 -0
  116. data/lib/fluent/plugin/in_exec.rb +149 -0
  117. data/lib/fluent/plugin/in_forward.rb +366 -0
  118. data/lib/fluent/plugin/in_gc_stat.rb +52 -0
  119. data/lib/fluent/plugin/in_http.rb +422 -0
  120. data/lib/fluent/plugin/in_monitor_agent.rb +401 -0
  121. data/lib/fluent/plugin/in_object_space.rb +90 -0
  122. data/lib/fluent/plugin/in_syslog.rb +204 -0
  123. data/lib/fluent/plugin/in_tail.rb +838 -0
  124. data/lib/fluent/plugin/in_tcp.rb +41 -0
  125. data/lib/fluent/plugin/in_udp.rb +37 -0
  126. data/lib/fluent/plugin/in_unix.rb +201 -0
  127. data/lib/fluent/plugin/input.rb +33 -0
  128. data/lib/fluent/plugin/multi_output.rb +95 -0
  129. data/lib/fluent/plugin/out_buffered_null.rb +59 -0
  130. data/lib/fluent/plugin/out_buffered_stdout.rb +70 -0
  131. data/lib/fluent/plugin/out_copy.rb +42 -0
  132. data/lib/fluent/plugin/out_exec.rb +114 -0
  133. data/lib/fluent/plugin/out_exec_filter.rb +393 -0
  134. data/lib/fluent/plugin/out_file.rb +167 -0
  135. data/lib/fluent/plugin/out_forward.rb +646 -0
  136. data/lib/fluent/plugin/out_null.rb +27 -0
  137. data/lib/fluent/plugin/out_relabel.rb +28 -0
  138. data/lib/fluent/plugin/out_roundrobin.rb +80 -0
  139. data/lib/fluent/plugin/out_stdout.rb +48 -0
  140. data/lib/fluent/plugin/out_stream.rb +130 -0
  141. data/lib/fluent/plugin/output.rb +1020 -0
  142. data/lib/fluent/plugin/owned_by_mixin.rb +42 -0
  143. data/lib/fluent/plugin/parser.rb +175 -0
  144. data/lib/fluent/plugin/parser_apache.rb +28 -0
  145. data/lib/fluent/plugin/parser_apache2.rb +84 -0
  146. data/lib/fluent/plugin/parser_apache_error.rb +26 -0
  147. data/lib/fluent/plugin/parser_csv.rb +33 -0
  148. data/lib/fluent/plugin/parser_json.rb +79 -0
  149. data/lib/fluent/plugin/parser_ltsv.rb +50 -0
  150. data/lib/fluent/plugin/parser_multiline.rb +104 -0
  151. data/lib/fluent/plugin/parser_nginx.rb +28 -0
  152. data/lib/fluent/plugin/parser_none.rb +36 -0
  153. data/lib/fluent/plugin/parser_regexp.rb +73 -0
  154. data/lib/fluent/plugin/parser_syslog.rb +82 -0
  155. data/lib/fluent/plugin/parser_tsv.rb +37 -0
  156. data/lib/fluent/plugin/socket_util.rb +22 -0
  157. data/lib/fluent/plugin/storage.rb +84 -0
  158. data/lib/fluent/plugin/storage_local.rb +132 -0
  159. data/lib/fluent/plugin/string_util.rb +22 -0
  160. data/lib/fluent/plugin_helper.rb +42 -0
  161. data/lib/fluent/plugin_helper/child_process.rb +298 -0
  162. data/lib/fluent/plugin_helper/compat_parameters.rb +224 -0
  163. data/lib/fluent/plugin_helper/event_emitter.rb +80 -0
  164. data/lib/fluent/plugin_helper/event_loop.rb +118 -0
  165. data/lib/fluent/plugin_helper/formatter.rb +149 -0
  166. data/lib/fluent/plugin_helper/inject.rb +125 -0
  167. data/lib/fluent/plugin_helper/parser.rb +147 -0
  168. data/lib/fluent/plugin_helper/retry_state.rb +177 -0
  169. data/lib/fluent/plugin_helper/storage.rb +331 -0
  170. data/lib/fluent/plugin_helper/thread.rb +147 -0
  171. data/lib/fluent/plugin_helper/timer.rb +90 -0
  172. data/lib/fluent/plugin_id.rb +63 -0
  173. data/lib/fluent/process.rb +504 -0
  174. data/lib/fluent/registry.rb +99 -0
  175. data/lib/fluent/root_agent.rb +314 -0
  176. data/lib/fluent/rpc.rb +94 -0
  177. data/lib/fluent/supervisor.rb +680 -0
  178. data/lib/fluent/system_config.rb +122 -0
  179. data/lib/fluent/test.rb +56 -0
  180. data/lib/fluent/test/base.rb +85 -0
  181. data/lib/fluent/test/driver/base.rb +179 -0
  182. data/lib/fluent/test/driver/base_owned.rb +70 -0
  183. data/lib/fluent/test/driver/base_owner.rb +125 -0
  184. data/lib/fluent/test/driver/event_feeder.rb +98 -0
  185. data/lib/fluent/test/driver/filter.rb +57 -0
  186. data/lib/fluent/test/driver/formatter.rb +30 -0
  187. data/lib/fluent/test/driver/input.rb +31 -0
  188. data/lib/fluent/test/driver/multi_output.rb +52 -0
  189. data/lib/fluent/test/driver/output.rb +76 -0
  190. data/lib/fluent/test/driver/parser.rb +30 -0
  191. data/lib/fluent/test/driver/test_event_router.rb +45 -0
  192. data/lib/fluent/test/filter_test.rb +77 -0
  193. data/lib/fluent/test/formatter_test.rb +65 -0
  194. data/lib/fluent/test/helpers.rb +79 -0
  195. data/lib/fluent/test/input_test.rb +172 -0
  196. data/lib/fluent/test/log.rb +73 -0
  197. data/lib/fluent/test/output_test.rb +156 -0
  198. data/lib/fluent/test/parser_test.rb +70 -0
  199. data/lib/fluent/time.rb +175 -0
  200. data/lib/fluent/timezone.rb +133 -0
  201. data/lib/fluent/unique_id.rb +39 -0
  202. data/lib/fluent/version.rb +21 -0
  203. data/lib/fluent/winsvc.rb +71 -0
  204. data/test/compat/test_calls_super.rb +166 -0
  205. data/test/compat/test_parser.rb +82 -0
  206. data/test/config/assertions.rb +42 -0
  207. data/test/config/test_config_parser.rb +507 -0
  208. data/test/config/test_configurable.rb +1194 -0
  209. data/test/config/test_configure_proxy.rb +386 -0
  210. data/test/config/test_dsl.rb +415 -0
  211. data/test/config/test_element.rb +403 -0
  212. data/test/config/test_literal_parser.rb +297 -0
  213. data/test/config/test_section.rb +184 -0
  214. data/test/config/test_system_config.rb +120 -0
  215. data/test/config/test_types.rb +171 -0
  216. data/test/helper.rb +119 -0
  217. data/test/plugin/data/2010/01/20100102-030405.log +0 -0
  218. data/test/plugin/data/2010/01/20100102-030406.log +0 -0
  219. data/test/plugin/data/2010/01/20100102.log +0 -0
  220. data/test/plugin/data/log/bar +0 -0
  221. data/test/plugin/data/log/foo/bar.log +0 -0
  222. data/test/plugin/data/log/test.log +0 -0
  223. data/test/plugin/test_bare_output.rb +118 -0
  224. data/test/plugin/test_base.rb +75 -0
  225. data/test/plugin/test_buf_file.rb +571 -0
  226. data/test/plugin/test_buf_memory.rb +42 -0
  227. data/test/plugin/test_buffer.rb +1200 -0
  228. data/test/plugin/test_buffer_chunk.rb +168 -0
  229. data/test/plugin/test_buffer_file_chunk.rb +771 -0
  230. data/test/plugin/test_buffer_memory_chunk.rb +265 -0
  231. data/test/plugin/test_file_util.rb +96 -0
  232. data/test/plugin/test_filter.rb +353 -0
  233. data/test/plugin/test_filter_grep.rb +119 -0
  234. data/test/plugin/test_filter_record_transformer.rb +600 -0
  235. data/test/plugin/test_filter_stdout.rb +211 -0
  236. data/test/plugin/test_formatter_csv.rb +94 -0
  237. data/test/plugin/test_formatter_json.rb +30 -0
  238. data/test/plugin/test_formatter_ltsv.rb +52 -0
  239. data/test/plugin/test_formatter_msgpack.rb +28 -0
  240. data/test/plugin/test_formatter_out_file.rb +95 -0
  241. data/test/plugin/test_formatter_single_value.rb +38 -0
  242. data/test/plugin/test_in_debug_agent.rb +28 -0
  243. data/test/plugin/test_in_dummy.rb +188 -0
  244. data/test/plugin/test_in_exec.rb +133 -0
  245. data/test/plugin/test_in_forward.rb +635 -0
  246. data/test/plugin/test_in_gc_stat.rb +39 -0
  247. data/test/plugin/test_in_http.rb +442 -0
  248. data/test/plugin/test_in_monitor_agent.rb +329 -0
  249. data/test/plugin/test_in_object_space.rb +64 -0
  250. data/test/plugin/test_in_syslog.rb +205 -0
  251. data/test/plugin/test_in_tail.rb +1001 -0
  252. data/test/plugin/test_in_tcp.rb +102 -0
  253. data/test/plugin/test_in_udp.rb +121 -0
  254. data/test/plugin/test_in_unix.rb +126 -0
  255. data/test/plugin/test_input.rb +122 -0
  256. data/test/plugin/test_multi_output.rb +180 -0
  257. data/test/plugin/test_out_buffered_null.rb +79 -0
  258. data/test/plugin/test_out_buffered_stdout.rb +122 -0
  259. data/test/plugin/test_out_copy.rb +160 -0
  260. data/test/plugin/test_out_exec.rb +155 -0
  261. data/test/plugin/test_out_exec_filter.rb +262 -0
  262. data/test/plugin/test_out_file.rb +383 -0
  263. data/test/plugin/test_out_forward.rb +590 -0
  264. data/test/plugin/test_out_null.rb +29 -0
  265. data/test/plugin/test_out_relabel.rb +28 -0
  266. data/test/plugin/test_out_roundrobin.rb +146 -0
  267. data/test/plugin/test_out_stdout.rb +92 -0
  268. data/test/plugin/test_out_stream.rb +93 -0
  269. data/test/plugin/test_output.rb +568 -0
  270. data/test/plugin/test_output_as_buffered.rb +1604 -0
  271. data/test/plugin/test_output_as_buffered_overflow.rb +250 -0
  272. data/test/plugin/test_output_as_buffered_retries.rb +839 -0
  273. data/test/plugin/test_output_as_buffered_secondary.rb +817 -0
  274. data/test/plugin/test_output_as_standard.rb +374 -0
  275. data/test/plugin/test_owned_by.rb +35 -0
  276. data/test/plugin/test_parser_apache.rb +42 -0
  277. data/test/plugin/test_parser_apache2.rb +38 -0
  278. data/test/plugin/test_parser_apache_error.rb +45 -0
  279. data/test/plugin/test_parser_base.rb +32 -0
  280. data/test/plugin/test_parser_csv.rb +104 -0
  281. data/test/plugin/test_parser_json.rb +107 -0
  282. data/test/plugin/test_parser_labeled_tsv.rb +129 -0
  283. data/test/plugin/test_parser_multiline.rb +100 -0
  284. data/test/plugin/test_parser_nginx.rb +48 -0
  285. data/test/plugin/test_parser_none.rb +53 -0
  286. data/test/plugin/test_parser_regexp.rb +277 -0
  287. data/test/plugin/test_parser_syslog.rb +66 -0
  288. data/test/plugin/test_parser_time.rb +46 -0
  289. data/test/plugin/test_parser_tsv.rb +121 -0
  290. data/test/plugin/test_storage.rb +167 -0
  291. data/test/plugin/test_storage_local.rb +8 -0
  292. data/test/plugin/test_string_util.rb +26 -0
  293. data/test/plugin_helper/test_child_process.rb +608 -0
  294. data/test/plugin_helper/test_compat_parameters.rb +242 -0
  295. data/test/plugin_helper/test_event_emitter.rb +51 -0
  296. data/test/plugin_helper/test_event_loop.rb +52 -0
  297. data/test/plugin_helper/test_formatter.rb +252 -0
  298. data/test/plugin_helper/test_inject.rb +487 -0
  299. data/test/plugin_helper/test_parser.rb +263 -0
  300. data/test/plugin_helper/test_retry_state.rb +399 -0
  301. data/test/plugin_helper/test_storage.rb +521 -0
  302. data/test/plugin_helper/test_thread.rb +164 -0
  303. data/test/plugin_helper/test_timer.rb +131 -0
  304. data/test/scripts/exec_script.rb +32 -0
  305. data/test/scripts/fluent/plugin/formatter_known.rb +8 -0
  306. data/test/scripts/fluent/plugin/out_test.rb +81 -0
  307. data/test/scripts/fluent/plugin/out_test2.rb +80 -0
  308. data/test/scripts/fluent/plugin/parser_known.rb +4 -0
  309. data/test/test_config.rb +179 -0
  310. data/test/test_configdsl.rb +148 -0
  311. data/test/test_event.rb +329 -0
  312. data/test/test_event_router.rb +331 -0
  313. data/test/test_event_time.rb +184 -0
  314. data/test/test_filter.rb +121 -0
  315. data/test/test_formatter.rb +319 -0
  316. data/test/test_input.rb +31 -0
  317. data/test/test_log.rb +572 -0
  318. data/test/test_match.rb +137 -0
  319. data/test/test_mixin.rb +351 -0
  320. data/test/test_output.rb +214 -0
  321. data/test/test_plugin_classes.rb +136 -0
  322. data/test/test_plugin_helper.rb +81 -0
  323. data/test/test_process.rb +48 -0
  324. data/test/test_root_agent.rb +278 -0
  325. data/test/test_supervisor.rb +339 -0
  326. data/test/test_time_formatter.rb +186 -0
  327. data/test/test_unique_id.rb +47 -0
  328. metadata +823 -0
@@ -0,0 +1,27 @@
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/output'
18
+
19
+ module Fluent::Plugin
20
+ class NullOutput < Output
21
+ Fluent::Plugin.register_output('null', self)
22
+
23
+ def process(tag, es)
24
+ # Do nothing
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,28 @@
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/output'
18
+
19
+ module Fluent::Plugin
20
+ class RelabelOutput < Output
21
+ Fluent::Plugin.register_output('relabel', self)
22
+ helpers :event_emitter
23
+
24
+ def process(tag, es)
25
+ router.emit_stream(tag, es)
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,80 @@
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/multi_output'
18
+ require 'fluent/config/error'
19
+
20
+ module Fluent::Plugin
21
+ class RoundRobinOutput < MultiOutput
22
+ Fluent::Plugin.register_output('roundrobin', self)
23
+
24
+ config_section :store do
25
+ config_param :weight, :integer, default: 1
26
+ end
27
+
28
+ def initialize
29
+ super
30
+ @weights = []
31
+ end
32
+
33
+ attr_reader :weights
34
+
35
+ def configure(conf)
36
+ super
37
+
38
+ @stores.each do |store|
39
+ @weights << store.weight
40
+ end
41
+ @rr = -1 # starts from @output[0]
42
+ @rand_seed = Random.new.seed
43
+ end
44
+
45
+ def start
46
+ super
47
+ rebuild_weight_array
48
+ end
49
+
50
+ def process(tag, es)
51
+ next_output.emit_events(tag, es)
52
+ end
53
+
54
+ private
55
+
56
+ def next_output
57
+ @rr = 0 if (@rr += 1) >= @weight_array.size
58
+ @weight_array[@rr]
59
+ end
60
+
61
+ def rebuild_weight_array
62
+ gcd = @weights.inject(0) {|r,w| r.gcd(w) }
63
+
64
+ weight_array = []
65
+ @outputs.zip(@weights).each {|output,weight|
66
+ (weight / gcd).times {
67
+ weight_array << output
68
+ }
69
+ }
70
+
71
+ # don't randomize order if all weight is 1 (=default)
72
+ if @weights.any? {|w| w > 1 }
73
+ r = Random.new(@rand_seed)
74
+ weight_array.sort_by! { r.rand }
75
+ end
76
+
77
+ @weight_array = weight_array
78
+ end
79
+ end
80
+ end
@@ -0,0 +1,48 @@
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/output'
18
+
19
+ module Fluent::Plugin
20
+ class StdoutOutput < Output
21
+ Fluent::Plugin.register_output('stdout', self)
22
+
23
+ helpers :inject, :formatter, :compat_parameters
24
+
25
+ DEFAULT_FORMAT_TYPE = 'json'
26
+
27
+ config_section :format do
28
+ config_set_default :@type, DEFAULT_FORMAT_TYPE
29
+ end
30
+
31
+ def configure(conf)
32
+ if conf['output_type'] && !conf['format']
33
+ conf['format'] = conf['output_type']
34
+ end
35
+ compat_parameters_convert(conf, :inject, :formatter)
36
+ super
37
+ @formatter = formatter_create(conf: conf.elements('format').first, default_type: DEFAULT_FORMAT_TYPE)
38
+ end
39
+
40
+ def process(tag, es)
41
+ es.each {|time,record|
42
+ r = inject_values_to_record(tag, time, record)
43
+ $log.write "#{Time.at(time).localtime} #{tag}: #{@formatter.format(tag, time, r).chomp}\n"
44
+ }
45
+ $log.flush
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,130 @@
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 'socket'
18
+ require 'fileutils'
19
+
20
+ require 'fluent/output'
21
+ require 'fluent/event'
22
+
23
+ module Fluent
24
+ # obsolete
25
+ class StreamOutput < BufferedOutput
26
+ config_param :send_timeout, :time, default: 60
27
+
28
+ helpers :compat_parameters
29
+
30
+ def configure(conf)
31
+ compat_parameters_convert(conf, :buffer)
32
+ super
33
+ end
34
+
35
+ def format_stream(tag, es)
36
+ # use PackedForward
37
+ [tag, es.to_msgpack_stream].to_msgpack
38
+ end
39
+
40
+ def write(chunk)
41
+ sock = connect
42
+ begin
43
+ opt = [1, @send_timeout.to_i].pack('I!I!') # { int l_onoff; int l_linger; }
44
+ sock.setsockopt(Socket::SOL_SOCKET, Socket::SO_LINGER, opt)
45
+
46
+ opt = [@send_timeout.to_i, 0].pack('L!L!') # struct timeval
47
+ sock.setsockopt(Socket::SOL_SOCKET, Socket::SO_SNDTIMEO, opt)
48
+
49
+ chunk.write_to(sock)
50
+ ensure
51
+ sock.close
52
+ end
53
+ end
54
+
55
+ def flush_secondary(secondary)
56
+ unless secondary.is_a?(StreamOutput)
57
+ secondary = ReformatWriter.new(secondary)
58
+ end
59
+ @buffer.pop(secondary)
60
+ end
61
+
62
+ class ReformatWriter
63
+ def initialize(secondary)
64
+ @secondary = secondary
65
+ end
66
+
67
+ def write(chunk)
68
+ chain = NullOutputChain.instance
69
+ chunk.open {|io|
70
+ # TODO use MessagePackIoEventStream
71
+ u = Fluent::Engine.msgpack_factory.unpacker(io)
72
+ begin
73
+ u.each {|(tag,entries)|
74
+ es = MultiEventStream.new
75
+ entries.each {|o|
76
+ es.add(o[0], o[1])
77
+ }
78
+ @secondary.emit(tag, es, chain)
79
+ }
80
+ rescue EOFError
81
+ end
82
+ }
83
+ end
84
+ end
85
+ end
86
+
87
+ # obsolete
88
+ class TcpOutput < StreamOutput
89
+ Plugin.register_output('tcp', self)
90
+
91
+ LISTEN_PORT = 24224
92
+
93
+ def initialize
94
+ super
95
+ $log.warn "'tcp' output is obsoleted and will be removed. Use 'forward' instead."
96
+ $log.warn "see 'forward' section in http://docs.fluentd.org/ for the high-availability configuration."
97
+ end
98
+
99
+ config_param :port, :integer, default: LISTEN_PORT
100
+ config_param :host, :string
101
+
102
+ def configure(conf)
103
+ super
104
+ end
105
+
106
+ def connect
107
+ TCPSocket.new(@host, @port)
108
+ end
109
+ end
110
+
111
+ # obsolete
112
+ class UnixOutput < StreamOutput
113
+ Plugin.register_output('unix', self)
114
+
115
+ def initialize
116
+ super
117
+ $log.warn "'unix' output is obsoleted and will be removed."
118
+ end
119
+
120
+ config_param :path, :string
121
+
122
+ def configure(conf)
123
+ super
124
+ end
125
+
126
+ def connect
127
+ UNIXSocket.new(@path)
128
+ end
129
+ end
130
+ end
@@ -0,0 +1,1020 @@
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
+
24
+ require 'time'
25
+ require 'monitor'
26
+
27
+ module Fluent
28
+ module Plugin
29
+ class Output < Base
30
+ include PluginId
31
+ include PluginLoggerMixin
32
+ include PluginHelper::Mixin
33
+ include UniqueId::Mixin
34
+
35
+ helpers :thread, :retry_state
36
+
37
+ CHUNK_KEY_PATTERN = /^[-_.@a-zA-Z0-9]+$/
38
+ CHUNK_KEY_PLACEHOLDER_PATTERN = /\$\{[-_.@a-zA-Z0-9]+\}/
39
+
40
+ CHUNKING_FIELD_WARN_NUM = 4
41
+
42
+ config_param :time_as_integer, :bool, default: false
43
+
44
+ # `<buffer>` and `<secondary>` sections are available only when '#format' and '#write' are implemented
45
+ config_section :buffer, param_name: :buffer_config, init: true, required: false, multi: false, final: true do
46
+ config_argument :chunk_keys, :array, value_type: :string, default: []
47
+ config_param :@type, :string, default: 'memory', alias: :type
48
+
49
+ config_param :timekey, :time, default: nil # range size to be used: `time.to_i / @timekey`
50
+ config_param :timekey_wait, :time, default: 600
51
+ # These are for #extract_placeholders
52
+ config_param :timekey_use_utc, :bool, default: false # default is localtime
53
+ config_param :timekey_zone, :string, default: Time.now.strftime('%z') # e.g., "-0700" or "Asia/Tokyo"
54
+
55
+ desc 'If true, plugin will try to flush buffer just before shutdown.'
56
+ config_param :flush_at_shutdown, :bool, default: nil # change default by buffer_plugin.persistent?
57
+
58
+ desc 'How to enqueue chunks to be flushed. "interval" flushes per flush_interval, "immediate" flushes just after event arrival.'
59
+ config_param :flush_mode, :enum, list: [:default, :lazy, :interval, :immediate], default: :default
60
+ config_param :flush_interval, :time, default: 60, desc: 'The interval between buffer chunk flushes.'
61
+
62
+ config_param :flush_thread_count, :integer, default: 1, desc: 'The number of threads to flush the buffer.'
63
+
64
+ config_param :flush_thread_interval, :float, default: 1.0, desc: 'Seconds to sleep between checks for buffer flushes in flush threads.'
65
+ config_param :flush_thread_burst_interval, :float, default: 1.0, desc: 'Seconds to sleep between flushes when many buffer chunks are queued.'
66
+
67
+ config_param :delayed_commit_timeout, :time, default: 60, desc: 'Seconds of timeout for buffer chunks to be committed by plugins later.'
68
+
69
+ 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.'
70
+
71
+ config_param :retry_forever, :bool, default: false, desc: 'If true, plugin will ignore retry_timeout and retry_max_times options and retry flushing forever.'
72
+ config_param :retry_timeout, :time, default: 72 * 60 * 60, desc: 'The maximum seconds to retry to flush while failing, until plugin discards buffer chunks.'
73
+ # 72hours == 17 times with exponential backoff (not to change default behavior)
74
+ config_param :retry_max_times, :integer, default: nil, desc: 'The maximum number of times to retry to flush while failing.'
75
+
76
+ config_param :retry_secondary_threshold, :float, default: 0.8, desc: 'ratio of retry_timeout to switch to use secondary while failing.'
77
+ # expornential backoff sequence will be initialized at the time of this threshold
78
+
79
+ desc 'How to wait next retry to flush buffer.'
80
+ config_param :retry_type, :enum, list: [:exponential_backoff, :periodic], default: :exponential_backoff
81
+ ### Periodic -> fixed :retry_wait
82
+ ### Exponencial backoff: k is number of retry times
83
+ # c: constant factor, @retry_wait
84
+ # b: base factor, @retry_exponential_backoff_base
85
+ # k: times
86
+ # total retry time: c + c * b^1 + (...) + c*b^k = c*b^(k+1) - 1
87
+ config_param :retry_wait, :time, default: 1, desc: 'Seconds to wait before next retry to flush, or constant factor of exponential backoff.'
88
+ config_param :retry_exponential_backoff_base, :float, default: 2, desc: 'The base number of exponencial backoff for retries.'
89
+ config_param :retry_max_interval, :time, default: nil, desc: 'The maximum interval seconds for exponencial backoff between retries while failing.'
90
+
91
+ config_param :retry_randomize, :bool, default: true, desc: 'If true, output plugin will retry after randomized interval not to do burst retries.'
92
+ end
93
+
94
+ config_section :secondary, param_name: :secondary_config, required: false, multi: false, final: true do
95
+ config_param :@type, :string, default: nil, alias: :type
96
+ config_section :buffer, required: false, multi: false do
97
+ # dummy to detect invalid specification for here
98
+ end
99
+ config_section :secondary, required: false, multi: false do
100
+ # dummy to detect invalid specification for here
101
+ end
102
+ end
103
+
104
+ def process(tag, es)
105
+ raise NotImplementedError, "BUG: output plugins MUST implement this method"
106
+ end
107
+
108
+ def write(chunk)
109
+ raise NotImplementedError, "BUG: output plugins MUST implement this method"
110
+ end
111
+
112
+ def try_write(chunk)
113
+ raise NotImplementedError, "BUG: output plugins MUST implement this method"
114
+ end
115
+
116
+ def format(tag, time, record)
117
+ # standard msgpack_event_stream chunk will be used if this method is not implemented in plugin subclass
118
+ raise NotImplementedError, "BUG: output plugins MUST implement this method"
119
+ end
120
+
121
+ def prefer_buffered_processing
122
+ # override this method to return false only when all of these are true:
123
+ # * plugin has both implementation for buffered and non-buffered methods
124
+ # * plugin is expected to work as non-buffered plugin if no `<buffer>` sections specified
125
+ true
126
+ end
127
+
128
+ def prefer_delayed_commit
129
+ # override this method to decide which is used of `write` or `try_write` if both are implemented
130
+ true
131
+ end
132
+
133
+ # Internal states
134
+ FlushThreadState = Struct.new(:thread, :next_time)
135
+ DequeuedChunkInfo = Struct.new(:chunk_id, :time, :timeout) do
136
+ def expired?
137
+ time + timeout < Time.now
138
+ end
139
+ end
140
+
141
+ attr_reader :as_secondary, :delayed_commit, :delayed_commit_timeout
142
+ attr_reader :num_errors, :emit_count, :emit_records, :write_count, :rollback_count
143
+
144
+ # for tests
145
+ attr_reader :buffer, :retry, :secondary, :chunk_keys, :chunk_key_time, :chunk_key_tag
146
+ attr_accessor :output_enqueue_thread_waiting, :in_tests
147
+
148
+ # output_enqueue_thread_waiting: for test of output.rb itself
149
+ # in_tests: for tests of plugins with test drivers
150
+
151
+ def initialize
152
+ super
153
+ @counters_monitor = Monitor.new
154
+ @buffering = false
155
+ @delayed_commit = false
156
+ @as_secondary = false
157
+ @in_tests = false
158
+ @primary_instance = nil
159
+
160
+ # TODO: well organized counters
161
+ @num_errors = 0
162
+ @emit_count = 0
163
+ @emit_records = 0
164
+ @write_count = 0
165
+ @rollback_count = 0
166
+
167
+ # How to process events is decided here at once, but it will be decided in delayed way on #configure & #start
168
+ if implement?(:synchronous)
169
+ if implement?(:buffered) || implement?(:delayed_commit)
170
+ @buffering = nil # do #configure or #start to determine this for full-featured plugins
171
+ else
172
+ @buffering = false
173
+ end
174
+ else
175
+ @buffering = true
176
+ end
177
+ @custom_format = implement?(:custom_format)
178
+
179
+ @buffer = nil
180
+ @secondary = nil
181
+ @retry = nil
182
+ @dequeued_chunks = nil
183
+ @output_flush_threads = nil
184
+
185
+ @simple_chunking = nil
186
+ @chunk_keys = @chunk_key_time = @chunk_key_tag = nil
187
+ @flush_mode = nil
188
+ end
189
+
190
+ def acts_as_secondary(primary)
191
+ @as_secondary = true
192
+ @primary_instance = primary
193
+ (class << self; self; end).module_eval do
194
+ define_method(:extract_placeholders){ |str, metadata| @primary_instance.extract_placeholders(str, metadata) }
195
+ define_method(:commit_write){ |chunk_id| @primary_instance.commit_write(chunk_id, delayed: delayed_commit, secondary: true) }
196
+ define_method(:rollback_write){ |chunk_id| @primary_instance.rollback_write(chunk_id) }
197
+ end
198
+ end
199
+
200
+ def configure(conf)
201
+ unless implement?(:synchronous) || implement?(:buffered) || implement?(:delayed_commit)
202
+ raise "BUG: output plugin must implement some methods. see developer documents."
203
+ end
204
+
205
+ has_buffer_section = (conf.elements(name: 'buffer').size > 0)
206
+
207
+ super
208
+
209
+ if has_buffer_section
210
+ unless implement?(:buffered) || implement?(:delayed_commit)
211
+ raise Fluent::ConfigError, "<buffer> section is configured, but plugin '#{self.class}' doesn't support buffering"
212
+ end
213
+ @buffering = true
214
+ else # no buffer sections
215
+ if implement?(:synchronous)
216
+ if !implement?(:buffered) && !implement?(:delayed_commit)
217
+ if @as_secondary
218
+ raise Fluent::ConfigError, "secondary plugin '#{self.class}' must support buffering, but doesn't."
219
+ end
220
+ @buffering = false
221
+ else
222
+ if @as_secondary
223
+ # secondary plugin always works as buffered plugin without buffer instance
224
+ @buffering = true
225
+ else
226
+ # @buffering.nil? shows that enabling buffering or not will be decided in lazy way in #start
227
+ @buffering = nil
228
+ end
229
+ end
230
+ else # buffered or delayed_commit is supported by `unless` of first line in this method
231
+ @buffering = true
232
+ end
233
+ end
234
+
235
+ if @as_secondary
236
+ if !@buffering && !@buffering.nil?
237
+ raise Fluent::ConfigError, "secondary plugin '#{self.class}' must support buffering, but doesn't"
238
+ end
239
+ end
240
+
241
+ if (@buffering || @buffering.nil?) && !@as_secondary
242
+ # When @buffering.nil?, @buffer_config was initialized with default value for all parameters.
243
+ # If so, this configuration MUST success.
244
+ @chunk_keys = @buffer_config.chunk_keys.dup
245
+ @chunk_key_time = !!@chunk_keys.delete('time')
246
+ @chunk_key_tag = !!@chunk_keys.delete('tag')
247
+ if @chunk_keys.any?{ |key| key !~ CHUNK_KEY_PATTERN }
248
+ raise Fluent::ConfigError, "chunk_keys specification includes invalid char"
249
+ end
250
+
251
+ if @chunk_key_time
252
+ raise Fluent::ConfigError, "<buffer ...> argument includes 'time', but timekey is not configured" unless @buffer_config.timekey
253
+ Fluent::Timezone.validate!(@buffer_config.timekey_zone)
254
+ @buffer_config.timekey_zone = '+0000' if @buffer_config.timekey_use_utc
255
+ @output_time_formatter_cache = {}
256
+ end
257
+
258
+ if (@chunk_key_tag ? 1 : 0) + @chunk_keys.size >= CHUNKING_FIELD_WARN_NUM
259
+ log.warn "many chunk keys specified, and it may cause too many chunks on your system."
260
+ end
261
+
262
+ # no chunk keys or only tags (chunking can be done without iterating event stream)
263
+ @simple_chunking = !@chunk_key_time && @chunk_keys.empty?
264
+
265
+ @flush_mode = @buffer_config.flush_mode
266
+ if @flush_mode == :default
267
+ @flush_mode = (@chunk_key_time ? :lazy : :interval)
268
+ end
269
+
270
+ buffer_type = @buffer_config[:@type]
271
+ buffer_conf = conf.elements(name: 'buffer').first || Fluent::Config::Element.new('buffer', '', {}, [])
272
+ @buffer = Plugin.new_buffer(buffer_type, parent: self)
273
+ @buffer.configure(buffer_conf)
274
+
275
+ @flush_at_shutdown = @buffer_config.flush_at_shutdown
276
+ if @flush_at_shutdown.nil?
277
+ @flush_at_shutdown = if @buffer.persistent?
278
+ false
279
+ else
280
+ true # flush_at_shutdown is true in default for on-memory buffer
281
+ end
282
+ elsif !@flush_at_shutdown && !@buffer.persistent?
283
+ buf_type = Plugin.lookup_type_from_class(@buffer.class)
284
+ log.warn "'flush_at_shutdown' is false, and buffer plugin '#{buf_type}' is not persistent buffer."
285
+ log.warn "your configuration will lose buffered data at shutdown. please confirm your configuration again."
286
+ end
287
+ end
288
+
289
+ if @secondary_config
290
+ raise Fluent::ConfigError, "Invalid <secondary> section for non-buffered plugin" unless @buffering
291
+ raise Fluent::ConfigError, "<secondary> section cannot have <buffer> section" if @secondary_config.buffer
292
+ raise Fluent::ConfigError, "<secondary> section cannot have <secondary> section" if @secondary_config.secondary
293
+ raise Fluent::ConfigError, "<secondary> section and 'retry_forever' are exclusive" if @buffer_config.retry_forever
294
+
295
+ secondary_type = @secondary_config[:@type]
296
+ unless secondary_type
297
+ secondary_type = conf['@type'] # primary plugin type
298
+ end
299
+ secondary_conf = conf.elements(name: 'secondary').first
300
+ @secondary = Plugin.new_output(secondary_type)
301
+ @secondary.acts_as_secondary(self)
302
+ @secondary.configure(secondary_conf)
303
+ @secondary.router = router if @secondary.has_router?
304
+ if (self.class != @secondary.class) && (@custom_format || @secondary.implement?(:custom_format))
305
+ log.warn "secondary type should be same with primary one", primary: self.class.to_s, secondary: @secondary.class.to_s
306
+ end
307
+ else
308
+ @secondary = nil
309
+ end
310
+
311
+ self
312
+ end
313
+
314
+ def start
315
+ super
316
+
317
+ if @buffering.nil?
318
+ @buffering = prefer_buffered_processing
319
+ if !@buffering && @buffer
320
+ @buffer.terminate # it's not started, so terminate will be enough
321
+ end
322
+ end
323
+
324
+ if @buffering
325
+ m = method(:emit_buffered)
326
+ (class << self; self; end).module_eval do
327
+ define_method(:emit_events, m)
328
+ end
329
+
330
+ @custom_format = implement?(:custom_format)
331
+ @delayed_commit = if implement?(:buffered) && implement?(:delayed_commit)
332
+ prefer_delayed_commit
333
+ else
334
+ implement?(:delayed_commit)
335
+ end
336
+ @delayed_commit_timeout = @buffer_config.delayed_commit_timeout
337
+ else # !@buffering
338
+ m = method(:emit_sync)
339
+ (class << self; self; end).module_eval do
340
+ define_method(:emit_events, m)
341
+ end
342
+ end
343
+
344
+ if @buffering && !@as_secondary
345
+ @retry = nil
346
+ @retry_mutex = Mutex.new
347
+
348
+ @buffer.start
349
+
350
+ @output_flush_threads = []
351
+ @output_flush_threads_mutex = Mutex.new
352
+ @output_flush_threads_running = true
353
+
354
+ # mainly for test: detect enqueue works as code below:
355
+ # @output.interrupt_flushes
356
+ # # emits
357
+ # @output.enqueue_thread_wait
358
+ @output_flush_interrupted = false
359
+ @output_enqueue_thread_mutex = Mutex.new
360
+ @output_enqueue_thread_waiting = false
361
+
362
+ @dequeued_chunks = []
363
+ @dequeued_chunks_mutex = Mutex.new
364
+
365
+ @buffer_config.flush_thread_count.times do |i|
366
+ thread_title = "flush_thread_#{i}".to_sym
367
+ thread_state = FlushThreadState.new(nil, nil)
368
+ thread = thread_create(thread_title) do
369
+ flush_thread_run(thread_state)
370
+ end
371
+ thread_state.thread = thread
372
+ @output_flush_threads_mutex.synchronize do
373
+ @output_flush_threads << thread_state
374
+ end
375
+ end
376
+ @output_flush_thread_current_position = 0
377
+
378
+ unless @in_tests
379
+ if @flush_mode == :interval || @chunk_key_time
380
+ thread_create(:enqueue_thread, &method(:enqueue_thread_run))
381
+ end
382
+ end
383
+ end
384
+ @secondary.start if @secondary
385
+ end
386
+
387
+ def after_start
388
+ super
389
+ @secondary.after_start if @secondary
390
+ end
391
+
392
+ def stop
393
+ @secondary.stop if @secondary
394
+ @buffer.stop if @buffering && @buffer
395
+
396
+ super
397
+ end
398
+
399
+ def before_shutdown
400
+ @secondary.before_shutdown if @secondary
401
+
402
+ if @buffering && @buffer
403
+ if @flush_at_shutdown
404
+ force_flush
405
+ end
406
+ @buffer.before_shutdown
407
+ end
408
+
409
+ super
410
+ end
411
+
412
+ def shutdown
413
+ @secondary.shutdown if @secondary
414
+ @buffer.shutdown if @buffering && @buffer
415
+
416
+ super
417
+ end
418
+
419
+ def after_shutdown
420
+ try_rollback_all if @buffering && !@as_secondary # rollback regardless with @delayed_commit, because secondary may do it
421
+ @secondary.after_shutdown if @secondary
422
+
423
+ if @buffering && @buffer
424
+ @buffer.after_shutdown
425
+
426
+ @output_flush_threads_running = false
427
+ if @output_flush_threads && !@output_flush_threads.empty?
428
+ @output_flush_threads.each do |state|
429
+ state.thread.run if state.thread.alive? # to wakeup thread and make it to stop by itself
430
+ end
431
+ @output_flush_threads.each do |state|
432
+ state.thread.join
433
+ end
434
+ end
435
+ end
436
+
437
+ super
438
+ end
439
+
440
+ def close
441
+ @buffer.close if @buffering && @buffer
442
+ @secondary.close if @secondary
443
+
444
+ super
445
+ end
446
+
447
+ def terminate
448
+ @buffer.terminate if @buffering && @buffer
449
+ @secondary.terminate if @secondary
450
+
451
+ super
452
+ end
453
+
454
+ def support_in_v12_style?(feature)
455
+ # for plugins written in v0.12 styles
456
+ case feature
457
+ when :synchronous then false
458
+ when :buffered then false
459
+ when :delayed_commit then false
460
+ when :custom_format then false
461
+ else
462
+ raise ArgumentError, "unknown feature: #{feature}"
463
+ end
464
+ end
465
+
466
+ def implement?(feature)
467
+ methods_of_plugin = self.class.instance_methods(false)
468
+ case feature
469
+ when :synchronous then methods_of_plugin.include?(:process) || support_in_v12_style?(:synchronous)
470
+ when :buffered then methods_of_plugin.include?(:write) || support_in_v12_style?(:buffered)
471
+ when :delayed_commit then methods_of_plugin.include?(:try_write)
472
+ when :custom_format then methods_of_plugin.include?(:format) || support_in_v12_style?(:custom_format)
473
+ else
474
+ raise ArgumentError, "Unknown feature for output plugin: #{feature}"
475
+ end
476
+ end
477
+
478
+ # TODO: optimize this code
479
+ def extract_placeholders(str, metadata)
480
+ if metadata.timekey.nil? && metadata.tag.nil? && metadata.variables.nil?
481
+ str
482
+ else
483
+ rvalue = str
484
+ # strftime formatting
485
+ if @chunk_key_time # this section MUST be earlier than rest to use raw 'str'
486
+ @output_time_formatter_cache[str] ||= Fluent::Timezone.formatter(@buffer_config.timekey_zone, str)
487
+ rvalue = @output_time_formatter_cache[str].call(metadata.timekey)
488
+ end
489
+ # ${tag}, ${tag[0]}, ${tag[1]}, ...
490
+ if @chunk_key_tag
491
+ if str =~ /\$\{tag\[\d+\]\}/
492
+ hash = {'${tag}' => metadata.tag}
493
+ metadata.tag.split('.').each_with_index do |part, i|
494
+ hash["${tag[#{i}]}"] = part
495
+ end
496
+ rvalue = rvalue.gsub(/\$\{tag(\[\d+\])?\}/, hash)
497
+ elsif str.include?('${tag}')
498
+ rvalue = rvalue.gsub('${tag}', metadata.tag)
499
+ end
500
+ end
501
+ # ${a_chunk_key}, ...
502
+ if !@chunk_keys.empty? && metadata.variables
503
+ hash = {'${tag}' => '${tag}'} # not to erase this wrongly
504
+ @chunk_keys.each do |key|
505
+ hash["${#{key}}"] = metadata.variables[key.to_sym]
506
+ end
507
+ rvalue = rvalue.gsub(CHUNK_KEY_PLACEHOLDER_PATTERN, hash)
508
+ end
509
+ rvalue
510
+ end
511
+ end
512
+
513
+ def emit_events(tag, es)
514
+ # actually this method will be overwritten by #configure
515
+ if @buffering
516
+ emit_buffered(tag, es)
517
+ else
518
+ emit_sync(tag, es)
519
+ end
520
+ end
521
+
522
+ def emit_sync(tag, es)
523
+ @counters_monitor.synchronize{ @emit_count += 1 }
524
+ begin
525
+ process(tag, es)
526
+ @counters_monitor.synchronize{ @emit_records += es.size }
527
+ rescue
528
+ @counters_monitor.synchronize{ @num_errors += 1 }
529
+ raise
530
+ end
531
+ end
532
+
533
+ def emit_buffered(tag, es)
534
+ @counters_monitor.synchronize{ @emit_count += 1 }
535
+ begin
536
+ execute_chunking(tag, es, enqueue: (@flush_mode == :immediate))
537
+ if !@retry && @buffer.queued?
538
+ submit_flush_once
539
+ end
540
+ rescue
541
+ # TODO: separate number of errors into emit errors and write/flush errors
542
+ @counters_monitor.synchronize{ @num_errors += 1 }
543
+ raise
544
+ end
545
+ end
546
+
547
+ # TODO: optimize this code
548
+ def metadata(tag, time, record)
549
+ # this arguments are ordered in output plugin's rule
550
+ # Metadata 's argument order is different from this one (timekey, tag, variables)
551
+
552
+ raise ArgumentError, "tag must be a String: #{tag.class}" unless tag.nil? || tag.is_a?(String)
553
+ raise ArgumentError, "time must be a Fluent::EventTime (or Integer): #{time.class}" unless time.nil? || time.is_a?(Fluent::EventTime) || time.is_a?(Integer)
554
+ raise ArgumentError, "record must be a Hash: #{record.class}" unless record.nil? || record.is_a?(Hash)
555
+
556
+ if @chunk_keys.nil? && @chunk_key_time.nil? && @chunk_key_tag.nil?
557
+ # for tests
558
+ return Struct.new(:timekey, :tag, :variables).new
559
+ end
560
+
561
+ # timekey is int from epoch, and `timekey - timekey % 60` is assumed to mach with 0s of each minutes.
562
+ # it's wrong if timezone is configured as one which supports leap second, but it's very rare and
563
+ # we can ignore it (especially in production systems).
564
+ if @chunk_keys.empty?
565
+ if !@chunk_key_time && !@chunk_key_tag
566
+ @buffer.metadata()
567
+ elsif @chunk_key_time && @chunk_key_tag
568
+ time_int = time.to_i
569
+ timekey = (time_int - (time_int % @buffer_config.timekey)).to_i
570
+ @buffer.metadata(timekey: timekey, tag: tag)
571
+ elsif @chunk_key_time
572
+ time_int = time.to_i
573
+ timekey = (time_int - (time_int % @buffer_config.timekey)).to_i
574
+ @buffer.metadata(timekey: timekey)
575
+ else
576
+ @buffer.metadata(tag: tag)
577
+ end
578
+ else
579
+ timekey = if @chunk_key_time
580
+ time_int = time.to_i
581
+ (time_int - (time_int % @buffer_config.timekey)).to_i
582
+ else
583
+ nil
584
+ end
585
+ pairs = Hash[@chunk_keys.map{|k| [k.to_sym, record[k]]}]
586
+ @buffer.metadata(timekey: timekey, tag: (@chunk_key_tag ? tag : nil), variables: pairs)
587
+ end
588
+ end
589
+
590
+ def execute_chunking(tag, es, enqueue: false)
591
+ if @simple_chunking
592
+ handle_stream_simple(tag, es, enqueue: enqueue)
593
+ elsif @custom_format
594
+ handle_stream_with_custom_format(tag, es, enqueue: enqueue)
595
+ else
596
+ handle_stream_with_standard_format(tag, es, enqueue: enqueue)
597
+ end
598
+ end
599
+
600
+ def write_guard(&block)
601
+ begin
602
+ block.call
603
+ rescue Fluent::Plugin::Buffer::BufferOverflowError
604
+ log.warn "failed to write data into buffer by buffer overflow"
605
+ case @buffer_config.overflow_action
606
+ when :throw_exception
607
+ raise
608
+ when :block
609
+ log.debug "buffer.write is now blocking"
610
+ until @buffer.storable?
611
+ sleep 1
612
+ end
613
+ log.debug "retrying buffer.write after blocked operation"
614
+ retry
615
+ when :drop_oldest_chunk
616
+ begin
617
+ oldest = @buffer.dequeue_chunk
618
+ if oldest
619
+ log.warn "dropping oldest chunk to make space after buffer overflow", chunk_id: oldest.unique_id
620
+ @buffer.purge_chunk(oldest.unique_id)
621
+ else
622
+ log.error "no queued chunks to be dropped for drop_oldest_chunk"
623
+ end
624
+ rescue
625
+ # ignore any errors
626
+ end
627
+ raise unless @buffer.storable?
628
+ retry
629
+ else
630
+ raise "BUG: unknown overflow_action '#{@buffer_config.overflow_action}'"
631
+ end
632
+ end
633
+ end
634
+
635
+ FORMAT_MSGPACK_STREAM = ->(e){ e.to_msgpack_stream }
636
+ FORMAT_MSGPACK_STREAM_TIME_INT = ->(e){ e.to_msgpack_stream(time_int: true) }
637
+
638
+ # metadata_and_data is a Hash of:
639
+ # (standard format) metadata => event stream
640
+ # (custom format) metadata => array of formatted event
641
+ # For standard format, formatting should be done for whole event stream, but
642
+ # "whole event stream" may be a split of "es" here when it's bigger than chunk_limit_size.
643
+ # `@buffer.write` will do this splitting.
644
+ # For custom format, formatting will be done here. Custom formatting always requires
645
+ # iteration of event stream, and it should be done just once even if total event stream size
646
+ # is biggar than chunk_limit_size because of performance.
647
+ def handle_stream_with_custom_format(tag, es, enqueue: false)
648
+ meta_and_data = {}
649
+ records = 0
650
+ es.each do |time, record|
651
+ meta = metadata(tag, time, record)
652
+ meta_and_data[meta] ||= []
653
+ meta_and_data[meta] << format(tag, time, record)
654
+ records += 1
655
+ end
656
+ write_guard do
657
+ @buffer.write(meta_and_data, enqueue: enqueue)
658
+ end
659
+ @counters_monitor.synchronize{ @emit_records += records }
660
+ true
661
+ end
662
+
663
+ def handle_stream_with_standard_format(tag, es, enqueue: false)
664
+ format_proc = @time_as_integer ? FORMAT_MSGPACK_STREAM_TIME_INT : FORMAT_MSGPACK_STREAM
665
+ meta_and_data = {}
666
+ records = 0
667
+ es.each do |time, record|
668
+ meta = metadata(tag, time, record)
669
+ meta_and_data[meta] ||= MultiEventStream.new
670
+ meta_and_data[meta].add(time, record)
671
+ records += 1
672
+ end
673
+ write_guard do
674
+ @buffer.write(meta_and_data, format: format_proc, enqueue: enqueue)
675
+ end
676
+ @counters_monitor.synchronize{ @emit_records += records }
677
+ true
678
+ end
679
+
680
+ def handle_stream_simple(tag, es, enqueue: false)
681
+ format_proc = nil
682
+ meta = metadata((@chunk_key_tag ? tag : nil), nil, nil)
683
+ records = es.size
684
+ if @custom_format
685
+ records = 0
686
+ data = []
687
+ es.each do |time, record|
688
+ data << format(tag, time, record)
689
+ records += 1
690
+ end
691
+ else
692
+ format_proc = @time_as_integer ? FORMAT_MSGPACK_STREAM_TIME_INT : FORMAT_MSGPACK_STREAM
693
+ data = es
694
+ end
695
+ write_guard do
696
+ @buffer.write({meta => data}, format: format_proc, enqueue: enqueue)
697
+ end
698
+ @counters_monitor.synchronize{ @emit_records += records }
699
+ true
700
+ end
701
+
702
+ def commit_write(chunk_id, delayed: @delayed_commit, secondary: false)
703
+ log.trace "committing write operation to a chunk", chunk: dump_unique_id_hex(chunk_id), delayed: delayed
704
+ if delayed
705
+ @dequeued_chunks_mutex.synchronize do
706
+ @dequeued_chunks.delete_if{ |info| info.chunk_id == chunk_id }
707
+ end
708
+ end
709
+ @buffer.purge_chunk(chunk_id)
710
+
711
+ @retry_mutex.synchronize do
712
+ if @retry # success to flush chunks in retries
713
+ if secondary
714
+ log.warn "retry succeeded by secondary.", plugin_id: plugin_id, chunk_id: dump_unique_id_hex(chunk_id)
715
+ else
716
+ log.warn "retry succeeded.", plugin_id: plugin_id, chunk_id: dump_unique_id_hex(chunk_id)
717
+ end
718
+ @retry = nil
719
+ end
720
+ end
721
+ end
722
+
723
+ def rollback_write(chunk_id)
724
+ # This API is to rollback chunks explicitly from plugins.
725
+ # 3rd party plugins can depend it on automatic rollback of #try_rollback_write
726
+ @dequeued_chunks_mutex.synchronize do
727
+ @dequeued_chunks.delete_if{ |info| info.chunk_id == chunk_id }
728
+ end
729
+ # returns true if chunk was rollbacked as expected
730
+ # false if chunk was already flushed and couldn't be rollbacked unexpectedly
731
+ # in many cases, false can be just ignored
732
+ if @buffer.takeback_chunk(chunk_id)
733
+ @counters_monitor.synchronize{ @rollback_count += 1 }
734
+ true
735
+ else
736
+ false
737
+ end
738
+ end
739
+
740
+ def try_rollback_write
741
+ @dequeued_chunks_mutex.synchronize do
742
+ while @dequeued_chunks.first && @dequeued_chunks.first.expired?
743
+ info = @dequeued_chunks.shift
744
+ if @buffer.takeback_chunk(info.chunk_id)
745
+ @counters_monitor.synchronize{ @rollback_count += 1 }
746
+ log.warn "failed to flush the buffer chunk, timeout to commit.", plugin_id: plugin_id, chunk_id: dump_unique_id_hex(info.chunk_id), flushed_at: info.time
747
+ end
748
+ end
749
+ end
750
+ end
751
+
752
+ def try_rollback_all
753
+ return unless @dequeued_chunks
754
+ @dequeued_chunks_mutex.synchronize do
755
+ until @dequeued_chunks.empty?
756
+ info = @dequeued_chunks.shift
757
+ if @buffer.takeback_chunk(info.chunk_id)
758
+ @counters_monitor.synchronize{ @rollback_count += 1 }
759
+ log.info "delayed commit for buffer chunks was cancelled in shutdown", plugin_id: plugin_id, chunk_id: dump_unique_id_hex(info.chunk_id)
760
+ end
761
+ end
762
+ end
763
+ end
764
+
765
+ def next_flush_time
766
+ if @buffer.queued?
767
+ @retry_mutex.synchronize do
768
+ @retry ? @retry.next_time : Time.now + @buffer_config.flush_thread_burst_interval
769
+ end
770
+ else
771
+ Time.now + @buffer_config.flush_thread_interval
772
+ end
773
+ end
774
+
775
+ def try_flush
776
+ chunk = @buffer.dequeue_chunk
777
+ return unless chunk
778
+
779
+ log.debug "trying flush for a chunk", chunk: dump_unique_id_hex(chunk.unique_id)
780
+
781
+ output = self
782
+ using_secondary = false
783
+ if @retry_mutex.synchronize{ @retry && @retry.secondary? }
784
+ output = @secondary
785
+ using_secondary = true
786
+ end
787
+
788
+ unless @custom_format
789
+ chunk.extend ChunkMessagePackEventStreamer
790
+ end
791
+
792
+ begin
793
+ if output.delayed_commit
794
+ log.trace "executing delayed write and commit", chunk: dump_unique_id_hex(chunk.unique_id)
795
+ @counters_monitor.synchronize{ @write_count += 1 }
796
+ output.try_write(chunk)
797
+ @dequeued_chunks_mutex.synchronize do
798
+ # delayed_commit_timeout for secondary is configured in <buffer> of primary (<secondary> don't get <buffer>)
799
+ @dequeued_chunks << DequeuedChunkInfo.new(chunk.unique_id, Time.now, self.delayed_commit_timeout)
800
+ end
801
+ else # output plugin without delayed purge
802
+ chunk_id = chunk.unique_id
803
+ dump_chunk_id = dump_unique_id_hex(chunk_id)
804
+ log.trace "adding write count", instance: self.object_id
805
+ @counters_monitor.synchronize{ @write_count += 1 }
806
+ log.trace "executing sync write", chunk: dump_chunk_id
807
+ output.write(chunk)
808
+ log.trace "write operation done, committing", chunk: dump_chunk_id
809
+ commit_write(chunk_id, secondary: using_secondary)
810
+ log.trace "done to commit a chunk", chunk: dump_chunk_id
811
+ end
812
+ rescue => e
813
+ log.debug "taking back chunk for errors.", plugin_id: plugin_id, chunk: dump_unique_id_hex(chunk.unique_id)
814
+ @buffer.takeback_chunk(chunk.unique_id)
815
+
816
+ @retry_mutex.synchronize do
817
+ if @retry
818
+ @counters_monitor.synchronize{ @num_errors += 1 }
819
+ if @retry.limit?
820
+ records = @buffer.queued_records
821
+ log.error "failed to flush the buffer, and hit limit for retries. dropping all chunks in the buffer queue.", plugin_id: plugin_id, retry_times: @retry.steps, records: records, error: e
822
+ log.error_backtrace e.backtrace
823
+ @buffer.clear_queue!
824
+ log.debug "buffer queue cleared", plugin_id: plugin_id
825
+ @retry = nil
826
+ else
827
+ @retry.step
828
+ msg = if using_secondary
829
+ "failed to flush the buffer with secondary output."
830
+ else
831
+ "failed to flush the buffer."
832
+ end
833
+ log.warn msg, plugin_id: plugin_id, retry_time: @retry.steps, next_retry: @retry.next_time, chunk: dump_unique_id_hex(chunk.unique_id), error: e
834
+ log.warn_backtrace e.backtrace
835
+ end
836
+ else
837
+ @retry = retry_state(@buffer_config.retry_randomize)
838
+ @counters_monitor.synchronize{ @num_errors += 1 }
839
+ log.warn "failed to flush the buffer.", plugin_id: plugin_id, retry_time: @retry.steps, next_retry: @retry.next_time, chunk: dump_unique_id_hex(chunk.unique_id), error: e
840
+ log.warn_backtrace e.backtrace
841
+ end
842
+ end
843
+ end
844
+ end
845
+
846
+ def retry_state(randomize)
847
+ if @secondary
848
+ retry_state_create(
849
+ :output_retries, @buffer_config.retry_type, @buffer_config.retry_wait, @buffer_config.retry_timeout,
850
+ forever: @buffer_config.retry_forever, max_steps: @buffer_config.retry_max_times, backoff_base: @buffer_config.retry_exponential_backoff_base,
851
+ max_interval: @buffer_config.retry_max_interval,
852
+ secondary: true, secondary_threshold: @buffer_config.retry_secondary_threshold,
853
+ randomize: randomize
854
+ )
855
+ else
856
+ retry_state_create(
857
+ :output_retries, @buffer_config.retry_type, @buffer_config.retry_wait, @buffer_config.retry_timeout,
858
+ forever: @buffer_config.retry_forever, max_steps: @buffer_config.retry_max_times, backoff_base: @buffer_config.retry_exponential_backoff_base,
859
+ max_interval: @buffer_config.retry_max_interval,
860
+ randomize: randomize
861
+ )
862
+ end
863
+ end
864
+
865
+ def submit_flush_once
866
+ # Without locks: it is rough but enough to select "next" writer selection
867
+ @output_flush_thread_current_position = (@output_flush_thread_current_position + 1) % @buffer_config.flush_thread_count
868
+ state = @output_flush_threads[@output_flush_thread_current_position]
869
+ state.next_time = 0
870
+ state.thread.run
871
+ end
872
+
873
+ def force_flush
874
+ if @buffering
875
+ @buffer.enqueue_all
876
+ submit_flush_all
877
+ end
878
+ end
879
+
880
+ def submit_flush_all
881
+ while !@retry && @buffer.queued?
882
+ submit_flush_once
883
+ sleep @buffer_config.flush_thread_burst_interval
884
+ end
885
+ end
886
+
887
+ # only for tests of output plugin
888
+ def interrupt_flushes
889
+ @output_flush_interrupted = true
890
+ end
891
+
892
+ # only for tests of output plugin
893
+ def enqueue_thread_wait
894
+ @output_enqueue_thread_mutex.synchronize do
895
+ @output_flush_interrupted = false
896
+ @output_enqueue_thread_waiting = true
897
+ end
898
+ require 'timeout'
899
+ Timeout.timeout(10) do
900
+ Thread.pass while @output_enqueue_thread_waiting
901
+ end
902
+ end
903
+
904
+ # only for tests of output plugin
905
+ def flush_thread_wakeup
906
+ @output_flush_threads.each do |state|
907
+ state.next_time = 0
908
+ state.thread.run
909
+ end
910
+ end
911
+
912
+ def enqueue_thread_run
913
+ value_for_interval = nil
914
+ if @flush_mode == :interval
915
+ value_for_interval = @buffer_config.flush_interval
916
+ end
917
+ if @chunk_key_time
918
+ if !value_for_interval || @buffer_config.timekey < value_for_interval
919
+ value_for_interval = @buffer_config.timekey
920
+ end
921
+ end
922
+ unless value_for_interval
923
+ raise "BUG: both of flush_interval and timekey are disabled"
924
+ end
925
+ interval = value_for_interval / 11.0
926
+ if interval < @buffer_config.flush_thread_interval
927
+ interval = @buffer_config.flush_thread_interval
928
+ end
929
+
930
+ while !self.after_started? && !self.stopped?
931
+ sleep 0.5
932
+ end
933
+ log.debug "enqueue_thread actually running"
934
+
935
+ begin
936
+ while @output_flush_threads_running
937
+ now_int = Time.now.to_i
938
+ if @output_flush_interrupted
939
+ sleep interval
940
+ next
941
+ end
942
+
943
+ @output_enqueue_thread_mutex.lock
944
+ begin
945
+ if @flush_mode == :interval
946
+ flush_interval = @buffer_config.flush_interval.to_i
947
+ # This block should be done by integer values.
948
+ # If both of flush_interval & flush_thread_interval are 1s, expected actual flush timing is 1.5s.
949
+ # If we use integered values for this comparison, expected actual flush timing is 1.0s.
950
+ @buffer.enqueue_all{ |metadata, chunk| chunk.created_at.to_i + flush_interval <= now_int }
951
+ end
952
+
953
+ if @chunk_key_time
954
+ timekey_unit = @buffer_config.timekey
955
+ timekey_wait = @buffer_config.timekey_wait
956
+ current_timekey = now_int - now_int % timekey_unit
957
+ @buffer.enqueue_all{ |metadata, chunk| metadata.timekey < current_timekey && metadata.timekey + timekey_unit + timekey_wait <= now_int }
958
+ end
959
+ rescue => e
960
+ log.error "unexpected error while checking flushed chunks. ignored.", plugin_id: plugin_id, error_class: e.class, error: e
961
+ log.error_backtrace
962
+ end
963
+ @output_enqueue_thread_waiting = false
964
+ @output_enqueue_thread_mutex.unlock
965
+ sleep interval
966
+ end
967
+ rescue => e
968
+ # normal errors are rescued by inner begin-rescue clause.
969
+ log.error "error on enqueue thread", plugin_id: plugin_id, error_class: e.class, error: e
970
+ log.error_backtrace
971
+ raise
972
+ end
973
+ end
974
+
975
+ def flush_thread_run(state)
976
+ flush_thread_interval = @buffer_config.flush_thread_interval
977
+
978
+ # If the given clock_id is not supported, Errno::EINVAL is raised.
979
+ clock_id = Process::CLOCK_MONOTONIC rescue Process::CLOCK_MONOTONIC_RAW
980
+ state.next_time = Process.clock_gettime(clock_id) + flush_thread_interval
981
+
982
+ while !self.after_started? && !self.stopped?
983
+ sleep 0.5
984
+ end
985
+ log.debug "flush_thread actually running"
986
+
987
+ begin
988
+ # This thread don't use `thread_current_running?` because this thread should run in `before_shutdown` phase
989
+ while @output_flush_threads_running
990
+ time = Process.clock_gettime(clock_id)
991
+ interval = state.next_time - time
992
+
993
+ if state.next_time <= time
994
+ try_flush
995
+ # next_flush_interval uses flush_thread_interval or flush_thread_burst_interval (or retrying)
996
+ interval = next_flush_time.to_f - Time.now.to_f
997
+ # TODO: if secondary && delayed-commit, next_flush_time will be much longer than expected (because @retry still exists)
998
+ # @retry should be cleard if delayed commit is enabled? Or any other solution?
999
+ state.next_time = Process.clock_gettime(clock_id) + interval
1000
+ end
1001
+
1002
+ if @dequeued_chunks_mutex.synchronize{ !@dequeued_chunks.empty? && @dequeued_chunks.first.expired? }
1003
+ unless @output_flush_interrupted
1004
+ try_rollback_write
1005
+ end
1006
+ end
1007
+
1008
+ sleep interval if interval > 0
1009
+ end
1010
+ rescue => e
1011
+ # normal errors are rescued by output plugins in #try_flush
1012
+ # so this rescue section is for critical & unrecoverable errors
1013
+ log.error "error on output thread", plugin_id: plugin_id, error_class: e.class, error: e
1014
+ log.error_backtrace
1015
+ raise
1016
+ end
1017
+ end
1018
+ end
1019
+ end
1020
+ end