fluentd 1.13.3 → 1.16.5

Sign up to get free protection for your applications and to get access to all the features.
Files changed (179) hide show
  1. checksums.yaml +4 -4
  2. data/.github/ISSUE_TEMPLATE/{bug_report.yaml → bug_report.yml} +2 -0
  3. data/.github/ISSUE_TEMPLATE/config.yml +2 -2
  4. data/.github/ISSUE_TEMPLATE/{feature_request.yaml → feature_request.yml} +1 -0
  5. data/.github/workflows/stale-actions.yml +11 -9
  6. data/.github/workflows/test.yml +32 -0
  7. data/CHANGELOG.md +490 -10
  8. data/CONTRIBUTING.md +2 -2
  9. data/MAINTAINERS.md +7 -5
  10. data/README.md +3 -23
  11. data/Rakefile +1 -1
  12. data/SECURITY.md +14 -0
  13. data/fluentd.gemspec +7 -8
  14. data/lib/fluent/command/cat.rb +13 -3
  15. data/lib/fluent/command/ctl.rb +6 -3
  16. data/lib/fluent/command/fluentd.rb +73 -65
  17. data/lib/fluent/command/plugin_config_formatter.rb +1 -1
  18. data/lib/fluent/compat/output.rb +9 -6
  19. data/lib/fluent/config/dsl.rb +1 -1
  20. data/lib/fluent/config/error.rb +12 -0
  21. data/lib/fluent/config/literal_parser.rb +2 -2
  22. data/lib/fluent/config/parser.rb +1 -1
  23. data/lib/fluent/config/v1_parser.rb +3 -3
  24. data/lib/fluent/config/yaml_parser/fluent_value.rb +47 -0
  25. data/lib/fluent/config/yaml_parser/loader.rb +108 -0
  26. data/lib/fluent/config/yaml_parser/parser.rb +166 -0
  27. data/lib/fluent/config/yaml_parser/section_builder.rb +107 -0
  28. data/lib/fluent/config/yaml_parser.rb +56 -0
  29. data/lib/fluent/config.rb +14 -1
  30. data/lib/fluent/counter/server.rb +1 -1
  31. data/lib/fluent/counter/validator.rb +3 -3
  32. data/lib/fluent/daemon.rb +2 -4
  33. data/lib/fluent/engine.rb +1 -1
  34. data/lib/fluent/env.rb +4 -0
  35. data/lib/fluent/error.rb +3 -0
  36. data/lib/fluent/event.rb +8 -4
  37. data/lib/fluent/event_router.rb +47 -2
  38. data/lib/fluent/file_wrapper.rb +137 -0
  39. data/lib/fluent/log/console_adapter.rb +66 -0
  40. data/lib/fluent/log.rb +44 -5
  41. data/lib/fluent/match.rb +1 -1
  42. data/lib/fluent/msgpack_factory.rb +6 -1
  43. data/lib/fluent/oj_options.rb +1 -2
  44. data/lib/fluent/plugin/bare_output.rb +49 -8
  45. data/lib/fluent/plugin/base.rb +26 -9
  46. data/lib/fluent/plugin/buf_file.rb +34 -5
  47. data/lib/fluent/plugin/buf_file_single.rb +32 -3
  48. data/lib/fluent/plugin/buffer/file_chunk.rb +1 -1
  49. data/lib/fluent/plugin/buffer.rb +216 -70
  50. data/lib/fluent/plugin/filter.rb +35 -1
  51. data/lib/fluent/plugin/filter_record_transformer.rb +1 -1
  52. data/lib/fluent/plugin/in_forward.rb +2 -2
  53. data/lib/fluent/plugin/in_http.rb +39 -10
  54. data/lib/fluent/plugin/in_monitor_agent.rb +4 -2
  55. data/lib/fluent/plugin/in_sample.rb +1 -1
  56. data/lib/fluent/plugin/in_syslog.rb +13 -1
  57. data/lib/fluent/plugin/in_tail/group_watch.rb +204 -0
  58. data/lib/fluent/plugin/in_tail/position_file.rb +33 -33
  59. data/lib/fluent/plugin/in_tail.rb +216 -84
  60. data/lib/fluent/plugin/in_tcp.rb +47 -2
  61. data/lib/fluent/plugin/input.rb +39 -1
  62. data/lib/fluent/plugin/metrics.rb +119 -0
  63. data/lib/fluent/plugin/metrics_local.rb +96 -0
  64. data/lib/fluent/plugin/multi_output.rb +43 -6
  65. data/lib/fluent/plugin/out_copy.rb +1 -1
  66. data/lib/fluent/plugin/out_exec_filter.rb +2 -2
  67. data/lib/fluent/plugin/out_file.rb +20 -2
  68. data/lib/fluent/plugin/out_forward/ack_handler.rb +19 -4
  69. data/lib/fluent/plugin/out_forward/socket_cache.rb +2 -0
  70. data/lib/fluent/plugin/out_forward.rb +17 -9
  71. data/lib/fluent/plugin/out_secondary_file.rb +39 -22
  72. data/lib/fluent/plugin/output.rb +167 -78
  73. data/lib/fluent/plugin/parser.rb +3 -4
  74. data/lib/fluent/plugin/parser_apache2.rb +1 -1
  75. data/lib/fluent/plugin/parser_json.rb +1 -1
  76. data/lib/fluent/plugin/parser_syslog.rb +1 -1
  77. data/lib/fluent/plugin/storage_local.rb +3 -5
  78. data/lib/fluent/plugin.rb +10 -1
  79. data/lib/fluent/plugin_helper/child_process.rb +3 -0
  80. data/lib/fluent/plugin_helper/event_emitter.rb +8 -1
  81. data/lib/fluent/plugin_helper/event_loop.rb +2 -2
  82. data/lib/fluent/plugin_helper/http_server/server.rb +2 -1
  83. data/lib/fluent/plugin_helper/metrics.rb +129 -0
  84. data/lib/fluent/plugin_helper/record_accessor.rb +1 -1
  85. data/lib/fluent/plugin_helper/retry_state.rb +14 -4
  86. data/lib/fluent/plugin_helper/server.rb +35 -6
  87. data/lib/fluent/plugin_helper/service_discovery.rb +2 -2
  88. data/lib/fluent/plugin_helper/socket.rb +13 -2
  89. data/lib/fluent/plugin_helper/thread.rb +3 -3
  90. data/lib/fluent/plugin_helper.rb +1 -0
  91. data/lib/fluent/plugin_id.rb +3 -2
  92. data/lib/fluent/registry.rb +2 -1
  93. data/lib/fluent/root_agent.rb +6 -0
  94. data/lib/fluent/rpc.rb +4 -3
  95. data/lib/fluent/supervisor.rb +283 -259
  96. data/lib/fluent/system_config.rb +13 -3
  97. data/lib/fluent/test/driver/base.rb +11 -5
  98. data/lib/fluent/test/driver/filter.rb +4 -0
  99. data/lib/fluent/test/startup_shutdown.rb +6 -8
  100. data/lib/fluent/time.rb +21 -20
  101. data/lib/fluent/version.rb +1 -1
  102. data/lib/fluent/win32api.rb +38 -0
  103. data/lib/fluent/winsvc.rb +5 -8
  104. data/templates/new_gem/test/helper.rb.erb +0 -1
  105. data/test/command/test_cat.rb +31 -2
  106. data/test/command/test_ctl.rb +1 -2
  107. data/test/command/test_fluentd.rb +209 -24
  108. data/test/command/test_plugin_config_formatter.rb +0 -1
  109. data/test/compat/test_parser.rb +6 -6
  110. data/test/config/test_system_config.rb +13 -11
  111. data/test/config/test_types.rb +1 -1
  112. data/test/log/test_console_adapter.rb +110 -0
  113. data/test/plugin/in_tail/test_io_handler.rb +26 -8
  114. data/test/plugin/in_tail/test_position_file.rb +48 -59
  115. data/test/plugin/out_forward/test_ack_handler.rb +39 -0
  116. data/test/plugin/out_forward/test_socket_cache.rb +26 -1
  117. data/test/plugin/test_bare_output.rb +14 -1
  118. data/test/plugin/test_base.rb +133 -1
  119. data/test/plugin/test_buf_file.rb +62 -23
  120. data/test/plugin/test_buf_file_single.rb +65 -0
  121. data/test/plugin/test_buffer.rb +267 -3
  122. data/test/plugin/test_buffer_chunk.rb +11 -0
  123. data/test/plugin/test_filter.rb +12 -1
  124. data/test/plugin/test_filter_parser.rb +1 -1
  125. data/test/plugin/test_filter_stdout.rb +2 -2
  126. data/test/plugin/test_in_forward.rb +9 -11
  127. data/test/plugin/test_in_http.rb +65 -3
  128. data/test/plugin/test_in_monitor_agent.rb +216 -11
  129. data/test/plugin/test_in_object_space.rb +9 -3
  130. data/test/plugin/test_in_syslog.rb +35 -0
  131. data/test/plugin/test_in_tail.rb +1393 -385
  132. data/test/plugin/test_in_tcp.rb +87 -2
  133. data/test/plugin/test_in_udp.rb +28 -0
  134. data/test/plugin/test_in_unix.rb +2 -2
  135. data/test/plugin/test_input.rb +12 -1
  136. data/test/plugin/test_metrics.rb +294 -0
  137. data/test/plugin/test_metrics_local.rb +96 -0
  138. data/test/plugin/test_multi_output.rb +25 -1
  139. data/test/plugin/test_out_exec.rb +6 -4
  140. data/test/plugin/test_out_exec_filter.rb +6 -2
  141. data/test/plugin/test_out_file.rb +34 -17
  142. data/test/plugin/test_out_forward.rb +78 -77
  143. data/test/plugin/test_out_http.rb +1 -0
  144. data/test/plugin/test_out_stdout.rb +2 -2
  145. data/test/plugin/test_output.rb +297 -12
  146. data/test/plugin/test_output_as_buffered.rb +44 -44
  147. data/test/plugin/test_output_as_buffered_compress.rb +32 -18
  148. data/test/plugin/test_output_as_buffered_retries.rb +54 -7
  149. data/test/plugin/test_output_as_buffered_secondary.rb +4 -4
  150. data/test/plugin/test_parser_regexp.rb +1 -6
  151. data/test/plugin/test_parser_syslog.rb +1 -1
  152. data/test/plugin_helper/test_cert_option.rb +1 -1
  153. data/test/plugin_helper/test_child_process.rb +38 -16
  154. data/test/plugin_helper/test_event_emitter.rb +29 -0
  155. data/test/plugin_helper/test_http_server_helper.rb +1 -1
  156. data/test/plugin_helper/test_metrics.rb +137 -0
  157. data/test/plugin_helper/test_retry_state.rb +602 -38
  158. data/test/plugin_helper/test_server.rb +78 -6
  159. data/test/plugin_helper/test_timer.rb +2 -2
  160. data/test/test_config.rb +191 -24
  161. data/test/test_event_router.rb +17 -0
  162. data/test/test_file_wrapper.rb +53 -0
  163. data/test/test_formatter.rb +24 -21
  164. data/test/test_log.rb +122 -40
  165. data/test/test_msgpack_factory.rb +32 -0
  166. data/test/test_plugin_classes.rb +102 -0
  167. data/test/test_root_agent.rb +30 -1
  168. data/test/test_supervisor.rb +477 -257
  169. data/test/test_time_parser.rb +22 -0
  170. metadata +55 -34
  171. data/.drone.yml +0 -35
  172. data/.github/workflows/issue-auto-closer.yml +0 -12
  173. data/.github/workflows/linux-test.yaml +0 -36
  174. data/.github/workflows/macos-test.yaml +0 -30
  175. data/.github/workflows/windows-test.yaml +0 -46
  176. data/.gitlab-ci.yml +0 -103
  177. data/lib/fluent/plugin/file_wrapper.rb +0 -187
  178. data/test/plugin/test_file_wrapper.rb +0 -126
  179. data/test/test_logger_initializer.rb +0 -46
