fluentd 1.4.2-x64-mingw32 → 1.5.0-x64-mingw32

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.

@@ -30,6 +30,12 @@ module Fluent
30
30
  desc 'Set JSON parser'
31
31
  config_param :json_parser, :enum, list: [:oj, :yajl, :json], default: :oj
32
32
 
33
+ # The Yajl library defines a default buffer size of 8KiB when parsing
34
+ # from IO streams, so maintain this for backwards-compatibility.
35
+ # https://www.rubydoc.info/github/brianmario/yajl-ruby/Yajl%2FParser:parse
36
+ desc 'Set the buffer size that Yajl will use when parsing streaming input'
37
+ config_param :stream_buffer_size, :integer, default: 8192
38
+
33
39
  config_set_default :time_type, :float
34
40
 
35
41
  def configure(conf)
@@ -81,7 +87,7 @@ module Fluent
81
87
  y.on_parse_complete = ->(record){
82
88
  block.call(parse_time(record), record)
83
89
  }
84
- y.parse(io)
90
+ y.parse(io, @stream_buffer_size)
85
91
  end
86
92
  end
87
93
  end
@@ -87,7 +87,12 @@ module Fluent
87
87
  if File.exist?(@path)
88
88
  raise Fluent::ConfigError, "Plugin storage path '#{@path}' is not readable/writable" unless File.readable?(@path) && File.writable?(@path)
89
89
  begin
90
- data = Yajl::Parser.parse(open(@path, 'r:utf-8'){ |io| io.read })
90
+ data = open(@path, 'r:utf-8') { |io| io.read }
91
+ if data.empty?
92
+ log.warn "detect empty plugin storage file during startup. Ignored: #{@path}"
93
+ return
94
+ end
95
+ data = Yajl::Parser.parse(data)
91
96
  raise Fluent::ConfigError, "Invalid contents (not object) in plugin storage file: '#{@path}'" unless data.is_a?(Hash)
92
97
  rescue => e
93
98
  log.error "failed to read data from plugin storage file", path: @path, error: e
@@ -128,7 +133,7 @@ module Fluent
128
133
  tmp_path = @path + '.tmp'
129
134
  begin
130
135
  json_string = Yajl::Encoder.encode(@store, pretty: @pretty_print)
131
- open(tmp_path, 'w:utf-8', @mode){ |io| io.write json_string }
136
+ open(tmp_path, 'w:utf-8', @mode) { |io| io.write json_string; io.fsync }
132
137
  File.rename(tmp_path, @path)
133
138
  rescue => e
134
139
  log.error "failed to save data for plugin storage to file", path: @path, tmp: tmp_path, error: e
@@ -751,8 +751,15 @@ module Fluent
751
751
  # Consider write_nonblock with {exception: false} when IO::WaitWritable error happens frequently.
752
752
  written_bytes = @_handler_socket.write_nonblock(@_handler_write_buffer)
753
753
  @_handler_write_buffer.slice!(0, written_bytes)
754
- super
755
754
  end
755
+
756
+ # No need to call `super` in a synchronized context because TLSServer doesn't use the inner buffer(::IO::Buffer) of Coolio::IO.
757
+ # Instead of using Coolio::IO's inner buffer, TLSServer has own buffer(`@_handler_write_buffer`). See also TLSServer#write.
758
+ # Actually, the only reason calling `super` here is call Coolio::IO#disable_write_watcher.
759
+ # If `super` is called in a synchronized context, it could cause a mutex recursive locking since Coolio::IO#on_write_complete
760
+ # eventually calls TLSServer#close which try to get a lock.
761
+ super
762
+
756
763
  close if @close_after_write_complete
757
764
  rescue IO::WaitWritable, IO::WaitReadable
758
765
  return
@@ -588,7 +588,7 @@ module Fluent
588
588
 
589
589
  def show_plugin_config
590
590
  name, type = @show_plugin_config.split(":") # input:tail
