fluentd 1.2.6 → 1.3.0

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.

checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 8429bf1a0e35278c81b67e3d517568038a67fb2c
4
- data.tar.gz: ba5260ae4f5db3933aebbf2ba598f0c03d9ab878
3
+ metadata.gz: c973680de36bad1981b1cc6bd3c23c391a04fc66
4
+ data.tar.gz: eb6b2c87bd06d19c2d90050355059e6030f47c14
5
5
  SHA512:
6
- metadata.gz: 575ca7960c2f0560d103001eecbbaf43763394c238280a4aebc4b8ab61336a069d29917c8dca25be95a2b78557c80e172e3dc0834ed8d165046bc87c822be426
7
- data.tar.gz: be8459ae73c470d11848afc2f5cdc5e4f57a9a74606715e3491a7660e1fd4e362b899c5c7ccbe72504c74bcf1c4d818c6cbb00db37707529e6b30dd4d98c13cf
6
+ metadata.gz: 390c4766aca79e9e3e235fad17b66e5ab0d979df6d77e91ac7f03b4be9a831b60214bef63a5cf0811548a8020f4954df4f27beda58dedeb5948a0f804b6a1c42
7
+ data.tar.gz: cef9e1fcf326748d4a72420c5ae4f6b50f3ed8af8178ba0f405ee667a675435121de90099a27170d96ac9592ed421e71655a40b2060a1967f58f62e1c311c047
@@ -9,13 +9,13 @@ matrix:
9
9
  include:
10
10
  - rvm: 2.1.10
11
11
  os: linux
12
- - rvm: 2.2.6
12
+ - rvm: 2.2.10
13
13
  os: linux
14
- - rvm: 2.3.3
14
+ - rvm: 2.3.8
15
15
  os: linux
16
- - rvm: 2.4.3
16
+ - rvm: 2.4.5
17
17
  os: linux
18
- - rvm: 2.5.0
18
+ - rvm: 2.5.3
19
19
  os: linux
20
20
  - rvm: ruby-head
21
21
  os: linux
@@ -1,3 +1,30 @@
1
+ # v1.3
2
+
3
+ ## Release v1.3.0 - 2018/11/10
4
+
5
+ ### New features
6
+
7
+ * output: Change thread execution control
8
+ https://github.com/fluent/fluentd/pull/2170
9
+ * in_syslog: Support octet counting frame
10
+ https://github.com/fluent/fluentd/pull/2147
11
+ * Use `flush_thread_count` value for `queued_chunks_limit_size` when `queued_chunks_limit_size` is not specified
12
+ https://github.com/fluent/fluentd/pull/2173
13
+
14
+ ### Enhancements
15
+
16
+ * output: Show backtrace for unrecoverable errors
17
+ https://github.com/fluent/fluentd/pull/2149
18
+ * in_http: Implement support for CORS preflight requests
19
+ https://github.com/fluent/fluentd/pull/2144
20
+
21
+ ### Bug fixes
22
+
23
+ * server: Fix deadlock between on_writable and close in sockets
24
+ https://github.com/fluent/fluentd/pull/2165
25
+ * output: show correct error when wrong plugin is specified for secondary
26
+ https://github.com/fluent/fluentd/pull/2169
27
+
1
28
  # v1.2
2
29
 
3
30
  ## Release v1.2.6 - 2018/10/03
@@ -29,6 +29,8 @@ submitting an issue to Fluentd. Even better you can submit a Pull Request with a
29
29
  If you find a bug of 3rd party plugins, please submit an issue to each plugin repository.
30
30
  And use [omnibus-td-agent](https://github.com/treasure-data/omnibus-td-agent) repository for td-agent releated issues.
31
31
 
32
+ Note: Before report the issue, check latest version first. Sometimes users report fixed bug with older version.
33
+
32
34
  ## Patch Guidelines
33
35
 
34
36
  Here are some things that would increase a chance that your patch is accepted:
@@ -174,9 +174,8 @@ module Fluent
174
174
  ss = StringScanner.new(data)
175
175
  V1Parser.new(ss, basepath, fname, @eval_context).parse_element(true, nil, attrs, elems)
176
176
  end
177
-
178
177
  rescue SystemCallError => e
179
- cpe = ConfigParseError.new("include error #{uri}")
178
+ cpe = ConfigParseError.new("include error #{uri} - #{e}")
180
179
  cpe.set_backtrace(e.backtrace)
181
180
  raise cpe
182
181
  end
@@ -372,7 +372,7 @@ module Fluent
372
372
  end
373
373
 
374
374
  def queue_full?
375
- @queued_chunks_limit_size && (synchronize { @queue.size } >= @queued_chunks_limit_size)
375
+ synchronize { @queue.size } >= @queued_chunks_limit_size
376
376
  end
377
377
 
378
378
  def queued_records
@@ -199,7 +199,7 @@ module Fluent::Plugin
199
199
  when :pingpong
200
200
  success, reason_or_salt, shared_key = check_ping(msg, conn.remote_addr, user_auth_salt, nonce)
201
201
  unless success
202
- conn.on(:write_complete) { |c| c.close }
202
+ conn.on(:write_complete) { |c| c.close_after_write_complete }
203
203
  send_data.call(serializer, generate_pong(false, reason_or_salt, nonce, shared_key))
204
204
  next
205
205
  end
@@ -342,6 +342,10 @@ module Fluent::Plugin
342
342
  # For multiple X-Forwarded-For headers. Use first header value.
343
343
  v = v.first if v.is_a?(Array)
344
344
  @remote_addr = v.split(",").first
345
+ when /Access-Control-Request-Method/i
346
+ @access_control_request_method = v
347
+ when /Access-Control-Request-Headers/i
348
+ @access_control_request_headers = v
345
349
  end
346
350
  }