@@ -1,11 +1,19 @@
1
1
  require_relative '../../helper'
2
2
 
3
3
  require 'fluent/plugin/in_tail'
4
+ require 'fluent/plugin/metrics_local'
4
5
  require 'tempfile'
5
6
 
6
7
  class IntailIOHandlerTest < Test::Unit::TestCase
7
8
  setup do
8
9
  @file = Tempfile.new('intail_io_handler').binmode
10
+ opened_file_metrics = Fluent::Plugin::LocalMetrics.new
11
+ opened_file_metrics.configure(config_element('metrics', '', {}))
12
+ closed_file_metrics = Fluent::Plugin::LocalMetrics.new
13
+ closed_file_metrics.configure(config_element('metrics', '', {}))
14
+ rotated_file_metrics = Fluent::Plugin::LocalMetrics.new
15
+ rotated_file_metrics.configure(config_element('metrics', '', {}))
16
+ @metrics = Fluent::Plugin::TailInput::MetricsInfo.new(opened_file_metrics, closed_file_metrics, rotated_file_metrics)
9
17
  end
10
18
 
11
19
  teardown do
@@ -13,12 +21,20 @@ class IntailIOHandlerTest < Test::Unit::TestCase
13
21
  @file.unlink rescue nil
14
22
  end
15
23
 
