fluentd 0.12.40 → 1.6.2

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