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,7 +1,10 @@
1
1
  require_relative '../helper'
2
- require 'fluent/test'
2
+ require 'fluent/test/driver/output'
3
+ require 'fluent/plugin/out_file'
3
4
  require 'fileutils'
4
5
  require 'time'
6
+ require 'timecop'
7
+ require 'zlib'
5
8
 
6
9
  class FileOutputTest < Test::Unit::TestCase
7
10
  def setup
@@ -11,109 +14,360 @@ class FileOutputTest < Test::Unit::TestCase
11
14
  end
12
15
 
13
16
  TMP_DIR = File.expand_path(File.dirname(__FILE__) + "/../tmp/out_file#{ENV['TEST_ENV_NUMBER']}")
14
- SYMLINK_PATH = File.expand_path("#{TMP_DIR}/current")
15
17
 
16
18
  CONFIG = %[
17
19
  path #{TMP_DIR}/out_file_test
18
20
  compress gz
19
21
  utc
22
+ <buffer>
23
+ timekey_use_utc true
24
+ </buffer>
20
25
  ]
21
26
 
22
- def create_driver(conf = CONFIG)
23
- Fluent::Test::TimeSlicedOutputTestDriver.new(Fluent::FileOutput).configure(conf)
27
+ def create_driver(conf = CONFIG, opts = {})
28
+ Fluent::Test::Driver::Output.new(Fluent::Plugin::FileOutput, opts: opts).configure(conf)
24
29
  end
25
30
 
26
- def with_timezone(timezone = 'UTC', &block)
27
- old = ENV['TZ']
28
- ENV['TZ'] = timezone
29
- output = yield
30
- ENV['TZ'] = old
31
- output
32
- end
31
+ sub_test_case 'configuration' do
32
+ test 'basic configuration' do
33
+ d = create_driver %[
34
+ path test_path
35
+ compress gz
36
+ ]
37
+ assert_equal 'test_path', d.instance.path
38
+ assert_equal :gz, d.instance.compress
39
+ assert_equal :gzip, d.instance.instance_eval{ @compress_method }
40
+ end
33
41
 
34
- def test_configure
35
- d = create_driver %[
36
- path test_path
37
- compress gz
38
- ]
39
- assert_equal 'test_path', d.instance.path
40
- assert_equal :gz, d.instance.compress
41
- end
42
+ test 'using root_dir for buffer path' do
43
+ system_conf_opts = {'root_dir' => File.join(TMP_DIR, 'testrootdir')}
44
+ buf_conf = config_element('buffer', '', {'flush_interval' => '1s'})
45
+ conf = config_element('match', '**', {'@id' => 'myout', 'path' => 'test_path', 'append' => 'true'}, [buf_conf])
46
+ d = create_driver(conf, system_conf_opts)
47
+
48
+ assert_equal 'test_path', d.instance.path
49
+ assert d.instance.append
42
50
 