24
+ def create_target_info
25
+ Fluent::Plugin::TailInput::TargetInfo.new(@file.path, Fluent::FileWrapper.stat(@file.path).ino)
26
+ end
27
+
28
+ def create_watcher
29
+ Fluent::Plugin::TailInput::TailWatcher.new(create_target_info, nil, nil, nil, nil, nil, nil, nil, nil)
30
+ end
31
+
16
32
  test '#on_notify load file content and passed it to receive_lines method' do
17
33
  text = "this line is test\ntest line is test\n"
18
34
  @file.write(text)
19
35
  @file.close
20
36
 
21
- watcher = 'watcher'
37
+ watcher = create_watcher
22
38
 
23
39
  update_pos = 0
24
40
 
@@ -30,7 +46,7 @@ class IntailIOHandlerTest < Test::Unit::TestCase
30
46
  end
31
47
 
32
48
  returned_lines = ''
33
- r = Fluent::Plugin::TailInput::TailWatcher::IOHandler.new(watcher, path: @file.path, read_lines_limit: 100, read_bytes_limit_per_second: -1, log: $log, open_on_every_update: false) do |lines, _watcher|
49
+ r = Fluent::Plugin::TailInput::TailWatcher::IOHandler.new(watcher, path: @file.path, read_lines_limit: 100, read_bytes_limit_per_second: -1, log: $log, open_on_every_update: false, metrics: @metrics) do |lines, _watcher|
34
50
  returned_lines << lines.join
35
51
  true
36
52
  end
@@ -53,7 +69,7 @@ class IntailIOHandlerTest < Test::Unit::TestCase
53
69
 
54
70
  update_pos = 0
55
71
 
56
- watcher = 'watcher'
72
+ watcher = create_watcher
57
73
  stub(watcher).pe do
58
74
  pe = 'position_file'
59
75
  stub(pe).read_pos { 0 }
@@ -62,7 +78,7 @@ class IntailIOHandlerTest < Test::Unit::TestCase
62
78
  end
63
79
 
64
80
  returned_lines = ''
65
- r = Fluent::Plugin::TailInput::TailWatcher::IOHandler.new(watcher, path: @file.path, read_lines_limit: 100, read_bytes_limit_per_second: -1, log: $log, open_on_every_update: true) do |lines, _watcher|
81
+ r = Fluent::Plugin::TailInput::TailWatcher::IOHandler.new(watcher, path: @file.path, read_lines_limit: 100, read_bytes_limit_per_second: -1, log: $log, open_on_every_update: true, metrics: @metrics) do |lines, _watcher|
66
82
  returned_lines << lines.join
67
83
  true
68
84
  end
@@ -84,7 +100,7 @@ class IntailIOHandlerTest < Test::Unit::TestCase
84
100
 
85
101
  update_pos = 0
86
102
 
87
- watcher = 'watcher'
103
+ watcher = create_watcher
88
104
  stub(watcher).pe do
89
105
  pe = 'position_file'
90
106
  stub(pe).read_pos { 0 }
@@ -93,12 +109,13 @@ class IntailIOHandlerTest < Test::Unit::TestCase
93
109
  end
94
110
 
95
111
  returned_lines = []
96
- r = Fluent::Plugin::TailInput::TailWatcher::IOHandler.new(watcher, path: @file.path, read_lines_limit: 5, read_bytes_limit_per_second: -1, log: $log, open_on_every_update: false) do |lines, _watcher|
112
+ r = Fluent::Plugin::TailInput::TailWatcher::IOHandler.new(watcher, path: @file.path, read_lines_limit: 5, read_bytes_limit_per_second: -1, log: $log, open_on_every_update: false, metrics: @metrics) do |lines, _watcher|
97
113
  returned_lines << lines.dup