347
351
  if expect
@@ -367,9 +371,43 @@ module Fluent::Plugin
367
371
  @body << chunk
368
372
  end
369
373
 
374
+ # Web browsers can send an OPTIONS request before performing POST
375
+ # to check if cross-origin requests are supported.
376
+ def handle_options_request
377
+ # Is CORS enabled in the first place?
378
+ if @cors_allow_origins.nil?
379
+ return send_response_and_close("403 Forbidden", {}, "")
380
+ end
381
+
382
+ # in_http does not support HTTP methods except POST
383
+ if @access_control_request_method != 'POST'
384
+ return send_response_and_close("403 Forbidden", {}, "")
385
+ end
386
+
387
+ header = {
388
+ "Access-Control-Allow-Methods" => "POST",
389
+ "Access-Control-Allow-Headers" => @access_control_request_headers || "",
390
+ }
391
+
392
+ # Check the origin and send back a CORS response
393
+ if @cors_allow_origins.include?('*')
394
+ header["Access-Control-Allow-Origin"] = "*"
395
+ send_response_and_close("200 OK", header, "")
396
+ elsif @cors_allow_origins.include?(@origin)
397
+ header["Access-Control-Allow-Origin"] = @origin
398
+ send_response_and_close("200 OK", header, "")
399
+ else
400
+ send_response_and_close("403 Forbidden", {}, "")
401
+ end
402
+ end
403
+
370
404
  def on_message_complete
371
405
  return if closing?
372
406
 
407
+ if @parser.http_method == 'OPTIONS'
408
+ return handle_options_request()
409
+ end
410
+
373
411
  # CORS check
374
412
  # ==========
375
413
  # For every incoming request, we check if we have some CORS
@@ -75,6 +75,8 @@ module Fluent::Plugin
75
75
  config_param :tag, :string
76
76
  desc 'The transport protocol used to receive logs.(udp, tcp)'
77
77
  config_param :protocol_type, :enum, list: [:tcp, :udp], default: :udp
78
+ desc 'The message frame type.(traditional, octet_count)'
79
+ config_param :frame_type, :enum, list: [:traditional, :octet_count], default: :traditional
78
80
 
79
81
  desc 'If true, add source host to event record.'
80
82
  config_param :include_source_host, :bool, default: false, deprecated: 'use "source_hostname_key" or "source_address_key" instead.'
@@ -152,18 +154,29 @@ module Fluent::Plugin
152
154
  end
153
155
 
154
156
  def start_tcp_server
155
- # syslog family add "\n" to each message and this seems only way to split messages in tcp stream
156
- delimiter = "\n"
157
+ octet_count_frame = @frame_type == :octet_count
158
+
159
+ # syslog family adds "\n" to each message when transport is TCP and traditional frame
160
+ delimiter = octet_count_frame ? " " : "\n"
157
161
  delimiter_size = delimiter.size
158
162
  server_create_connection(:in_syslog_tcp_server, @port, bind: @bind, resolve_name: @resolve_hostname) do |conn|
159
163
  conn.data do |data|
160
164
  buffer = conn.buffer
161
165
  buffer << data
162
166
  pos = 0
163
- while idx = buffer.index(delimiter, pos)
164
- msg = buffer[pos...idx]
165
- pos = idx + delimiter_size
166
- message_handler(msg, conn)
167
+ if octet_count_frame
168
+ while idx = buffer.index(delimiter, pos)
169
+ num = Integer(buffer[pos..idx])
170
+ pos = idx + num
171
+ msg = buffer[idx + 1...pos]
172
+ message_handler(msg, conn)
173
+ end
174
+ else
175
+ while idx = buffer.index(delimiter, pos)
176
+ msg = buffer[pos...idx]
177
+ pos = idx + delimiter_size
178
+ message_handler(msg, conn)
179
+ end
167
180
  end