591
- $log.info "Use fluent-plugin-config-format --format=txt #{name} #{type}"
591
+ $log.info "show_plugin_config option is deprecated. Use fluent-plugin-config-format --format=txt #{name} #{type}"
592
592
  exit 0
593
593
  end
594
594
 
@@ -16,6 +16,6 @@
16
16
 
17
17
  module Fluent
18
18
 
19
- VERSION = '1.4.2'
19
+ VERSION = '1.5.0'
20
20
 
21
21
  end
@@ -680,7 +680,7 @@ class ParserFilterTest < Test::Unit::TestCase
680
680
 
681
681
  def test_call_emit_error_event_when_pattern_is_mismached
682
682
  flexmock(@d.instance.router).should_receive(:emit_error_event).
683
- with(String, Integer, Hash, ParserError.new("pattern not match with data '#{INVALID_MESSAGE}'")).once
683
+ with(String, Integer, Hash, ParserError.new("pattern not matched with data '#{INVALID_MESSAGE}'")).once
684
684
  @d.run do
685
685
  @d.feed(@tag, Fluent::EventTime.now.to_i, {'message' => INVALID_MESSAGE})
686
686
  end
@@ -82,6 +82,14 @@ class ForwardInputTest < Test::Unit::TestCase
82
82
  assert_equal 1, d.instance.security.clients.size
83
83
  end
84
84
 
85
+ data(tag: "tag",
86
+ add_tag_prefix: "add_tag_prefix")
87
+ test 'tag parameters' do |data|
88
+ assert_raise(Fluent::ConfigError.new("'#{data}' parameter must not be empty")) {
89
+ create_driver(CONFIG + "#{data} ''")
90
+ }
91
+ end
92
+
85
93
  test 'send_keepalive_packet is disabled by default' do
86
94
  @d = d = create_driver(CONFIG_AUTH)
87
95
  assert_false d.instance.send_keepalive_packet
@@ -269,6 +277,34 @@ class ForwardInputTest < Test::Unit::TestCase
269
277
  assert_equal(records, d.events)
270
278
  end
271
279
 