98
114
  true
99
115
  end
100
116
 
101
117
  r.on_notify
118
+ assert_equal text.bytesize, update_pos
102
119
  assert_equal 8, returned_lines[0].size
103
120
  end
104
121
 
@@ -110,7 +127,7 @@ class IntailIOHandlerTest < Test::Unit::TestCase
110
127
 
111
128
  update_pos = 0
112
129
 
113
- watcher = 'watcher'
130
+ watcher = create_watcher
114
131
  stub(watcher).pe do
115
132
  pe = 'position_file'
116
133
  stub(pe).read_pos { 0 }
@@ -119,12 +136,13 @@ class IntailIOHandlerTest < Test::Unit::TestCase
119
136
  end
120
137
 
121
138
  returned_lines = []
122
- r = Fluent::Plugin::TailInput::TailWatcher::IOHandler.new(watcher, path: @file.path, read_lines_limit: 5, read_bytes_limit_per_second: -1, log: $log, open_on_every_update: false) do |lines, _watcher|
139
+ r = Fluent::Plugin::TailInput::TailWatcher::IOHandler.new(watcher, path: @file.path, read_lines_limit: 5, read_bytes_limit_per_second: -1, log: $log, open_on_every_update: false, metrics: @metrics) do |lines, _watcher|
123
140
  returned_lines << lines.dup
124
141
  true
125
142
  end
126
143
 
127
144
  r.on_notify
145
+ assert_equal text.bytesize, update_pos
128
146
  assert_equal 5, returned_lines[0].size
129
147
  assert_equal 3, returned_lines[1].size
130
148
  end
@@ -22,6 +22,14 @@ class IntailPositionFileTest < Test::Unit::TestCase
22
22
  invalidpath100000000000000000000000000000000
23
23
  unwatched\t#{UNWATCHED_STR}\t0000000000000000
24
24
  EOF
25
+ TEST_CONTENT_PATHS = {
26
+ "valid_path" => Fluent::Plugin::TailInput::TargetInfo.new("valid_path", 1),
27
+ "inode23bit" => Fluent::Plugin::TailInput::TargetInfo.new("inode23bit", 0),
28
+ }
29
+ TEST_CONTENT_INODES = {
30
+ 1 => Fluent::Plugin::TailInput::TargetInfo.new("valid_path", 1),
31
+ 0 => Fluent::Plugin::TailInput::TargetInfo.new("inode23bit", 0),
32
+ }
25
33
 
26
34
  def write_data(f, content)
27
35
  f.write(content)
@@ -36,7 +44,7 @@ class IntailPositionFileTest < Test::Unit::TestCase
36
44
 
37
45
  test '.load' do
38
46
  write_data(@file, TEST_CONTENT)
39
- Fluent::Plugin::TailInput::PositionFile.load(@file, false, {}, **{logger: $log})
47
+ Fluent::Plugin::TailInput::PositionFile.load(@file, false, TEST_CONTENT_PATHS, **{logger: $log})
40
48
 
41
49
  @file.seek(0)
42
50
  lines = @file.readlines
@@ -118,7 +126,7 @@ class IntailPositionFileTest < Test::Unit::TestCase
118
126
 
119
127
  test 'should ignore initial existing files on follow_inode' do
120
128
  write_data(@file, TEST_CONTENT)
121
- pos_file = Fluent::Plugin::TailInput::PositionFile.load(@file, true, {}, **{logger: $log})
129
+ pos_file = Fluent::Plugin::TailInput::PositionFile.load(@file, true, TEST_CONTENT_PATHS, **{logger: $log})
122
130
  @file.seek(0)
123
131
  assert_equal([], @file.readlines)
124
132
 
@@ -138,7 +146,7 @@ class IntailPositionFileTest < Test::Unit::TestCase
138
146
  sub_test_case '#load' do
139
147
  test 'compact invalid and convert 32 bit inode value' do
140
148
  write_data(@file, TEST_CONTENT)
141
- Fluent::Plugin::TailInput::PositionFile.load(@file, false, {}, **{logger: $log})
149
+ Fluent::Plugin::TailInput::PositionFile.load(@file, false, TEST_CONTENT_PATHS, **{logger: $log})
142
150
 
143
151
  @file.seek(0)
144
152
  lines = @file.readlines
@@ -147,6 +155,15 @@ class IntailPositionFileTest < Test::Unit::TestCase
147
155
  assert_equal "inode23bit\t0000000000000000\t0000000000000000\n", lines[1]
148
156
  end
149
157
 
158
+ test 'compact deleted paths' do
159
+ write_data(@file, TEST_CONTENT)
160
+ Fluent::Plugin::TailInput::PositionFile.load(@file, false, {}, **{logger: $log})
161
+
162
+ @file.seek(0)
163
+ lines = @file.readlines
164
+ assert_equal [], lines
165
+ end
166
+
150
167
  test 'compact data if duplicated line' do
151
168
  write_data(@file, <<~EOF)
152
169
  valid_path\t0000000000000002\t0000000000000001
@@ -163,7 +180,7 @@ class IntailPositionFileTest < Test::Unit::TestCase
163
180
  sub_test_case '#[]' do
164
181
  test 'return entry' do
165
182
  write_data(@file, TEST_CONTENT)
166
- pf = Fluent::Plugin::TailInput::PositionFile.load(@file, false, {}, **{logger: $log})
183
+ pf = Fluent::Plugin::TailInput::PositionFile.load(@file, false, TEST_CONTENT_PATHS, **{logger: $log})
167
184
 
168
185
  valid_target_info = Fluent::Plugin::TailInput::TargetInfo.new('valid_path', File.stat(@file).ino)
169
186
  f = pf[valid_target_info]
