fluentd 1.13.3 → 1.16.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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