168
181
  buffer.slice!(0, pos) if pos > 0
169
182
  end
@@ -828,7 +828,7 @@ module Fluent::Plugin
828
828
  when :pingpong
829
829
  succeeded, reason = check_pong(ri, data)
830
830
  unless succeeded
831
- @log.warn "connection refused to #{@name}: #{reason}"
831
+ @log.warn "connection refused to #{@name || @host}: #{reason}"
832
832
  disable! # shutdown
833
833
  return
834
834
  end
@@ -154,7 +154,7 @@ module Fluent
154
154
  end
155
155
 
156
156
  # Internal states
157
- FlushThreadState = Struct.new(:thread, :next_clock)
157
+ FlushThreadState = Struct.new(:thread, :next_clock, :mutex, :cond_var)
158
158
  DequeuedChunkInfo = Struct.new(:chunk_id, :time, :timeout) do
159
159
  def expired?
160
160
  time + timeout < Time.now
@@ -354,6 +354,10 @@ module Fluent
354
354
  log.warn "'flush_interval' is ignored because default 'flush_mode' is not 'interval': '#{@flush_mode}'"
355
355
  end
356
356
  end
357
+
358
+ if @buffer.queued_chunks_limit_size.nil?
359
+ @buffer.queued_chunks_limit_size = @buffer_config.flush_thread_count
360
+ end
357
361
  end
358
362
 
359
363
  if @secondary_config
@@ -368,6 +372,9 @@ module Fluent
368
372
  end
369
373
  secondary_conf = conf.elements(name: 'secondary').first
370
374
  @secondary = Plugin.new_output(secondary_type)
375
+ unless @secondary.respond_to?(:acts_as_secondary)
376
+ raise Fluent::ConfigError, "Failed to setup secondary plugin in '#{conf['@type']}'. '#{secondary_type}' plugin in not allowed due to non buffered output"
377
+ end
371
378
  @secondary.acts_as_secondary(self)
372
379
  @secondary.configure(secondary_conf)
373
380
  if (self.class != @secondary.class) && (@custom_format || @secondary.implement?(:custom_format))
@@ -440,7 +447,7 @@ module Fluent
440
447
 
441
448
  @buffer_config.flush_thread_count.times do |i|
442
449
  thread_title = "flush_thread_#{i}".to_sym
443
- thread_state = FlushThreadState.new(nil, nil)
450
+ thread_state = FlushThreadState.new(nil, nil, Mutex.new, ConditionVariable.new)
444
451
  thread = thread_create(thread_title) do
445
452
  flush_thread_run(thread_state)
446
453
  end
@@ -506,9 +513,14 @@ module Fluent
506
513
  @output_flush_threads_running = false
507
514
  if @output_flush_threads && !@output_flush_threads.empty?
508
515
  @output_flush_threads.each do |state|
509
- state.thread.run if state.thread.alive? # to wakeup thread and make it to stop by itself
510
- end
511
- @output_flush_threads.each do |state|
516
+ # to wakeup thread and make it to stop by itself
517
+ state.mutex.synchronize {
518
+ if state.thread && state.thread.status
519
+ state.next_clock = 0
520
+ state.cond_var.signal
521
+ end
522
+ }
523
+ Thread.pass
512
524
  state.thread.join
513
525
  end
514
526
  end
@@ -1119,24 +1131,29 @@ module Fluent
1119
1131
  if @secondary
1120
1132
  if using_secondary
1121
1133
  log.warn "got unrecoverable error in secondary.", error: e
1134
+ log.warn_backtrace
1122
1135
  backup_chunk(chunk, using_secondary, output.delayed_commit)
1123
1136
  else
1124
1137
  if (self.class == @secondary.class)
1125
1138
  log.warn "got unrecoverable error in primary and secondary type is same as primary. Skip secondary", error: e
1139
+ log.warn_backtrace
1126
1140
  backup_chunk(chunk, using_secondary, output.delayed_commit)
1127
1141
  else
1128
1142
  # Call secondary output directly without retry update.
1129
1143
  # In this case, delayed commit causes inconsistent state in dequeued chunks so async output in secondary is not allowed for now.
1130
1144
  if @secondary.delayed_commit
1131
1145
  log.warn "got unrecoverable error in primary and secondary is async output. Skip secondary for backup", error: e
1146
+ log.warn_backtrace
1132
1147
  backup_chunk(chunk, using_secondary, output.delayed_commit)
1133
1148
  else
1134
1149
  log.warn "got unrecoverable error in primary. Skip retry and flush chunk to secondary", error: e
1150
+ log.warn_backtrace
1135
1151
  begin