@@ -208,7 +225,7 @@ class IntailPositionFileTest < Test::Unit::TestCase
208
225
  end
209
226
 
210
227
  sub_test_case '#unwatch' do
211
- test 'deletes entry by path' do
228
+ test 'unwatch entry by path' do
212
229
  write_data(@file, TEST_CONTENT)
213
230
  pf = Fluent::Plugin::TailInput::PositionFile.load(@file, false, {}, logger: $log)
214
231
  inode1 = File.stat(@file).ino
@@ -226,6 +243,32 @@ class IntailPositionFileTest < Test::Unit::TestCase
226
243
 
227
244
  assert_not_equal p1, p2
228
245
  end
246
+
247
+ test 'unwatch entries by inode' do
248
+ write_data(@file, TEST_CONTENT)
249
+ pf = Fluent::Plugin::TailInput::PositionFile.load(@file, true, TEST_CONTENT_INODES, logger: $log)
250
+
251
+ existing_targets = TEST_CONTENT_INODES.select do |inode, target_info|
252
+ inode == 1
253
+ end
254
+ pe_to_unwatch = pf[TEST_CONTENT_INODES[0]]
255
+
256
+ pf.unwatch_removed_targets(existing_targets)
257
+
258
+ assert_equal(
259
+ {
260
+ map_keys: [TEST_CONTENT_INODES[1].ino],
261
+ unwatched_pe_pos: Fluent::Plugin::TailInput::PositionFile::UNWATCHED_POSITION,
262
+ },
263
+ {
264
+ map_keys: pf.instance_variable_get(:@map).keys,
265
+ unwatched_pe_pos: pe_to_unwatch.read_pos,
266
+ }
267
+ )
268
+
269
+ unwatched_pe_retaken = pf[TEST_CONTENT_INODES[0]]
270
+ assert_not_equal pe_to_unwatch, unwatched_pe_retaken
271
+ end
229
272
  end
230
273
 
231
274
  sub_test_case 'FilePositionEntry' do
@@ -300,58 +343,4 @@ class IntailPositionFileTest < Test::Unit::TestCase
300
343
  assert_equal 2, f.read_inode
301
344
  end
302
345
  end
303
-
304
- sub_test_case "TargetInfo equality rules" do
305
- sub_test_case "== operator" do
306
- def test_equal
307
- t1 = Fluent::Plugin::TailInput::TargetInfo.new("test", 1234)
308
- t2 = Fluent::Plugin::TailInput::TargetInfo.new("test", 1235)
309
-
310
- assert_equal t1, t2
311
- end
312
-
313
- def test_not_equal
314
- t1 = Fluent::Plugin::TailInput::TargetInfo.new("test", 1234)
315
- t2 = Fluent::Plugin::TailInput::TargetInfo.new("test2", 1234)
316
-
317
- assert_not_equal t1, t2
318
- end
319
- end
320
-
321
- sub_test_case "eql? method" do
322
- def test_eql?
323
- t1 = Fluent::Plugin::TailInput::TargetInfo.new("test", 1234)
324
- t2 = Fluent::Plugin::TailInput::TargetInfo.new("test", 5321)
325
-
326
- assert do
327
- t1.eql? t2
328
- end
329
- end
330
-
331
- def test_not_eql?
332
- t1 = Fluent::Plugin::TailInput::TargetInfo.new("test2", 1234)
333
- t2 = Fluent::Plugin::TailInput::TargetInfo.new("test3", 1234)
334
-
335
- assert do
336
- !t1.eql? t2
337
- end
338
- end
339
- end
340
-
341
- sub_test_case "hash" do
342
- def test_equal
343
- t1 = Fluent::Plugin::TailInput::TargetInfo.new("test", 1234)
344
- t2 = Fluent::Plugin::TailInput::TargetInfo.new("test", 7321)
345
-
346
- assert_equal t1.hash, t2.hash
347
- end
348
-
349
- def test_not_equal
350
- t1 = Fluent::Plugin::TailInput::TargetInfo.new("test", 1234)
351
- t2 = Fluent::Plugin::TailInput::TargetInfo.new("test2", 1234)
352
-
353
- assert_not_equal t1.hash, t2.hash
354
- end
355
- end
356
- end
357
346
  end
@@ -98,4 +98,43 @@ class AckHandlerTest < Test::Unit::TestCase
98
98
  w.close rescue nil
99
99
  end
100
100
  end
101
+
102
+ # ForwardOutput uses AckHandler in multiple threads, so we need to assume this case.
103
+ # If exclusive control for this case is implemented, this test may not be necessary.
104
+ test 'raises no error when another thread closes a socket' do
105
+ ack_handler = Fluent::Plugin::ForwardOutput::AckHandler.new(timeout: 10, log: $log, read_length: 100)
106
+
107
+ node = flexmock('node', host: '127.0.0.1', port: '1000') # for log
108
+ chunk_id = 'chunk_id 111'
109
+ ack = ack_handler.create_ack(chunk_id, node)
110
+
111
+ r, w = IO.pipe
112
+ begin
113
+ w.write(chunk_id)
114
+ stub(r).recv { |_|
115
+ sleep(1) # To ensure that multiple threads select the socket before closing.
116
+ raise IOError, 'stream closed in another thread' if r.closed?
117
+ MessagePack.pack({ 'ack' => Base64.encode64('chunk_id 111') })
118
+ }
119
+ ack.enqueue(r)
120
+
121
+ threads = []
122
+ 2.times do
123
+ threads << Thread.new do
124
+ ack_handler.collect_response(1) do |cid, n, s, ret|
125
+ s&.close
126
+ end
127
+ end
128
+ end
129
+
130
+ assert_true threads.map{ |t| t.join(10) }.all?
131
+ assert_false(
132
+ $log.out.logs.any? { |log| log.include?('[error]') },
133
+ $log.out.logs.select{ |log| log.include?('[error]') }.join('\n')
134
+ )
135
+ ensure
136
+ r.close rescue nil
137
+ w.close rescue nil
138
+ end
139
+ end
101
140
  end