280
+ data(tag: {
281
+ param: "tag new_tag",
282
+ result: "new_tag"
283
+ },
284
+ add_tag_prefix: {
285
+ param: "add_tag_prefix new_prefix",
286
+ result: "new_prefix.tag1"
287
+ })
288
+ test 'tag parameters' do |data|
289
+ @d = create_driver(CONFIG + data[:param])
290
+ time = event_time("2011-01-02 13:14:15 UTC")
291
+ options = {auth: false}
292
+
293
+ records = [
294
+ ["tag1", time, {"a"=>1}],
295
+ ["tag1", time, {"a"=>2}],
296
+ ]
297
+
298
+ @d.run(expect_records: records.length, timeout: 20) do
299
+ entries = []
300
+ records.each {|tag, _time, record|
301
+ entries << [_time, record]
302
+ }
303
+ send_data packer.write(["tag1", entries]).to_s, **options
304
+ end
305
+ assert_equal(data[:result], @d.events[0][0])
306
+ end
307
+
272
308
  data(tcp: {
273
309
  config: CONFIG,
274
310
  options: {
@@ -378,6 +414,34 @@ class ForwardInputTest < Test::Unit::TestCase
378
414
  assert_equal(records, d.events)
379
415
  end
380
416
 
417
+ data(tag: {
418
+ param: "tag new_tag",
419
+ result: "new_tag"
420
+ },
421
+ add_tag_prefix: {
422
+ param: "add_tag_prefix new_prefix",
423
+ result: "new_prefix.tag1"
424
+ })
425
+ test 'tag parameters' do |data|
426
+ @d = create_driver(CONFIG + data[:param])
427
+ time = event_time("2011-01-02 13:14:15 UTC")
428
+ options = {auth: false}
429
+
430
+ records = [
431
+ ["tag1", time, {"a"=>1}],
432
+ ["tag1", time, {"a"=>2}],
433
+ ]
434
+
435
+ @d.run(expect_records: records.length, timeout: 20) do
436
+ entries = ''
437
+ records.each {|_tag, _time, record|
438
+ packer(entries).write([_time, record]).flush
439
+ }
440
+ send_data packer.write(["tag1", entries]).to_s, **options
441
+ end
442
+ assert_equal(data[:result], @d.events[0][0])
443
+ end
444
+
381
445
  data(tcp: {
382
446
  config: CONFIG,
383
447
  options: {
@@ -55,6 +55,35 @@ EOS
55
55
  end
56
56
  end
57
57
 
58
+ data('Use protocol_type' => ['protocol_type tcp', :tcp, :udp],
59
+ 'Use transport' => ["<transport tcp>\n </transport>", nil, :tcp],
60
+ 'Use transport and protocol' => ["protocol_type udp\n<transport tcp>\n </transport>", :udp, :tcp])
61
+ def test_configure_protocol(param)
62
+ conf, proto_type, transport_proto_type = *param
63
+ d = create_driver([CONFIG, conf].join("\n"))
64
+
65
+ assert_equal(d.instance.protocol_type, proto_type)
66
+ assert_equal(d.instance.transport_config.protocol, transport_proto_type)
67
+ end
68
+
69
+ # For backward compat
70
+ def test_respect_protocol_type_than_transport
71
+ d = create_driver([CONFIG, "<transport tcp> \n</transport>", "protocol_type udp"].join("\n"))
72
+ tests = create_test_case
73
+
74
+ d.run(expect_emits: 2) do
75
+ u = UDPSocket.new
76
+ u.connect('127.0.0.1', PORT)
77
+ tests.each {|test|
78
+ u.send(test['msg'], 0)
79
+ }
80
+ end
81
+
82
+ assert(d.events.size > 0)
83
+ compare_test_result(d.events, tests)
84
+ end
85
+
86
+
58
87
  data(
59
88
  ipv4: ['127.0.0.1', CONFIG, ::Socket::AF_INET],
60
89
  ipv6: ['::1', IPv6_CONFIG, ::Socket::AF_INET6],
@@ -119,7 +148,7 @@ EOS
119
148
  end
120
149
 
121
150
  def test_msg_size_with_tcp
122
- d = create_driver([CONFIG, 'protocol_type tcp'].join("\n"))
151
+ d = create_driver([CONFIG, "<transport tcp> \n</transport>"].join("\n"))
123
152
  tests = create_test_case
124
153
 
125
154
  d.run(expect_emits: 2) do
@@ -135,7 +164,7 @@ EOS
135
164
  end
136
165
 
137
166
  def test_msg_size_with_same_tcp_connection
138
- d = create_driver([CONFIG, 'protocol_type tcp'].join("\n"))
167
+ d = create_driver([CONFIG, "<transport tcp> \n</transport>"].join("\n"))
139
168
  tests = create_test_case
140
169
 
141
170
  d.run(expect_emits: 2) do
@@ -289,7 +318,7 @@ EOS
289
318
 
290
319
  sub_test_case 'octet counting frame' do
291
320
  def test_msg_size_with_tcp
292
- d = create_driver([CONFIG, 'protocol_type tcp', 'frame_type octet_count'].join("\n"))
321
+ d = create_driver([CONFIG, "<transport tcp> \n</transport>", 'frame_type octet_count'].join("\n"))
293
322
  tests = create_test_case
294
323
 
295
324
  d.run(expect_emits: 2) do
@@ -305,7 +334,7 @@ EOS
305
334
  end
306
335
 
307
336
  def test_msg_size_with_same_tcp_connection
308
- d = create_driver([CONFIG, 'protocol_type tcp', 'frame_type octet_count'].join("\n"))
337
+ d = create_driver([CONFIG, "<transport tcp> \n</transport>", 'frame_type octet_count'].join("\n"))
309
338
  tests = create_test_case
310
339
 
311
340
  d.run(expect_emits: 2) do
@@ -328,6 +328,7 @@ class ExecFilterOutputTest < Test::Unit::TestCase
328
328
  </format>
329
329
  <parse>
330
330
  @type json
331
+ stream_buffer_size 1
331
332
  </parse>
332
333
  <extract>
333
334
  tag_key tag
@@ -338,6 +339,7 @@ class ExecFilterOutputTest < Test::Unit::TestCase
338
339
  command cat
339
340
  in_keys message
340
341
  out_format json
342
+ out_stream_buffer_size 1
341
343
  time_key time
342
344
  tag_key tag
343
345
  ]
@@ -372,6 +374,7 @@ class ExecFilterOutputTest < Test::Unit::TestCase
372
374
  </format>
373
375
  <parse>
374
376
  @type json
377
+ stream_buffer_size 1
375
378
  </parse>
376
379
  <extract>
377
380
  tag_key tag
@@ -382,6 +385,7 @@ class ExecFilterOutputTest < Test::Unit::TestCase
382
385
  command cat
383
386
  in_keys message
384
387
  out_format json
388
+ out_stream_buffer_size 1
385
389
  time_key time
386
390
  tag_key tag
387
391
  ]
@@ -414,6 +418,7 @@ class ExecFilterOutputTest < Test::Unit::TestCase
414
418
  </format>
415
419
  <parse>
416
420
  @type json
421
+ stream_buffer_size 1
417
422
  </parse>
418
423
  <extract>
419
424
  tag_key tag
@@ -426,6 +431,7 @@ class ExecFilterOutputTest < Test::Unit::TestCase
426
431
  command cat
427
432
  in_keys message
428
433
  out_format json
434
+ out_stream_buffer_size 1
429
435
  time_key time
430
436
  time_format %d/%b/%Y %H:%M:%S.%N %z
431
437
  tag_key tag
@@ -920,4 +920,213 @@ EOL
920
920
  assert_equal(['test', time, records[1]], events[1])
921
921
  end
922
922
  end
923
+
924
+ test 'Create new connection per send_data' do
925
+ target_input_driver = create_target_input_driver(conf: TARGET_CONFIG)
926
+ output_conf = CONFIG
927
+ d = create_driver(output_conf)
928
+ d.instance_start
929
+
930
+ begin
931
+ chunk = Fluent::Plugin::Buffer::MemoryChunk.new(Fluent::Plugin::Buffer::Metadata.new(nil, nil, nil))
932
+ mock.proxy(d.instance).create_transfer_socket(TARGET_HOST, TARGET_PORT, 'test') { |sock| mock(sock).close.once; sock }.twice
933
+
934
+ target_input_driver.run(timeout: 15) do
935
+ d.run(shutdown: false) do
936
+ node = d.instance.nodes.first
937
+ 2.times do
938
+ node.send_data('test', chunk) rescue nil
939
+ end
940
+ end
941
+ end
942
+ ensure
943
+ d.instance_shutdown
944
+ end
945
+ end
946
+
947
+ sub_test_case 'keepalive' do
948
+ test 'Do not create connection per send_data' do
949
+ target_input_driver = create_target_input_driver(conf: TARGET_CONFIG)
950
+ output_conf = CONFIG + %[
951
+ keepalive true
952
+ keepalive_timeout 2
953
+ ]
954
+ d = create_driver(output_conf)
955
+ d.instance_start
956
+
957
+ begin
958
+ chunk = Fluent::Plugin::Buffer::MemoryChunk.new(Fluent::Plugin::Buffer::Metadata.new(nil, nil, nil))
959
+ mock.proxy(d.instance).create_transfer_socket(TARGET_HOST, TARGET_PORT, 'test') { |sock| mock(sock).close.once; sock }.once
960
+
961
+ target_input_driver.run(timeout: 15) do
962
+ d.run(shutdown: false) do
963
+ node = d.instance.nodes.first
964
+ 2.times do
965
+ node.send_data('test', chunk) rescue nil
966
+ end
967
+ end
968
+ end
969
+ ensure
970
+ d.instance_shutdown
971
+ end
972
+ end
973
+
974
+ sub_test_case 'with require_ack_response' do
975
+ test 'Do not create connection per send_data' do
976
+ target_input_driver = create_target_input_driver(conf: TARGET_CONFIG)
977
+ output_conf = CONFIG + %[
978
+ require_ack_response true
979
+ keepalive true
980
+ keepalive_timeout 2
981
+ ]
982
+ d = create_driver(output_conf)
983
+ d.instance_start
984
+
985
+ begin
986
+ chunk = Fluent::Plugin::Buffer::MemoryChunk.new(Fluent::Plugin::Buffer::Metadata.new(nil, nil, nil))
987
+ mock.proxy(d.instance).create_transfer_socket(TARGET_HOST, TARGET_PORT, 'test') { |sock| mock(sock).close.once; sock }.once
988
+
989
+ target_input_driver.run(timeout: 15) do
990
+ d.run(shutdown: false) do
991
+ node = d.instance.nodes.first
992
+ 2.times do
993
+ node.send_data('test', chunk) rescue nil
994
+ end
995
+ end
996
+ end
997
+ ensure
998
+ d.instance_shutdown
999
+ end
1000
+ end
1001
+ end
1002
+ end
1003
+
1004
+ sub_test_case 'SocketCache' do
1005
+ sub_test_case 'fetch_or' do
1006
+ test 'when gived key does not exist' do
1007
+ c = Fluent::Plugin::ForwardOutput::Node::SocketCache.new(10, Logger.new(nil))
1008
+ sock = mock!.open { 1 }.subject
1009
+ assert_equal(1, c.fetch_or { sock.open })
1010
+ end
1011
+
1012
+ test 'when given key exists' do
1013
+ c = Fluent::Plugin::ForwardOutput::Node::SocketCache.new(10, Logger.new(nil))
1014
+ assert_equal(1, c.fetch_or { 1 })
1015
+
1016
+ sock = dont_allow(mock!).open
1017
+ assert_equal(1, c.fetch_or { sock.open })
1018
+ end
1019
+
1020
+ test "when given key's value was expired" do
1021
+ c = Fluent::Plugin::ForwardOutput::Node::SocketCache.new(0, Logger.new(nil))
1022
+ assert_equal(1, c.fetch_or { 1 })
1023
+
1024
+ sock = mock!.open { 1 }.subject
1025
+ assert_equal(1, c.fetch_or { sock.open })
1026
+ end
1027
+ end
1028
+
1029
+ test 'revoke' do
1030
+ c = Fluent::Plugin::ForwardOutput::Node::SocketCache.new(10, Logger.new(nil))
1031
+ c.fetch_or { 1 }
1032
+ c.revoke
1033
+
1034
+ sock = mock!.open { 1 }.subject
1035
+ assert_equal(1, c.fetch_or { sock.open })
1036
+ end
1037
+
1038
+ test 'revoke_by_value' do
1039
+ c = Fluent::Plugin::ForwardOutput::Node::SocketCache.new(10, Logger.new(nil))
1040
+ c.fetch_or { 1 }
1041
+ c.revoke_by_value(1)
1042
+
1043
+ sock = mock!.open { 1 }.subject
1044
+ assert_equal(1, c.fetch_or { sock.open })
1045
+ end
1046
+
1047
+ sub_test_case 'dec_ref' do
1048
+ test 'when value exists in active_socks' do
1049
+ c = Fluent::Plugin::ForwardOutput::Node::SocketCache.new(10, Logger.new(nil))
1050
+ c.fetch_or { 1 }
1051
+ c.dec_ref
1052
+
1053
+ assert_equal(0, c.instance_variable_get(:@active_socks)[Thread.current.object_id].ref)
1054
+ end
1055
+
1056
+ test 'when value exists in inactive_socks' do
1057
+ c = Fluent::Plugin::ForwardOutput::Node::SocketCache.new(10, Logger.new(nil))
1058
+ c.fetch_or { 1 }
1059
+ c.revoke
1060
+ c.dec_ref
1061
+ assert_equal(-1, c.instance_variable_get(:@inactive_socks)[Thread.current.object_id].ref)
1062
+ end
1063
+ end
1064
+
1065
+ sub_test_case 'dec_ref_by_value' do
1066
+ test 'when value exists in active_socks' do
1067
+ c = Fluent::Plugin::ForwardOutput::Node::SocketCache.new(10, Logger.new(nil))
1068
+ c.fetch_or { 1 }
1069
+ c.dec_ref_by_value(1)
1070
+
1071
+ assert_equal(0, c.instance_variable_get(:@active_socks)[Thread.current.object_id].ref)
1072
+ end
1073
+
1074
+ test 'when value exists in inactive_socks' do
1075
+ c = Fluent::Plugin::ForwardOutput::Node::SocketCache.new(10, Logger.new(nil))
1076
+ c.fetch_or { 1 }
1077
+ c.revoke
1078
+ c.dec_ref_by_value(1)
1079
+ assert_equal(-1, c.instance_variable_get(:@inactive_socks)[Thread.current.object_id].ref)
1080
+ end
1081
+ end
1082
+
1083
+ sub_test_case 'clear' do
1084
+ test 'when value is in active_socks' do
1085
+ c = Fluent::Plugin::ForwardOutput::Node::SocketCache.new(10, Logger.new(nil))
1086
+ m = mock!.close { 'closed' }.subject
1087
+ c.fetch_or { m }
1088
+ assert_true(!c.instance_variable_get(:@active_socks).empty?)
1089
+
1090
+ c.clear
1091
+ assert_true(c.instance_variable_get(:@active_socks).empty?)
1092
+ end
1093
+
1094
+ test 'when value is in inactive_socks' do
1095
+ c = Fluent::Plugin::ForwardOutput::Node::SocketCache.new(10, Logger.new(nil))
1096
+ m = mock!.close { 'closed' }.subject
1097
+ c.fetch_or { m }
1098
+ c.revoke
1099
+ assert_true(!c.instance_variable_get(:@inactive_socks).empty?)
1100
+
1101
+ c.clear
1102
+ assert_true(c.instance_variable_get(:@active_socks).empty?)
1103
+ end
1104
+ end
1105
+
1106
+ sub_test_case 'purge_obsolete_socks' do
1107
+ test 'delete key in inactive_socks' do
1108
+ c = Fluent::Plugin::ForwardOutput::Node::SocketCache.new(10, Logger.new(nil))
1109
+ m = mock!.close { 'closed' }.subject
1110
+ c.fetch_or { m }
1111
+ c.revoke
1112
+ assert_true(!c.instance_variable_get(:@inactive_socks).empty?)
1113
+
1114
+ c.purge_obsolete_socks
1115
+ assert_true(c.instance_variable_get(:@active_socks).empty?)
1116
+ end
1117
+
1118
+ test 'move key from active_socks to inactive_socks' do
1119
+ c = Fluent::Plugin::ForwardOutput::Node::SocketCache.new(10, Logger.new(nil))
1120
+ m = dont_allow(mock!).close
1121
+ stub(m).inspect # for log
1122
+ c.fetch_or { m }
1123
+ assert_true(!c.instance_variable_get(:@active_socks).empty?)
1124
+ assert_true(c.instance_variable_get(:@inactive_socks).empty?)
1125
+
1126
+ c.purge_obsolete_socks
1127
+ assert_true(!c.instance_variable_get(:@active_socks).empty?)
1128
+ assert_true(c.instance_variable_get(:@inactive_socks).empty?)
1129
+ end
1130
+ end
1131
+ end
923
1132
  end