1136
1152
  @secondary.write(chunk)
1137
1153
  commit_write(chunk_id, delayed: output.delayed_commit, secondary: true)
1138
1154
  rescue => e
1139
1155
  log.warn "got an error in secondary for unrecoverable error", error: e
1156
+ log.warn_backtrace
1140
1157
  backup_chunk(chunk, using_secondary, output.delayed_commit)
1141
1158
  end
1142
1159
  end
@@ -1144,6 +1161,7 @@ module Fluent
1144
1161
  end
1145
1162
  else
1146
1163
  log.warn "got unrecoverable error in primary and no secondary", error: e
1164
+ log.warn_backtrace
1147
1165
  backup_chunk(chunk, using_secondary, output.delayed_commit)
1148
1166
  end
1149
1167
  rescue => e
@@ -1254,12 +1272,15 @@ module Fluent
1254
1272
  # Without locks: it is rough but enough to select "next" writer selection
1255
1273
  @output_flush_thread_current_position = (@output_flush_thread_current_position + 1) % @buffer_config.flush_thread_count
1256
1274
  state = @output_flush_threads[@output_flush_thread_current_position]
1257
- state.next_clock = 0
1258
- if state.thread && state.thread.status # "run"/"sleep"/"aborting" or false(successfully stop) or nil(killed by exception)
1259
- state.thread.run
1260
- else
1261
- log.warn "thread is already dead"
1262
- end
1275
+ state.mutex.synchronize {
1276
+ if state.thread && state.thread.status # "run"/"sleep"/"aborting" or false(successfully stop) or nil(killed by exception)
1277
+ state.next_clock = 0
1278
+ state.cond_var.signal
1279
+ else
1280
+ log.warn "thread is already dead"
1281
+ end
1282
+ }
1283
+ Thread.pass
1263
1284
  end
1264
1285
 
1265
1286
  def force_flush
@@ -1296,8 +1317,13 @@ module Fluent
1296
1317
  # only for tests of output plugin
1297
1318
  def flush_thread_wakeup
1298
1319
  @output_flush_threads.each do |state|
1299
- state.next_clock = 0
1300
- state.thread.run
1320
+ state.mutex.synchronize {
1321
+ if state.thread && state.thread.status
1322
+ state.next_clock = 0
1323
+ state.cond_var.signal
1324
+ end
1325
+ }
1326
+ Thread.pass
1301
1327
  end
1302
1328
  end
1303
1329
 
@@ -1376,6 +1402,7 @@ module Fluent
1376
1402
  end
1377
1403
  log.debug "flush_thread actually running"
1378
1404
 
1405
+ state.mutex.lock
1379
1406
  begin
1380
1407
  # This thread don't use `thread_current_running?` because this thread should run in `before_shutdown` phase
1381
1408
  while @output_flush_threads_running
@@ -1391,23 +1418,32 @@ module Fluent
1391
1418
  elsif next_retry_time && next_retry_time > Time.now
1392
1419
  interval = next_retry_time.to_f - Time.now.to_f
1393
1420
  else
1394
- try_flush
1395
-
1396
- # next_flush_time uses flush_thread_interval or flush_thread_burst_interval (or retrying)
1397
- interval = next_flush_time.to_f - Time.now.to_f
1398
- # TODO: if secondary && delayed-commit, next_flush_time will be much longer than expected
1399
- # because @retry still exists (#commit_write is not called yet in #try_flush)
1400
- # @retry should be cleared if delayed commit is enabled? Or any other solution?
1401
- state.next_clock = Fluent::Clock.now + interval
1421
+ state.mutex.unlock
1422
+ begin
1423
+ try_flush
1424
+ # next_flush_time uses flush_thread_interval or flush_thread_burst_interval (or retrying)
1425
+ interval = next_flush_time.to_f - Time.now.to_f
1426
+ # TODO: if secondary && delayed-commit, next_flush_time will be much longer than expected
1427
+ # because @retry still exists (#commit_write is not called yet in #try_flush)
1428
+ # @retry should be cleared if delayed commit is enabled? Or any other solution?
1429
+ state.next_clock = Fluent::Clock.now + interval
1430
+ ensure
1431
+ state.mutex.lock
1432
+ end
1402
1433
  end
1403
1434
 
1404
1435
  if @dequeued_chunks_mutex.synchronize{ !@dequeued_chunks.empty? && @dequeued_chunks.first.expired? }
1405
1436
  unless @output_flush_interrupted
1406
- try_rollback_write
1437
+ state.mutex.unlock
1438
+ begin
1439
+ try_rollback_write
1440
+ ensure
1441
+ state.mutex.lock
1442
+ end
1407
1443
  end
1408
1444
  end
1409
1445
 