@@ -110,6 +110,10 @@ class SocketCacheTest < Test::Unit::TestCase
110
110
  end
111
111
 
112
112
  sub_test_case 'purge_obsolete_socks' do
113
+ def teardown
114
+ Timecop.return
115
+ end
116
+
113
117
  test 'delete key in inactive_socks' do
114
118
  c = Fluent::Plugin::ForwardOutput::SocketCache.new(10, $log)
115
119
  sock = mock!.close { 'closed' }.subject
@@ -134,7 +138,7 @@ class SocketCacheTest < Test::Unit::TestCase
134
138
  c.checkin(sock)
135
139
 
136
140
  # wait timeout
137
- Timecop.freeze(Time.parse('2016-04-13 14:20:00 +0900'))
141
+ Timecop.freeze(Time.parse('2016-04-13 14:00:11 +0900'))
138
142
  c.checkout_or('key') { sock2 }
139
143
 
140
144
  assert_equal(1, c.instance_variable_get(:@inflight_sockets).size)
@@ -145,5 +149,26 @@ class SocketCacheTest < Test::Unit::TestCase
145
149
  assert_equal(1, c.instance_variable_get(:@inflight_sockets).size)
146
150
  assert_equal(sock2, c.instance_variable_get(:@inflight_sockets).values.first.sock)
147
151
  end
152
+
153
+ test 'should not purge just after checkin and purge after timeout' do
154
+ Timecop.freeze(Time.parse('2016-04-13 14:00:00 +0900'))
155
+
156
+ c = Fluent::Plugin::ForwardOutput::SocketCache.new(10, $log)
157
+ sock = dont_allow(mock!).close
158
+ stub(sock).inspect
159
+ c.checkout_or('key') { sock }
160
+
161
+ Timecop.freeze(Time.parse('2016-04-13 14:00:11 +0900'))
162
+ c.checkin(sock)
163
+
164
+ assert_equal(1, c.instance_variable_get(:@available_sockets).size)
165
+ c.purge_obsolete_socks
166
+ assert_equal(1, c.instance_variable_get(:@available_sockets).size)
167
+
168
+ Timecop.freeze(Time.parse('2016-04-13 14:00:22 +0900'))
169
+ assert_equal(1, c.instance_variable_get(:@available_sockets).size)
170
+ c.purge_obsolete_socks
171
+ assert_equal(0, c.instance_variable_get(:@available_sockets).size)
172
+ end
148
173
  end
149
174
  end
@@ -83,7 +83,7 @@ class BareOutputTest < Test::Unit::TestCase
83
83
 
84
84
  @p.configure(config_element('ROOT', '', {'@log_level' => 'debug'}))
85
85
 
86
- assert{ @p.log.object_id != original_logger.object_id }
86
+ assert(@p.log.object_id != original_logger.object_id)
87
87
  assert_equal Fluent::Log::LEVEL_DEBUG, @p.log.level
88
88
  end
89
89
 
@@ -95,6 +95,19 @@ class BareOutputTest < Test::Unit::TestCase
95
95
  end
96
96
  end
97
97
 
98
+ test 'can use metrics plugins and fallback methods' do
99
+ @p.configure(config_element('ROOT', '', {'@log_level' => 'debug'}))
100
+
101
+ %w[num_errors_metrics emit_count_metrics emit_size_metrics emit_records_metrics].each do |metric_name|
102
+ assert_true @p.instance_variable_get(:"@#{metric_name}").is_a?(Fluent::Plugin::Metrics)
103
+ end
104
+
105
+ assert_equal 0, @p.num_errors
106
+ assert_equal 0, @p.emit_count
107
+ assert_equal 0, @p.emit_size
108
+ assert_equal 0, @p.emit_records
109
+ end
110
+
98
111
  test 'can get input event stream to write' do
99
112
  @p.configure(config_element('ROOT'))
100
113
  @p.start
@@ -1,4 +1,5 @@
1
1
  require_relative '../helper'
2
+ require 'tmpdir'
2
3
  require 'fluent/plugin/base'
3
4
 
4
5
  module FluentPluginBaseTest
@@ -107,9 +108,140 @@ class BaseTest < Test::Unit::TestCase
107
108
  @p.extend m
108
109
  assert_equal [], logger.logs
109
110
 
110
- ret = @p.string_safe_encoding("abc\xff.\x01f"){|s| s.split(/\./) }
111
+ ret = @p.string_safe_encoding("abc\xff.\x01f"){|s| s.split(".") }
111
112
  assert_equal ['abc?', "\u0001f"], ret
112
113
  assert_equal 1, logger.logs.size
113
114
  assert{ logger.logs.first.include?("invalid byte sequence is replaced in ") }
114
115
  end
