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,167 @@
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 'fileutils'
18
+ require 'zlib'
19
+
20
+ require 'fluent/output'
21
+ require 'fluent/config/error'
22
+ require 'fluent/system_config'
23
+
24
+ module Fluent
25
+ class FileOutput < TimeSlicedOutput
26
+ include SystemConfig::Mixin
27
+
28
+ Plugin.register_output('file', self)
29
+
30
+ SUPPORTED_COMPRESS = {
31
+ 'gz' => :gz,
32
+ 'gzip' => :gz,
33
+ }
34
+
35
+ FILE_PERMISSION = 0644
36
+ DIR_PERMISSION = 0755
37
+
38
+ desc "The Path of the file."
39
+ config_param :path, :string
40
+ desc "The format of the file content. The default is out_file."
41
+ config_param :format, :string, default: 'out_file', skip_accessor: true
42
+ desc "The flushed chunk is appended to existence file or not."
43
+ config_param :append, :bool, default: false
44
+ desc "Compress flushed file."
45
+ config_param :compress, default: nil do |val|
46
+ c = SUPPORTED_COMPRESS[val]
47
+ unless c
48
+ raise ConfigError, "Unsupported compression algorithm '#{val}'"
49
+ end
50
+ c
51
+ end
52
+ desc "Create symlink to temporary buffered file when buffer_type is file."
53
+ config_param :symlink_path, :string, default: nil
54
+
55
+ module SymlinkBufferMixin
56
+ def symlink_path=(path)
57
+ @_symlink_path = path
58
+ end
59
+
60
+ def generate_chunk(metadata)
61
+ chunk = super
62
+ latest_chunk = metadata_list.sort_by(&:timekey).last
63
+ if chunk.metadata == latest_chunk
64
+ FileUtils.ln_sf(chunk.path, @_symlink_path)
65
+ end
66
+ chunk
67
+ end
68
+ end
69
+
70
+ def initialize
71
+ require 'zlib'
72
+ require 'time'
73
+ require 'fluent/plugin/file_util'
74
+ super
75
+ end
76
+
77
+ def configure(conf)
78
+ if path = conf['path']
79
+ @path = path
80
+ end
81
+ unless @path
82
+ raise ConfigError, "'path' parameter is required on file output"
83
+ end
84
+
85
+ if pos = @path.index('*')
86
+ @path_prefix = @path[0,pos]
87
+ @path_suffix = @path[pos+1..-1]
88
+ conf['buffer_path'] ||= "#{@path}"
89
+ else
90
+ @path_prefix = @path+"."
91
+ @path_suffix = ".log"
92
+ conf['buffer_path'] ||= "#{@path}.*"
93
+ end
94
+
95
+ test_path = generate_path(Time.now.strftime(@time_slice_format))
96
+ unless ::Fluent::FileUtil.writable_p?(test_path)
97
+ raise ConfigError, "out_file: `#{test_path}` is not writable"
98
+ end
99
+
100
+ super
101
+
102
+ @formatter = Plugin.new_formatter(@format)
103
+ @formatter.configure(conf)
104
+
105
+ if @symlink_path && @buffer.respond_to?(:path)
106
+ @buffer.extend SymlinkBufferMixin
107
+ @buffer.symlink_path = @symlink_path
108
+ end
109
+
110
+ @dir_perm = system_config.dir_permission || DIR_PERMISSION
111
+ @file_perm = system_config.file_permission || FILE_PERMISSION
112
+ end
113
+
114
+ def format(tag, time, record)
115
+ @formatter.format(tag, time, record)
116
+ end
117
+
118
+ def write(chunk)
119
+ path = generate_path(chunk.key)
120
+ FileUtils.mkdir_p File.dirname(path), mode: @dir_perm
121
+
122
+ case @compress
123
+ when nil
124
+ File.open(path, "ab", @file_perm) {|f|
125
+ chunk.write_to(f)
126
+ }
127
+ when :gz
128
+ File.open(path, "ab", @file_perm) {|f|
129
+ gz = Zlib::GzipWriter.new(f)
130
+ chunk.write_to(gz)
131
+ gz.close
132
+ }
133
+ end
134
+
135
+ return path # for test
136
+ end
137
+
138
+ def secondary_init(primary)
139
+ # don't warn even if primary.class is not FileOutput
140
+ end
141
+
142
+ private
143
+
144
+ def suffix
145
+ case @compress
146
+ when nil
147
+ ''
148
+ when :gz
149
+ ".gz"
150
+ end
151
+ end
152
+
153
+ def generate_path(time_string)
154
+ if @append
155
+ "#{@path_prefix}#{time_string}#{@path_suffix}#{suffix}"
156
+ else
157
+ path = nil
158
+ i = 0
159
+ begin
160
+ path = "#{@path_prefix}#{time_string}_#{i}#{@path_suffix}#{suffix}"
161
+ i += 1
162
+ end while File.exist?(path)
163
+ path
164
+ end
165
+ end
166
+ end
167
+ end
@@ -0,0 +1,646 @@
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 'base64'
18
+ require 'socket'
19
+ require 'fileutils'
20
+
21
+ require 'cool.io'
22
+
23
+ require 'fluent/output'
24
+ require 'fluent/config/error'
25
+
26
+ module Fluent
27
+ class ForwardOutputError < StandardError
28
+ end
29
+
30
+ class ForwardOutputResponseError < ForwardOutputError
31
+ end
32
+
33
+ class ForwardOutputConnectionClosedError < ForwardOutputError
34
+ end
35
+
36
+ class ForwardOutputACKTimeoutError < ForwardOutputResponseError
37
+ end
38
+
39
+ class ForwardOutput < ObjectBufferedOutput
40
+ Plugin.register_output('forward', self)
41
+
42
+ LISTEN_PORT = 24224
43
+
44
+ def initialize
45
+ super
46
+ require 'fluent/plugin/socket_util'
47
+ @nodes = [] #=> [Node]
48
+ @loop = nil
49
+ @thread = nil
50
+ @finished = false
51
+ end
52
+
53
+ desc 'The timeout time when sending event logs.'
54
+ config_param :send_timeout, :time, default: 60
55
+ desc 'The transport protocol to use for heartbeats.(udp,tcp,none)'
56
+ config_param :heartbeat_type, default: :tcp do |val|
57
+ case val.downcase
58
+ when 'tcp'
59
+ :tcp
60
+ when 'udp'
61
+ :udp
62
+ when 'none'
63
+ :none
64
+ else
65
+ raise ConfigError, "forward output heartbeat type should be 'tcp', 'udp', or 'none'"
66
+ end
67
+ end
68
+ desc 'The interval of the heartbeat packer.'
69
+ config_param :heartbeat_interval, :time, default: 1
70
+ desc 'The wait time before accepting a server fault recovery.'
71
+ config_param :recover_wait, :time, default: 10
72
+ desc 'The hard timeout used to detect server failure.'
73
+ config_param :hard_timeout, :time, default: 60
74
+ desc 'Set TTL to expire DNS cache in seconds.'
75
+ config_param :expire_dns_cache, :time, default: nil # 0 means disable cache
76
+ desc 'The threshold parameter used to detect server faults.'
77
+ config_param :phi_threshold, :integer, default: 16
78
+ desc 'Use the "Phi accrual failure detector" to detect server failure.'
79
+ config_param :phi_failure_detector, :bool, default: true
80
+
81
+ desc 'Change the protocol to at-least-once.'
82
+ config_param :require_ack_response, :bool, default: false # require in_forward to respond with ack
83
+ desc 'This option is used when require_ack_response is true.'
84
+ config_param :ack_response_timeout, :time, default: 190 # 0 means do not wait for ack responses
85
+ # Linux default tcp_syn_retries is 5 (in many environment)
86
+ # 3 + 6 + 12 + 24 + 48 + 96 -> 189 (sec)
87
+ desc 'Enable client-side DNS round robin.'
88
+ config_param :dns_round_robin, :bool, default: false # heartbeat_type 'udp' is not available for this
89
+
90
+ attr_reader :nodes
91
+
92
+ config_param :port, :integer, default: LISTEN_PORT, obsoleted: "User <server> section instead."
93
+ config_param :host, :string, default: nil, obsoleted: "Use <server> section instead."
94
+
95
+ def configure(conf)
96
+ super
97
+
98
+ recover_sample_size = @recover_wait / @heartbeat_interval
99
+
100
+ if @dns_round_robin
101
+ if @heartbeat_type == :udp
102
+ raise ConfigError, "forward output heartbeat type must be 'tcp' or 'none' to use dns_round_robin option"
103
+ end
104
+ end
105
+
106
+ conf.elements.each {|e|
107
+ next if e.name != "server"
108
+
109
+ host = e['host']
110
+ port = e['port']
111
+ port = port ? port.to_i : LISTEN_PORT
112
+
113
+ weight = e['weight']
114
+ weight = weight ? weight.to_i : 60
115
+
116
+ standby = !!e['standby']
117
+
118
+ name = e['name']
119
+ unless name
120
+ name = "#{host}:#{port}"
121
+ end
122
+
123
+ failure = FailureDetector.new(@heartbeat_interval, @hard_timeout, Time.now.to_i.to_f)
124
+
125
+ node_conf = NodeConfig.new(name, host, port, weight, standby, failure,
126
+ @phi_threshold, recover_sample_size, @expire_dns_cache, @phi_failure_detector, @dns_round_robin)
127
+
128
+ if @heartbeat_type == :none
129
+ @nodes << NoneHeartbeatNode.new(log, node_conf)
130
+ else
131
+ @nodes << Node.new(log, node_conf)
132
+ end
133
+ log.info "adding forwarding server '#{name}'", host: host, port: port, weight: weight, plugin_id: plugin_id
134
+ }
135
+
136
+ if @nodes.empty?
137
+ raise ConfigError, "forward output plugin requires at least one <server> is required"
138
+ end
139
+ end
140
+
141
+ def start
142
+ super
143
+
144
+ @rand_seed = Random.new.seed
145
+ rebuild_weight_array
146
+ @rr = 0
147
+
148
+ unless @heartbeat_type == :none
149
+ @loop = Coolio::Loop.new
150
+
151
+ if @heartbeat_type == :udp
152
+ # assuming all hosts use udp
153
+ @usock = SocketUtil.create_udp_socket(@nodes.first.host)
154
+ @usock.fcntl(Fcntl::F_SETFL, Fcntl::O_NONBLOCK)
155
+ @hb = HeartbeatHandler.new(@usock, method(:on_heartbeat))
156
+ @loop.attach(@hb)
157
+ end
158
+
159
+ @timer = HeartbeatRequestTimer.new(@heartbeat_interval, method(:on_timer))
160
+ @loop.attach(@timer)
161
+
162
+ @thread = Thread.new(&method(:run))
163
+ end
164
+ end
165
+
166
+ def shutdown
167
+ @finished = true
168
+ if @loop
169
+ @loop.watchers.each {|w| w.detach }
170
+ @loop.stop
171
+ end
172
+ @thread.join if @thread
173
+ @usock.close if @usock
174
+
175
+ super
176
+ end
177
+
178
+ def run
179
+ @loop.run if @loop
180
+ rescue
181
+ log.error "unexpected error", error: $!.to_s
182
+ log.error_backtrace
183
+ end
184
+
185
+ def write_objects(tag, chunk)
186
+ return if chunk.empty?
187
+
188
+ error = nil
189
+
190
+ wlen = @weight_array.length
191
+ wlen.times do
192
+ @rr = (@rr + 1) % wlen
193
+ node = @weight_array[@rr]
194
+
195
+ if node.available?
196
+ begin
197
+ send_data(node, tag, chunk)
198
+ return
199
+ rescue
200
+ # for load balancing during detecting crashed servers
201
+ error = $! # use the latest error
202
+ end
203
+ end
204
+ end
205
+
206
+ if error
207
+ raise error
208
+ else
209
+ raise "no nodes are available" # TODO message
210
+ end
211
+ end
212
+
213
+ private
214
+
215
+ def rebuild_weight_array
216
+ standby_nodes, regular_nodes = @nodes.partition {|n|
217
+ n.standby?
218
+ }
219
+
220
+ lost_weight = 0
221
+ regular_nodes.each {|n|
222
+ unless n.available?
223
+ lost_weight += n.weight
224
+ end
225
+ }
226
+ log.debug "rebuilding weight array", lost_weight: lost_weight
227
+
228
+ if lost_weight > 0
229
+ standby_nodes.each {|n|
230
+ if n.available?
231
+ regular_nodes << n
232
+ log.warn "using standby node #{n.host}:#{n.port}", weight: n.weight
233
+ lost_weight -= n.weight
234
+ break if lost_weight <= 0
235
+ end
236
+ }
237
+ end
238
+
239
+ weight_array = []
240
+ gcd = regular_nodes.map {|n| n.weight }.inject(0) {|r,w| r.gcd(w) }
241
+ regular_nodes.each {|n|
242
+ (n.weight / gcd).times {
243
+ weight_array << n
244
+ }
245
+ }
246
+
247
+ # for load balancing during detecting crashed servers
248
+ coe = (regular_nodes.size * 6) / weight_array.size
249
+ weight_array *= coe if coe > 1
250
+
251
+ r = Random.new(@rand_seed)
252
+ weight_array.sort_by! { r.rand }
253
+
254
+ @weight_array = weight_array
255
+ end
256
+
257
+ # MessagePack FixArray length is 3
258
+ FORWARD_HEADER = [0x93].pack('C').freeze
259
+ def forward_header
260
+ FORWARD_HEADER
261
+ end
262
+
263
+ #FORWARD_TCP_HEARTBEAT_DATA = FORWARD_HEADER + ''.to_msgpack + [].to_msgpack
264
+ def send_heartbeat_tcp(node)
265
+ sock = connect(node)
266
+ begin
267
+ opt = [1, @send_timeout.to_i].pack('I!I!') # { int l_onoff; int l_linger; }
268
+ sock.setsockopt(Socket::SOL_SOCKET, Socket::SO_LINGER, opt)
269
+ opt = [@send_timeout.to_i, 0].pack('L!L!') # struct timeval
270
+ # don't send any data to not cause a compatibility problem
271
+ #sock.setsockopt(Socket::SOL_SOCKET, Socket::SO_SNDTIMEO, opt)
272
+ #sock.write FORWARD_TCP_HEARTBEAT_DATA
273
+ node.heartbeat(true)
274
+ ensure
275
+ sock.close_write
276
+ sock.close
277
+ end
278
+ end
279
+
280
+ def send_data(node, tag, chunk)
281
+ sock = connect(node)
282
+ begin
283
+ opt = [1, @send_timeout.to_i].pack('I!I!') # { int l_onoff; int l_linger; }
284
+ sock.setsockopt(Socket::SOL_SOCKET, Socket::SO_LINGER, opt)
285
+
286
+ opt = [@send_timeout.to_i, 0].pack('L!L!') # struct timeval
287
+ sock.setsockopt(Socket::SOL_SOCKET, Socket::SO_SNDTIMEO, opt)
288
+
289
+ # beginArray(3)
290
+ sock.write forward_header
291
+
292
+ # writeRaw(tag)
293
+ sock.write tag.to_msgpack # tag
294
+
295
+ # beginRaw(size)
296
+ sz = chunk.size
297
+ #if sz < 32
298
+ # # FixRaw
299
+ # sock.write [0xa0 | sz].pack('C')
300
+ #elsif sz < 65536
301
+ # # raw 16
302
+ # sock.write [0xda, sz].pack('Cn')
303
+ #else
304
+ # raw 32
305
+ sock.write [0xdb, sz].pack('CN')
306
+ #end
307
+
308
+ # writeRawBody(packed_es)
309
+ chunk.write_to(sock)
310
+
311
+ option = { 'size' => chunk.size_of_events }
312
+ option['chunk'] = Base64.encode64(chunk.unique_id) if @require_ack_response
313
+ sock.write option.to_msgpack
314
+
315
+ if @require_ack_response && @ack_response_timeout > 0
316
+ # Waiting for a response here results in a decrease of throughput because a chunk queue is locked.
317
+ # To avoid a decrease of troughput, it is necessary to prepare a list of chunks that wait for responses
318
+ # and process them asynchronously.
319
+ if IO.select([sock], nil, nil, @ack_response_timeout)
320
+ raw_data = sock.recv(1024)
321
+
322
+ # When connection is closed by remote host, socket is ready to read and #recv returns an empty string that means EOF.
323
+ # If this happens we assume the data wasn't delivered and retry it.
324
+ if raw_data.empty?
325
+ @log.warn "node #{node.host}:#{node.port} closed the connection. regard it as unavailable."
326
+ node.disable!
327
+ raise ForwardOutputConnectionClosedError, "node #{node.host}:#{node.port} closed connection"
328
+ else
329
+ # Serialization type of the response is same as sent data.
330
+ res = MessagePack.unpack(raw_data)
331
+
332
+ if res['ack'] != option['chunk']
333
+ # Some errors may have occured when ack and chunk id is different, so send the chunk again.
334
+ raise ForwardOutputResponseError, "ack in response and chunk id in sent data are different"
335
+ end
336
+ end
337
+
338
+ else
339
+ # IO.select returns nil on timeout.
340
+ # There are 2 types of cases when no response has been received:
341
+ # (1) the node does not support sending responses
342
+ # (2) the node does support sending response but responses have not arrived for some reasons.
343
+ @log.warn "no response from #{node.host}:#{node.port}. regard it as unavailable."
344
+ node.disable!
345
+ raise ForwardOutputACKTimeoutError, "node #{node.host}:#{node.port} does not return ACK"
346
+ end
347
+ end
348
+
349
+ node.heartbeat(false)
350
+ res # for test
351
+ ensure
352
+ sock.close_write
353
+ sock.close
354
+ end
355
+ end
356
+
357
+ def connect(node)
358
+ # TODO unix socket?
359
+ TCPSocket.new(node.resolved_host, node.port)
360
+ end
361
+
362
+ class HeartbeatRequestTimer < Coolio::TimerWatcher
363
+ def initialize(interval, callback)
364
+ super(interval, true)
365
+ @callback = callback
366
+ end
367
+
368
+ def on_timer
369
+ @callback.call
370
+ rescue
371
+ # TODO log?
372
+ end
373
+ end
374
+
375
+ def on_timer
376
+ return if @finished
377
+ @nodes.each {|n|
378
+ if n.tick
379
+ rebuild_weight_array
380
+ end
381
+ begin
382
+ #log.trace "sending heartbeat #{n.host}:#{n.port} on #{@heartbeat_type}"
383
+ if @heartbeat_type == :tcp
384
+ send_heartbeat_tcp(n)
385
+ else
386
+ @usock.send "\0", 0, Socket.pack_sockaddr_in(n.port, n.resolved_host)
387
+ end
388
+ rescue Errno::EAGAIN, Errno::EWOULDBLOCK, Errno::EINTR, Errno::ECONNREFUSED
389
+ # TODO log
390
+ log.debug "failed to send heartbeat packet to #{n.host}:#{n.port}", error: $!.to_s
391
+ end
392
+ }
393
+ end
394
+
395
+ class HeartbeatHandler < Coolio::IO
396
+ def initialize(io, callback)
397
+ super(io)
398
+ @io = io
399
+ @callback = callback
400
+ end
401
+
402
+ def on_readable
403
+ begin
404
+ msg, addr = @io.recvfrom(1024)
405
+ rescue Errno::EAGAIN, Errno::EWOULDBLOCK, Errno::EINTR
406
+ return
407
+ end
408
+ host = addr[3]
409
+ port = addr[1]
410
+ sockaddr = Socket.pack_sockaddr_in(port, host)
411
+ @callback.call(sockaddr, msg)
412
+ rescue
413
+ # TODO log?
414
+ end
415
+ end
416
+
417
+ def on_heartbeat(sockaddr, msg)
418
+ if node = @nodes.find {|n| n.sockaddr == sockaddr }
419
+ #log.trace "heartbeat from '#{node.name}'", :host=>node.host, :port=>node.port
420
+ if node.heartbeat
421
+ rebuild_weight_array
422
+ end
423
+ end
424
+ end
425
+
426
+ NodeConfig = Struct.new("NodeConfig", :name, :host, :port, :weight, :standby, :failure,
427
+ :phi_threshold, :recover_sample_size, :expire_dns_cache, :phi_failure_detector, :dns_round_robin)
428
+
429
+ class Node
430
+ def initialize(log, conf)
431
+ @log = log
432
+ @conf = conf
433
+ @name = @conf.name
434
+ @host = @conf.host
435
+ @port = @conf.port
436
+ @weight = @conf.weight
437
+ @failure = @conf.failure
438
+ @available = true
439
+
440
+ @resolved_host = nil
441
+ @resolved_time = 0
442
+ resolved_host # check dns
443
+ end
444
+
445
+ attr_reader :conf
446
+ attr_reader :name, :host, :port, :weight
447
+ attr_reader :sockaddr # used by on_heartbeat
448
+ attr_reader :failure, :available # for test
449
+
450
+ def available?
451
+ @available
452
+ end
453
+
454
+ def disable!
455
+ @available = false
456
+ end
457
+
458
+ def standby?
459
+ @conf.standby
460
+ end
461
+
462
+ def resolved_host
463
+ case @conf.expire_dns_cache
464
+ when 0
465
+ # cache is disabled
466
+ return resolve_dns!
467
+
468
+ when nil
469
+ # persistent cache
470
+ return @resolved_host ||= resolve_dns!
471
+
472
+ else
473
+ now = Engine.now
474
+ rh = @resolved_host
475
+ if !rh || now - @resolved_time >= @conf.expire_dns_cache
476
+ rh = @resolved_host = resolve_dns!
477
+ @resolved_time = now
478
+ end
479
+ return rh
480
+ end
481
+ end
482
+
483
+ def resolve_dns!
484
+ addrinfo_list = Socket.getaddrinfo(@host, @port, nil, Socket::SOCK_STREAM)
485
+ addrinfo = @conf.dns_round_robin ? addrinfo_list.sample : addrinfo_list.first
486
+ @sockaddr = Socket.pack_sockaddr_in(addrinfo[1], addrinfo[3]) # used by on_heartbeat
487
+ addrinfo[3]
488
+ end
489
+ private :resolve_dns!
490
+
491
+ def tick
492
+ now = Time.now.to_f
493
+ if !@available
494
+ if @failure.hard_timeout?(now)
495
+ @failure.clear
496
+ end
497
+ return nil
498
+ end
499
+
500
+ if @failure.hard_timeout?(now)
501
+ @log.warn "detached forwarding server '#{@name}'", host: @host, port: @port, hard_timeout: true
502
+ @available = false
503
+ @resolved_host = nil # expire cached host
504
+ @failure.clear
505
+ return true
506
+ end
507
+
508
+ if @conf.phi_failure_detector
509
+ phi = @failure.phi(now)
510
+ #$log.trace "phi '#{@name}'", :host=>@host, :port=>@port, :phi=>phi
511
+ if phi > @conf.phi_threshold
512
+ @log.warn "detached forwarding server '#{@name}'", host: @host, port: @port, phi: phi
513
+ @available = false
514
+ @resolved_host = nil # expire cached host
515
+ @failure.clear
516
+ return true
517
+ end
518
+ end
519
+ return false
520
+ end
521
+
522
+ def heartbeat(detect=true)
523
+ now = Time.now.to_f
524
+ @failure.add(now)
525
+ #@log.trace "heartbeat from '#{@name}'", :host=>@host, :port=>@port, :available=>@available, :sample_size=>@failure.sample_size
526
+ if detect && !@available && @failure.sample_size > @conf.recover_sample_size
527
+ @available = true
528
+ @log.warn "recovered forwarding server '#{@name}'", host: @host, port: @port
529
+ return true
530
+ else
531
+ return nil
532
+ end
533
+ end
534
+
535
+ def to_msgpack(out = '')
536
+ [@host, @port, @weight, @available].to_msgpack(out)
537
+ end
538
+ end
539
+
540
+ # Override Node to disable heartbeat
541
+ class NoneHeartbeatNode < Node
542
+ def available?
543
+ true
544
+ end
545
+
546
+ def tick
547
+ false
548
+ end
549
+
550
+ def heartbeat(detect=true)
551
+ true
552
+ end
553
+ end
554
+
555
+ class FailureDetector
556
+ PHI_FACTOR = 1.0 / Math.log(10.0)
557
+ SAMPLE_SIZE = 1000
558
+
559
+ def initialize(heartbeat_interval, hard_timeout, init_last)
560
+ @heartbeat_interval = heartbeat_interval
561
+ @last = init_last
562
+ @hard_timeout = hard_timeout
563
+
564
+ # microsec
565
+ @init_gap = (heartbeat_interval * 1e6).to_i
566
+ @window = [@init_gap]
567
+ end
568
+
569
+ def hard_timeout?(now)
570
+ now - @last > @hard_timeout
571
+ end
572
+
573
+ def add(now)
574
+ if @window.empty?
575
+ @window << @init_gap
576
+ @last = now
577
+ else
578
+ gap = now - @last
579
+ @window << (gap * 1e6).to_i
580
+ @window.shift if @window.length > SAMPLE_SIZE
581
+ @last = now
582
+ end
583
+ end
584
+
585
+ def phi(now)
586
+ size = @window.size
587
+ return 0.0 if size == 0
588
+
589
+ # Calculate weighted moving average
590
+ mean_usec = 0
591
+ fact = 0
592
+ @window.each_with_index {|gap,i|
593
+ mean_usec += gap * (1+i)
594
+ fact += (1+i)
595
+ }
596
+ mean_usec = mean_usec / fact
597
+
598
+ # Normalize arrive intervals into 1sec
599
+ mean = (mean_usec.to_f / 1e6) - @heartbeat_interval + 1
600
+
601
+ # Calculate phi of the phi accrual failure detector
602
+ t = now - @last - @heartbeat_interval + 1
603
+ phi = PHI_FACTOR * t / mean
604
+
605
+ return phi
606
+ end
607
+
608
+ def sample_size
609
+ @window.size
610
+ end
611
+
612
+ def clear
613
+ @window.clear
614
+ @last = 0
615
+ end
616
+ end
617
+
618
+ ## TODO
619
+ #class RPC
620
+ # def initialize(this)
621
+ # @this = this
622
+ # end
623
+ #
624
+ # def list_nodes
625
+ # @this.nodes
626
+ # end
627
+ #
628
+ # def list_fault_nodes
629
+ # list_nodes.select {|n| !n.available? }
630
+ # end
631
+ #
632
+ # def list_available_nodes
633
+ # list_nodes.select {|n| n.available? }
634
+ # end
635
+ #
636
+ # def add_node(name, host, port, weight)
637
+ # end
638
+ #
639
+ # def recover_node(host, port)
640
+ # end
641
+ #
642
+ # def remove_node(host, port)
643
+ # end
644
+ #end
645
+ end
646
+ end