1410
- sleep interval if interval > 0
1446
+ state.cond_var.wait(state.mutex, interval) if interval > 0
1411
1447
  end
1412
1448
  rescue => e
1413
1449
  # normal errors are rescued by output plugins in #try_flush
@@ -1415,6 +1451,8 @@ module Fluent
1415
1451
  log.error "error on output thread", error: e
1416
1452
  log.error_backtrace
1417
1453
  raise
1454
+ ensure
1455
+ state.mutex.unlock
1418
1456
  end
1419
1457
  end
1420
1458
  end
@@ -408,6 +408,10 @@ module Fluent
408
408
  raise "not implemented here"
409
409
  end
410
410
 
411
+ def close_after_write_complete
412
+ @sock.close_after_write_complete = true
413
+ end
414
+
411
415
  def close
412
416
  @sock.close if @close_socket
413
417
  end
@@ -492,6 +496,8 @@ module Fluent
492
496
 
493
497
  module EventHandler
494
498
  class UDPServer < Coolio::IO
499
+ attr_writer :close_after_write_complete # dummy for consistent method call in callbacks
500
+
495
501
  def initialize(sock, max_bytes, flags, close_socket, log, under_plugin_development, &callback)
496
502
  raise ArgumentError, "socket must be a UDPSocket: sock = #{sock}" unless sock.is_a?(UDPSocket)
497
503
 
@@ -543,6 +549,7 @@ module Fluent
543
549
 
544
550
  class TCPServer < Coolio::TCPSocket
545
551
  attr_reader :closing
552
+ attr_writer :close_after_write_complete
546
553
 
547
554
  def initialize(sock, socket_option_setter, close_callback, log, under_plugin_development, connect_callback)
548
555
  raise ArgumentError, "socket must be a TCPSocket: sock=#{sock}" unless sock.is_a?(TCPSocket)
@@ -560,6 +567,7 @@ module Fluent
560
567
  @close_callback = close_callback
561
568
 
562
569
  @callback_connection = nil
570
+ @close_after_write_complete = false
563
571
  @closing = false
564
572
 
565
573
  @mutex = Mutex.new # to serialize #write and #close
@@ -587,6 +595,11 @@ module Fluent
587
595
  end
588
596
  end
589
597
 
598
+ def on_writable
599
+ super
600
+ close if @close_after_write_complete
601
+ end
602
+
590
603
  def on_connect
591
604
  @callback_connection = TCPCallbackSocket.new(self)
592
605
  @connect_callback.call(@callback_connection)
@@ -625,6 +638,7 @@ module Fluent
625
638
 
626
639
  class TLSServer < Coolio::Socket
627
640
  attr_reader :closing
641
+ attr_writer :close_after_write_complete
628
642
 
629
643
  # It can't use Coolio::TCPSocket, because Coolio::TCPSocket checks that underlying socket (1st argument of super) is TCPSocket.
630
644
  def initialize(sock, context, socket_option_setter, close_callback, log, under_plugin_development, connect_callback)
@@ -645,6 +659,7 @@ module Fluent
645
659
  @close_callback = close_callback
646
660
 
647
661
  @callback_connection = nil
662
+ @close_after_write_complete = false
648
663
  @closing = false
649
664
 
650
665
  @mutex = Mutex.new # to serialize #write and #close
@@ -738,6 +753,7 @@ module Fluent
738
753
  @_handler_write_buffer.slice!(0, written_bytes)
739
754
  super
740
755
  end
756
+ close if @close_after_write_complete
741
757
  rescue IO::WaitWritable, IO::WaitReadable
742
758
  return
743
759
  rescue Errno::EINTR
@@ -16,6 +16,6 @@
16
16
 
17
17
  module Fluent
18
18
 
19
- VERSION = '1.2.6'
19
+ VERSION = '1.3.0'
20
20
 
21
21
  end
@@ -170,7 +170,7 @@ class BufferTest < Test::Unit::TestCase
170
170
 
171
171
  sub_test_case 'with default configuration and dummy implementation' do
172
172
  setup do
173
- @p = create_buffer({})
173
+ @p = create_buffer({'queued_chunks_limit_size' => 100})
174
174
  @dm0 = create_metadata(Time.parse('2016-04-11 16:00:00 +0000').to_i, nil, nil)
175
175
  @dm1 = create_metadata(Time.parse('2016-04-11 16:10:00 +0000').to_i, nil, nil)
176
176
  @dm2 = create_metadata(Time.parse('2016-04-11 16:20:00 +0000').to_i, nil, nil)
@@ -624,6 +624,23 @@ class HttpInputTest < Test::Unit::TestCase
624
624
  end
625
625
  end
626
626
 