116
+
117
+ test 'generates worker lock path safely' do
118
+ Dir.mktmpdir("test-fluentd-lock-") do |lock_dir|
119
+ ENV['FLUENTD_LOCK_DIR'] = lock_dir
120
+ p = FluentPluginBaseTest::DummyPlugin.new
121
+ path = p.get_lock_path("Aa\\|=~/_123");
122
+
123
+ assert_equal lock_dir, File.dirname(path)
124
+ assert_equal "fluentd-Aa______123.lock", File.basename(path)
125
+ end
126
+ end
127
+
128
+ test 'can acquire inter-worker locking' do
129
+ Dir.mktmpdir("test-fluentd-lock-") do |lock_dir|
130
+ ENV['FLUENTD_LOCK_DIR'] = lock_dir
131
+ p = FluentPluginBaseTest::DummyPlugin.new
132
+ lock_path = p.get_lock_path("test_base")
133
+
134
+ p.acquire_worker_lock("test_base") do
135
+ # With LOCK_NB set, flock() returns `false` when the
136
+ # file is already locked.
137
+ File.open(lock_path, "w") do |f|
138
+ assert_equal false, f.flock(File::LOCK_EX|File::LOCK_NB)
139
+ end
140
+ end
141
+
142
+ # Lock should be release by now. In that case, flock
143
+ # must return 0.
144
+ File.open(lock_path, "w") do |f|
145
+ assert_equal 0, f.flock(File::LOCK_EX|File::LOCK_NB)
146
+ end
147
+ end
148
+ end
149
+
150
+ test '`ArgumentError` when `conf` is not `Fluent::Config::Element`' do
151
+ assert_raise ArgumentError.new('BUG: type of conf must be Fluent::Config::Element, but Hash is passed.') do
152
+ @p.configure({})
153
+ end
154
+ end
155
+
156
+ sub_test_case 'system_config.workers value after configure' do
157
+ def assert_system_config_workers_value(data)
158
+ conf = config_element()
159
+ conf.set_target_worker_ids(data[:target_worker_ids])
160
+ @p.configure(conf)
161
+ assert{ @p.system_config.workers == data[:expected] }
162
+ end
163
+
164
+ def stub_supervisor_mode
165
+ stub(Fluent::Engine).supervisor_mode { true }
166
+ stub(Fluent::Engine).worker_id { -1 }
167
+ end
168
+
169
+ sub_test_case 'with <system> workers 3 </system>' do
170
+ setup do
171
+ system_config = Fluent::SystemConfig.new
172
+ system_config.workers = 3
173
+ stub(Fluent::Engine).system_config { system_config }
174
+ end
175
+
176
+ data(
177
+ 'without <worker> directive',
178
+ {
179
+ target_worker_ids: [],
180
+ expected: 3
181
+ },
182
+ keep: true
183
+ )
184
+ data(
185
+ 'with <worker 0>',
186
+ {
187
+ target_worker_ids: [0],
188
+ expected: 1
189
+ },
190
+ keep: true
191
+ )
192
+ data(
193
+ 'with <worker 0-1>',
194
+ {
195
+ target_worker_ids: [0, 1],
196
+ expected: 2
197
+ },
198
+ keep: true
199
+ )
200
+ data(
201
+ 'with <worker 0-2>',
202
+ {
203
+ target_worker_ids: [0, 1, 2],
204
+ expected: 3
205
+ },
206
+ keep: true
207
+ )
208
+
209
+ test 'system_config.workers value after configure' do
210
+ assert_system_config_workers_value(data)
211
+ end
212
+
213
+ test 'system_config.workers value after configure with supervisor_mode' do
214
+ stub_supervisor_mode
215
+ assert_system_config_workers_value(data)
216
+ end
217
+ end
218
+
219
+ sub_test_case 'without <system> directive' do
220
+ data(
221
+ 'without <worker> directive',
222
+ {
223
+ target_worker_ids: [],
224
+ expected: 1
225
+ },
226
+ keep: true
227
+ )
228
+ data(
229
+ 'with <worker 0>',
230
+ {
231
+ target_worker_ids: [0],
232
+ expected: 1
233
+ },
234
+ keep: true
235
+ )
236
+
237
+ test 'system_config.workers value after configure' do
238
+ assert_system_config_workers_value(data)
239
+ end
240
+
241
+ test 'system_config.workers value after configure with supervisor_mode' do
242
+ stub_supervisor_mode
243
+ assert_system_config_workers_value(data)
244
+ end
245
+ end
246
+ end
115
247
  end
@@ -1151,15 +1151,13 @@ class FileBufferTest < Test::Unit::TestCase
1151
1151
 
1152
1152
  sub_test_case 'there are existing broken file chunks' do
1153
1153
  setup do
1154
+ @id_output = 'backup_test'
1154
1155
  @bufdir = File.expand_path('../../tmp/broken_buffer_file', __FILE__)
1155
- FileUtils.mkdir_p @bufdir unless File.exist?(@bufdir)
1156
+ FileUtils.rm_rf @bufdir rescue nil
1157
+ FileUtils.mkdir_p @bufdir
1156
1158
  @bufpath = File.join(@bufdir, 'broken_test.*.log')
1157
1159
 
1158
1160
  Fluent::Test.setup
1159
- @d = FluentPluginFileBufferTest::DummyOutputPlugin.new
1160
- @p = Fluent::Plugin::FileBuffer.new
1161
- @p.owner = @d
1162
- @p.configure(config_element('buffer', '', {'path' => @bufpath}))
1163
1161
  end
1164
1162
 
1165
1163
  teardown do
@@ -1171,12 +1169,12 @@ class FileBufferTest < Test::Unit::TestCase
1171
1169
  @p.close unless @p.closed?
1172
1170
  @p.terminate unless @p.terminated?
1173
1171
  end
1174
- if @bufdir
1175
- Dir.glob(File.join(@bufdir, '*')).each do |path|
1176
- next if ['.', '..'].include?(File.basename(path))
1177
- File.delete(path)
1178
- end
1179
- end
1172
+ end
1173
+
1174
+ def setup_plugins(buf_conf)
1175
+ @d = FluentPluginFileBufferTest::DummyOutputPlugin.new
1176
+ @d.configure(config_element('ROOT', '', {'@id' => @id_output}, [config_element('buffer', '', buf_conf)]))
1177
+ @p = @d.buffer
1180
1178
  end
1181
1179
 