43
- def test_path_writable
44
- assert_nothing_raised do
45
- create_driver %[path #{TMP_DIR}/test_path]
51
+ assert d.instance.buffer.respond_to?(:path) # file buffer
52
+ assert_equal 1, d.instance.buffer_config.flush_interval
53
+
54
+ assert_equal File.join(TMP_DIR, 'testrootdir', 'worker0', 'myout'), d.instance.plugin_root_dir
55
+
56
+ buffer_path_under_root_dir = File.join(TMP_DIR, 'testrootdir', 'worker0', 'myout', 'buffer', 'buffer.*.log')
57
+ assert_equal buffer_path_under_root_dir, d.instance.buffer.path
46
58
  end
47
59
 
48
- assert_nothing_raised do
49
- FileUtils.mkdir_p("#{TMP_DIR}/test_dir")
50
- File.chmod(0777, "#{TMP_DIR}/test_dir")
51
- create_driver %[path #{TMP_DIR}/test_dir/foo/bar/baz]
60
+ test 'path should be writable' do
61
+ assert_raise(Fluent::ConfigError.new("'path' parameter is required")) do
62
+ create_driver ""
63
+ end
64
+
65
+ assert_nothing_raised do
66
+ create_driver %[path #{TMP_DIR}/test_path]
67
+ end
68
+
69
+ assert_nothing_raised do
70
+ FileUtils.mkdir_p("#{TMP_DIR}/test_dir")
71
+ File.chmod(0777, "#{TMP_DIR}/test_dir")
72
+ create_driver %[path #{TMP_DIR}/test_dir/foo/bar/baz]
73
+ end
74
+
75
+ if Process.uid.nonzero?
76
+ assert_raise(Fluent::ConfigError) do
77
+ FileUtils.mkdir_p("#{TMP_DIR}/test_dir")
78
+ File.chmod(0555, "#{TMP_DIR}/test_dir")
79
+ create_driver %[path #{TMP_DIR}/test_dir/foo/bar/baz]
80
+ end
81
+ end
52
82
  end
53
83
 
54
- assert_raise(Fluent::ConfigError) do
55
- FileUtils.mkdir_p("#{TMP_DIR}/test_dir")
56
- File.chmod(0555, "#{TMP_DIR}/test_dir")
57
- create_driver %[path #{TMP_DIR}/test_dir/foo/bar/baz]
84
+ test 'default timezone is localtime' do
85
+ d = create_driver(%[path #{TMP_DIR}/out_file_test])
86
+ time = event_time("2011-01-02 13:14:15 UTC")
87
+
88
+ with_timezone(Fluent.windows? ? 'NST-8' : 'Asia/Taipei') do
89
+ d.run(default_tag: 'test') do
90
+ d.feed(time, {"a"=>1})
91
+ end
92
+ end
93
+ assert_equal 1, d.formatted.size
94
+ assert_equal %[2011-01-02T21:14:15+08:00\ttest\t{"a":1}\n], d.formatted[0]
58
95
  end
59
- end
60
96
 
61
- def test_default_localtime
62
- d = create_driver(%[path #{TMP_DIR}/out_file_test])
63
- time = Time.parse("2011-01-02 13:14:15 UTC").to_i
97
+ test 'no configuration error raised for basic configuration using "*" (v0.12 style)' do
98
+ conf = config_element('match', '**', {
99
+ 'path' => "#{TMP_DIR}/test_out.*.log",
100
+ 'time_slice_format' => '%Y%m%d',
101
+ })
102
+ assert_nothing_raised do
103
+ create_driver(conf)
104
+ end
105
+ end
64
106
 
65
- with_timezone('Asia/Taipei') do
66
- d.emit({"a"=>1}, time)
67
- d.expect_format %[2011-01-02T21:14:15+08:00\ttest\t{"a":1}\n]
68
- d.run
107
+ if Process.uid.nonzero?
108
+ test 'configuration error raised if specified directory via template is not writable' do
109
+ Timecop.freeze(Time.parse("2016-10-04 21:33:27 UTC")) do
110
+ conf = config_element('match', '**', {
111
+ 'path' => "#{TMP_DIR}/prohibited/${tag}/file.%Y%m%d.log",
112
+ }, [ config_element('buffer', 'time,tag', {'timekey' => 86400, 'timekey_zone' => '+0000'}) ])
113
+ FileUtils.mkdir_p("#{TMP_DIR}/prohibited")
114
+ File.chmod(0555, "#{TMP_DIR}/prohibited")
115
+ assert_raise Fluent::ConfigError.new("out_file: `#{TMP_DIR}/prohibited/a/file.20161004.log_**.log` is not writable") do
116
+ create_driver(conf)
117
+ end
118
+ end
119
+ end
120
+ end
121
+
122
+ test 'configuration using inject/format/buffer sections fully' do
123
+ conf = config_element('match', '**', {
124
+ 'path' => "#{TMP_DIR}/${tag}/${type}/conf_test.%Y%m%d.%H%M.log",
125
+ 'add_path_suffix' => 'false',
126
+ 'append' => "true",
127
+ 'symlink_path' => "#{TMP_DIR}/conf_test.current.log",
128
+ 'compress' => 'gzip',
129
+ 'recompress' => 'true',
130
+ }, [
131
+ config_element('inject', '', {
132
+ 'hostname_key' => 'hostname',
133
+ 'hostname' => 'testing.local',
134
+ 'tag_key' => 'tag',
135
+ 'time_key' => 'time',
136
+ 'time_type' => 'string',
137
+ 'time_format' => '%Y/%m/%d %H:%M:%S %z',
138
+ 'timezone' => '+0900',
139
+ }),
140
+ config_element('format', '', {
141
+ '@type' => 'out_file',
142
+ 'include_tag' => 'true',
143
+ 'include_time' => 'true',
144
+ 'delimiter' => 'COMMA',
145
+ 'time_type' => 'string',
146
+ 'time_format' => '%Y-%m-%d %H:%M:%S %z',
147
+ 'utc' => 'true',
148
+ }),
149
+ config_element('buffer', 'time,tag,type', {
150
+ '@type' => 'file',
151
+ 'timekey' => '15m',
152
+ 'timekey_wait' => '5s',
153
+ 'timekey_zone' => '+0000',
154
+ 'path' => "#{TMP_DIR}/buf_conf_test",
155
+ 'chunk_limit_size' => '50m',
156
+ 'total_limit_size' => '1g',
157
+ 'compress' => 'gzip',
158
+ }),
159
+ ])
160
+ assert_nothing_raised do
161
+ create_driver(conf)
162
+ end
163
+ end
164
+
165
+ test 'configured as secondary with primary using chunk_key_tag and not using chunk_key_time' do
166
+ require 'fluent/plugin/out_null'
167
+ conf = config_element('match', '**', {
168
+ }, [
169
+ config_element('buffer', 'tag', {
170
+ }),
171
+ config_element('secondary', '', {
172
+ '@type' => 'file',
173
+ 'path' => "#{TMP_DIR}/testing_to_dump_by_out_file",
174
+ }),
175
+ ])
176
+ assert_nothing_raised do
177
+ Fluent::Test::Driver::Output.new(Fluent::Plugin::NullOutput).configure(conf)
178
+ end
69
179
  end
70
180
  end
71
181
 
72
- def test_format
73
- d = create_driver
182
+ sub_test_case 'fully configured output' do
183
+ setup do
184
+ Timecop.freeze(Time.parse("2016-10-03 23:58:00 UTC"))
185
+ conf = config_element('match', '**', {
186
+ 'path' => "#{TMP_DIR}/${tag}/${type}/full.%Y%m%d.%H%M.log",
187
+ 'add_path_suffix' => 'false',
188
+ 'append' => "true",
189
+ 'symlink_path' => "#{TMP_DIR}/full.current.log",
190
+ 'compress' => 'gzip',
191
+ 'recompress' => 'true',
192
+ }, [
193
+ config_element('inject', '', {
194
+ 'hostname_key' => 'hostname',
195
+ 'hostname' => 'testing.local',
196
+ 'tag_key' => 'tag',
197
+ 'time_key' => 'time',
198
+ 'time_type' => 'string',
199
+ 'time_format' => '%Y/%m/%d %H:%M:%S %z',
200
+ 'timezone' => '+0900',
201
+ }),
202
+ config_element('format', '', {
203
+ '@type' => 'out_file',
204
+ 'include_tag' => 'true',
205
+ 'include_time' => 'true',
206
+ 'delimiter' => 'COMMA',
207
+ 'time_type' => 'string',
208
+ 'time_format' => '%Y-%m-%d %H:%M:%S %z',
209
+ 'utc' => 'true',
210
+ }),
211
+ config_element('buffer', 'time,tag,type', {
212
+ '@type' => 'file',
213
+ 'timekey' => '15m',
214
+ 'timekey_wait' => '5s',
215
+ 'timekey_zone' => '+0000',
216
+ 'path' => "#{TMP_DIR}/buf_full",
217
+ 'chunk_limit_size' => '50m',
218
+ 'total_limit_size' => '1g',
219
+ 'compress' => 'gzip',
220
+ }),
221
+ ])
222
+ @d = create_driver(conf)
223
+ end
74
224
 
75
- time = Time.parse("2011-01-02 13:14:15 UTC").to_i
76
- d.emit({"a"=>1}, time)
77
- d.emit({"a"=>2}, time)
225
+ teardown do
226
+ FileUtils.rm_rf("#{TMP_DIR}/buf_full")
227
+ FileUtils.rm_rf("#{TMP_DIR}/my.data")
228
+ FileUtils.rm_rf("#{TMP_DIR}/your.data")
229
+ FileUtils.rm_rf("#{TMP_DIR}/full.current.log")
230
+ Timecop.return
231
+ end
78
232
 
79
- d.expect_format %[2011-01-02T13:14:15Z\ttest\t{"a":1}\n]
80
- d.expect_format %[2011-01-02T13:14:15Z\ttest\t{"a":2}\n]
233
+ test 'can format/write data correctly' do
234
+ d = @d
81
235
 
82
- d.run
83
- end
236
+ assert_equal 50*1024*1024, d.instance.buffer.chunk_limit_size
237
+ assert_equal 1*1024*1024*1024, d.instance.buffer.total_limit_size
84
238
 
85
- def test_timezone_1
86
- d = create_driver %[
87
- path #{TMP_DIR}/out_file_test
88
- timezone Asia/Taipei
89
- ]
239
+ assert !(File.symlink?("#{TMP_DIR}/full.current.log"))
90
240
 
91
- time = Time.parse("2011-01-02 13:14:15 UTC").to_i
241
+ t1 = event_time("2016-10-03 23:58:09 UTC")
242
+ t2 = event_time("2016-10-03 23:59:33 UTC")
243
+ t3 = event_time("2016-10-03 23:59:57 UTC")
244
+ t4 = event_time("2016-10-04 00:00:17 UTC")
245
+ t5 = event_time("2016-10-04 00:01:59 UTC")
92
246
 
93
- d.emit({"a"=>1}, time)
94
- d.expect_format %[2011-01-02T21:14:15+08:00\ttest\t{"a":1}\n]
95
- d.run
96
- end
247
+ Timecop.freeze(Time.parse("2016-10-03 23:58:30 UTC"))
97
248
 
98
- def test_timezone_2
99
- d = create_driver %[
100
- path #{TMP_DIR}/out_file_test
101
- timezone -03:30
102
- ]
249
+ d.run(start: true, flush: false, shutdown: false) do
250
+ d.feed('my.data', t1, {"type" => "a", "message" => "data raw content"})
251
+ d.feed('my.data', t2, {"type" => "a", "message" => "data raw content"})
252
+ d.feed('your.data', t3, {"type" => "a", "message" => "data raw content"})
253
+ end
254
+
255
+ assert_equal 3, d.formatted.size
256
+
257
+ assert Dir.exist?("#{TMP_DIR}/buf_full")
258
+ assert !(Dir.exist?("#{TMP_DIR}/my.data/a"))
259
+ assert !(Dir.exist?("#{TMP_DIR}/your.data/a"))
260
+ buffer_files = Dir.entries("#{TMP_DIR}/buf_full").reject{|e| e =~ /^\.+$/ }
261
+ assert_equal 2, buffer_files.select{|n| n.end_with?('.meta') }.size
262
+ assert_equal 2, buffer_files.select{|n| !n.end_with?('.meta') }.size
103
263
 
104
- time = Time.parse("2011-01-02 13:14:15 UTC").to_i
264
+ m1 = d.instance.metadata('my.data', t1, {"type" => "a"})
265
+ m2 = d.instance.metadata('your.data', t3, {"type" => "a"})
266
+
267
+ assert_equal 2, d.instance.buffer.stage.size
268
+ b1_path = d.instance.buffer.stage[m1].path
269
+ b1_size = File.lstat(b1_path).size
270
+
271
+ unless Fluent.windows?
272
+ assert File.symlink?("#{TMP_DIR}/full.current.log")
273
+ assert_equal d.instance.buffer.stage[m2].path, File.readlink("#{TMP_DIR}/full.current.log")
274
+ end
275
+
276
+ Timecop.freeze(Time.parse("2016-10-04 00:00:06 UTC"))
277
+
278
+ d.run(start: false, flush: true, shutdown: true) do
279
+ d.feed('my.data', t4, {"type" => "a", "message" => "data raw content"})
280
+ d.feed('your.data', t5, {"type" => "a", "message" => "data raw content"})
281
+ end
105
282
 
106
- d.emit({"a"=>1}, time)
107
- d.expect_format %[2011-01-02T09:44:15-03:30\ttest\t{"a":1}\n]
108
- d.run
283
+ assert Dir.exist?("#{TMP_DIR}/buf_full")
284
+ assert Dir.exist?("#{TMP_DIR}/my.data/a")
285
+ assert Dir.exist?("#{TMP_DIR}/your.data/a")
286
+
287
+ buffer_files = Dir.entries("#{TMP_DIR}/buf_full").reject{|e| e =~ /^\.+$/ }
288
+ assert_equal 0, buffer_files.size
289
+
290
+ assert File.exist?("#{TMP_DIR}/my.data/a/full.20161003.2345.log.gz")
291
+ assert File.exist?("#{TMP_DIR}/my.data/a/full.20161004.0000.log.gz")
292
+ assert File.exist?("#{TMP_DIR}/your.data/a/full.20161003.2345.log.gz")
293
+ assert File.exist?("#{TMP_DIR}/your.data/a/full.20161004.0000.log.gz")
294
+
295
+ assert{ File.lstat("#{TMP_DIR}/my.data/a/full.20161003.2345.log.gz").size < b1_size } # recompress
296
+
297
+ assert_equal 5, d.formatted.size
298
+
299
+ r1 = %!2016-10-03 23:58:09 +0000,my.data,{"type":"a","message":"data raw content","hostname":"testing.local","tag":"my.data","time":"2016/10/04 08:58:09 +0900"}\n!
300
+ r2 = %!2016-10-03 23:59:33 +0000,my.data,{"type":"a","message":"data raw content","hostname":"testing.local","tag":"my.data","time":"2016/10/04 08:59:33 +0900"}\n!
301
+ r3 = %!2016-10-03 23:59:57 +0000,your.data,{"type":"a","message":"data raw content","hostname":"testing.local","tag":"your.data","time":"2016/10/04 08:59:57 +0900"}\n!
302
+ r4 = %!2016-10-04 00:00:17 +0000,my.data,{"type":"a","message":"data raw content","hostname":"testing.local","tag":"my.data","time":"2016/10/04 09:00:17 +0900"}\n!
303
+ r5 = %!2016-10-04 00:01:59 +0000,your.data,{"type":"a","message":"data raw content","hostname":"testing.local","tag":"your.data","time":"2016/10/04 09:01:59 +0900"}\n!
304
+ assert_equal r1, d.formatted[0]
305
+ assert_equal r2, d.formatted[1]
306
+ assert_equal r3, d.formatted[2]
307
+ assert_equal r4, d.formatted[3]
308
+ assert_equal r5, d.formatted[4]
309
+
310
+ read_gunzip = ->(path){
311
+ File.open(path){ |fio|
312
+ Zlib::GzipReader.new(StringIO.new(fio.read)).read
313
+ }
314
+ }
315
+ assert_equal r1 + r2, read_gunzip.call("#{TMP_DIR}/my.data/a/full.20161003.2345.log.gz")
316
+ assert_equal r3, read_gunzip.call("#{TMP_DIR}/your.data/a/full.20161003.2345.log.gz")
317
+ assert_equal r4, read_gunzip.call("#{TMP_DIR}/my.data/a/full.20161004.0000.log.gz")
318
+ assert_equal r5, read_gunzip.call("#{TMP_DIR}/your.data/a/full.20161004.0000.log.gz")
319
+ end
109
320
  end
110
321
 
111
- def test_timezone_invalid
112
- assert_raise(Fluent::ConfigError) do
113
- create_driver %[
322
+ sub_test_case 'format' do
323
+ test 'timezone UTC specified' do
324
+ d = create_driver
325
+
326
+ time = event_time("2011-01-02 13:14:15 UTC")
327
+ d.run(default_tag: 'test') do
328
+ d.feed(time, {"a"=>1})
329
+ d.feed(time, {"a"=>2})
330
+ end
331
+ assert_equal 2, d.formatted.size
332
+ assert_equal %[2011-01-02T13:14:15Z\ttest\t{"a":1}\n], d.formatted[0]
333
+ assert_equal %[2011-01-02T13:14:15Z\ttest\t{"a":2}\n], d.formatted[1]
334
+ end
335
+
336
+ test 'time formatted with specified timezone, using area name' do
337
+ d = create_driver %[
338
+ path #{TMP_DIR}/out_file_test
339
+ timezone Asia/Taipei
340
+ ]
341
+
342
+ time = event_time("2011-01-02 13:14:15 UTC")
343
+ d.run(default_tag: 'test') do
344
+ d.feed(time, {"a"=>1})
345
+ end
346
+ assert_equal 1, d.formatted.size
347
+ assert_equal %[2011-01-02T21:14:15+08:00\ttest\t{"a":1}\n], d.formatted[0]
348
+ end
349
+
350
+ test 'time formatted with specified timezone, using offset' do
351
+ d = create_driver %[
114
352
  path #{TMP_DIR}/out_file_test
115
- timezone Invalid/Invalid
353
+ timezone -03:30
116
354
  ]
355
+
356
+ time = event_time("2011-01-02 13:14:15 UTC")
357
+ d.run(default_tag: 'test') do
358
+ d.feed(time, {"a"=>1})
359
+ end
360
+ assert_equal 1, d.formatted.size
361
+ assert_equal %[2011-01-02T09:44:15-03:30\ttest\t{"a":1}\n], d.formatted[0]
362
+ end
363
+
364
+ test 'configuration error raised for invalid timezone' do
365
+ assert_raise(Fluent::ConfigError) do
366
+ create_driver %[
367
+ path #{TMP_DIR}/out_file_test
368
+ timezone Invalid/Invalid
369
+ ]
370
+ end
117
371
  end
118
372
  end
119
373
 
@@ -121,9 +375,9 @@ class FileOutputTest < Test::Unit::TestCase
121
375
  # Zlib::GzipReader has a bug of concatenated file: https://bugs.ruby-lang.org/issues/9790
122
376
  # Following code from https://www.ruby-forum.com/topic/971591#979520
123
377
  result = ''
124
- File.open(path) { |io|
378
+ File.open(path, "rb") { |io|
125
379
  loop do
126
- gzr = Zlib::GzipReader.new(io)
380
+ gzr = Zlib::GzipReader.new(StringIO.new(io.read))
127
381
  result << gzr.read
128
382
  unused = gzr.unused
129
383
  gzr.finish
@@ -135,89 +389,149 @@ class FileOutputTest < Test::Unit::TestCase
135
389
  assert_equal expect, result
136
390
  end
137
391
 
138
- def test_write
139
- d = create_driver
392
+ sub_test_case 'write' do
393
+ test 'basic case' do
394
+ d = create_driver
140
395
 
141
- time = Time.parse("2011-01-02 13:14:15 UTC").to_i
142
- d.emit({"a"=>1}, time)
143
- d.emit({"a"=>2}, time)
396
+ assert_false File.exist?("#{TMP_DIR}/out_file_test.20110102_0.log.gz")
144
397
 
145
- # FileOutput#write returns path
146
- paths = d.run
147
- expect_paths = ["#{TMP_DIR}/out_file_test.20110102_0.log.gz"]
148
- assert_equal expect_paths, paths
398
+ time = event_time("2011-01-02 13:14:15 UTC")
399
+ d.run(default_tag: 'test') do
400
+ d.feed(time, {"a"=>1})
401
+ d.feed(time, {"a"=>2})
402
+ end
149
403
 
150
- check_gzipped_result(paths[0], %[2011-01-02T13:14:15Z\ttest\t{"a":1}\n] + %[2011-01-02T13:14:15Z\ttest\t{"a":2}\n])
404
+ assert File.exist?("#{TMP_DIR}/out_file_test.20110102_0.log.gz")
405
+ check_gzipped_result("#{TMP_DIR}/out_file_test.20110102_0.log.gz", %[2011-01-02T13:14:15Z\ttest\t{"a":1}\n] + %[2011-01-02T13:14:15Z\ttest\t{"a":2}\n])
406
+ end
151
407
  end
152
408
 
153
- def test_write_with_format_json
154
- d = create_driver [CONFIG, 'format json', 'include_time_key true', 'time_as_epoch'].join("\n")
409
+ sub_test_case 'file/directory permissions' do
410
+ TMP_DIR_WITH_SYSTEM = File.expand_path(File.dirname(__FILE__) + "/../tmp/out_file_system#{ENV['TEST_ENV_NUMBER']}")
411
+ # 0750 interprets as "488". "488".to_i(8) # => 4. So, it makes wrong permission. Umm....
412
+ OVERRIDE_DIR_PERMISSION = 750
413
+ OVERRIDE_FILE_PERMISSION = 0620
414
+ CONFIG_WITH_SYSTEM = %[
415
+ path #{TMP_DIR_WITH_SYSTEM}/out_file_test
416
+ compress gz
417
+ utc
418
+ <buffer>
419
+ timekey_use_utc true
420
+ </buffer>
421
+ <system>
422
+ file_permission #{OVERRIDE_FILE_PERMISSION}
423
+ dir_permission #{OVERRIDE_DIR_PERMISSION}
424
+ </system>
425
+ ]
426
+
427
+ setup do
428
+ omit "NTFS doesn't support UNIX like permissions" if Fluent.windows?
429
+ FileUtils.rm_rf(TMP_DIR_WITH_SYSTEM)
430
+ end
431
+
432
+ def parse_system(text)
433
+ basepath = File.expand_path(File.dirname(__FILE__) + '/../../')
434
+ Fluent::Config.parse(text, '(test)', basepath, true).elements.find { |e| e.name == 'system' }
435
+ end
155
436
 
156
- time = Time.parse("2011-01-02 13:14:15 UTC").to_i
157
- d.emit({"a"=>1}, time)
158
- d.emit({"a"=>2}, time)
437
+ test 'write to file with permission specifications' do
438
+ system_conf = parse_system(CONFIG_WITH_SYSTEM)
439
+ sc = Fluent::SystemConfig.new(system_conf)
440
+ Fluent::Engine.init(sc)
441
+ d = create_driver CONFIG_WITH_SYSTEM
159
442
 
160
- # FileOutput#write returns path
161
- paths = d.run
162
- check_gzipped_result(paths[0], %[#{Yajl.dump({"a" => 1, 'time' => time})}\n] + %[#{Yajl.dump({"a" => 2, 'time' => time})}\n])
163
- end
443
+ assert_false File.exist?("#{TMP_DIR_WITH_SYSTEM}/out_file_test.20110102_0.log.gz")
164
444
 
165
- def test_write_with_format_ltsv
166
- d = create_driver [CONFIG, 'format ltsv', 'include_time_key true'].join("\n")
445
+ time = event_time("2011-01-02 13:14:15 UTC")
446
+ d.run(default_tag: 'test') do
447
+ d.feed(time, {"a"=>1})
448
+ d.feed(time, {"a"=>2})
449
+ end
167
450
 
168
- time = Time.parse("2011-01-02 13:14:15 UTC").to_i
169
- d.emit({"a"=>1}, time)
170
- d.emit({"a"=>2}, time)
451
+ assert File.exist?("#{TMP_DIR_WITH_SYSTEM}/out_file_test.20110102_0.log.gz")
171
452
 
172
- # FileOutput#write returns path
173
- paths = d.run
174
- check_gzipped_result(paths[0], %[a:1\ttime:2011-01-02T13:14:15Z\n] + %[a:2\ttime:2011-01-02T13:14:15Z\n])
453
+ check_gzipped_result("#{TMP_DIR_WITH_SYSTEM}/out_file_test.20110102_0.log.gz", %[2011-01-02T13:14:15Z\ttest\t{"a":1}\n] + %[2011-01-02T13:14:15Z\ttest\t{"a":2}\n])
454
+ dir_mode = "%o" % File::stat(TMP_DIR_WITH_SYSTEM).mode
455
+ assert_equal(OVERRIDE_DIR_PERMISSION, dir_mode[-3, 3].to_i)
456
+ file_mode = "%o" % File::stat("#{TMP_DIR_WITH_SYSTEM}/out_file_test.20110102_0.log.gz").mode
457
+ assert_equal(OVERRIDE_FILE_PERMISSION, file_mode[-3, 3].to_i)
458
+ end
175
459
  end
176
460
 
177
- def test_write_with_format_single_value
178
- d = create_driver [CONFIG, 'format single_value', 'message_key a'].join("\n")
461
+ sub_test_case 'format specified' do
462
+ test 'json' do
463
+ d = create_driver [CONFIG, 'format json', 'include_time_key true', 'time_as_epoch'].join("\n")
464
+
465
+ time = event_time("2011-01-02 13:14:15 UTC")
466
+ d.run(default_tag: 'test') do
467
+ d.feed(time, {"a"=>1})
468
+ d.feed(time, {"a"=>2})
469
+ end
470
+
471
+ path = d.instance.last_written_path
472
+ check_gzipped_result(path, %[#{Yajl.dump({"a" => 1, 'time' => time.to_i})}\n] + %[#{Yajl.dump({"a" => 2, 'time' => time.to_i})}\n])
473
+ end
474
+
475
+ test 'ltsv' do
476
+ d = create_driver [CONFIG, 'format ltsv', 'include_time_key true'].join("\n")
179
477
 
180
- time = Time.parse("2011-01-02 13:14:15 UTC").to_i
181
- d.emit({"a"=>1}, time)
182
- d.emit({"a"=>2}, time)
478
+ time = event_time("2011-01-02 13:14:15 UTC")
479
+ d.run(default_tag: 'test') do
480
+ d.feed(time, {"a"=>1})
481
+ d.feed(time, {"a"=>2})
482
+ end
483
+
484
+ path = d.instance.last_written_path
485
+ check_gzipped_result(path, %[a:1\ttime:2011-01-02T13:14:15Z\n] + %[a:2\ttime:2011-01-02T13:14:15Z\n])
486
+ end
487
+
488
+ test 'single_value' do
489
+ d = create_driver [CONFIG, 'format single_value', 'message_key a'].join("\n")
490
+
491
+ time = event_time("2011-01-02 13:14:15 UTC")
492
+ d.run(default_tag: 'test') do
493
+ d.feed(time, {"a"=>1})
494
+ d.feed(time, {"a"=>2})
495
+ end
183
496
 
184
- # FileOutput#write returns path
185
- paths = d.run
186
- check_gzipped_result(paths[0], %[1\n] + %[2\n])
497
+ path = d.instance.last_written_path
498
+ check_gzipped_result(path, %[1\n] + %[2\n])
499
+ end
187
500
  end
188
501
 
189
- def test_write_path_increment
190
- time = Time.parse("2011-01-02 13:14:15 UTC").to_i
502
+ test 'path with index number' do
503
+ time = event_time("2011-01-02 13:14:15 UTC")
191
504
  formatted_lines = %[2011-01-02T13:14:15Z\ttest\t{"a":1}\n] + %[2011-01-02T13:14:15Z\ttest\t{"a":2}\n]
192
505
 
193
506
  write_once = ->(){
194
507
  d = create_driver
195
- d.emit({"a"=>1}, time)
196
- d.emit({"a"=>2}, time)
197
- d.run
508
+ d.run(default_tag: 'test'){
509
+ d.feed(time, {"a"=>1})
510
+ d.feed(time, {"a"=>2})
511
+ }
512
+ d.instance.last_written_path
198
513
  }
199
514
 
200
515
  assert !File.exist?("#{TMP_DIR}/out_file_test.20110102_0.log.gz")
201
516
 
202
- # FileOutput#write returns path
203
- paths = write_once.call
204
- assert_equal ["#{TMP_DIR}/out_file_test.20110102_0.log.gz"], paths
205
- check_gzipped_result(paths[0], formatted_lines)
517
+ path = write_once.call
518
+ assert_equal "#{TMP_DIR}/out_file_test.20110102_0.log.gz", path
519
+ check_gzipped_result(path, formatted_lines)
206
520
  assert_equal 1, Dir.glob("#{TMP_DIR}/out_file_test.*").size
207
521
 
208
- paths = write_once.call
209
- assert_equal ["#{TMP_DIR}/out_file_test.20110102_1.log.gz"], paths
210
- check_gzipped_result(paths[0], formatted_lines)
522
+ path = write_once.call
523
+ assert_equal "#{TMP_DIR}/out_file_test.20110102_1.log.gz", path
524
+ check_gzipped_result(path, formatted_lines)
211
525
  assert_equal 2, Dir.glob("#{TMP_DIR}/out_file_test.*").size
212
526
 
213
- paths = write_once.call
214
- assert_equal ["#{TMP_DIR}/out_file_test.20110102_2.log.gz"], paths
215
- check_gzipped_result(paths[0], formatted_lines)
527
+ path = write_once.call
528
+ assert_equal "#{TMP_DIR}/out_file_test.20110102_2.log.gz", path
529
+ check_gzipped_result(path, formatted_lines)
216
530
  assert_equal 3, Dir.glob("#{TMP_DIR}/out_file_test.*").size
217
531
  end
218
532
 
219
- def test_write_with_append
220
- time = Time.parse("2011-01-02 13:14:15 UTC").to_i
533
+ test 'append' do
534
+ time = event_time("2011-01-02 13:14:15 UTC")
221
535
  formatted_lines = %[2011-01-02T13:14:15Z\ttest\t{"a":1}\n] + %[2011-01-02T13:14:15Z\ttest\t{"a":2}\n]
222
536
 
223
537
  write_once = ->(){
@@ -226,50 +540,184 @@ class FileOutputTest < Test::Unit::TestCase
226
540
  compress gz
227
541
  utc
228
542
  append true
543
+ <buffer>
544
+ timekey_use_utc true
545
+ </buffer>
229
546
  ]
230
- d.emit({"a"=>1}, time)
231
- d.emit({"a"=>2}, time)
232
- d.run
547
+ d.run(default_tag: 'test'){
548
+ d.feed(time, {"a"=>1})
549
+ d.feed(time, {"a"=>2})
550
+ }
551
+ d.instance.last_written_path
233
552
  }
234
553
 
235
- # FileOutput#write returns path
236
- paths = write_once.call
237
- assert_equal ["#{TMP_DIR}/out_file_test.20110102.log.gz"], paths
238
- check_gzipped_result(paths[0], formatted_lines)
239
- paths = write_once.call
240
- assert_equal ["#{TMP_DIR}/out_file_test.20110102.log.gz"], paths
241
- check_gzipped_result(paths[0], formatted_lines * 2)
242
- paths = write_once.call
243
- assert_equal ["#{TMP_DIR}/out_file_test.20110102.log.gz"], paths
244
- check_gzipped_result(paths[0], formatted_lines * 3)
554
+ path = write_once.call
555
+ assert_equal "#{TMP_DIR}/out_file_test.20110102.log.gz", path
556
+ check_gzipped_result(path, formatted_lines)
557
+
558
+ path = write_once.call
559
+ assert_equal "#{TMP_DIR}/out_file_test.20110102.log.gz", path
560
+ check_gzipped_result(path, formatted_lines * 2)
561
+
562
+ path = write_once.call
563
+ assert_equal "#{TMP_DIR}/out_file_test.20110102.log.gz", path
564
+ check_gzipped_result(path, formatted_lines * 3)
245
565
  end
246
566
 
247
- def test_write_with_symlink
248
- conf = CONFIG + %[
249
- symlink_path #{SYMLINK_PATH}
250
- ]
251
- symlink_path = "#{SYMLINK_PATH}"
567
+ test 'append when JST' do
568
+ with_timezone(Fluent.windows? ? "JST-9" : "Asia/Tokyo") do
569
+ time = event_time("2011-01-02 03:14:15+09:00")
570
+ formatted_lines = %[2011-01-02T03:14:15+09:00\ttest\t{"a":1}\n] + %[2011-01-02T03:14:15+09:00\ttest\t{"a":2}\n]
571
+
572
+ write_once = ->(){
573
+ d = create_driver %[
574
+ path #{TMP_DIR}/out_file_test
575
+ compress gz
576
+ append true
577
+ <buffer>
578
+ timekey_use_utc false
579
+ timekey_zone Asia/Tokyo
580
+ </buffer>
581
+ ]
582
+ d.run(default_tag: 'test'){
583
+ d.feed(time, {"a"=>1})
584
+ d.feed(time, {"a"=>2})
585
+ }
586
+ d.instance.last_written_path
587
+ }
588
+
589
+ path = write_once.call
590
+ assert_equal "#{TMP_DIR}/out_file_test.20110102.log.gz", path
591
+ check_gzipped_result(path, formatted_lines)
592
+
593
+ path = write_once.call
594
+ assert_equal "#{TMP_DIR}/out_file_test.20110102.log.gz", path
595
+ check_gzipped_result(path, formatted_lines * 2)
596
+
597
+ path = write_once.call
598
+ assert_equal "#{TMP_DIR}/out_file_test.20110102.log.gz", path
599
+ check_gzipped_result(path, formatted_lines * 3)
600
+ end
601
+ end
252
602
 
253
- Fluent::FileBuffer.clear_buffer_paths
254
- d = Fluent::Test::TestDriver.new(Fluent::FileOutput).configure(conf)
603
+ test 'append when UTC-02 but timekey_zone is +0900' do
604
+ with_timezone("UTC-02") do # +0200
605
+ time = event_time("2011-01-02 17:14:15+02:00")
606
+ formatted_lines = %[2011-01-02T17:14:15+02:00\ttest\t{"a":1}\n] + %[2011-01-02T17:14:15+02:00\ttest\t{"a":2}\n]
607
+
608
+ write_once = ->(){
609
+ d = create_driver %[
610
+ path #{TMP_DIR}/out_file_test
611
+ compress gz
612
+ append true
613
+ <buffer>
614
+ timekey_use_utc false
615
+ timekey_zone +0900
616
+ </buffer>
617
+ ]
618
+ d.run(default_tag: 'test'){
619
+ d.feed(time, {"a"=>1})
620
+ d.feed(time, {"a"=>2})
621
+ }
622
+ d.instance.last_written_path
623
+ }
624
+
625
+ path = write_once.call
626
+ # Rotated at 2011-01-02 17:00:00+02:00
627
+ assert_equal "#{TMP_DIR}/out_file_test.20110103.log.gz", path
628
+ check_gzipped_result(path, formatted_lines)
629
+
630
+ path = write_once.call
631
+ assert_equal "#{TMP_DIR}/out_file_test.20110103.log.gz", path
632
+ check_gzipped_result(path, formatted_lines * 2)
633
+
634
+ path = write_once.call
635
+ assert_equal "#{TMP_DIR}/out_file_test.20110103.log.gz", path
636
+ check_gzipped_result(path, formatted_lines * 3)
637
+ end
638
+ end
255
639
 
256
- begin
257
- d.instance.start
258
- 10.times { sleep 0.05 }
259
- time = Time.parse("2011-01-02 13:14:15 UTC").to_i
260
- es = Fluent::OneEventStream.new(time, {"a"=>1})
261
- d.instance.emit('tag', es, Fluent::NullOutputChain.instance)
640
+ test '${chunk_id}' do
641
+ time = event_time("2011-01-02 13:14:15 UTC")
262
642
 
263
- assert File.exists?(symlink_path)
264
- assert File.symlink?(symlink_path)
643
+ write_once = ->(){
644
+ d = create_driver %[
645
+ path #{TMP_DIR}/out_file_chunk_id_${chunk_id}
646
+ utc
647
+ append true
648
+ <buffer>
649
+ timekey_use_utc true
650
+ </buffer>
651
+ ]
652
+ d.run(default_tag: 'test'){
653
+ d.feed(time, {"a"=>1})
654
+ d.feed(time, {"a"=>2})
655
+ }
656
+ d.instance.last_written_path
657
+ }
658
+
659
+ path = write_once.call
660
+ if File.basename(path) =~ /out_file_chunk_id_([-_.@a-zA-Z0-9].*).20110102.log/
661
+ unique_id = Fluent::UniqueId.hex(Fluent::UniqueId.generate)
662
+ assert_equal unique_id.size, $1.size, "chunk_id size is mismatched"
663
+ else
664
+ flunk "chunk_id is not included in the path"
665
+ end
666
+ end
265
667
 
266
- d.instance.enqueue_buffer
668
+ SYMLINK_PATH = File.expand_path("#{TMP_DIR}/current")
267
669
 
268
- assert !File.exists?(symlink_path)
269
- assert File.symlink?(symlink_path)
270
- ensure
271
- d.instance.shutdown
272
- FileUtils.rm_rf(symlink_path)
670
+ sub_test_case 'symlink' do
671
+ test 'static symlink' do
672
+ omit "Windows doesn't support symlink" if Fluent.windows?
673
+ conf = CONFIG + %[
674
+ symlink_path #{SYMLINK_PATH}
675
+ ]
676
+ symlink_path = "#{SYMLINK_PATH}"
677
+
678
+ d = create_driver(conf)
679
+ begin
680
+ run_and_check(d, symlink_path)
681
+ ensure
682
+ FileUtils.rm_rf(symlink_path)
683
+ end
684
+ end
685
+
686
+ test 'symlink with placeholders' do
687
+ omit "Windows doesn't support symlink" if Fluent.windows?
688
+ conf = %[
689
+ path #{TMP_DIR}/${tag}/out_file_test
690
+ symlink_path #{SYMLINK_PATH}/foo/${tag}
691
+ <buffer tag,time>
692
+ </buffer>
693
+ ]
694
+ symlink_path = "#{SYMLINK_PATH}/foo/tag"
695
+
696
+ d = create_driver(conf)
697
+ begin
698
+ run_and_check(d, symlink_path)
699
+ ensure
700
+ FileUtils.rm_rf(symlink_path)
701
+ end
702
+ end
703
+
704
+ def run_and_check(d, symlink_path)
705
+ d.run(default_tag: 'tag') do
706
+ es = Fluent::OneEventStream.new(event_time("2011-01-02 13:14:15 UTC"), {"a"=>1})
707
+ d.feed(es)
708
+
709
+ assert File.symlink?(symlink_path)
710
+ assert File.exist?(symlink_path) # This checks dest of symlink exists or not.
711
+
712
+ es = Fluent::OneEventStream.new(event_time("2011-01-03 14:15:16 UTC"), {"a"=>2})
713
+ d.feed(es)
714
+
715
+ assert File.symlink?(symlink_path)
716
+ assert File.exist?(symlink_path)
717
+
718
+ meta = d.instance.metadata('tag', event_time("2011-01-03 14:15:16 UTC"), {})
719
+ assert_equal d.instance.buffer.instance_eval{ @stage[meta].path }, File.readlink(symlink_path)
720
+ end
273
721
  end
274
722
  end
275
723
 
@@ -280,11 +728,12 @@ class FileOutputTest < Test::Unit::TestCase
280
728
  time_slice_format %Y-%m-%d-%H
281
729
  utc true
282
730
  ])
283
- time = Time.parse("2011-01-02 13:14:15 UTC").to_i
284
- d.emit({"a"=>1}, time)
285
- # FileOutput#write returns path
286
- paths = d.run
287
- assert_equal ["#{TMP_DIR}/out_file_test.2011-01-02-13_0.log"], paths
731
+ time = event_time("2011-01-02 13:14:15 UTC")
732
+ d.run(default_tag: 'test') do
733
+ d.feed(time, {"a"=>1})
734
+ end
735
+ path = d.instance.last_written_path
736
+ assert_equal "#{TMP_DIR}/out_file_test.2011-01-02-13_0.log", path
288
737
  end
289
738
 
290
739
  test 'normal with append' do
@@ -294,22 +743,26 @@ class FileOutputTest < Test::Unit::TestCase
294
743
  utc true
295
744
  append true
296
745
  ])
297
- time = Time.parse("2011-01-02 13:14:15 UTC").to_i
298
- d.emit({"a"=>1}, time)
299
- paths = d.run
300
- assert_equal ["#{TMP_DIR}/out_file_test.2011-01-02-13.log"], paths
301
- end
746
+ time = event_time("2011-01-02 13:14:15 UTC")
747
+ d.run(default_tag: 'test') do
748
+ d.feed(time, {"a"=>1})
749
+ end
750
+ path = d.instance.last_written_path
751
+ assert_equal "#{TMP_DIR}/out_file_test.2011-01-02-13.log", path
752
+ end
302
753
 
303
- test '*' do
754
+ test '*' do
304
755
  d = create_driver(%[
305
756
  path #{TMP_DIR}/out_file_test.*.txt
306
757
  time_slice_format %Y-%m-%d-%H
307
758
  utc true
308
759
  ])
309
- time = Time.parse("2011-01-02 13:14:15 UTC").to_i
310
- d.emit({"a"=>1}, time)
311
- paths = d.run
312
- assert_equal ["#{TMP_DIR}/out_file_test.2011-01-02-13_0.txt"], paths
760
+ time = event_time("2011-01-02 13:14:15 UTC")
761
+ d.run(default_tag: 'test') do
762
+ d.feed(time, {"a"=>1})
763
+ end
764
+ path = d.instance.last_written_path
765
+ assert_equal "#{TMP_DIR}/out_file_test.2011-01-02-13_0.txt", path
313
766
  end
314
767
 
315
768
  test '* with append' do
@@ -319,11 +772,245 @@ class FileOutputTest < Test::Unit::TestCase
319
772
  utc true
320
773
  append true
321
774
  ])
322
- time = Time.parse("2011-01-02 13:14:15 UTC").to_i
323
- d.emit({"a"=>1}, time)
324
- paths = d.run
325
- assert_equal ["#{TMP_DIR}/out_file_test.2011-01-02-13.txt"], paths
775
+ time = event_time("2011-01-02 13:14:15 UTC")
776
+ d.run(default_tag: 'test') do
777
+ d.feed(time, {"a"=>1})
778
+ end
779
+ path = d.instance.last_written_path
780
+ assert_equal "#{TMP_DIR}/out_file_test.2011-01-02-13.txt", path
326
781
  end
327
782
  end
328
- end
329
783
 
784
+ sub_test_case '#timekey_to_timeformat' do
785
+ setup do
786
+ @d = create_driver
787
+ @i = @d.instance
788
+ end
789
+
790
+ test 'returns empty string for nil' do
791
+ assert_equal '', @i.timekey_to_timeformat(nil)
792
+ end
793
+
794
+ test 'returns timestamp string with seconds for timekey smaller than 60' do
795
+ assert_equal '%Y%m%d%H%M%S', @i.timekey_to_timeformat(1)
796
+ assert_equal '%Y%m%d%H%M%S', @i.timekey_to_timeformat(30)
797
+ assert_equal '%Y%m%d%H%M%S', @i.timekey_to_timeformat(59)
798
+ end
799
+
800
+ test 'returns timestamp string with minutes for timekey smaller than 3600' do
801
+ assert_equal '%Y%m%d%H%M', @i.timekey_to_timeformat(60)
802
+ assert_equal '%Y%m%d%H%M', @i.timekey_to_timeformat(180)
803
+ assert_equal '%Y%m%d%H%M', @i.timekey_to_timeformat(1800)
804
+ assert_equal '%Y%m%d%H%M', @i.timekey_to_timeformat(3599)
805
+ end
806
+
807
+ test 'returns timestamp string with hours for timekey smaller than 86400 (1 day)' do
808
+ assert_equal '%Y%m%d%H', @i.timekey_to_timeformat(3600)
809
+ assert_equal '%Y%m%d%H', @i.timekey_to_timeformat(7200)
810
+ assert_equal '%Y%m%d%H', @i.timekey_to_timeformat(86399)
811
+ end
812
+
813
+ test 'returns timestamp string with days for timekey equal or greater than 86400' do
814
+ assert_equal '%Y%m%d', @i.timekey_to_timeformat(86400)
815
+ assert_equal '%Y%m%d', @i.timekey_to_timeformat(1000000)
816
+ assert_equal '%Y%m%d', @i.timekey_to_timeformat(1000000000)
817
+ end
818
+ end
819
+
820
+ sub_test_case '#compression_suffix' do
821
+ setup do
822
+ @i = create_driver.instance
823
+ end
824
+
825
+ test 'returns empty string for nil (no compression method specified)' do
826
+ assert_equal '', @i.compression_suffix(nil)
827
+ end
828
+
829
+ test 'returns .gz for gzip' do
830
+ assert_equal '.gz', @i.compression_suffix(:gzip)
831
+ end
832
+ end
833
+
834
+ sub_test_case '#generate_path_template' do
835
+ setup do
836
+ @i = create_driver.instance
837
+ end
838
+
839
+ data(
840
+ 'day' => [86400, '%Y%m%d', '%Y-%m-%d'],
841
+ 'hour' => [3600, '%Y%m%d%H', '%Y-%m-%d_%H'],
842
+ 'minute' => [60, '%Y%m%d%H%M', '%Y-%m-%d_%H%M'],
843
+ )
844
+ test 'generates path with timestamp placeholder for original path with tailing star with timekey' do |data|
845
+ timekey, placeholder, time_slice_format = data
846
+ # with index placeholder, without compression suffix when append disabled and compression disabled
847
+ assert_equal "/path/to/file.#{placeholder}_**", @i.generate_path_template('/path/to/file.*', timekey, false, nil)
848
+ # with index placeholder, with .gz suffix when append disabled and gzip compression enabled
849
+ assert_equal "/path/to/file.#{placeholder}_**.gz", @i.generate_path_template('/path/to/file.*', timekey, false, :gzip)
850
+ # without index placeholder, without compression suffix when append enabled and compression disabled
851
+ assert_equal "/path/to/file.#{placeholder}", @i.generate_path_template('/path/to/file.*', timekey, true, nil)
852
+ # without index placeholder, with .gz suffix when append disabled and gzip compression enabled
853
+ assert_equal "/path/to/file.#{placeholder}.gz", @i.generate_path_template('/path/to/file.*', timekey, true, :gzip)
854
+
855
+ # time_slice_format will used instead of computed placeholder if specified
856
+ assert_equal "/path/to/file.#{time_slice_format}_**", @i.generate_path_template('/path/to/file.*', timekey, false, nil, time_slice_format: time_slice_format)
857
+ assert_equal "/path/to/file.#{time_slice_format}_**.gz", @i.generate_path_template('/path/to/file.*', timekey, false, :gzip, time_slice_format: time_slice_format)
858
+ assert_equal "/path/to/file.#{time_slice_format}", @i.generate_path_template('/path/to/file.*', timekey, true, nil, time_slice_format: time_slice_format)
859
+ assert_equal "/path/to/file.#{time_slice_format}.gz", @i.generate_path_template('/path/to/file.*', timekey, true, :gzip, time_slice_format: time_slice_format)
860
+ end
861
+
862
+ data(
863
+ 'day' => [86400 * 2, '%Y%m%d', '%Y-%m-%d'],
864
+ 'hour' => [7200, '%Y%m%d%H', '%Y-%m-%d_%H'],
865
+ 'minute' => [180, '%Y%m%d%H%M', '%Y-%m-%d_%H%M'],
866
+ )
867
+ test 'generates path with timestamp placeholder for original path with star and suffix with timekey' do |data|
868
+ timekey, placeholder, time_slice_format = data
869
+ # with index placeholder, without compression suffix when append disabled and compression disabled
870
+ assert_equal "/path/to/file.#{placeholder}_**.data", @i.generate_path_template('/path/to/file.*.data', timekey, false, nil)
871
+ # with index placeholder, with .gz suffix when append disabled and gzip compression enabled
872
+ assert_equal "/path/to/file.#{placeholder}_**.data.gz", @i.generate_path_template('/path/to/file.*.data', timekey, false, :gzip)
873
+ # without index placeholder, without compression suffix when append enabled and compression disabled
874
+ assert_equal "/path/to/file.#{placeholder}.data", @i.generate_path_template('/path/to/file.*.data', timekey, true, nil)
875
+ # without index placeholder, with .gz suffix when append disabled and gzip compression enabled
876
+ assert_equal "/path/to/file.#{placeholder}.data.gz", @i.generate_path_template('/path/to/file.*.data', timekey, true, :gzip)
877
+
878
+ # time_slice_format will used instead of computed placeholder if specified
879
+ assert_equal "/path/to/file.#{time_slice_format}_**.data", @i.generate_path_template('/path/to/file.*.data', timekey, false, nil, time_slice_format: time_slice_format)
880
+ assert_equal "/path/to/file.#{time_slice_format}_**.data.gz", @i.generate_path_template('/path/to/file.*.data', timekey, false, :gzip, time_slice_format: time_slice_format)
881
+ assert_equal "/path/to/file.#{time_slice_format}.data", @i.generate_path_template('/path/to/file.*.data', timekey, true, nil, time_slice_format: time_slice_format)
882
+ assert_equal "/path/to/file.#{time_slice_format}.data.gz", @i.generate_path_template('/path/to/file.*.data', timekey, true, :gzip, time_slice_format: time_slice_format)
883
+ end
884
+
885
+ test 'raise error to show it is a bug when path including * specified without timekey' do
886
+ assert_raise RuntimeError.new("BUG: configuration error must be raised for path including '*' without timekey") do
887
+ @i.generate_path_template('/path/to/file.*.log', nil, false, nil)
888
+ end
889
+ end
890
+
891
+ data(
892
+ 'day' => [86400 * 7, '%Y%m%d', '%Y-%m-%d'],
893
+ 'hour' => [3600 * 6, '%Y%m%d%H', '%Y-%m-%d_%H'],
894
+ 'minute' => [60 * 15, '%Y%m%d%H%M', '%Y-%m-%d_%H%M'],
895
+ )
896
+ test 'generates path with timestamp placeholder for original path without time placeholders & star with timekey, and path_suffix configured' do |data|
897
+ timekey, placeholder, time_slice_format = data
898
+ # with index placeholder, without compression suffix when append disabled and compression disabled
899
+ assert_equal "/path/to/file.#{placeholder}_**.log", @i.generate_path_template('/path/to/file', timekey, false, nil, path_suffix: '.log')
900
+ # with index placeholder, with .gz suffix when append disabled and gzip compression enabled
901
+ assert_equal "/path/to/file.#{placeholder}_**.log.gz", @i.generate_path_template('/path/to/file', timekey, false, :gzip, path_suffix: '.log')
902
+ # without index placeholder, without compression suffix when append enabled and compression disabled
903
+ assert_equal "/path/to/file.#{placeholder}.log", @i.generate_path_template('/path/to/file', timekey, true, nil, path_suffix: '.log')
904
+ # without index placeholder, with compression suffix when append enabled and gzip compression enabled
905
+ assert_equal "/path/to/file.#{placeholder}.log.gz", @i.generate_path_template('/path/to/file', timekey, true, :gzip, path_suffix: '.log')
906
+
907
+ # time_slice_format will be appended always if it's specified
908
+ assert_equal "/path/to/file.#{time_slice_format}_**.log", @i.generate_path_template('/path/to/file', timekey, false, nil, path_suffix: '.log', time_slice_format: time_slice_format)
909
+ assert_equal "/path/to/file.#{time_slice_format}_**.log.gz", @i.generate_path_template('/path/to/file', timekey, false, :gzip, path_suffix: '.log', time_slice_format: time_slice_format)
910
+ assert_equal "/path/to/file.#{time_slice_format}.log", @i.generate_path_template('/path/to/file', timekey, true, nil, path_suffix: '.log', time_slice_format: time_slice_format)
911
+ assert_equal "/path/to/file.#{time_slice_format}.log.gz", @i.generate_path_template('/path/to/file', timekey, true, :gzip, path_suffix: '.log', time_slice_format: time_slice_format)
912
+ end
913
+
914
+ data(
915
+ 'day' => [86400, '%Y%m%d'],
916
+ 'hour' => [3600, '%Y%m%d%H'],
917
+ 'minute' => [60, '%Y%m%d%H%M'],
918
+ )
919
+ test 'generates path with timestamp placeholder for original path without star with timekey, and path_suffix not configured' do |data|
920
+ timekey, placeholder = data
921
+ # with index placeholder, without compression suffix when append disabled and compression disabled
922
+ assert_equal "/path/to/file.#{placeholder}_**", @i.generate_path_template('/path/to/file', timekey, false, nil)
923
+ # with index placeholder, with .gz suffix when append disabled and gzip compression enabled
924
+ assert_equal "/path/to/file.#{placeholder}_**.gz", @i.generate_path_template('/path/to/file', timekey, false, :gzip)
925
+ # without index placeholder, without compression suffix when append enabled and compression disabled
926
+ assert_equal "/path/to/file.#{placeholder}", @i.generate_path_template('/path/to/file', timekey, true, nil)
927
+ # without index placeholder, with compression suffix when append enabled and gzip compression enabled
928
+ assert_equal "/path/to/file.#{placeholder}.gz", @i.generate_path_template('/path/to/file', timekey, true, :gzip)
929
+ end
930
+
931
+ test 'generates path without adding timestamp placeholder part if original path has enough placeholders for specified timekey' do
932
+ assert_equal "/path/to/file.%Y%m%d", @i.generate_path_template('/path/to/file.%Y%m%d', 86400, true, nil)
933
+ assert_equal "/path/to/%Y%m%d/file", @i.generate_path_template('/path/to/%Y%m%d/file', 86400, true, nil)
934
+
935
+ assert_equal "/path/to/%Y%m%d/file_**", @i.generate_path_template('/path/to/%Y%m%d/file', 86400, false, nil)
936
+
937
+ assert_raise Fluent::ConfigError.new("insufficient timestamp placeholders in path") do
938
+ @i.generate_path_template('/path/to/%Y%m/file', 86400, true, nil)
939
+ end
940
+ assert_raise Fluent::ConfigError.new("insufficient timestamp placeholders in path") do
941
+ @i.generate_path_template('/path/to/file.%Y%m%d.log', 3600, true, nil)
942
+ end
943
+
944
+ assert_equal "/path/to/file.%Y%m%d_%H_**.log.gz", @i.generate_path_template('/path/to/file.%Y%m%d_%H', 7200, false, :gzip, path_suffix: '.log')
945
+ assert_equal "/path/to/${tag}/file.%Y%m%d_%H_**.log.gz", @i.generate_path_template('/path/to/${tag}/file.%Y%m%d_%H', 7200, false, :gzip, path_suffix: '.log')
946
+ end
947
+
948
+ test 'generates path with specified time_slice_format appended even if path has sufficient timestamp placeholders' do
949
+ assert_equal "/path/to/%Y%m%d/file.%Y-%m-%d_%H_**", @i.generate_path_template('/path/to/%Y%m%d/file', 86400, false, nil, time_slice_format: '%Y-%m-%d_%H')
950
+ assert_equal "/path/to/%Y%m%d/file.%Y-%m-%d_%H", @i.generate_path_template('/path/to/%Y%m%d/file', 86400, true, nil, time_slice_format: '%Y-%m-%d_%H')
951
+ assert_equal "/path/to/%Y%m%d/file.%Y-%m-%d_%H_**.log", @i.generate_path_template('/path/to/%Y%m%d/file', 86400, false, nil, time_slice_format: '%Y-%m-%d_%H', path_suffix: '.log')
952
+ assert_equal "/path/to/%Y%m%d/file.%Y-%m-%d_%H.log", @i.generate_path_template('/path/to/%Y%m%d/file', 86400, true, nil, time_slice_format: '%Y-%m-%d_%H', path_suffix: '.log')
953
+ assert_equal "/path/to/%Y%m%d/file.%Y-%m-%d_%H.log.gz", @i.generate_path_template('/path/to/%Y%m%d/file', 86400, true, :gzip, time_slice_format: '%Y-%m-%d_%H', path_suffix: '.log')
954
+ end
955
+
956
+ test 'generates path without timestamp placeholder when path does not include * and timekey not specified' do
957
+ assert_equal '/path/to/file.log', @i.generate_path_template('/path/to/file.log', nil, true, nil)
958
+ assert_equal '/path/to/file.log_**', @i.generate_path_template('/path/to/file.log', nil, false, nil)
959
+ assert_equal '/path/to/file.${tag}.log_**', @i.generate_path_template('/path/to/file.${tag}.log', nil, false, nil)
960
+ assert_equal '/path/to/file.${tag}_**.log', @i.generate_path_template('/path/to/file.${tag}', nil, false, nil, path_suffix: '.log')
961
+ end
962
+ end
963
+
964
+ sub_test_case '#find_filepath_available' do
965
+ setup do
966
+ @tmp = File.join(TMP_DIR, 'find_filepath_test')
967
+ FileUtils.mkdir_p @tmp
968
+ @i = create_driver.instance
969
+ end
970
+
971
+ teardown do
972
+ FileUtils.rm_rf @tmp
973
+ end
974
+
975
+ test 'raise error if argument path does not include index placeholder' do
976
+ assert_raise RuntimeError.new("BUG: index placeholder not found in path: #{@tmp}/myfile") do
977
+ @i.find_filepath_available("#{@tmp}/myfile") do |path|
978
+ # ...
979
+ end
980
+ end
981
+ end
982
+
983
+ data(
984
+ 'without suffix' => ['myfile_0', 'myfile_**'],
985
+ 'with timestamp' => ['myfile_20161003_0', 'myfile_20161003_**'],
986
+ 'with base suffix' => ['myfile_0.log', 'myfile_**.log'],
987
+ 'with compression suffix' => ['myfile_0.log.gz', 'myfile_**.log.gz'],
988
+ )
989
+ test 'returns filepath with _0 at first' do |data|
990
+ expected, argument = data
991
+ @i.find_filepath_available(File.join(@tmp, argument)) do |path|
992
+ assert_equal File.join(@tmp, expected), path
993
+ end
994
+ end
995
+
996
+ test 'returns filepath with index which does not exist yet' do
997
+ 5.times do |i|
998
+ File.open(File.join(@tmp, "exist_#{i}.log"), 'a'){|f| } # open(create) and close
999
+ end
1000
+ @i.find_filepath_available(File.join(@tmp, "exist_**.log")) do |path|
1001
+ assert_equal File.join(@tmp, "exist_5.log"), path
1002
+ end
1003
+ end
1004
+
1005
+ test 'creates lock directory when with_lock is true to exclude operations of other worker process' do
1006
+ 5.times do |i|
1007
+ File.open(File.join(@tmp, "exist_#{i}.log"), 'a')
1008
+ end
1009
+ Dir.mkdir(File.join(@tmp, "exist_5.log.lock"))
1010
+ @i.find_filepath_available(File.join(@tmp, "exist_**.log"), with_lock: true) do |path|
1011
+ assert Dir.exist?(File.join(@tmp, "exist_6.log.lock"))
1012
+ assert_equal File.join(@tmp, "exist_6.log"), path
1013
+ end
1014
+ end
1015
+ end
1016
+ end