627
+ def test_cors_preflight
628
+ d = create_driver(CONFIG + 'cors_allow_origins ["*"]')
629
+
630
+ d.run do
631
+ header = {
632
+ "Origin" => "http://foo.com",
633
+ "Access-Control-Request-Method" => "POST",
634
+ "Access-Control-Request-Headers" => "Content-Type",
635
+ }
636
+ res = options("/cors.test", {}, header)
637
+
638
+ assert_equal "200", res.code
639
+ assert_equal "*", res["Access-Control-Allow-Origin"]
640
+ assert_equal "POST", res["Access-Control-Allow-Methods"]
641
+ end
642
+ end
643
+
627
644
  def test_content_encoding_gzip
628
645
  d = create_driver
629
646
 
@@ -744,6 +761,12 @@ class HttpInputTest < Test::Unit::TestCase
744
761
  assert_equal $test_in_http_connection_object_ids[0], $test_in_http_connection_object_ids[1]
745
762
  end
746
763
 
764
+ def options(path, params, header = {})
765
+ http = Net::HTTP.new("127.0.0.1", PORT)
766
+ req = Net::HTTP::Options.new(path, header)
767
+ http.request(req)
768
+ end
769
+
747
770
  def post(path, params, header = {}, &block)
748
771
  http = Net::HTTP.new("127.0.0.1", PORT)
749
772
  req = Net::HTTP::Post.new(path, header)
@@ -286,4 +286,50 @@ EOS
286
286
  assert_equal(options[:facility], events[i][2]['facility']) if options[:facility]
287
287
  }
288
288
  end
289
+
290
+ sub_test_case 'octet counting frame' do
291
+ def test_msg_size_with_tcp
292
+ d = create_driver([CONFIG, 'protocol_type tcp', 'frame_type octet_count'].join("\n"))
293
+ tests = create_test_case
294
+
295
+ d.run(expect_emits: 2) do
296
+ tests.each {|test|
297
+ TCPSocket.open('127.0.0.1', PORT) do |s|
298
+ s.send(test['msg'], 0)
299
+ end
300
+ }
301
+ end
302
+
303
+ assert(d.events.size > 0)
304
+ compare_test_result(d.events, tests)
305
+ end
306
+
307
+ def test_msg_size_with_same_tcp_connection
308
+ d = create_driver([CONFIG, 'protocol_type tcp', 'frame_type octet_count'].join("\n"))
309
+ tests = create_test_case
310
+
311
+ d.run(expect_emits: 2) do
312
+ TCPSocket.open('127.0.0.1', PORT) do |s|
313
+ tests.each {|test|
314
+ s.send(test['msg'], 0)
315
+ }
316
+ end
317
+ end
318
+
319
+ assert(d.events.size > 0)
320
+ compare_test_result(d.events, tests)
321
+ end
322
+
323
+ def create_test_case(large_message: false)
324
+ msgs = [
325
+ {'msg' => '<6>Sep 10 00:00:00 localhost logger: ' + 'x' * 100, 'expected' => 'x' * 100},
326
+ {'msg' => '<6>Sep 10 00:00:00 localhost logger: ' + 'x' * 1024, 'expected' => 'x' * 1024},
327
+ ]
328
+ msgs.each { |msg|
329
+ m = msg['msg']
330
+ msg['msg'] = "#{m.size + 1} #{m}"
331
+ }
332
+ msgs
333
+ end
334
+ end
289
335
  end
@@ -828,34 +828,43 @@ class OutputTest < Test::Unit::TestCase
828
828
  assert_equal :interval, @i.instance_variable_get(:@flush_mode)
829
829
  end
830
830
 
831
- test "Warn if primary type is different from secondary type and either primary or secondary has custom_format" do
832
- o = create_output(:buffered)
833
- mock(o.log).warn("secondary type should be same with primary one",
834
- { primary: o.class.to_s, secondary: "Fluent::Plugin::TestOutput" })
831
+ sub_test_case 'configure secondary' do
832
+ test "Warn if primary type is different from secondary type and either primary or secondary has custom_format" do
833
+ o = create_output(:buffered)
834
+ mock(o.log).warn("secondary type should be same with primary one",
835
+ { primary: o.class.to_s, secondary: "Fluent::Plugin::TestOutput" })
836
+
837
+ o.configure(config_element('ROOT','',{},[config_element('secondary','',{'@type'=>'test', 'name' => "cool"})]))
838
+ assert_not_nil o.instance_variable_get(:@secondary)
839
+ end
835
840
 
836
- o.configure(config_element('ROOT','',{},[config_element('secondary','',{'@type'=>'test', 'name' => "cool"})]))
837
- assert_not_nil o.instance_variable_get(:@secondary)
838
- end
841
+ test "don't warn if primary type is the same as secondary type" do
842
+ o = Fluent::Plugin::TestOutput.new
843
+ mock(o.log).warn("secondary type should be same with primary one",
844
+ { primary: o.class.to_s, secondary: "Fluent::Plugin::TestOutput" }).never
839
845
 