1182
1180
  def create_first_chunk(mode)
@@ -1232,44 +1230,85 @@ class FileBufferTest < Test::Unit::TestCase
1232
1230
  assert { logs.any? { |log| log.include?(msg) } }
1233
1231
  end
1234
1232
 
1235
- test '#resume ignores staged empty chunk' do
1236
- _, p1 = create_first_chunk('b')
1233
+ test '#resume backups staged empty chunk' do
1234
+ setup_plugins({'path' => @bufpath})
1235
+ c1id, p1 = create_first_chunk('b')
1237
1236
  File.open(p1, 'wb') { |f| } # create staged empty chunk file
1238
1237
  c2id, _ = create_second_chunk('b')
1239
1238
 
1240
- @p.start
1239
+ Fluent::SystemConfig.overwrite_system_config('root_dir' => @bufdir) do
1240
+ @p.start
1241
+ end
1242
+
1241
1243
  compare_staged_chunk(@p.stage, c2id, '2016-04-17 14:01:00 -0700', 3, :staged)
1242
1244
  compare_log(@p, 'staged file chunk is empty')
1245
+ assert { not File.exist?(p1) }
1246
+ assert { File.exist?("#{@bufdir}/backup/worker0/#{@id_output}/#{@d.dump_unique_id_hex(c1id)}.log") }
1243
1247
  end
1244
1248
 
1245
- test '#resume ignores staged broken metadata' do
1249
+ test '#resume backups staged broken metadata' do
1250
+ setup_plugins({'path' => @bufpath})
1246
1251
  c1id, _ = create_first_chunk('b')
1247
- _, p2 = create_second_chunk('b')
1252
+ c2id, p2 = create_second_chunk('b')
1248
1253
  File.open(p2 + '.meta', 'wb') { |f| f.write("\0" * 70) } # create staged broken meta file
1249
1254
 
1250
- @p.start
1255
+ Fluent::SystemConfig.overwrite_system_config('root_dir' => @bufdir) do
1256
+ @p.start
1257
+ end
1258
+
1251
1259
  compare_staged_chunk(@p.stage, c1id, '2016-04-17 14:00:00 -0700', 4, :staged)
1252
1260
  compare_log(@p, 'staged meta file is broken')
1261
+ assert { not File.exist?(p2) }
1262
+ assert { File.exist?("#{@bufdir}/backup/worker0/#{@id_output}/#{@d.dump_unique_id_hex(c2id)}.log") }
1253
1263
  end
1254
1264
 
1255
- test '#resume ignores enqueued empty chunk' do
1256
- _, p1 = create_first_chunk('q')
1265
+ test '#resume backups enqueued empty chunk' do
1266
+ setup_plugins({'path' => @bufpath})
1267
+ c1id, p1 = create_first_chunk('q')
1257
1268
  File.open(p1, 'wb') { |f| } # create enqueued empty chunk file
1258
1269
  c2id, _ = create_second_chunk('q')
1259
1270
 
1260
- @p.start
1271
+ Fluent::SystemConfig.overwrite_system_config('root_dir' => @bufdir) do
1272
+ @p.start
1273
+ end
1274
+
1261
1275
  compare_queued_chunk(@p.queue, c2id, 3, :queued)
1262
1276
  compare_log(@p, 'enqueued file chunk is empty')
1277
+ assert { not File.exist?(p1) }
1278
+ assert { File.exist?("#{@bufdir}/backup/worker0/#{@id_output}/#{@d.dump_unique_id_hex(c1id)}.log") }
1263
1279
  end
1264
1280
 
1265
- test '#resume ignores enqueued broken metadata' do
1281
+ test '#resume backups enqueued broken metadata' do
1282
+ setup_plugins({'path' => @bufpath})
1266
1283
  c1id, _ = create_first_chunk('q')
1267
- _, p2 = create_second_chunk('q')
1284
+ c2id, p2 = create_second_chunk('q')
1268
1285
  File.open(p2 + '.meta', 'wb') { |f| f.write("\0" * 70) } # create enqueued broken meta file
1269
1286
 
1270
- @p.start
1287
+ Fluent::SystemConfig.overwrite_system_config('root_dir' => @bufdir) do
1288
+ @p.start
1289
+ end
1290
+
1271
1291
  compare_queued_chunk(@p.queue, c1id, 4, :queued)
1272
1292
  compare_log(@p, 'enqueued meta file is broken')
1293
+ assert { not File.exist?(p2) }
1294
+ assert { File.exist?("#{@bufdir}/backup/worker0/#{@id_output}/#{@d.dump_unique_id_hex(c2id)}.log") }
1295
+ end
1296
+
1297
+ test '#resume throws away broken chunk with disable_chunk_backup' do
1298
+ setup_plugins({'path' => @bufpath, 'disable_chunk_backup' => true})
1299
+ c1id, _ = create_first_chunk('b')
1300
+ c2id, p2 = create_second_chunk('b')
1301
+ File.open(p2 + '.meta', 'wb') { |f| f.write("\0" * 70) } # create staged broken meta file
1302
+
1303
+ Fluent::SystemConfig.overwrite_system_config('root_dir' => @bufdir) do
1304
+ @p.start
1305
+ end
1306
+
1307
+ compare_staged_chunk(@p.stage, c1id, '2016-04-17 14:00:00 -0700', 4, :staged)
1308
+ compare_log(@p, 'staged meta file is broken')
1309
+ compare_log(@p, 'disable_chunk_backup is true')
1310
+ assert { not File.exist?(p2) }
1311
+ assert { not File.exist?("#{@bufdir}/backup/worker0/#{@id_output}/#{@d.dump_unique_id_hex(c2id)}.log") }
1273
1312
  end
1274
1313
  end
1275
1314
  end