fluentd-hubspot 0.14.14.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (396) hide show
  1. data/.github/ISSUE_TEMPLATE.md +6 -0
  2. data/.gitignore +28 -0
  3. data/.travis.yml +51 -0
  4. data/AUTHORS +2 -0
  5. data/CONTRIBUTING.md +42 -0
  6. data/COPYING +14 -0
  7. data/ChangeLog +593 -0
  8. data/Gemfile +9 -0
  9. data/README.md +76 -0
  10. data/Rakefile +74 -0
  11. data/Vagrantfile +17 -0
  12. data/appveyor.yml +43 -0
  13. data/bin/fluent-binlog-reader +7 -0
  14. data/bin/fluent-debug +5 -0
  15. data/bin/fluent-plugin-config-format +5 -0
  16. data/bin/fluent-plugin-generate +5 -0
  17. data/code-of-conduct.md +3 -0
  18. data/example/copy_roundrobin.conf +39 -0
  19. data/example/filter_stdout.conf +22 -0
  20. data/example/in_dummy_blocks.conf +17 -0
  21. data/example/in_dummy_with_compression.conf +23 -0
  22. data/example/in_forward.conf +14 -0
  23. data/example/in_forward_client.conf +37 -0
  24. data/example/in_forward_shared_key.conf +15 -0
  25. data/example/in_forward_tls.conf +14 -0
  26. data/example/in_forward_users.conf +24 -0
  27. data/example/in_forward_workers.conf +21 -0
  28. data/example/in_http.conf +14 -0
  29. data/example/in_out_forward.conf +17 -0
  30. data/example/in_syslog.conf +15 -0
  31. data/example/in_tail.conf +14 -0
  32. data/example/in_tcp.conf +13 -0
  33. data/example/in_udp.conf +13 -0
  34. data/example/logevents.conf +25 -0
  35. data/example/multi_filters.conf +61 -0
  36. data/example/out_copy.conf +20 -0
  37. data/example/out_exec_filter.conf +42 -0
  38. data/example/out_file.conf +13 -0
  39. data/example/out_forward.conf +35 -0
  40. data/example/out_forward_buf_file.conf +23 -0
  41. data/example/out_forward_client.conf +109 -0
  42. data/example/out_forward_heartbeat_none.conf +16 -0
  43. data/example/out_forward_shared_key.conf +36 -0
  44. data/example/out_forward_tls.conf +18 -0
  45. data/example/out_forward_users.conf +65 -0
  46. data/example/out_null.conf +36 -0
  47. data/example/secondary_file.conf +41 -0
  48. data/example/suppress_config_dump.conf +7 -0
  49. data/example/v0_12_filter.conf +78 -0
  50. data/example/v1_literal_example.conf +36 -0
  51. data/fluent.conf +139 -0
  52. data/fluentd.gemspec +51 -0
  53. data/lib/fluent/agent.rb +163 -0
  54. data/lib/fluent/clock.rb +62 -0
  55. data/lib/fluent/command/binlog_reader.rb +234 -0
  56. data/lib/fluent/command/bundler_injection.rb +45 -0
  57. data/lib/fluent/command/cat.rb +330 -0
  58. data/lib/fluent/command/debug.rb +102 -0
  59. data/lib/fluent/command/fluentd.rb +301 -0
  60. data/lib/fluent/command/plugin_config_formatter.rb +258 -0
  61. data/lib/fluent/command/plugin_generator.rb +301 -0
  62. data/lib/fluent/compat/call_super_mixin.rb +67 -0
  63. data/lib/fluent/compat/detach_process_mixin.rb +25 -0
  64. data/lib/fluent/compat/exec_util.rb +129 -0
  65. data/lib/fluent/compat/file_util.rb +54 -0
  66. data/lib/fluent/compat/filter.rb +68 -0
  67. data/lib/fluent/compat/formatter.rb +111 -0
  68. data/lib/fluent/compat/formatter_utils.rb +85 -0
  69. data/lib/fluent/compat/handle_tag_and_time_mixin.rb +62 -0
  70. data/lib/fluent/compat/handle_tag_name_mixin.rb +53 -0
  71. data/lib/fluent/compat/input.rb +59 -0
  72. data/lib/fluent/compat/output.rb +728 -0
  73. data/lib/fluent/compat/output_chain.rb +60 -0
  74. data/lib/fluent/compat/parser.rb +310 -0
  75. data/lib/fluent/compat/parser_utils.rb +40 -0
  76. data/lib/fluent/compat/propagate_default.rb +62 -0
  77. data/lib/fluent/compat/record_filter_mixin.rb +34 -0
  78. data/lib/fluent/compat/set_tag_key_mixin.rb +50 -0
  79. data/lib/fluent/compat/set_time_key_mixin.rb +69 -0
  80. data/lib/fluent/compat/socket_util.rb +165 -0
  81. data/lib/fluent/compat/string_util.rb +34 -0
  82. data/lib/fluent/compat/structured_format_mixin.rb +26 -0
  83. data/lib/fluent/compat/type_converter.rb +90 -0
  84. data/lib/fluent/config.rb +56 -0
  85. data/lib/fluent/config/basic_parser.rb +123 -0
  86. data/lib/fluent/config/configure_proxy.rb +418 -0
  87. data/lib/fluent/config/dsl.rb +149 -0
  88. data/lib/fluent/config/element.rb +218 -0
  89. data/lib/fluent/config/error.rb +26 -0
  90. data/lib/fluent/config/literal_parser.rb +251 -0
  91. data/lib/fluent/config/parser.rb +107 -0
  92. data/lib/fluent/config/section.rb +223 -0
  93. data/lib/fluent/config/types.rb +136 -0
  94. data/lib/fluent/config/v1_parser.rb +190 -0
  95. data/lib/fluent/configurable.rb +200 -0
  96. data/lib/fluent/daemon.rb +15 -0
  97. data/lib/fluent/engine.rb +266 -0
  98. data/lib/fluent/env.rb +28 -0
  99. data/lib/fluent/error.rb +30 -0
  100. data/lib/fluent/event.rb +334 -0
  101. data/lib/fluent/event_router.rb +269 -0
  102. data/lib/fluent/filter.rb +21 -0
  103. data/lib/fluent/formatter.rb +23 -0
  104. data/lib/fluent/input.rb +21 -0
  105. data/lib/fluent/label.rb +46 -0
  106. data/lib/fluent/load.rb +35 -0
  107. data/lib/fluent/log.rb +546 -0
  108. data/lib/fluent/match.rb +178 -0
  109. data/lib/fluent/mixin.rb +31 -0
  110. data/lib/fluent/msgpack_factory.rb +62 -0
  111. data/lib/fluent/output.rb +29 -0
  112. data/lib/fluent/output_chain.rb +23 -0
  113. data/lib/fluent/parser.rb +23 -0
  114. data/lib/fluent/plugin.rb +183 -0
  115. data/lib/fluent/plugin/bare_output.rb +63 -0
  116. data/lib/fluent/plugin/base.rb +165 -0
  117. data/lib/fluent/plugin/buf_file.rb +184 -0
  118. data/lib/fluent/plugin/buf_memory.rb +34 -0
  119. data/lib/fluent/plugin/buffer.rb +617 -0
  120. data/lib/fluent/plugin/buffer/chunk.rb +221 -0
  121. data/lib/fluent/plugin/buffer/file_chunk.rb +364 -0
  122. data/lib/fluent/plugin/buffer/memory_chunk.rb +90 -0
  123. data/lib/fluent/plugin/compressable.rb +92 -0
  124. data/lib/fluent/plugin/exec_util.rb +22 -0
  125. data/lib/fluent/plugin/file_util.rb +22 -0
  126. data/lib/fluent/plugin/file_wrapper.rb +120 -0
  127. data/lib/fluent/plugin/filter.rb +93 -0
  128. data/lib/fluent/plugin/filter_grep.rb +75 -0
  129. data/lib/fluent/plugin/filter_parser.rb +119 -0
  130. data/lib/fluent/plugin/filter_record_transformer.rb +322 -0
  131. data/lib/fluent/plugin/filter_stdout.rb +53 -0
  132. data/lib/fluent/plugin/formatter.rb +50 -0
  133. data/lib/fluent/plugin/formatter_csv.rb +52 -0
  134. data/lib/fluent/plugin/formatter_hash.rb +33 -0
  135. data/lib/fluent/plugin/formatter_json.rb +55 -0
  136. data/lib/fluent/plugin/formatter_ltsv.rb +42 -0
  137. data/lib/fluent/plugin/formatter_msgpack.rb +33 -0
  138. data/lib/fluent/plugin/formatter_out_file.rb +51 -0
  139. data/lib/fluent/plugin/formatter_single_value.rb +34 -0
  140. data/lib/fluent/plugin/formatter_stdout.rb +75 -0
  141. data/lib/fluent/plugin/formatter_tsv.rb +34 -0
  142. data/lib/fluent/plugin/in_debug_agent.rb +64 -0
  143. data/lib/fluent/plugin/in_dummy.rb +139 -0
  144. data/lib/fluent/plugin/in_exec.rb +108 -0
  145. data/lib/fluent/plugin/in_forward.rb +455 -0
  146. data/lib/fluent/plugin/in_gc_stat.rb +56 -0
  147. data/lib/fluent/plugin/in_http.rb +433 -0
  148. data/lib/fluent/plugin/in_monitor_agent.rb +448 -0
  149. data/lib/fluent/plugin/in_object_space.rb +93 -0
  150. data/lib/fluent/plugin/in_syslog.rb +209 -0
  151. data/lib/fluent/plugin/in_tail.rb +905 -0
  152. data/lib/fluent/plugin/in_tcp.rb +85 -0
  153. data/lib/fluent/plugin/in_udp.rb +81 -0
  154. data/lib/fluent/plugin/in_unix.rb +201 -0
  155. data/lib/fluent/plugin/input.rb +37 -0
  156. data/lib/fluent/plugin/multi_output.rb +157 -0
  157. data/lib/fluent/plugin/out_copy.rb +46 -0
  158. data/lib/fluent/plugin/out_exec.rb +105 -0
  159. data/lib/fluent/plugin/out_exec_filter.rb +317 -0
  160. data/lib/fluent/plugin/out_file.rb +302 -0
  161. data/lib/fluent/plugin/out_forward.rb +912 -0
  162. data/lib/fluent/plugin/out_null.rb +74 -0
  163. data/lib/fluent/plugin/out_relabel.rb +32 -0
  164. data/lib/fluent/plugin/out_roundrobin.rb +84 -0
  165. data/lib/fluent/plugin/out_secondary_file.rb +133 -0
  166. data/lib/fluent/plugin/out_stdout.rb +75 -0
  167. data/lib/fluent/plugin/out_stream.rb +130 -0
  168. data/lib/fluent/plugin/output.rb +1291 -0
  169. data/lib/fluent/plugin/owned_by_mixin.rb +42 -0
  170. data/lib/fluent/plugin/parser.rb +191 -0
  171. data/lib/fluent/plugin/parser_apache.rb +28 -0
  172. data/lib/fluent/plugin/parser_apache2.rb +84 -0
  173. data/lib/fluent/plugin/parser_apache_error.rb +26 -0
  174. data/lib/fluent/plugin/parser_csv.rb +39 -0
  175. data/lib/fluent/plugin/parser_json.rb +81 -0
  176. data/lib/fluent/plugin/parser_ltsv.rb +42 -0
  177. data/lib/fluent/plugin/parser_msgpack.rb +50 -0
  178. data/lib/fluent/plugin/parser_multiline.rb +105 -0
  179. data/lib/fluent/plugin/parser_nginx.rb +28 -0
  180. data/lib/fluent/plugin/parser_none.rb +36 -0
  181. data/lib/fluent/plugin/parser_regexp.rb +63 -0
  182. data/lib/fluent/plugin/parser_syslog.rb +121 -0
  183. data/lib/fluent/plugin/parser_tsv.rb +42 -0
  184. data/lib/fluent/plugin/socket_util.rb +22 -0
  185. data/lib/fluent/plugin/storage.rb +84 -0
  186. data/lib/fluent/plugin/storage_local.rb +159 -0
  187. data/lib/fluent/plugin/string_util.rb +22 -0
  188. data/lib/fluent/plugin_helper.rb +70 -0
  189. data/lib/fluent/plugin_helper/cert_option.rb +159 -0
  190. data/lib/fluent/plugin_helper/child_process.rb +364 -0
  191. data/lib/fluent/plugin_helper/compat_parameters.rb +331 -0
  192. data/lib/fluent/plugin_helper/event_emitter.rb +93 -0
  193. data/lib/fluent/plugin_helper/event_loop.rb +161 -0
  194. data/lib/fluent/plugin_helper/extract.rb +104 -0
  195. data/lib/fluent/plugin_helper/formatter.rb +147 -0
  196. data/lib/fluent/plugin_helper/inject.rb +151 -0
  197. data/lib/fluent/plugin_helper/parser.rb +147 -0
  198. data/lib/fluent/plugin_helper/retry_state.rb +201 -0
  199. data/lib/fluent/plugin_helper/server.rb +738 -0
  200. data/lib/fluent/plugin_helper/socket.rb +241 -0
  201. data/lib/fluent/plugin_helper/socket_option.rb +69 -0
  202. data/lib/fluent/plugin_helper/storage.rb +349 -0
  203. data/lib/fluent/plugin_helper/thread.rb +179 -0
  204. data/lib/fluent/plugin_helper/timer.rb +91 -0
  205. data/lib/fluent/plugin_id.rb +80 -0
  206. data/lib/fluent/process.rb +22 -0
  207. data/lib/fluent/registry.rb +116 -0
  208. data/lib/fluent/root_agent.rb +323 -0
  209. data/lib/fluent/rpc.rb +94 -0
  210. data/lib/fluent/supervisor.rb +741 -0
  211. data/lib/fluent/system_config.rb +159 -0
  212. data/lib/fluent/test.rb +58 -0
  213. data/lib/fluent/test/base.rb +78 -0
  214. data/lib/fluent/test/driver/base.rb +224 -0
  215. data/lib/fluent/test/driver/base_owned.rb +70 -0
  216. data/lib/fluent/test/driver/base_owner.rb +135 -0
  217. data/lib/fluent/test/driver/event_feeder.rb +98 -0
  218. data/lib/fluent/test/driver/filter.rb +57 -0
  219. data/lib/fluent/test/driver/formatter.rb +30 -0
  220. data/lib/fluent/test/driver/input.rb +31 -0
  221. data/lib/fluent/test/driver/multi_output.rb +53 -0
  222. data/lib/fluent/test/driver/output.rb +102 -0
  223. data/lib/fluent/test/driver/parser.rb +30 -0
  224. data/lib/fluent/test/driver/test_event_router.rb +45 -0
  225. data/lib/fluent/test/filter_test.rb +77 -0
  226. data/lib/fluent/test/formatter_test.rb +65 -0
  227. data/lib/fluent/test/helpers.rb +134 -0
  228. data/lib/fluent/test/input_test.rb +174 -0
  229. data/lib/fluent/test/log.rb +79 -0
  230. data/lib/fluent/test/output_test.rb +156 -0
  231. data/lib/fluent/test/parser_test.rb +70 -0
  232. data/lib/fluent/test/startup_shutdown.rb +46 -0
  233. data/lib/fluent/time.rb +412 -0
  234. data/lib/fluent/timezone.rb +133 -0
  235. data/lib/fluent/unique_id.rb +39 -0
  236. data/lib/fluent/version.rb +21 -0
  237. data/lib/fluent/winsvc.rb +71 -0
  238. data/templates/new_gem/Gemfile +3 -0
  239. data/templates/new_gem/README.md.erb +43 -0
  240. data/templates/new_gem/Rakefile +13 -0
  241. data/templates/new_gem/fluent-plugin.gemspec.erb +27 -0
  242. data/templates/new_gem/lib/fluent/plugin/filter.rb.erb +14 -0
  243. data/templates/new_gem/lib/fluent/plugin/formatter.rb.erb +14 -0
  244. data/templates/new_gem/lib/fluent/plugin/input.rb.erb +11 -0
  245. data/templates/new_gem/lib/fluent/plugin/output.rb.erb +11 -0
  246. data/templates/new_gem/lib/fluent/plugin/parser.rb.erb +15 -0
  247. data/templates/new_gem/test/helper.rb.erb +8 -0
  248. data/templates/new_gem/test/plugin/test_filter.rb.erb +18 -0
  249. data/templates/new_gem/test/plugin/test_formatter.rb.erb +18 -0
  250. data/templates/new_gem/test/plugin/test_input.rb.erb +18 -0
  251. data/templates/new_gem/test/plugin/test_output.rb.erb +18 -0
  252. data/templates/new_gem/test/plugin/test_parser.rb.erb +18 -0
  253. data/templates/plugin_config_formatter/param.md-compact.erb +25 -0
  254. data/templates/plugin_config_formatter/param.md.erb +34 -0
  255. data/templates/plugin_config_formatter/section.md.erb +12 -0
  256. data/test/command/test_binlog_reader.rb +346 -0
  257. data/test/command/test_fluentd.rb +618 -0
  258. data/test/command/test_plugin_config_formatter.rb +275 -0
  259. data/test/command/test_plugin_generator.rb +66 -0
  260. data/test/compat/test_calls_super.rb +166 -0
  261. data/test/compat/test_parser.rb +92 -0
  262. data/test/config/assertions.rb +42 -0
  263. data/test/config/test_config_parser.rb +513 -0
  264. data/test/config/test_configurable.rb +1587 -0
  265. data/test/config/test_configure_proxy.rb +566 -0
  266. data/test/config/test_dsl.rb +415 -0
  267. data/test/config/test_element.rb +403 -0
  268. data/test/config/test_literal_parser.rb +297 -0
  269. data/test/config/test_section.rb +184 -0
  270. data/test/config/test_system_config.rb +168 -0
  271. data/test/config/test_types.rb +191 -0
  272. data/test/helper.rb +153 -0
  273. data/test/plugin/data/2010/01/20100102-030405.log +0 -0
  274. data/test/plugin/data/2010/01/20100102-030406.log +0 -0
  275. data/test/plugin/data/2010/01/20100102.log +0 -0
  276. data/test/plugin/data/log/bar +0 -0
  277. data/test/plugin/data/log/foo/bar.log +0 -0
  278. data/test/plugin/data/log/foo/bar2 +0 -0
  279. data/test/plugin/data/log/test.log +0 -0
  280. data/test/plugin/test_bare_output.rb +118 -0
  281. data/test/plugin/test_base.rb +115 -0
  282. data/test/plugin/test_buf_file.rb +843 -0
  283. data/test/plugin/test_buf_memory.rb +42 -0
  284. data/test/plugin/test_buffer.rb +1220 -0
  285. data/test/plugin/test_buffer_chunk.rb +198 -0
  286. data/test/plugin/test_buffer_file_chunk.rb +844 -0
  287. data/test/plugin/test_buffer_memory_chunk.rb +338 -0
  288. data/test/plugin/test_compressable.rb +84 -0
  289. data/test/plugin/test_file_util.rb +96 -0
  290. data/test/plugin/test_filter.rb +357 -0
  291. data/test/plugin/test_filter_grep.rb +119 -0
  292. data/test/plugin/test_filter_parser.rb +700 -0
  293. data/test/plugin/test_filter_record_transformer.rb +556 -0
  294. data/test/plugin/test_filter_stdout.rb +202 -0
  295. data/test/plugin/test_formatter_csv.rb +111 -0
  296. data/test/plugin/test_formatter_hash.rb +35 -0
  297. data/test/plugin/test_formatter_json.rb +51 -0
  298. data/test/plugin/test_formatter_ltsv.rb +59 -0
  299. data/test/plugin/test_formatter_msgpack.rb +28 -0
  300. data/test/plugin/test_formatter_out_file.rb +95 -0
  301. data/test/plugin/test_formatter_single_value.rb +38 -0
  302. data/test/plugin/test_in_debug_agent.rb +28 -0
  303. data/test/plugin/test_in_dummy.rb +192 -0
  304. data/test/plugin/test_in_exec.rb +245 -0
  305. data/test/plugin/test_in_forward.rb +1120 -0
  306. data/test/plugin/test_in_gc_stat.rb +39 -0
  307. data/test/plugin/test_in_http.rb +588 -0
  308. data/test/plugin/test_in_monitor_agent.rb +516 -0
  309. data/test/plugin/test_in_object_space.rb +64 -0
  310. data/test/plugin/test_in_syslog.rb +271 -0
  311. data/test/plugin/test_in_tail.rb +1216 -0
  312. data/test/plugin/test_in_tcp.rb +118 -0
  313. data/test/plugin/test_in_udp.rb +152 -0
  314. data/test/plugin/test_in_unix.rb +126 -0
  315. data/test/plugin/test_input.rb +126 -0
  316. data/test/plugin/test_multi_output.rb +180 -0
  317. data/test/plugin/test_out_copy.rb +160 -0
  318. data/test/plugin/test_out_exec.rb +310 -0
  319. data/test/plugin/test_out_exec_filter.rb +613 -0
  320. data/test/plugin/test_out_file.rb +873 -0
  321. data/test/plugin/test_out_forward.rb +685 -0
  322. data/test/plugin/test_out_null.rb +105 -0
  323. data/test/plugin/test_out_relabel.rb +28 -0
  324. data/test/plugin/test_out_roundrobin.rb +146 -0
  325. data/test/plugin/test_out_secondary_file.rb +442 -0
  326. data/test/plugin/test_out_stdout.rb +170 -0
  327. data/test/plugin/test_out_stream.rb +93 -0
  328. data/test/plugin/test_output.rb +870 -0
  329. data/test/plugin/test_output_as_buffered.rb +1932 -0
  330. data/test/plugin/test_output_as_buffered_compress.rb +165 -0
  331. data/test/plugin/test_output_as_buffered_overflow.rb +250 -0
  332. data/test/plugin/test_output_as_buffered_retries.rb +839 -0
  333. data/test/plugin/test_output_as_buffered_secondary.rb +877 -0
  334. data/test/plugin/test_output_as_standard.rb +374 -0
  335. data/test/plugin/test_owned_by.rb +35 -0
  336. data/test/plugin/test_parser.rb +359 -0
  337. data/test/plugin/test_parser_apache.rb +42 -0
  338. data/test/plugin/test_parser_apache2.rb +46 -0
  339. data/test/plugin/test_parser_apache_error.rb +45 -0
  340. data/test/plugin/test_parser_csv.rb +103 -0
  341. data/test/plugin/test_parser_json.rb +114 -0
  342. data/test/plugin/test_parser_labeled_tsv.rb +128 -0
  343. data/test/plugin/test_parser_multiline.rb +100 -0
  344. data/test/plugin/test_parser_nginx.rb +48 -0
  345. data/test/plugin/test_parser_none.rb +52 -0
  346. data/test/plugin/test_parser_regexp.rb +281 -0
  347. data/test/plugin/test_parser_syslog.rb +242 -0
  348. data/test/plugin/test_parser_tsv.rb +122 -0
  349. data/test/plugin/test_storage.rb +167 -0
  350. data/test/plugin/test_storage_local.rb +335 -0
  351. data/test/plugin/test_string_util.rb +26 -0
  352. data/test/plugin_helper/test_child_process.rb +794 -0
  353. data/test/plugin_helper/test_compat_parameters.rb +331 -0
  354. data/test/plugin_helper/test_event_emitter.rb +51 -0
  355. data/test/plugin_helper/test_event_loop.rb +52 -0
  356. data/test/plugin_helper/test_extract.rb +194 -0
  357. data/test/plugin_helper/test_formatter.rb +255 -0
  358. data/test/plugin_helper/test_inject.rb +519 -0
  359. data/test/plugin_helper/test_parser.rb +264 -0
  360. data/test/plugin_helper/test_retry_state.rb +422 -0
  361. data/test/plugin_helper/test_server.rb +1677 -0
  362. data/test/plugin_helper/test_storage.rb +542 -0
  363. data/test/plugin_helper/test_thread.rb +164 -0
  364. data/test/plugin_helper/test_timer.rb +132 -0
  365. data/test/scripts/exec_script.rb +32 -0
  366. data/test/scripts/fluent/plugin/formatter1/formatter_test1.rb +7 -0
  367. data/test/scripts/fluent/plugin/formatter2/formatter_test2.rb +7 -0
  368. data/test/scripts/fluent/plugin/formatter_known.rb +8 -0
  369. data/test/scripts/fluent/plugin/out_test.rb +81 -0
  370. data/test/scripts/fluent/plugin/out_test2.rb +80 -0
  371. data/test/scripts/fluent/plugin/parser_known.rb +4 -0
  372. data/test/test_clock.rb +164 -0
  373. data/test/test_config.rb +179 -0
  374. data/test/test_configdsl.rb +148 -0
  375. data/test/test_event.rb +515 -0
  376. data/test/test_event_router.rb +331 -0
  377. data/test/test_event_time.rb +186 -0
  378. data/test/test_filter.rb +121 -0
  379. data/test/test_formatter.rb +312 -0
  380. data/test/test_input.rb +31 -0
  381. data/test/test_log.rb +828 -0
  382. data/test/test_match.rb +137 -0
  383. data/test/test_mixin.rb +351 -0
  384. data/test/test_output.rb +273 -0
  385. data/test/test_plugin.rb +251 -0
  386. data/test/test_plugin_classes.rb +253 -0
  387. data/test/test_plugin_helper.rb +81 -0
  388. data/test/test_plugin_id.rb +101 -0
  389. data/test/test_process.rb +14 -0
  390. data/test/test_root_agent.rb +611 -0
  391. data/test/test_supervisor.rb +373 -0
  392. data/test/test_test_drivers.rb +135 -0
  393. data/test/test_time_formatter.rb +282 -0
  394. data/test/test_time_parser.rb +211 -0
  395. data/test/test_unique_id.rb +47 -0
  396. metadata +898 -0