840
- test "don't warn if primary type is the same as secondary type" do
841
- o = Fluent::Plugin::TestOutput.new
842
- mock(o.log).warn("secondary type should be same with primary one",
843
- { primary: o.class.to_s, secondary: "Fluent::Plugin::TestOutput" }).never
846
+ o.configure(config_element('ROOT','',{'name' => "cool2"},
847
+ [config_element('secondary','',{'@type'=>'test', 'name' => "cool"}),
848
+ config_element('buffer','',{'@type'=>'memory'})]
849
+ ))
850
+ assert_not_nil o.instance_variable_get(:@secondary)
851
+ end
844
852
 
845
- o.configure(config_element('ROOT','',{'name' => "cool2"},
846
- [config_element('secondary','',{'@type'=>'test', 'name' => "cool"}),
847
- config_element('buffer','',{'@type'=>'memory'})]
848
- ))
849
- assert_not_nil o.instance_variable_get(:@secondary)
850
- end
853
+ test "don't warn if primary type is different from secondary type and both don't have custom_format" do
854
+ o = create_output(:standard)
855
+ mock(o.log).warn("secondary type should be same with primary one",
856
+ { primary: o.class.to_s, secondary: "Fluent::Plugin::TestOutput" }).never
851
857
 
852
- test "don't warn if primary type is different from secondary type and both don't have custom_format" do
853
- o = create_output(:standard)
854
- mock(o.log).warn("secondary type should be same with primary one",
855
- { primary: o.class.to_s, secondary: "Fluent::Plugin::TestOutput" }).never
858
+ o.configure(config_element('ROOT','',{},[config_element('secondary','',{'@type'=>'test', 'name' => "cool"})]))
859
+ assert_not_nil o.instance_variable_get(:@secondary)
860
+ end
856
861
 
857
- o.configure(config_element('ROOT','',{},[config_element('secondary','',{'@type'=>'test', 'name' => "cool"})]))
858
- assert_not_nil o.instance_variable_get(:@secondary)
862
+ test "raise configuration error if secondary type specifies non buffered output" do
863
+ o = create_output(:standard)
864
+ assert_raise Fluent::ConfigError do
865
+ o.configure(config_element('ROOT','',{},[config_element('secondary','',{'@type'=>'copy'})]))
866
+ end
867
+ end
859
868
  end
860
869
  end
861
870
 
@@ -219,6 +219,24 @@ class BufferedOutputTest < Test::Unit::TestCase
219
219
  Timecop.return
220
220
  end
221
221
 
222
+ test 'queued_chunks_limit_size is same as flush_thread_count by default' do
223
+ hash = {'flush_thread_count' => 4}
224
+ i = create_output
225
+ i.register(:prefer_buffered_processing) { true }
226
+ i.configure(config_element('ROOT', '', {}, [config_element('buffer','tag',hash)]))
227
+
228
+ assert_equal 4, i.buffer.queued_chunks_limit_size
229
+ end
230
+
231
+ test 'prefer queued_chunks_limit_size parameter than flush_thread_count' do
232
+ hash = {'flush_thread_count' => 4, 'queued_chunks_limit_size' => 2}
233
+ i = create_output
234
+ i.register(:prefer_buffered_processing) { true }
235
+ i.configure(config_element('ROOT', '', {}, [config_element('buffer','tag',hash)]))
236
+
237
+ assert_equal 2, i.buffer.queued_chunks_limit_size
238
+ end
239
+
222
240
  sub_test_case 'chunk feature in #write for output plugins' do
223
241
  setup do
224
242
  @stored_global_logger = $log
@@ -1064,6 +1082,7 @@ class BufferedOutputTest < Test::Unit::TestCase
1064
1082
  'flush_thread_count' => 1,
1065
1083
  'flush_thread_burst_interval' => 0.1,
1066
1084
  'chunk_limit_size' => 1024,
1085
+ 'queued_chunks_limit_size' => 100
1067
1086
  }
1068
1087
  @i = create_output(:buffered)
1069
1088
  @i.configure(config_element('ROOT','',{},[config_element('buffer',chunk_key,hash)]))
@@ -123,6 +123,7 @@ class BufferedOutputRetryTest < Test::Unit::TestCase
123
123
  'flush_interval' => 1,
124
124
  'flush_thread_burst_interval' => 0.1,
125
125
  'retry_randomize' => false,
126
+ 'queued_chunks_limit_size' => 100
126
127
  }
127
128
  @i.configure(config_element('ROOT','',{},[config_element('buffer',chunk_key,hash)]))
128
129
  @i.register(:prefer_buffered_processing){ true }
@@ -252,6 +253,7 @@ class BufferedOutputRetryTest < Test::Unit::TestCase
252
253
  'flush_thread_burst_interval' => 0.1,
253
254
  'retry_randomize' => false,
254
255
  'retry_timeout' => 3600,
256
+ 'queued_chunks_limit_size' => 100
255
257
  }
256
258
  @i.configure(config_element('ROOT','',{},[config_element('buffer',chunk_key,hash)]))
257
259
  @i.register(:prefer_buffered_processing){ true }
@@ -342,6 +344,7 @@ class BufferedOutputRetryTest < Test::Unit::TestCase
342
344
  'flush_thread_burst_interval' => 0.1,
343
345
  'retry_randomize' => false,
344
346
  'retry_max_times' => 10,
347
+ 'queued_chunks_limit_size' => 100
345
348
  }
346
349
  @i.configure(config_element('ROOT','',{},[config_element('buffer',chunk_key,hash)]))
347
350
  @i.register(:prefer_buffered_processing){ true }
@@ -485,6 +488,7 @@ class BufferedOutputRetryTest < Test::Unit::TestCase
485
488
  'retry_type' => :periodic,
486
489
  'retry_wait' => 3,
487
490
  'retry_randomize' => false,
491
+ 'queued_chunks_limit_size' => 100
488
492
  }
489
493
  @i.configure(config_element('ROOT','',{},[config_element('buffer',chunk_key,hash)]))
490
494
  @i.register(:prefer_buffered_processing){ true }
@@ -531,6 +535,7 @@ class BufferedOutputRetryTest < Test::Unit::TestCase
531
535
  'retry_wait' => 30,
532
536
  'retry_randomize' => false,
533
537
  'retry_timeout' => 120,
538
+ 'queued_chunks_limit_size' => 100
534
539
  }
535
540
  @i.configure(config_element('ROOT','',{},[config_element('buffer',chunk_key,hash)]))
536
541
  @i.register(:prefer_buffered_processing){ true }
@@ -626,6 +631,7 @@ class BufferedOutputRetryTest < Test::Unit::TestCase
626
631
  'retry_wait' => 3,
627
632
  'retry_randomize' => false,
628
633
  'retry_max_times' => 10,
634
+ 'queued_chunks_limit_size' => 100
629
635
  }
630
636
  @i.configure(config_element('ROOT','',{},[config_element('buffer',chunk_key,hash)]))
631
637
  @i.register(:prefer_buffered_processing){ true }
@@ -724,6 +730,7 @@ class BufferedOutputRetryTest < Test::Unit::TestCase
724
730
  'retry_randomize' => false,
725
731
  'retry_timeout' => 3600,
726
732
  'retry_max_times' => 10,
733
+ 'queued_chunks_limit_size' => 100
727
734
  }
728
735
  @i.configure(config_element('ROOT','',{},[config_element('buffer',chunk_key,hash)]))
729
736
  @i.register(:prefer_buffered_processing){ true }
@@ -795,6 +802,7 @@ class BufferedOutputRetryTest < Test::Unit::TestCase
795
802
  'retry_wait' => 30,
796
803
  'retry_timeout' => 360,
797
804
  'retry_max_times' => 10,
805
+ 'queued_chunks_limit_size' => 100
798
806
  }
799
807
  @i.configure(config_element('ROOT','',{},[config_element('buffer',chunk_key,hash)]))
800
808
  @i.register(:prefer_buffered_processing){ true }
@@ -544,7 +544,7 @@ class BufferedOutputSecondaryTest < Test::Unit::TestCase
544
544
  test 'secondary plugin can do delayed commit even if primary does not do it, and non-committed chunks will be rollbacked by primary' do
545
545
  written = []
546
546
  chunks = []
547
- priconf = config_element('buffer','tag',{'flush_interval' => 1, 'retry_type' => :periodic, 'retry_wait' => 3, 'retry_timeout' => 60, 'delayed_commit_timeout' => 2, 'retry_randomize' => false})
547
+ priconf = config_element('buffer','tag',{'flush_interval' => 1, 'retry_type' => :periodic, 'retry_wait' => 3, 'retry_timeout' => 60, 'delayed_commit_timeout' => 2, 'retry_randomize' => false, 'queued_chunks_limit_size' => 10})
548
548
  secconf = config_element('secondary','',{'@type' => 'output_secondary_test2'})
549
549
  @i.configure(config_element('ROOT','',{},[priconf,secconf]))
550
550
  @i.register(:prefer_buffered_processing){ true }
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: fluentd
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.2.6
4
+ version: 1.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Sadayuki Furuhashi
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2018-10-04 00:00:00.000000000 Z
11
+ date: 2018-11-10 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: msgpack