@@ -0,0 +1,64 @@
1
+ require_relative '../helper'
2
+ require 'fluent/test/driver/input'
3
+ require 'fluent/plugin/in_object_space'
4
+
5
+ require 'timeout'
6
+
7
+ class ObjectSpaceInputTest < Test::Unit::TestCase
8
+ def waiting(seconds, instance)
9
+ begin
10
+ Timeout.timeout(seconds) do
11
+ yield
12
+ end
13
+ rescue Timeout::Error
14
+ STDERR.print(*instance.log.out.logs)
15
+ raise
16
+ end
17
+ end
18
+
19
+ class FailObject
20
+ def self.class
21
+ raise "error"
22
+ end
23
+ end
24
+
25
+ def setup
26
+ Fluent::Test.setup
27
+ end
28
+
29
+ TESTCONFIG = %[
30
+ emit_interval 1
31
+ tag t1
32
+ top 2
33
+ ]
34
+
35
+ def create_driver(conf=TESTCONFIG)
36
+ Fluent::Test::Driver::Input.new(Fluent::Plugin::ObjectSpaceInput).configure(conf)
37
+ end
38
+
39
+ def test_configure
40
+ d = create_driver
41
+ assert_equal 1, d.instance.emit_interval
42
+ assert_equal "t1", d.instance.tag
43
+ assert_equal 2, d.instance.top
44
+ end
45
+
46
+ def test_emit
47
+ d = create_driver
48
+
49
+ d.run do
50
+ waiting(10, d.instance) do
51
+ sleep 0.5 until d.events.size > 3
52
+ end
53
+ end
54
+
55
+ emits = d.events
56
+ assert{ emits.length > 0 }
57
+
58
+ emits.each { |tag, time, record|
59
+ assert_equal d.instance.tag, tag
60
+ assert_equal d.instance.top, record.keys.size
61
+ assert(time.is_a?(Fluent::EventTime))
62
+ }
63
+ end
64
+ end
@@ -0,0 +1,271 @@
1
+ require_relative '../helper'
2
+ require 'fluent/test/driver/input'
3
+ require 'fluent/plugin/in_syslog'
4
+
5
+ class SyslogInputTest < Test::Unit::TestCase
6
+ def setup
7
+ Fluent::Test.setup
8
+ end
9
+
10
+ PORT = unused_port
11
+ CONFIG = %[
12
+ port #{PORT}
13
+ bind 127.0.0.1
14
+ tag syslog
15
+ ]
16
+
17
+ IPv6_CONFIG = %[
18
+ port #{PORT}
19
+ bind ::1
20
+ tag syslog
21
+ ]
22
+
23
+ def create_driver(conf=CONFIG)
24
+ Fluent::Test::Driver::Input.new(Fluent::Plugin::SyslogInput).configure(conf)
25
+ end
26
+
27
+ data(
28
+ ipv4: ['127.0.0.1', CONFIG, ::Socket::AF_INET],
29
+ ipv6: ['::1', IPv6_CONFIG, ::Socket::AF_INET6],
30
+ )
31
+ def test_configure(data)
32
+ bind_addr, config, family = data
33
+ omit "IPv6 unavailable" if family == ::Socket::AF_INET6 && !ipv6_enabled?
34
+
35
+ d = create_driver(config)
36
+ assert_equal PORT, d.instance.port
37
+ assert_equal bind_addr, d.instance.bind
38
+ end
39
+
40
+ data(
41
+ ipv4: ['127.0.0.1', CONFIG, ::Socket::AF_INET],
42
+ ipv6: ['::1', IPv6_CONFIG, ::Socket::AF_INET6],
43
+ )
44
+ def test_time_format(data)
45
+ bind_addr, config, family = data
46
+ omit "IPv6 unavailable" if family == ::Socket::AF_INET6 && !ipv6_enabled?
47
+
48
+ d = create_driver(config)
49
+
50
+ tests = [
51
+ {'msg' => '<6>Dec 11 00:00:00 localhost logger: foo', 'expected' => Fluent::EventTime.from_time(Time.strptime('Dec 11 00:00:00', '%b %d %H:%M:%S'))},
52
+ {'msg' => '<6>Dec 1 00:00:00 localhost logger: foo', 'expected' => Fluent::EventTime.from_time(Time.strptime('Dec 1 00:00:00', '%b %d %H:%M:%S'))},
53
+ ]
54
+ d.run(expect_emits: 2) do
55
+ u = UDPSocket.new(family)
56
+ u.connect(bind_addr, PORT)
57
+ tests.each {|test|
58
+ u.send(test['msg'], 0)
59
+ }
60
+ end
61
+
62
+ events = d.events
63
+ assert(events.size > 0)
64
+ events.each_index {|i|
65
+ assert_equal_event_time(tests[i]['expected'], events[i][1])
66
+ }
67
+ end
68
+
69
+ def test_msg_size
70
+ d = create_driver
71
+ tests = create_test_case
72
+
73
+ d.run(expect_emits: 2) do
74
+ u = UDPSocket.new
75
+ u.connect('127.0.0.1', PORT)
76
+ tests.each {|test|
77
+ u.send(test['msg'], 0)
78
+ }
79
+ end
80
+
81
+ assert(d.events.size > 0)
82
+ compare_test_result(d.events, tests)
83
+ end
84
+
85
+ def test_msg_size_udp_for_large_msg
86
+ d = create_driver(CONFIG + %[
87
+ message_length_limit 5k
88
+ ])
89
+ tests = create_test_case(large_message: true)
90
+
91
+ d.run(expect_emits: 3) do
92
+ u = UDPSocket.new
93
+ u.connect('127.0.0.1', PORT)
94
+ tests.each {|test|
95
+ u.send(test['msg'], 0)
96
+ }
97
+ end
98
+
99
+ assert(d.events.size > 0)
100
+ compare_test_result(d.events, tests)
101
+ end
102
+
103
+ def test_msg_size_with_tcp
104
+ d = create_driver([CONFIG, 'protocol_type tcp'].join("\n"))
105
+ tests = create_test_case
106
+
107
+ d.run(expect_emits: 2) do
108
+ tests.each {|test|
109
+ TCPSocket.open('127.0.0.1', PORT) do |s|
110
+ s.send(test['msg'], 0)
111
+ end
112
+ }
113
+ end
114
+
115
+ assert(d.events.size > 0)
116
+ compare_test_result(d.events, tests)
117
+ end
118
+
119
+ def test_msg_size_with_same_tcp_connection
120
+ d = create_driver([CONFIG, 'protocol_type tcp'].join("\n"))
121
+ tests = create_test_case
122
+
123
+ d.run(expect_emits: 2) do
124
+ TCPSocket.open('127.0.0.1', PORT) do |s|
125
+ tests.each {|test|
126
+ s.send(test['msg'], 0)
127
+ }
128
+ end
129
+ end
130
+
131
+ assert(d.events.size > 0)
132
+ compare_test_result(d.events, tests)
133
+ end
134
+
135
+ def test_msg_size_with_json_format
136
+ d = create_driver([CONFIG, 'format json'].join("\n"))
137
+ time = Time.parse('2013-09-18 12:00:00 +0900').to_i
138
+ tests = ['Hello!', 'Syslog!'].map { |msg|
139
+ event = {'time' => time, 'message' => msg}
140
+ {'msg' => '<6>' + event.to_json + "\n", 'expected' => msg}
141
+ }
142
+
143
+ d.run(expect_emits: 2) do
144
+ u = UDPSocket.new
145
+ u.connect('127.0.0.1', PORT)
146
+ tests.each {|test|
147
+ u.send(test['msg'], 0)
148
+ }
149
+ end
150
+
151
+ assert(d.events.size > 0)
152
+ compare_test_result(d.events, tests)
153
+ end
154
+
155
+ def test_msg_size_with_include_source_host
156
+ d = create_driver([CONFIG, 'include_source_host true'].join("\n"))
157
+ tests = create_test_case
158
+
159
+ host = nil
160
+ d.run(expect_emits: 2) do
161
+ u = UDPSocket.new
162
+ u.connect('127.0.0.1', PORT)
163
+ host = u.peeraddr[2]
164
+ tests.each {|test|
165
+ u.send(test['msg'], 0)
166
+ }
167
+ end
168
+
169
+ assert(d.events.size > 0)
170
+ compare_test_result(d.events, tests, {host: host})
171
+ end
172
+
173
+ def test_msg_size_with_priority_key
174
+ d = create_driver([CONFIG, 'priority_key priority'].join("\n"))
175
+ tests = create_test_case
176
+
177
+ priority = 'info'
178
+ d.run(expect_emits: 2) do
179
+ u = UDPSocket.new
180
+ u.connect('127.0.0.1', PORT)
181
+ tests.each {|test|
182
+ u.send(test['msg'], 0)
183
+ }
184
+ end
185
+
186
+ assert(d.events.size > 0)
187
+ compare_test_result(d.events, tests, {priority: priority})
188
+ end
189
+
190
+ def test_msg_size_with_facility_key
191
+ d = create_driver([CONFIG, 'facility_key facility'].join("\n"))
192
+ tests = create_test_case
193
+
194
+ facility = 'kern'
195
+ d.run(expect_emits: 2) do
196
+ u = UDPSocket.new
197
+ u.connect('127.0.0.1', PORT)
198
+ tests.each {|test|
199
+ u.send(test['msg'], 0)
200
+ }
201
+ end
202
+
203
+ assert(d.events.size > 0)
204
+ compare_test_result(d.events, tests, {facility: facility})
205
+ end
206
+
207
+ def test_msg_size_with_source_address_key
208
+ d = create_driver([CONFIG, 'source_address_key source_address'].join("\n"))
209
+ tests = create_test_case
210
+
211
+ address = nil
212
+ d.run(expect_emits: 2) do
213
+ u = UDPSocket.new
214
+ u.connect('127.0.0.1', PORT)
215
+ address = u.peeraddr[3]
216
+ tests.each {|test|
217
+ u.send(test['msg'], 0)
218
+ }
219
+ end
220
+
221
+ assert(d.events.size > 0)
222
+ compare_test_result(d.events, tests, {address: address})
223
+ end
224
+
225
+ def test_msg_size_with_source_hostname_key
226
+ d = create_driver([CONFIG, 'source_hostname_key source_hostname'].join("\n"))
227
+ tests = create_test_case
228
+
229
+ hostname = nil
230
+ d.run(expect_emits: 2) do
231
+ u = UDPSocket.new
232
+ u.do_not_reverse_lookup = false
233
+ u.connect('127.0.0.1', PORT)
234
+ hostname = u.peeraddr[2]
235
+ tests.each {|test|
236
+ u.send(test['msg'], 0)
237
+ }
238
+ end
239
+
240
+ assert(d.events.size > 0)
241
+ compare_test_result(d.events, tests, {hostname: hostname})
242
+ end
243
+
244
+ def create_test_case(large_message: false)
245
+ # actual syslog message has "\n"
246
+ if large_message
247
+ [
248
+ {'msg' => '<6>Sep 10 00:00:00 localhost logger: ' + 'x' * 100 + "\n", 'expected' => 'x' * 100},
249
+ {'msg' => '<6>Sep 10 00:00:00 localhost logger: ' + 'x' * 1024 + "\n", 'expected' => 'x' * 1024},
250
+ {'msg' => '<6>Sep 10 00:00:00 localhost logger: ' + 'x' * 4096 + "\n", 'expected' => 'x' * 4096},
251
+ ]
252
+ else
253
+ [
254
+ {'msg' => '<6>Sep 10 00:00:00 localhost logger: ' + 'x' * 100 + "\n", 'expected' => 'x' * 100},
255
+ {'msg' => '<6>Sep 10 00:00:00 localhost logger: ' + 'x' * 1024 + "\n", 'expected' => 'x' * 1024},
256
+ ]
257
+ end
258
+ end
259
+
260
+ def compare_test_result(events, tests, options = {})
261
+ events.each_index { |i|
262
+ assert_equal('syslog.kern.info', events[i][0]) # <6> means kern.info
263
+ assert_equal(tests[i]['expected'], events[i][2]['message'])
264
+ assert_equal(options[:host], events[i][2]['source_host']) if options[:host]
265
+ assert_equal(options[:address], events[i][2]['source_address']) if options[:address]
266
+ assert_equal(options[:hostname], events[i][2]['source_hostname']) if options[:hostname]
267
+ assert_equal(options[:priority], events[i][2]['priority']) if options[:priority]
268
+ assert_equal(options[:facility], events[i][2]['facility']) if options[:facility]
269
+ }
270
+ end
271
+ end
@@ -0,0 +1,1216 @@
1
+ require_relative '../helper'
2
+ require 'fluent/test/driver/input'
3
+ require 'fluent/plugin/in_tail'
4
+ require 'fluent/plugin/buffer'
5
+ require 'fluent/system_config'
6
+ require 'net/http'
7
+ require 'flexmock/test_unit'
8
+ require 'timecop'
9
+
10
+ class TailInputTest < Test::Unit::TestCase
11
+ include FlexMock::TestCase
12
+
13
+ def setup
14
+ Fluent::Test.setup
15
+ cleanup_directory(TMP_DIR)
16
+ end
17
+
18
+ def teardown
19
+ super
20
+ Fluent::Engine.stop
21
+ end
22
+
23
+ def cleanup_directory(path)
24
+ FileUtils.rm_rf(path, secure: true)
25
+ if File.exist?(path)
26
+ # ensure files are closed for Windows, on which deleted files
27
+ # are still visible from filesystem
28
+ GC.start(full_mark: true, immediate_mark: true, immediate_sweep: true)
29
+ FileUtils.remove_entry_secure(path, true)
30
+ end
31
+ FileUtils.mkdir_p(path)
32
+ end
33
+
34
+ TMP_DIR = File.dirname(__FILE__) + "/../tmp/tail#{ENV['TEST_ENV_NUMBER']}"
35
+
36
+ CONFIG = config_element("ROOT", "", {
37
+ "path" => "#{TMP_DIR}/tail.txt",
38
+ "tag" => "t1",
39
+ "rotate_wait" => "2s"
40
+ })
41
+ COMMON_CONFIG = CONFIG + config_element("", "", { "pos_file" => "#{TMP_DIR}/tail.pos" })
42
+ CONFIG_READ_FROM_HEAD = config_element("", "", { "read_from_head" => true })
43
+ CONFIG_ENABLE_WATCH_TIMER = config_element("", "", { "enable_watch_timer" => false })
44
+ CONFIG_OPEN_ON_EVERY_UPDATE = config_element("", "", { "open_on_every_update" => true })
45
+ SINGLE_LINE_CONFIG = config_element("", "", { "format" => "/(?<message>.*)/" })
46
+ PARSE_SINGLE_LINE_CONFIG = config_element("", "", {}, [config_element("parse", "", { "@type" => "/(?<message>.*)/" })])
47
+ MULTILINE_CONFIG = config_element(
48
+ "", "", {
49
+ "format" => "multiline",
50
+ "format1" => "/^s (?<message1>[^\\n]+)(\\nf (?<message2>[^\\n]+))?(\\nf (?<message3>.*))?/",
51
+ "format_firstline" => "/^[s]/"
52
+ })
53
+ PARSE_MULTILINE_CONFIG = config_element(
54
+ "", "", {},
55
+ [config_element("parse", "", {
56
+ "@type" => "multiline",
57
+ "format1" => "/^s (?<message1>[^\\n]+)(\\nf (?<message2>[^\\n]+))?(\\nf (?<message3>.*))?/",
58
+ "format_firstline" => "/^[s]/"
59
+ })
60
+ ])
61
+
62
+ def create_driver(conf = SINGLE_LINE_CONFIG, use_common_conf = true)
63
+ config = use_common_conf ? COMMON_CONFIG + conf : conf
64
+ Fluent::Test::Driver::Input.new(Fluent::Plugin::TailInput).configure(config)
65
+ end
66
+
67
+ sub_test_case "configure" do
68
+ test "plain single line" do
69
+ d = create_driver
70
+ assert_equal ["#{TMP_DIR}/tail.txt"], d.instance.paths
71
+ assert_equal "t1", d.instance.tag
72
+ assert_equal 2, d.instance.rotate_wait
73
+ assert_equal "#{TMP_DIR}/tail.pos", d.instance.pos_file
74
+ assert_equal 1000, d.instance.read_lines_limit
75
+ end
76
+
77
+ data("empty" => config_element,
78
+ "w/o @type" => config_element("", "", {}, [config_element("parse", "", {})]))
79
+ test "w/o parse section" do |conf|
80
+ assert_raise(Fluent::ConfigError) do
81
+ create_driver(conf)
82
+ end
83
+ end
84
+
85
+ sub_test_case "encoding" do
86
+ test "valid" do
87
+ conf = SINGLE_LINE_CONFIG + config_element("", "", { "encoding" => "utf-8" })
88
+ d = create_driver(conf)
89
+ assert_equal Encoding::UTF_8, d.instance.encoding
90
+ end
91
+
92
+ test "invalid" do
93
+ conf = SINGLE_LINE_CONFIG + config_element("", "", { "encoding" => "no-such-encoding" })
94
+ assert_raise(Fluent::ConfigError) do
95
+ create_driver(conf)
96
+ end
97
+ end
98
+ end
99
+
100
+ sub_test_case "from_encoding" do
101
+ test "only specified from_encoding raise ConfigError" do
102
+ conf = SINGLE_LINE_CONFIG + config_element("", "", { "from_encoding" => "utf-8" })
103
+ assert_raise(Fluent::ConfigError) do
104
+ create_driver(conf)
105
+ end
106
+ end
107
+
108
+ test "valid" do
109
+ conf = SINGLE_LINE_CONFIG + config_element("", "", {
110
+ "from_encoding" => "utf-8",
111
+ "encoding" => "utf-8"
112
+ })
113
+ d = create_driver(conf)
114
+ assert_equal(Encoding::UTF_8, d.instance.from_encoding)
115
+ end
116
+
117
+ test "invalid" do
118
+ conf = SINGLE_LINE_CONFIG + config_element("", "", {
119
+ "from_encoding" => "no-such-encoding",
120
+ "encoding" => "utf-8"
121
+ })
122
+ assert_raise(Fluent::ConfigError) do
123
+ create_driver(conf)
124
+ end
125
+ end
126
+ end
127
+ end
128
+
129
+ sub_test_case "singleline" do
130
+ data(flat: SINGLE_LINE_CONFIG,
131
+ parse: PARSE_SINGLE_LINE_CONFIG)
132
+ def test_emit(data)
133
+ config = data
134
+ File.open("#{TMP_DIR}/tail.txt", "wb") {|f|
135
+ f.puts "test1"
136
+ f.puts "test2"
137
+ }
138
+
139
+ d = create_driver(config)
140
+
141
+ d.run(expect_emits: 1) do
142
+ File.open("#{TMP_DIR}/tail.txt", "ab") {|f|
143
+ f.puts "test3\ntest4"
144
+ }
145
+ end
146
+
147
+ events = d.events
148
+ assert_equal(true, events.length > 0)
149
+ assert_equal({"message" => "test3"}, events[0][2])
150
+ assert_equal({"message" => "test4"}, events[1][2])
151
+ assert(events[0][1].is_a?(Fluent::EventTime))
152
+ assert(events[1][1].is_a?(Fluent::EventTime))
153
+ assert_equal(1, d.emit_count)
154
+ end
155
+
156
+ def test_emit_with_emit_unmatched_lines_true
157
+ config = config_element("", "", { "format" => "/^(?<message>test.*)/", "emit_unmatched_lines" => true })
158
+ File.open("#{TMP_DIR}/tail.txt", "wb") { |f| }
159
+
160
+ d = create_driver(config)
161
+ d.run(expect_emits: 1) do
162
+ File.open("#{TMP_DIR}/tail.txt", "ab") {|f|
163
+ f.puts "test line 1"
164
+ f.puts "test line 2"
165
+ f.puts "bad line 1"
166
+ f.puts "test line 3"
167
+ }
168
+ end
169
+
170
+ events = d.events
171
+ assert_equal(4, events.length)
172
+ assert_equal({"message" => "test line 1"}, events[0][2])
173
+ assert_equal({"message" => "test line 2"}, events[1][2])
174
+ assert_equal({"unmatched_line" => "bad line 1"}, events[2][2])
175
+ assert_equal({"message" => "test line 3"}, events[3][2])
176
+ end
177
+
178
+ data('flat 1' => [:flat, 1, 2],
179
+ 'flat 10' => [:flat, 10, 1],
180
+ 'parse 1' => [:parse, 1, 2],
181
+ 'parse 10' => [:parse, 10, 1])
182
+ def test_emit_with_read_lines_limit(data)
183
+ config_style, limit, num_events = data
184
+ case config_style
185
+ when :flat
186
+ config = CONFIG_READ_FROM_HEAD + SINGLE_LINE_CONFIG + config_element("", "", { "read_lines_limit" => limit })
187
+ when :parse
188
+ config = CONFIG_READ_FROM_HEAD + config_element("", "", { "read_lines_limit" => limit }) + PARSE_SINGLE_LINE_CONFIG
189
+ end
190
+ d = create_driver(config)
191
+ msg = 'test' * 500 # in_tail reads 2048 bytes at once.
192
+
193
+ d.run(expect_emits: 1) do
194
+ File.open("#{TMP_DIR}/tail.txt", "ab") {|f|
195
+ f.puts msg
196
+ f.puts msg
197
+ }
198
+ end
199
+
200
+ events = d.events
201
+ assert_equal(true, events.length > 0)
202
+ assert_equal({"message" => msg}, events[0][2])
203
+ assert_equal({"message" => msg}, events[1][2])
204
+ assert_equal(num_events, d.emit_count)
205
+ end
206
+
207
+ data(flat: CONFIG_READ_FROM_HEAD + SINGLE_LINE_CONFIG,
208
+ parse: CONFIG_READ_FROM_HEAD + PARSE_SINGLE_LINE_CONFIG)
209
+ def test_emit_with_read_from_head(data)
210
+ config = data
211
+ File.open("#{TMP_DIR}/tail.txt", "wb") {|f|
212
+ f.puts "test1"
213
+ f.puts "test2"
214
+ }
215
+
216
+ d = create_driver(config)
217
+
218
+ d.run(expect_emits: 2) do
219
+ File.open("#{TMP_DIR}/tail.txt", "ab") {|f|
220
+ f.puts "test3"
221
+ f.puts "test4"
222
+ }
223
+ end
224
+
225
+ events = d.events
226
+ assert(events.length > 0)
227
+ assert_equal({"message" => "test1"}, events[0][2])
228
+ assert_equal({"message" => "test2"}, events[1][2])
229
+ assert_equal({"message" => "test3"}, events[2][2])
230
+ assert_equal({"message" => "test4"}, events[3][2])
231
+ end
232
+
233
+ data(flat: CONFIG_ENABLE_WATCH_TIMER + SINGLE_LINE_CONFIG,
234
+ parse: CONFIG_ENABLE_WATCH_TIMER + PARSE_SINGLE_LINE_CONFIG)
235
+ def test_emit_with_enable_watch_timer(data)
236
+ config = data
237
+ File.open("#{TMP_DIR}/tail.txt", "wb") {|f|
238
+ f.puts "test1"
239
+ f.puts "test2"
240
+ }
241
+
242
+ d = create_driver(config)
243
+
244
+ d.run(expect_emits: 1) do
245
+ File.open("#{TMP_DIR}/tail.txt", "ab") {|f|
246
+ f.puts "test3"
247
+ f.puts "test4"
248
+ }
249
+ # according to cool.io's stat_watcher.c, systems without inotify will use
250
+ # an "automatic" value, typically around 5 seconds
251
+ end
252
+
253
+ events = d.events
254
+ assert(events.length > 0)
255
+ assert_equal({"message" => "test3"}, events[0][2])
256
+ assert_equal({"message" => "test4"}, events[1][2])
257
+ end
258
+ end
259
+
260
+ class TestWithSystem < self
261
+ include Fluent::SystemConfig::Mixin
262
+
263
+ OVERRIDE_FILE_PERMISSION = 0620
264
+ CONFIG_SYSTEM = %[
265
+ <system>
266
+ file_permission #{OVERRIDE_FILE_PERMISSION}
267
+ </system>
268
+ ]
269
+
270
+ def setup
271
+ omit "NTFS doesn't support UNIX like permissions" if Fluent.windows?
272
+ # Store default permission
273
+ @default_permission = system_config.instance_variable_get(:@file_permission)
274
+ end
275
+
276
+ def teardown
277
+ # Restore default permission
278
+ system_config.instance_variable_set(:@file_permission, @default_permission)
279
+ end
280
+
281
+ def parse_system(text)
282
+ basepath = File.expand_path(File.dirname(__FILE__) + '/../../')
283
+ Fluent::Config.parse(text, '(test)', basepath, true).elements.find { |e| e.name == 'system' }
284
+ end
285
+
286
+ def test_emit_with_system
287
+ system_conf = parse_system(CONFIG_SYSTEM)
288
+ sc = Fluent::SystemConfig.new(system_conf)
289
+ Fluent::Engine.init(sc)
290
+ File.open("#{TMP_DIR}/tail.txt", "wb") {|f|
291
+ f.puts "test1"
292
+ f.puts "test2"
293
+ }
294
+
295
+ d = create_driver
296
+
297
+ d.run(expect_emits: 1) do
298
+ File.open("#{TMP_DIR}/tail.txt", "ab") {|f|
299
+ f.puts "test3"
300
+ f.puts "test4"
301
+ }
302
+ end
303
+
304
+ events = d.events
305
+ assert_equal(true, events.length > 0)
306
+ assert_equal({"message" => "test3"}, events[0][2])
307
+ assert_equal({"message" => "test4"}, events[1][2])
308
+ assert(events[0][1].is_a?(Fluent::EventTime))
309
+ assert(events[1][1].is_a?(Fluent::EventTime))
310
+ assert_equal(1, d.emit_count)
311
+ pos = d.instance.instance_variable_get(:@pf_file)
312
+ mode = "%o" % File.stat(pos).mode
313
+ assert_equal OVERRIDE_FILE_PERMISSION, mode[-3, 3].to_i
314
+ end
315
+ end
316
+
317
+ sub_test_case "rotate file" do
318
+ data(flat: SINGLE_LINE_CONFIG,
319
+ parse: PARSE_SINGLE_LINE_CONFIG)
320
+ def test_rotate_file(data)
321
+ config = data
322
+ events = sub_test_rotate_file(config, expect_emits: 2)
323
+ assert_equal(4, events.length)
324
+ assert_equal({"message" => "test3"}, events[0][2])
325
+ assert_equal({"message" => "test4"}, events[1][2])
326
+ assert_equal({"message" => "test5"}, events[2][2])
327
+ assert_equal({"message" => "test6"}, events[3][2])
328
+ end
329
+
330
+ data(flat: CONFIG_READ_FROM_HEAD + SINGLE_LINE_CONFIG,
331
+ parse: CONFIG_READ_FROM_HEAD + PARSE_SINGLE_LINE_CONFIG)
332
+ def test_rotate_file_with_read_from_head(data)
333
+ config = data
334
+ events = sub_test_rotate_file(config, expect_records: 6)
335
+ assert_equal(6, events.length)
336
+ assert_equal({"message" => "test1"}, events[0][2])
337
+ assert_equal({"message" => "test2"}, events[1][2])
338
+ assert_equal({"message" => "test3"}, events[2][2])
339
+ assert_equal({"message" => "test4"}, events[3][2])
340
+ assert_equal({"message" => "test5"}, events[4][2])
341
+ assert_equal({"message" => "test6"}, events[5][2])
342
+ end
343
+
344
+ data(flat: CONFIG_OPEN_ON_EVERY_UPDATE + CONFIG_READ_FROM_HEAD + SINGLE_LINE_CONFIG,
345
+ parse: CONFIG_OPEN_ON_EVERY_UPDATE + CONFIG_READ_FROM_HEAD + PARSE_SINGLE_LINE_CONFIG)
346
+ def test_rotate_file_with_open_on_every_update(data)
347
+ config = data
348
+ events = sub_test_rotate_file(config, expect_records: 6)
349
+ assert_equal(6, events.length)
350
+ assert_equal({"message" => "test1"}, events[0][2])
351
+ assert_equal({"message" => "test2"}, events[1][2])
352
+ assert_equal({"message" => "test3"}, events[2][2])
353
+ assert_equal({"message" => "test4"}, events[3][2])
354
+ assert_equal({"message" => "test5"}, events[4][2])
355
+ assert_equal({"message" => "test6"}, events[5][2])
356
+ end
357
+
358
+ data(flat: SINGLE_LINE_CONFIG,
359
+ parse: PARSE_SINGLE_LINE_CONFIG)
360
+ def test_rotate_file_with_write_old(data)
361
+ config = data
362
+ events = sub_test_rotate_file(config, expect_emits: 3) { |rotated_file|
363
+ File.open("#{TMP_DIR}/tail.txt", "wb") { |f| }
364
+ rotated_file.puts "test7"
365
+ rotated_file.puts "test8"
366
+ rotated_file.flush
367
+
368
+ sleep 1
369
+ File.open("#{TMP_DIR}/tail.txt", "ab") { |f|
370
+ f.puts "test5"
371
+ f.puts "test6"
372
+ }
373
+ }
374
+ # This test sometimes fails and it shows a potential bug of in_tail
375
+ # https://github.com/fluent/fluentd/issues/1434
376
+ assert_equal(6, events.length)
377
+ assert_equal({"message" => "test3"}, events[0][2])
378
+ assert_equal({"message" => "test4"}, events[1][2])
379
+ assert_equal({"message" => "test7"}, events[2][2])
380
+ assert_equal({"message" => "test8"}, events[3][2])
381
+ assert_equal({"message" => "test5"}, events[4][2])
382
+ assert_equal({"message" => "test6"}, events[5][2])
383
+ end
384
+
385
+ data(flat: SINGLE_LINE_CONFIG,
386
+ parse: PARSE_SINGLE_LINE_CONFIG)
387
+ def test_rotate_file_with_write_old_and_no_new_file(data)
388
+ config = data
389
+ events = sub_test_rotate_file(config, expect_emits: 2) { |rotated_file|
390
+ rotated_file.puts "test7"
391
+ rotated_file.puts "test8"
392
+ rotated_file.flush
393
+ }
394
+ assert_equal(4, events.length)
395
+ assert_equal({"message" => "test3"}, events[0][2])
396
+ assert_equal({"message" => "test4"}, events[1][2])
397
+ assert_equal({"message" => "test7"}, events[2][2])
398
+ assert_equal({"message" => "test8"}, events[3][2])
399
+ end
400
+
401
+ def sub_test_rotate_file(config = nil, expect_emits: nil, expect_records: nil, timeout: nil)
402
+ file = Fluent::FileWrapper.open("#{TMP_DIR}/tail.txt", "wb")
403
+ file.puts "test1"
404
+ file.puts "test2"
405
+ file.flush
406
+
407
+ d = create_driver(config)
408
+ d.run(expect_emits: expect_emits, expect_records: expect_records, timeout: timeout) do
409
+ size = d.emit_count
410
+ file.puts "test3"
411
+ file.puts "test4"
412
+ file.flush
413
+ sleep(0.1) until d.emit_count >= size + 1
414
+ size = d.emit_count
415
+
416
+ if Fluent.windows?
417
+ file.close
418
+ FileUtils.mv("#{TMP_DIR}/tail.txt", "#{TMP_DIR}/tail2.txt", force: true)
419
+ file = File.open("#{TMP_DIR}/tail.txt", "ab")
420
+ else
421
+ FileUtils.mv("#{TMP_DIR}/tail.txt", "#{TMP_DIR}/tail2.txt")
422
+ end
423
+ if block_given?
424
+ yield file
425
+ else
426
+ File.open("#{TMP_DIR}/tail.txt", "wb") { |f| }
427
+ sleep 1
428
+
429
+ File.open("#{TMP_DIR}/tail.txt", "ab") { |f|
430
+ f.puts "test5"
431
+ f.puts "test6"
432
+ }
433
+ end
434
+ end
435
+
436
+ d.events
437
+ ensure
438
+ file.close if file && !file.closed?
439
+ end
440
+ end
441
+
442
+ def test_lf
443
+ File.open("#{TMP_DIR}/tail.txt", "wb") {|f| }
444
+
445
+ d = create_driver
446
+
447
+ d.run(expect_emits: 1) do
448
+ File.open("#{TMP_DIR}/tail.txt", "ab") {|f|
449
+ f.print "test3"
450
+ }
451
+ sleep 1
452
+
453
+ File.open("#{TMP_DIR}/tail.txt", "ab") {|f|
454
+ f.puts "test4"
455
+ }
456
+ end
457
+
458
+ events = d.events
459
+ assert_equal(true, events.length > 0)
460
+ assert_equal({"message" => "test3test4"}, events[0][2])
461
+ end
462
+
463
+ def test_whitespace
464
+ File.open("#{TMP_DIR}/tail.txt", "wb") {|f| }
465
+
466
+ d = create_driver
467
+
468
+ d.run(expect_emits: 1) do
469
+ File.open("#{TMP_DIR}/tail.txt", "ab") {|f|
470
+ f.puts " " # 4 spaces
471
+ f.puts " 4 spaces"
472
+ f.puts "4 spaces "
473
+ f.puts " " # tab
474
+ f.puts " tab"
475
+ f.puts "tab "
476
+ }
477
+ end
478
+
479
+ events = d.events
480
+ assert_equal(true, events.length > 0)
481
+ assert_equal({"message" => " "}, events[0][2])
482
+ assert_equal({"message" => " 4 spaces"}, events[1][2])
483
+ assert_equal({"message" => "4 spaces "}, events[2][2])
484
+ assert_equal({"message" => " "}, events[3][2])
485
+ assert_equal({"message" => " tab"}, events[4][2])
486
+ assert_equal({"message" => "tab "}, events[5][2])
487
+ end
488
+
489
+ data(
490
+ 'flat default encoding' => [SINGLE_LINE_CONFIG, Encoding::ASCII_8BIT],
491
+ 'flat explicit encoding config' => [SINGLE_LINE_CONFIG + config_element("", "", { "encoding" => "utf-8" }), Encoding::UTF_8],
492
+ 'parse default encoding' => [PARSE_SINGLE_LINE_CONFIG, Encoding::ASCII_8BIT],
493
+ 'parse explicit encoding config' => [PARSE_SINGLE_LINE_CONFIG + config_element("", "", { "encoding" => "utf-8" }), Encoding::UTF_8])
494
+ def test_encoding(data)
495
+ encoding_config, encoding = data
496
+
497
+ d = create_driver(CONFIG_READ_FROM_HEAD + encoding_config)
498
+
499
+ d.run(expect_emits: 1) do
500
+ File.open("#{TMP_DIR}/tail.txt", "wb") {|f|
501
+ f.puts "test"
502
+ }
503
+ end
504
+
505
+ events = d.events
506
+ assert_equal(encoding, events[0][2]['message'].encoding)
507
+ end
508
+
509
+ def test_from_encoding
510
+ conf = config_element(
511
+ "", "", {
512
+ "format" => "/(?<message>.*)/",
513
+ "read_from_head" => "true",
514
+ "from_encoding" => "cp932",
515
+ "encoding" => "utf-8"
516
+ })
517
+ d = create_driver(conf)
518
+ cp932_message = "\x82\xCD\x82\xEB\x81\x5B\x82\xED\x81\x5B\x82\xE9\x82\xC7".force_encoding(Encoding::CP932)
519
+ utf8_message = cp932_message.encode(Encoding::UTF_8)
520
+
521
+ d.run(expect_emits: 1) do
522
+ File.open("#{TMP_DIR}/tail.txt", "w:cp932") {|f|
523
+ f.puts cp932_message
524
+ }
525
+ end
526
+
527
+ events = d.events
528
+ assert_equal(utf8_message, events[0][2]['message'])
529
+ assert_equal(Encoding::UTF_8, events[0][2]['message'].encoding)
530
+ end
531
+
532
+ def test_from_encoding_utf16
533
+ conf = config_element(
534
+ "", "", {
535
+ "format" => "/(?<message>.*)/",
536
+ "read_from_head" => "true",
537
+ "from_encoding" => "utf-16le",
538
+ "encoding" => "utf-8"
539
+ })
540
+ d = create_driver(conf)
541
+ utf16_message = "\u306F\u308D\u30FC\u308F\u30FC\u308B\u3069\n".encode(Encoding::UTF_16LE)
542
+ utf8_message = utf16_message.encode(Encoding::UTF_8).strip
543
+
544
+ d.run(expect_emits: 1) do
545
+ File.open("#{TMP_DIR}/tail.txt", "w:utf-16le") { |f|
546
+ f.write utf16_message
547
+ }
548
+ end
549
+
550
+ events = d.events
551
+ assert_equal(utf8_message, events[0][2]['message'])
552
+ assert_equal(Encoding::UTF_8, events[0][2]['message'].encoding)
553
+ end
554
+
555
+
556
+ sub_test_case "multiline" do
557
+ data(flat: MULTILINE_CONFIG,
558
+ parse: PARSE_MULTILINE_CONFIG)
559
+ def test_multiline(data)
560
+ config = data
561
+ File.open("#{TMP_DIR}/tail.txt", "wb") { |f| }
562
+
563
+ d = create_driver(config)
564
+ d.run(expect_emits: 1) do
565
+ File.open("#{TMP_DIR}/tail.txt", "ab") { |f|
566
+ f.puts "f test1"
567
+ f.puts "s test2"
568
+ f.puts "f test3"
569
+ f.puts "f test4"
570
+ f.puts "s test5"
571
+ f.puts "s test6"
572
+ f.puts "f test7"
573
+ f.puts "s test8"
574
+ }
575
+ end
576
+
577
+ events = d.events
578
+ assert_equal(4, events.length)
579
+ assert_equal({"message1" => "test2", "message2" => "test3", "message3" => "test4"}, events[0][2])
580
+ assert_equal({"message1" => "test5"}, events[1][2])
581
+ assert_equal({"message1" => "test6", "message2" => "test7"}, events[2][2])
582
+ assert_equal({"message1" => "test8"}, events[3][2])
583
+ end
584
+
585
+ data(flat: MULTILINE_CONFIG,
586
+ parse: PARSE_MULTILINE_CONFIG)
587
+ def test_multiline_with_emit_unmatched_lines_true(data)
588
+ config = data + config_element("", "", { "emit_unmatched_lines" => true })
589
+ File.open("#{TMP_DIR}/tail.txt", "wb") { |f| }
590
+
591
+ d = create_driver(config)
592
+ d.run(expect_emits: 1) do
593
+ File.open("#{TMP_DIR}/tail.txt", "ab") { |f|
594
+ f.puts "f test1"
595
+ f.puts "s test2"
596
+ f.puts "f test3"
597
+ f.puts "f test4"
598
+ f.puts "s test5"
599
+ f.puts "s test6"
600
+ f.puts "f test7"
601
+ f.puts "s test8"
602
+ }
603
+ end
604
+
605
+ events = d.events
606
+ assert_equal(5, events.length)
607
+ assert_equal({"unmatched_line" => "f test1"}, events[0][2])
608
+ assert_equal({"message1" => "test2", "message2" => "test3", "message3" => "test4"}, events[1][2])
609
+ assert_equal({"message1" => "test5"}, events[2][2])
610
+ assert_equal({"message1" => "test6", "message2" => "test7"}, events[3][2])
611
+ assert_equal({"message1" => "test8"}, events[4][2])
612
+ end
613
+
614
+ data(flat: MULTILINE_CONFIG,
615
+ parse: PARSE_MULTILINE_CONFIG)
616
+ def test_multiline_with_flush_interval(data)
617
+ File.open("#{TMP_DIR}/tail.txt", "wb") { |f| }
618
+
619
+ config = data + config_element("", "", { "multiline_flush_interval" => "2s" })
620
+ d = create_driver(config)
621
+
622
+ assert_equal 2, d.instance.multiline_flush_interval
623
+
624
+ d.run(expect_emits: 1) do
625
+ File.open("#{TMP_DIR}/tail.txt", "ab") { |f|
626
+ f.puts "f test1"
627
+ f.puts "s test2"
628
+ f.puts "f test3"
629
+ f.puts "f test4"
630
+ f.puts "s test5"
631
+ f.puts "s test6"
632
+ f.puts "f test7"
633
+ f.puts "s test8"
634
+ }
635
+ end
636
+
637
+ events = d.events
638
+ assert_equal(4, events.length)
639
+ assert_equal({"message1" => "test2", "message2" => "test3", "message3" => "test4"}, events[0][2])
640
+ assert_equal({"message1" => "test5"}, events[1][2])
641
+ assert_equal({"message1" => "test6", "message2" => "test7"}, events[2][2])
642
+ assert_equal({"message1" => "test8"}, events[3][2])
643
+ end
644
+
645
+ data(
646
+ 'flat default encoding' => [MULTILINE_CONFIG, Encoding::ASCII_8BIT],
647
+ 'flat explicit encoding config' => [MULTILINE_CONFIG + config_element("", "", { "encoding" => "utf-8" }), Encoding::UTF_8],
648
+ 'parse default encoding' => [PARSE_MULTILINE_CONFIG, Encoding::ASCII_8BIT],
649
+ 'parse explicit encoding config' => [PARSE_MULTILINE_CONFIG + config_element("", "", { "encoding" => "utf-8" }), Encoding::UTF_8])
650
+ def test_multiline_encoding_of_flushed_record(data)
651
+ encoding_config, encoding = data
652
+
653
+ config = config_element("", "", {
654
+ "multiline_flush_interval" => "2s",
655
+ "read_from_head" => "true",
656
+ })
657
+ d = create_driver(config + encoding_config)
658
+
659
+ d.run(expect_emits: 1) do
660
+ File.open("#{TMP_DIR}/tail.txt", "wb") { |f|
661
+ f.puts "s test"
662
+ }
663
+ end
664
+ events = d.events
665
+ assert_equal(1, events.length)
666
+ assert_equal(encoding, events[0][2]['message1'].encoding)
667
+ end
668
+
669
+ def test_multiline_from_encoding_of_flushed_record
670
+ conf = MULTILINE_CONFIG + config_element(
671
+ "", "",
672
+ {
673
+ "multiline_flush_interval" => "1s",
674
+ "read_from_head" => "true",
675
+ "from_encoding" => "cp932",
676
+ "encoding" => "utf-8"
677
+ })
678
+ d = create_driver(conf)
679
+
680
+ cp932_message = "s \x82\xCD\x82\xEB\x81\x5B\x82\xED\x81\x5B\x82\xE9\x82\xC7".force_encoding(Encoding::CP932)
681
+ utf8_message = "\x82\xCD\x82\xEB\x81\x5B\x82\xED\x81\x5B\x82\xE9\x82\xC7".encode(Encoding::UTF_8, Encoding::CP932)
682
+ d.run(expect_emits: 1) do
683
+ File.open("#{TMP_DIR}/tail.txt", "w:cp932") { |f|
684
+ f.puts cp932_message
685
+ }
686
+ end
687
+
688
+ events = d.events
689
+ assert_equal(1, events.length)
690
+ assert_equal(utf8_message, events[0][2]['message1'])
691
+ assert_equal(Encoding::UTF_8, events[0][2]['message1'].encoding)
692
+ end
693
+
694
+ data(flat: config_element(
695
+ "", "", {
696
+ "format" => "multiline",
697
+ "format1" => "/^s (?<message1>[^\\n]+)\\n?/",
698
+ "format2" => "/(f (?<message2>[^\\n]+)\\n?)?/",
699
+ "format3" => "/(f (?<message3>.*))?/",
700
+ "format_firstline" => "/^[s]/"
701
+ }),
702
+ parse: config_element(
703
+ "", "", {},
704
+ [config_element("parse", "", {
705
+ "@type" => "multiline",
706
+ "format1" => "/^s (?<message1>[^\\n]+)\\n?/",
707
+ "format2" => "/(f (?<message2>[^\\n]+)\\n?)?/",
708
+ "format3" => "/(f (?<message3>.*))?/",
709
+ "format_firstline" => "/^[s]/"
710
+ })
711
+ ])
712
+ )
713
+ def test_multiline_with_multiple_formats(data)
714
+ config = data
715
+ File.open("#{TMP_DIR}/tail.txt", "wb") { |f| }
716
+
717
+ d = create_driver(config)
718
+ d.run(expect_emits: 1) do
719
+ File.open("#{TMP_DIR}/tail.txt", "ab") { |f|
720
+ f.puts "f test1"
721
+ f.puts "s test2"
722
+ f.puts "f test3"
723
+ f.puts "f test4"
724
+ f.puts "s test5"
725
+ f.puts "s test6"
726
+ f.puts "f test7"
727
+ f.puts "s test8"
728
+ }
729
+ end
730
+
731
+ events = d.events
732
+ assert(events.length > 0)
733
+ assert_equal({"message1" => "test2", "message2" => "test3", "message3" => "test4"}, events[0][2])
734
+ assert_equal({"message1" => "test5"}, events[1][2])
735
+ assert_equal({"message1" => "test6", "message2" => "test7"}, events[2][2])
736
+ assert_equal({"message1" => "test8"}, events[3][2])
737
+ end
738
+
739
+ data(flat: config_element(
740
+ "", "", {
741
+ "format" => "multiline",
742
+ "format1" => "/^[s|f] (?<message>.*)/",
743
+ "format_firstline" => "/^[s]/"
744
+ }),
745
+ parse: config_element(
746
+ "", "", {},
747
+ [config_element("parse", "", {
748
+ "@type" => "multiline",
749
+ "format1" => "/^[s|f] (?<message>.*)/",
750
+ "format_firstline" => "/^[s]/"
751
+ })
752
+ ])
753
+ )
754
+ def test_multilinelog_with_multiple_paths(data)
755
+ files = ["#{TMP_DIR}/tail1.txt", "#{TMP_DIR}/tail2.txt"]
756
+ files.each { |file| File.open(file, "wb") { |f| } }
757
+
758
+ config = data + config_element("", "", {
759
+ "path" => "#{files[0]},#{files[1]}",
760
+ "tag" => "t1",
761
+ })
762
+ d = create_driver(config, false)
763
+ d.run(expect_emits: 2) do
764
+ files.each do |file|
765
+ File.open(file, 'ab') { |f|
766
+ f.puts "f #{file} line should be ignored"
767
+ f.puts "s test1"
768
+ f.puts "f test2"
769
+ f.puts "f test3"
770
+ f.puts "s test4"
771
+ }
772
+ end
773
+ end
774
+
775
+ events = d.events
776
+ assert_equal({"message" => "test1\nf test2\nf test3"}, events[0][2])
777
+ assert_equal({"message" => "test1\nf test2\nf test3"}, events[1][2])
778
+ # "test4" events are here because these events are flushed at shutdown phase
779
+ assert_equal({"message" => "test4"}, events[2][2])
780
+ assert_equal({"message" => "test4"}, events[3][2])
781
+ end
782
+
783
+ data(flat: config_element("", "", {
784
+ "format" => "multiline",
785
+ "format1" => "/(?<var1>foo \\d)\\n/",
786
+ "format2" => "/(?<var2>bar \\d)\\n/",
787
+ "format3" => "/(?<var3>baz \\d)/"
788
+ }),
789
+ parse: config_element(
790
+ "", "", {},
791
+ [config_element("parse", "", {
792
+ "@type" => "multiline",
793
+ "format1" => "/(?<var1>foo \\d)\\n/",
794
+ "format2" => "/(?<var2>bar \\d)\\n/",
795
+ "format3" => "/(?<var3>baz \\d)/"
796
+ })
797
+ ])
798
+ )
799
+ def test_multiline_without_firstline(data)
800
+ File.open("#{TMP_DIR}/tail.txt", "wb") { |f| }
801
+
802
+ config = data
803
+ d = create_driver(config)
804
+ d.run(expect_emits: 1) do
805
+ File.open("#{TMP_DIR}/tail.txt", "ab") { |f|
806
+ f.puts "foo 1"
807
+ f.puts "bar 1"
808
+ f.puts "baz 1"
809
+ f.puts "foo 2"
810
+ f.puts "bar 2"
811
+ f.puts "baz 2"
812
+ }
813
+ end
814
+
815
+ events = d.events
816
+ assert_equal(2, events.length)
817
+ assert_equal({"var1" => "foo 1", "var2" => "bar 1", "var3" => "baz 1"}, events[0][2])
818
+ assert_equal({"var1" => "foo 2", "var2" => "bar 2", "var3" => "baz 2"}, events[1][2])
819
+ end
820
+ end
821
+
822
+ sub_test_case "path" do
823
+ # * path test
824
+ # TODO: Clean up tests
825
+ EX_RORATE_WAIT = 0
826
+
827
+ EX_CONFIG = config_element("", "", {
828
+ "tag" => "tail",
829
+ "path" => "test/plugin/*/%Y/%m/%Y%m%d-%H%M%S.log,test/plugin/data/log/**/*.log",
830
+ "format" => "none",
831
+ "pos_file" => "#{TMP_DIR}/tail.pos",
832
+ "read_from_head" => true,
833
+ "refresh_interval" => 30,
834
+ "rotate_wait" => "#{EX_RORATE_WAIT}s",
835
+ })
836
+ EX_PATHS = [
837
+ 'test/plugin/data/2010/01/20100102-030405.log',
838
+ 'test/plugin/data/log/foo/bar.log',
839
+ 'test/plugin/data/log/test.log'
840
+ ]
841
+
842
+ def test_expand_paths
843
+ plugin = create_driver(EX_CONFIG, false).instance
844
+ flexstub(Time) do |timeclass|
845
+ timeclass.should_receive(:now).with_no_args.and_return(Time.new(2010, 1, 2, 3, 4, 5))
846
+ assert_equal EX_PATHS, plugin.expand_paths.sort
847
+ end
848
+
849
+ # Test exclusion
850
+ exclude_config = EX_CONFIG + config_element("", "", { "exclude_path" => %Q(["#{EX_PATHS.last}"]) })
851
+ plugin = create_driver(exclude_config, false).instance
852
+ assert_equal EX_PATHS - [EX_PATHS.last], plugin.expand_paths.sort
853
+ end
854
+
855
+ def test_log_file_without_extension
856
+ expected_files = [
857
+ 'test/plugin/data/log/bar',
858
+ 'test/plugin/data/log/foo/bar.log',
859
+ 'test/plugin/data/log/foo/bar2',
860
+ 'test/plugin/data/log/test.log'
861
+ ]
862
+
863
+ config = config_element("", "", {
864
+ "tag" => "tail",
865
+ "path" => "test/plugin/data/log/**/*",
866
+ "format" => "none",
867
+ "pos_file" => "#{TMP_DIR}/tail.pos"
868
+ })
869
+
870
+ plugin = create_driver(config, false).instance
871
+ assert_equal expected_files, plugin.expand_paths.sort
872
+ end
873
+
874
+ # For https://github.com/fluent/fluentd/issues/1455
875
+ # This test is fragile because test content depends on internal implementaion.
876
+ # So if you modify in_tail internal, this test may break.
877
+ def test_unwatched_files_should_be_removed
878
+ config = config_element("", "", {
879
+ "tag" => "tail",
880
+ "path" => "#{TMP_DIR}/*.txt",
881
+ "format" => "none",
882
+ "pos_file" => "#{TMP_DIR}/tail.pos",
883
+ "read_from_head" => true,
884
+ "refresh_interval" => 1,
885
+ })
886
+ d = create_driver(config, false)
887
+ d.run(expect_emits: 1, shutdown: false) do
888
+ File.open("#{TMP_DIR}/tail.txt", "ab") { |f| f.puts "test3\n" }
889
+ end
890
+
891
+ assert_equal 1, d.instance.instance_variable_get(:@tails).keys.size
892
+ cleanup_directory(TMP_DIR)
893
+ waiting(20) { sleep 0.1 until Dir.glob("#{TMP_DIR}/*.txt").size == 0 } # Ensure file is deleted on Windows
894
+ waiting(5) { sleep 0.1 until d.instance.instance_variable_get(:@tails).keys.size == 0 }
895
+
896
+ # Previous implementaion has an infinite watcher creation bug.
897
+ # Following code checks such unexpected bug by couting actual object allocation.
898
+ base_num = count_timer_object
899
+ 2.times {
900
+ sleep 1
901
+ num = count_timer_object
902
+ assert_equal base_num, num
903
+ }
904
+
905
+ d.instance_shutdown
906
+ end
907
+
908
+ def count_timer_object
909
+ num = 0
910
+ ObjectSpace.each_object(Fluent::PluginHelper::Timer::TimerWatcher) { |obj|
911
+ num += 1
912
+ }
913
+ num
914
+ end
915
+ end
916
+
917
+ def test_z_refresh_watchers
918
+ plugin = create_driver(EX_CONFIG, false).instance
919
+ sio = StringIO.new
920
+ plugin.instance_eval do
921
+ @pf = Fluent::Plugin::TailInput::PositionFile.parse(sio)
922
+ @loop = Coolio::Loop.new
923
+ end
924
+
925
+ Timecop.freeze(2010, 1, 2, 3, 4, 5) do
926
+ flexstub(Fluent::Plugin::TailInput::TailWatcher) do |watcherclass|
927
+ EX_PATHS.each do |path|
928
+ watcherclass.should_receive(:new).with(path, EX_RORATE_WAIT, Fluent::Plugin::TailInput::FilePositionEntry, any, true, true, 1000, any, any, any, any, any, any).once.and_return do
929
+ flexmock('TailWatcher') { |watcher|
930
+ watcher.should_receive(:attach).once
931
+ watcher.should_receive(:unwatched=).zero_or_more_times
932
+ watcher.should_receive(:line_buffer).zero_or_more_times
933
+ }
934
+ end
935
+ end
936
+ plugin.refresh_watchers
937
+ end
938
+ end
939
+
940
+ plugin.instance_eval do
941
+ @tails['test/plugin/data/2010/01/20100102-030405.log'].should_receive(:close).zero_or_more_times
942
+ end
943
+
944
+ Timecop.freeze(2010, 1, 2, 3, 4, 6) do
945
+ flexstub(Fluent::Plugin::TailInput::TailWatcher) do |watcherclass|
946
+ watcherclass.should_receive(:new).with('test/plugin/data/2010/01/20100102-030406.log', EX_RORATE_WAIT, Fluent::Plugin::TailInput::FilePositionEntry, any, true, true, 1000, any, any, any, any, any, any).once.and_return do
947
+ flexmock('TailWatcher') do |watcher|
948
+ watcher.should_receive(:attach).once
949
+ watcher.should_receive(:unwatched=).zero_or_more_times
950
+ watcher.should_receive(:line_buffer).zero_or_more_times
951
+ end
952
+ end
953
+ plugin.refresh_watchers
954
+ end
955
+
956
+ flexstub(Fluent::Plugin::TailInput::TailWatcher) do |watcherclass|
957
+ watcherclass.should_receive(:new).never
958
+ plugin.refresh_watchers
959
+ end
960
+ end
961
+ end
962
+
963
+ sub_test_case "receive_lines" do
964
+ DummyWatcher = Struct.new("DummyWatcher", :tag)
965
+
966
+ def test_tag
967
+ d = create_driver(EX_CONFIG, false)
968
+ d.run {}
969
+ plugin = d.instance
970
+ mock(plugin.router).emit_stream('tail', anything).once
971
+ plugin.receive_lines(['foo', 'bar'], DummyWatcher.new('foo.bar.log'))
972
+ end
973
+
974
+ def test_tag_prefix
975
+ config = config_element("", "", {
976
+ "tag" => "pre.*",
977
+ "path" => "test/plugin/*/%Y/%m/%Y%m%d-%H%M%S.log,test/plugin/data/log/**/*.log",
978
+ "format" => "none",
979
+ "read_from_head" => true
980
+ })
981
+ d = create_driver(config, false)
982
+ d.run {}
983
+ plugin = d.instance
984
+ mock(plugin.router).emit_stream('pre.foo.bar.log', anything).once
985
+ plugin.receive_lines(['foo', 'bar'], DummyWatcher.new('foo.bar.log'))
986
+ end
987
+
988
+ def test_tag_suffix
989
+ config = config_element("", "", {
990
+ "tag" => "*.post",
991
+ "path" => "test/plugin/*/%Y/%m/%Y%m%d-%H%M%S.log,test/plugin/data/log/**/*.log",
992
+ "format" => "none",
993
+ "read_from_head" => true
994
+ })
995
+ d = create_driver(config, false)
996
+ d.run {}
997
+ plugin = d.instance
998
+ mock(plugin.router).emit_stream('foo.bar.log.post', anything).once
999
+ plugin.receive_lines(['foo', 'bar'], DummyWatcher.new('foo.bar.log'))
1000
+ end
1001
+
1002
+ def test_tag_prefix_and_suffix
1003
+ config = config_element("", "", {
1004
+ "tag" => "pre.*.post",
1005
+ "path" => "test/plugin/*/%Y/%m/%Y%m%d-%H%M%S.log,test/plugin/data/log/**/*.log",
1006
+ "format" => "none",
1007
+ "read_from_head" => true
1008
+ })
1009
+ d = create_driver(config, false)
1010
+ d.run {}
1011
+ plugin = d.instance
1012
+ mock(plugin.router).emit_stream('pre.foo.bar.log.post', anything).once
1013
+ plugin.receive_lines(['foo', 'bar'], DummyWatcher.new('foo.bar.log'))
1014
+ end
1015
+
1016
+ def test_tag_prefix_and_suffix_ignore
1017
+ config = config_element("", "", {
1018
+ "tag" => "pre.*.post*ignore",
1019
+ "path" => "test/plugin/*/%Y/%m/%Y%m%d-%H%M%S.log,test/plugin/data/log/**/*.log",
1020
+ "format" => "none",
1021
+ "read_from_head" => true
1022
+ })
1023
+ d = create_driver(config, false)
1024
+ d.run {}
1025
+ plugin = d.instance
1026
+ mock(plugin.router).emit_stream('pre.foo.bar.log.post', anything).once
1027
+ plugin.receive_lines(['foo', 'bar'], DummyWatcher.new('foo.bar.log'))
1028
+ end
1029
+ end
1030
+
1031
+ # Ensure that no fatal exception is raised when a file is missing and that
1032
+ # files that do exist are still tailed as expected.
1033
+ def test_missing_file
1034
+ File.open("#{TMP_DIR}/tail.txt", "wb") {|f|
1035
+ f.puts "test1"
1036
+ f.puts "test2"
1037
+ }
1038
+
1039
+ # Try two different configs - one with read_from_head and one without,
1040
+ # since their interactions with the filesystem differ.
1041
+ config1 = config_element("", "", {
1042
+ "tag" => "t1",
1043
+ "path" => "#{TMP_DIR}/non_existent_file.txt,#{TMP_DIR}/tail.txt",
1044
+ "format" => "none",
1045
+ "rotate_wait" => "2s",
1046
+ "pos_file" => "#{TMP_DIR}/tail.pos"
1047
+ })
1048
+ config2 = config1 + config_element("", "", { "read_from_head" => true })
1049
+ [config1, config2].each do |config|
1050
+ d = create_driver(config, false)
1051
+ d.run(expect_emits: 1) do
1052
+ File.open("#{TMP_DIR}/tail.txt", "ab") {|f|
1053
+ f.puts "test3"
1054
+ f.puts "test4"
1055
+ }
1056
+ end
1057
+ # This test sometimes fails and it shows a potential bug of in_tail
1058
+ # https://github.com/fluent/fluentd/issues/1434
1059
+ events = d.events
1060
+ assert_equal(2, events.length)
1061
+ assert_equal({"message" => "test3"}, events[0][2])
1062
+ assert_equal({"message" => "test4"}, events[1][2])
1063
+ end
1064
+ end
1065
+
1066
+ sub_test_case "tail_path" do
1067
+ def test_tail_path_with_singleline
1068
+ File.open("#{TMP_DIR}/tail.txt", "wb") {|f|
1069
+ f.puts "test1"
1070
+ f.puts "test2"
1071
+ }
1072
+
1073
+ d = create_driver(SINGLE_LINE_CONFIG + config_element("", "", { "path_key" => "path" }))
1074
+
1075
+ d.run(expect_emits: 1) do
1076
+ File.open("#{TMP_DIR}/tail.txt", "ab") {|f|
1077
+ f.puts "test3"
1078
+ f.puts "test4"
1079
+ }
1080
+ end
1081
+
1082
+ events = d.events
1083
+ assert_equal(true, events.length > 0)
1084
+ events.each do |emit|
1085
+ assert_equal("#{TMP_DIR}/tail.txt", emit[2]["path"])
1086
+ end
1087
+ end
1088
+
1089
+ def test_tail_path_with_multiline_with_firstline
1090
+ File.open("#{TMP_DIR}/tail.txt", "wb") { |f| }
1091
+
1092
+ config = config_element("", "", {
1093
+ "path_key" => "path",
1094
+ "format" => "multiline",
1095
+ "format1" => "/^s (?<message1>[^\\n]+)(\\nf (?<message2>[^\\n]+))?(\\nf (?<message3>.*))?/",
1096
+ "format_firstline" => "/^[s]/"
1097
+ })
1098
+ d = create_driver(config)
1099
+ d.run(expect_emits: 1) do
1100
+ File.open("#{TMP_DIR}/tail.txt", "ab") { |f|
1101
+ f.puts "f test1"
1102
+ f.puts "s test2"
1103
+ f.puts "f test3"
1104
+ f.puts "f test4"
1105
+ f.puts "s test5"
1106
+ f.puts "s test6"
1107
+ f.puts "f test7"
1108
+ f.puts "s test8"
1109
+ }
1110
+ end
1111
+
1112
+ events = d.events
1113
+ assert_equal(4, events.length)
1114
+ events.each do |emit|
1115
+ assert_equal("#{TMP_DIR}/tail.txt", emit[2]["path"])
1116
+ end
1117
+ end
1118
+
1119
+ def test_tail_path_with_multiline_without_firstline
1120
+ File.open("#{TMP_DIR}/tail.txt", "wb") { |f| }
1121
+
1122
+ config = config_element("", "", {
1123
+ "path_key" => "path",
1124
+ "format" => "multiline",
1125
+ "format1" => "/(?<var1>foo \\d)\\n/",
1126
+ "format2" => "/(?<var2>bar \\d)\\n/",
1127
+ "format3" => "/(?<var3>baz \\d)/",
1128
+ })
1129
+ d = create_driver(config)
1130
+ d.run(expect_emits: 1) do
1131
+ File.open("#{TMP_DIR}/tail.txt", "ab") { |f|
1132
+ f.puts "foo 1"
1133
+ f.puts "bar 1"
1134
+ f.puts "baz 1"
1135
+ }
1136
+ end
1137
+
1138
+ events = d.events
1139
+ assert(events.length > 0)
1140
+ events.each do |emit|
1141
+ assert_equal("#{TMP_DIR}/tail.txt", emit[2]["path"])
1142
+ end
1143
+ end
1144
+
1145
+ def test_tail_path_with_multiline_with_multiple_paths
1146
+ files = ["#{TMP_DIR}/tail1.txt", "#{TMP_DIR}/tail2.txt"]
1147
+ files.each { |file| File.open(file, "wb") { |f| } }
1148
+
1149
+ config = config_element("", "", {
1150
+ "path" => "#{files[0]},#{files[1]}",
1151
+ "path_key" => "path",
1152
+ "tag" => "t1",
1153
+ "format" => "multiline",
1154
+ "format1" => "/^[s|f] (?<message>.*)/",
1155
+ "format_firstline" => "/^[s]/"
1156
+ })
1157
+ d = create_driver(config, false)
1158
+ d.run(expect_emits: 2) do
1159
+ files.each do |file|
1160
+ File.open(file, 'ab') { |f|
1161
+ f.puts "f #{file} line should be ignored"
1162
+ f.puts "s test1"
1163
+ f.puts "f test2"
1164
+ f.puts "f test3"
1165
+ f.puts "s test4"
1166
+ }
1167
+ end
1168
+ end
1169
+
1170
+ events = d.events
1171
+ assert_equal(4, events.length)
1172
+ assert_equal(files, [events[0][2]["path"], events[1][2]["path"]].sort)
1173
+ # "test4" events are here because these events are flushed at shutdown phase
1174
+ assert_equal(files, [events[2][2]["path"], events[3][2]["path"]].sort)
1175
+ end
1176
+ end
1177
+
1178
+ def test_limit_recently_modified
1179
+ now = Time.new(2010, 1, 2, 3, 4, 5)
1180
+ FileUtils.touch("#{TMP_DIR}/tail_unwatch.txt", mtime: (now - 3601))
1181
+ FileUtils.touch("#{TMP_DIR}/tail_watch1.txt", mtime: (now - 3600))
1182
+ FileUtils.touch("#{TMP_DIR}/tail_watch2.txt", mtime: now)
1183
+
1184
+ config = config_element('', '', {
1185
+ 'tag' => 'tail',
1186
+ 'path' => "#{TMP_DIR}/*.txt",
1187
+ 'format' => 'none',
1188
+ 'limit_recently_modified' => '3600s'
1189
+ })
1190
+
1191
+ expected_files = [
1192
+ "#{TMP_DIR}/tail_watch1.txt",
1193
+ "#{TMP_DIR}/tail_watch2.txt"
1194
+ ]
1195
+
1196
+ Timecop.freeze(now) do
1197
+ plugin = create_driver(config, false).instance
1198
+ assert_equal expected_files, plugin.expand_paths.sort
1199
+ end
1200
+ end
1201
+
1202
+ def test_skip_refresh_on_startup
1203
+ FileUtils.touch("#{TMP_DIR}/tail.txt")
1204
+ config = config_element('', '', {
1205
+ 'format' => 'none',
1206
+ 'refresh_interval' => 1,
1207
+ 'skip_refresh_on_startup' => true
1208
+ })
1209
+ d = create_driver(config)
1210
+ d.run(shutdown: false) {}
1211
+ assert_equal 0, d.instance.instance_variable_get(:@tails).keys.size
1212
+ # detect a file at first execution of in_tail_refresh_watchers timer
1213
+ waiting(5) { sleep 0.1 until d.instance.instance_variable_get(:@tails).keys.size == 1 }
1214
+ d.instance_shutdown
1215
+ end
1216
+ end