fluentd 1.4.2 → 1.5.0.rc1

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: e8855d66a886343a3865b3f224936aa433822027
4
- data.tar.gz: 96d848425ac12776baf47bb2d999127697a6e7b1
3
+ metadata.gz: 7ba10a2d9e94456e95d304989cd7487771c3dc71
4
+ data.tar.gz: ce09baf07ad2c3af4fdea5665c6c57a4387e433a
5
5
  SHA512:
6
- metadata.gz: f9e0ab8e68c24424f1eb75e459211b8195fb4a6ee1b2be99ef1a88da3d5e3e643b5243cadb87f9e68aa0f23eedad0d1c73aa9a53e72f75adfd52d7709dfe47af
7
- data.tar.gz: 11e5d4293430e2675c70e323111044599be4de9bbf1bcb22045f7514f90788011949ddd27a07ba66b2c9b1ac2ee319d948b0f29113ccbd098ef4b5eb1f7cdebe
6
+ metadata.gz: '084c5d106ce8f806f3d52798a7a45498155d5a78c87eefcb6ee0163a79d4cccab42dac9c4e86c90ba644975f06ca0359fc5afa8f65c273f00841312491f7efee'
7
+ data.tar.gz: 166d9c9a41e3e6b9ae6b3881655be16348ab8d562e4890fe0d27202431a94f2fd37f203b928915b4272dc9da975c2932bb2c449fca69553227eec6f8d18df5db
@@ -11,11 +11,15 @@ matrix:
11
11
  os: linux
12
12
  - rvm: 2.2.10
13
13
  os: linux
14
- - rvm: 2.4.5
14
+ - rvm: 2.2.10
15
+ os: linux-ppc64le
16
+ - rvm: 2.4.6
15
17
  os: linux
16
- - rvm: 2.5.3
18
+ - rvm: 2.4.6
19
+ os: linux-ppc64le
20
+ - rvm: 2.5.5
17
21
  os: linux
18
- - rvm: 2.6.0
22
+ - rvm: 2.6.3
19
23
  os: linux
20
24
  - rvm: ruby-head
21
25
  os: linux
@@ -28,7 +32,7 @@ matrix:
28
32
  # - rvm: 2.3.3
29
33
  # os: osx
30
34
  # osx_image: xcode8.2 # OSX 10.12
31
- - rvm: 2.4.1
35
+ - rvm: 2.4.6
32
36
  os: osx
33
37
  osx_image: xcode8.3 # OSX 10.12
34
38
  - rvm: ruby-head
@@ -40,7 +44,7 @@ matrix:
40
44
  - rvm: 2.1.10
41
45
  os: osx
42
46
  osx_image: xcode8.3
43
- - rvm: 2.4.1
47
+ - rvm: 2.4.6
44
48
  os: osx
45
49
  osx_image: xcode8.3
46
50
  - rvm: ruby-head
@@ -1,3 +1,28 @@
1
+ # v1.5
2
+
3
+ ## Pre release
4
+
5
+ ### New feature
6
+
7
+ * out_forward: Support keepalive feature
8
+ https://github.com/fluent/fluentd/pull/2393
9
+
10
+ ### Enhancement
11
+
12
+ * in_syslog: Add delimiter parameter
13
+ https://github.com/fluent/fluentd/pull/2378
14
+ * in_forward: Add tag/add_tag_prefix parameters
15
+ https://github.com/fluent/fluentd/pull/2396
16
+ * parser_json: Add stream_buffer_size parameter for yajl
17
+ https://github.com/fluent/fluentd/pull/2381
18
+ * command: Add deprecated message to show-plugin-config option
19
+ https://github.com/fluent/fluentd/pull/2401
20
+
21
+ ### Bug fixes
22
+
23
+ * out_forward: Don't use SO_LINGER on SSL/TLS WinSock
24
+ https://github.com/fluent/fluentd/pull/2398
25
+
1
26
  # v1.4
2
27
 
3
28
  ## Release v1.4.2 - 2019/04/02
data/README.md CHANGED
@@ -30,6 +30,11 @@ Mobile/Web Application Logging | Fluentd can function as middleware to enable as
30
30
 
31
31
  ## Development
32
32
 
33
+ ### Branch
34
+
35
+ - master: For v1 development.
36
+ - v0.12: For v0.12. This is security maintenance mode. Only security fix is accepted.
37
+
33
38
  ### Prerequisites
34
39
 
35
40
  - Ruby 2.1 or later
@@ -6,7 +6,9 @@
6
6
  keepalive_timeout 10
7
7
  # backlog 0
8
8
  add_http_headers false
9
- format default
9
+ <parse>
10
+ @type json
11
+ </parse>
10
12
  </source>
11
13
 
12
14
  <match test>
@@ -40,7 +40,7 @@ op.on('--dry-run', "Check fluentd setup is correct or not", TrueClass) {|b|
40
40
  opts[:dry_run] = b
41
41
  }
42
42
 
43
- op.on('--show-plugin-config=PLUGIN', "Show PLUGIN configuration and exit(ex: input:dummy)") {|plugin|
43
+ op.on('--show-plugin-config=PLUGIN', "[DEPRECATED] Show PLUGIN configuration and exit(ex: input:dummy)") {|plugin|
44
44
  opts[:show_plugin_config] = plugin
45
45
  }
46
46
 
@@ -148,10 +148,10 @@ module Fluent
148
148
  end
149
149
 
150
150
  attr_reader :format
151
+ attr_reader :time_format
151
152
  attr_accessor :log_event_enabled
152
153
  attr_accessor :out
153
154
  attr_accessor :level
154
- attr_accessor :time_format
155
155
  attr_accessor :optional_header, :optional_attrs
156
156
 
157
157
  def logdev=(logdev)
@@ -62,6 +62,11 @@ module Fluent::Plugin
62
62
  desc "The field name of the client's hostname."
63
63
  config_param :source_hostname_key, :string, default: nil
64
64
 
65
+ desc "New tag instead of incoming tag"
66
+ config_param :tag, :string, default: nil
67
+ desc "Add prefix to incoming tag"
68
+ config_param :add_tag_prefix, :string, default: nil
69
+
65
70
  config_section :security, required: false, multi: false do
66
71
  desc 'The hostname'
67
72
  config_param :self_hostname, :string
@@ -106,6 +111,9 @@ module Fluent::Plugin
106
111
  end
107
112
  @enable_field_injection = @source_address_key || @source_hostname_key
108
113
 
114
+ raise Fluent::ConfigError, "'tag' parameter must not be empty" if @tag && @tag.empty?
115
+ raise Fluent::ConfigError, "'add_tag_prefix' parameter must not be empty" if @add_tag_prefix && @add_tag_prefix.empty?
116
+
109
117
  if @security
110
118
  if @security.user_auth && @security.users.empty?
111
119
  raise Fluent::ConfigError, "<user> sections required if user_auth enabled"
@@ -293,6 +301,9 @@ module Fluent::Plugin
293
301
  log.warn "Input chunk size is larger than 'chunk_size_warn_limit':", tag: tag, host: conn.remote_host, limit: @chunk_size_warn_limit, size: chunk_size
294
302
  end
295
303
 
304
+ tag = @tag.dup if @tag
305
+ tag = "#{@add_tag_prefix}.#{tag}" if @add_tag_prefix
306
+
296
307
  case entries
297
308
  when String
298
309
  # PackedForward
@@ -99,6 +99,9 @@ module Fluent::Plugin
99
99
 
100
100
  config_param :blocking_timeout, :time, default: 0.5
101
101
 
102
+ desc 'The delimiter value "\n"'
103
+ config_param :delimiter, :string, default: "\n" # syslog family add "\n" to each message
104
+
102
105
  config_section :parse do
103
106
  config_set_default :@type, DEFAULT_PARSER
104
107
  config_param :with_priority, :bool, default: true
@@ -156,8 +159,7 @@ module Fluent::Plugin
156
159
  def start_tcp_server
157
160
  octet_count_frame = @frame_type == :octet_count
158
161
 
159
- # syslog family adds "\n" to each message when transport is TCP and traditional frame
160
- delimiter = octet_count_frame ? " " : "\n"
162
+ delimiter = octet_count_frame ? " " : @delimiter
161
163
  delimiter_size = delimiter.size
162
164
  server_create_connection(:in_syslog_tcp_server, @port, bind: @bind, resolve_name: @resolve_hostname) do |conn|
163
165
  conn.data do |data|
@@ -95,6 +95,7 @@ module Fluent::Plugin
95
95
  COMPAT_PARSE_PARAMS = {
96
96
  'out_format' => '@type',
97
97
  'out_keys' => 'keys',
98
+ 'out_stream_buffer_size' => 'stream_buffer_size',
98
99
  }
99
100
  COMPAT_EXTRACT_PARAMS = {
100
101
  'out_tag_key' => 'tag_key',
@@ -36,7 +36,6 @@ module Fluent::Plugin
36
36
  desc 'The transport protocol.'
37
37
  config_param :transport, :enum, list: [:tcp, :tls], default: :tcp
38
38
  # TODO: TLS session cache/tickets
39
- # TODO: Connection keepalive
40
39
 
41
40
  desc 'The timeout time when sending event logs.'
42
41
  config_param :send_timeout, :time, default: 60
@@ -103,6 +102,10 @@ module Fluent::Plugin
103
102
  config_param :tls_client_private_key_path, :string, default: nil
104
103
  desc 'The client private key passphrase for TLS.'
105
104
  config_param :tls_client_private_key_passphrase, :string, default: nil, secret: true
105
+ desc "Enable keepalive connection."
106
+ config_param :keepalive, :bool, default: false
107
+ desc "Expired time of keepalive. Default value is nil, which means to keep connection as long as possible"
108
+ config_param :keepalive_timeout, :time, default: nil
106
109
 
107
110
  config_section :security, required: false, multi: false do
108
111
  desc 'The hostname'
@@ -151,6 +154,7 @@ module Fluent::Plugin
151
154
  @usock = nil
152
155
  @sock_ack_waiting = nil
153
156
  @sock_ack_waiting_mutex = nil
157
+ @keep_alive_watcher_interval = 5 # TODO
154
158
  end
155
159
 
156
160
  def configure(conf)
@@ -201,9 +205,9 @@ module Fluent::Plugin
201
205
 
202
206
  log.info "adding forwarding server '#{name}'", host: server.host, port: server.port, weight: server.weight, plugin_id: plugin_id
203
207
  if @heartbeat_type == :none
204
- @nodes << NoneHeartbeatNode.new(self, server, failure: failure)
208
+ @nodes << NoneHeartbeatNode.new(self, server, failure: failure, keepalive: @keepalive, keepalive_timeout: @keepalive_timeout)
205
209
  else
206
- node = Node.new(self, server, failure: failure)
210
+ node = Node.new(self, server, failure: failure, keepalive: @keepalive, keepalive_timeout: @keepalive_timeout)
207
211
  begin
208
212
  node.validate_host_resolution!
209
213
  rescue => e
@@ -227,6 +231,10 @@ module Fluent::Plugin
227
231
  raise Fluent::ConfigError, "forward output plugin requires at least one <server> is required"
228
232
  end
229
233
 
234
+ if !@keepalive && @keepalive_timeout
235
+ log.warn('The value of keepalive_timeout is ignored. if you want to use keepalive, please add `keepalive true` to your conf.')
236
+ end
237
+
230
238
  raise Fluent::ConfigError, "ack_response_timeout must be a positive integer" if @ack_response_timeout < 1
231
239
  end
232
240
 
@@ -279,6 +287,10 @@ module Fluent::Plugin
279
287
  end
280
288
  end
281
289
  end
290
+
291
+ if @keepalive && @keepalive_timeout
292
+ timer_execute(:out_forward_keep_alived_socket_watcher, @keep_alive_watcher_interval, &method(:on_purge_obsolete_socks))
293
+ end
282
294
  end
283
295
 
284
296
  def close
@@ -286,6 +298,10 @@ module Fluent::Plugin
286
298
  # close socket and ignore errors: this socket will not be used anyway.
287
299
  @usock.close rescue nil
288
300
  end
301
+
302
+ if @keepalive && @keepalive_timeout
303
+ @nodes.each(&:clear)
304
+ end
289
305
  super
290
306
  end
291
307
 
@@ -354,7 +370,10 @@ module Fluent::Plugin
354
370
  cert_path: @tls_client_cert_path,
355
371
  private_key_path: @tls_client_private_key_path,
356
372
  private_key_passphrase: @tls_client_private_key_passphrase,
357
- linger_timeout: @send_timeout,
373
+
374
+ # Enabling SO_LINGER causes data loss on Windows
375
+ # https://github.com/fluent/fluentd/issues/1968
376
+ linger_timeout: Fluent.windows? ? nil : @send_timeout,
358
377
  send_timeout: @send_timeout,
359
378
  recv_timeout: @ack_response_timeout,
360
379
  &block
@@ -450,6 +469,10 @@ module Fluent::Plugin
450
469
  end
451
470
  end
452
471
 
472
+ def on_purge_obsolete_socks
473
+ @nodes.each(&:purge_obsolete_socks)
474
+ end
475
+
453
476
  # return chunk id to be committed
454
477
  def read_ack_from_sock(sock, unpacker)
455
478
  begin
@@ -484,8 +507,13 @@ module Fluent::Plugin
484
507
  log.error "unexpected error while receiving ack message", error: e
485
508
  log.error_backtrace
486
509
  ensure
487
- info.sock.close_write rescue nil
488
- info.sock.close rescue nil
510
+ if @keepalive
511
+ info.node.socket_cache.dec_ref_by_value(info.sock)
512
+ else
513
+ info.sock.close_write rescue nil
514
+ info.sock.close rescue nil
515
+ end
516
+
489
517
  @sock_ack_waiting_mutex.synchronize do
490
518
  @sock_ack_waiting.delete(info)
491
519
  end
@@ -513,6 +541,9 @@ module Fluent::Plugin
513
541
  # (2) the node does support sending response but responses have not arrived for some reasons.
514
542
  log.warn "no response from node. regard it as unavailable.", host: info.node.host, port: info.node.port
515
543
  info.node.disable!
544
+ if @keepalive
545
+ info.node.socket_cache.revoke_by_value(info.sock)
546
+ end
516
547
  info.sock.close rescue nil
517
548
  rollback_write(info.chunk_id, update_retry: false)
518
549
  else
@@ -538,7 +569,142 @@ module Fluent::Plugin
538
569
  end
539
570
 
540
571
  class Node
541
- def initialize(sender, server, failure:)
572
+ class SocketCache
573
+ TimedSocket = Struct.new(:timeout, :sock, :ref)
574
+
575
+ def initialize(timeout, log)
576
+ @log = log
577
+ @timeout = timeout
578
+ @active_socks = {}
579
+ @inactive_socks = {}
580
+ @mutex = Mutex.new
581
+ end
582
+
583
+ def revoke(key = Thread.current.object_id)
584
+ @mutex.synchronize do
585
+ if @active_socks[key]
586
+ @inactive_socks[key] = @active_socks.delete(key)
587
+ @inactive_socks[key].ref = 0
588
+ end
589
+ end
590
+ end
591
+
592
+ def clear
593
+ @mutex.synchronize do
594
+ @inactive_socks.values.each do |s|
595
+ s.sock.close rescue nil
596
+ end
597
+ @inactive_socks.clear
598
+
599
+ @active_socks.values.each do |s|
600
+ s.sock.close rescue nil
601
+ end
602
+ @active_socks.clear
603
+ end
604
+ end
605
+
606
+ def purge_obsolete_socks
607
+ @mutex.synchronize do
608
+ @inactive_socks.keys.each do |k|
609
+ # 0 means sockets stored in this class received all acks
610
+ if @inactive_socks[k].ref <= 0
611
+ s = @inactive_socks.delete(k)
612
+ s.sock.close rescue nil
613
+ @log.debug("purged obsolete socket #{s.sock}")
614
+ end
615
+ end
616
+
617
+ @active_socks.keys.each do |k|
618
+ if expired?(k) && @active_socks[k].ref <= 0
619
+ @inactive_socks[k] = @active_socks.delete(k)
620
+ end
621
+ end
622
+ end
623
+ end
624
+
625
+ # We expect that `yield` returns a unique object in this class
626
+ def fetch_or(key = Thread.current.object_id)
627
+ @mutex.synchronize do
628
+ unless @active_socks[key]
629
+ @active_socks[key] = TimedSocket.new(timeout, yield, 1)
630
+ @log.debug("connect new socket #{@active_socks[key]}")
631
+ return @active_socks[key].sock
632
+ end
633
+
634
+ if expired?(key)
635
+ # Do not close this socket here in case of it will be used by other place (e.g. wait for receiving ack)
636
+ @inactive_socks[key] = @active_socks.delete(key)
637
+ @log.debug("connection #{@inactive_socks[key]} is expired. reconnecting...")
638
+ @active_socks[key] = TimedSocket.new(timeout, yield, 0)
639
+ end
640
+
641
+ @active_socks[key].ref += 1;
642
+ @active_socks[key].sock
643
+ end
644
+ end
645
+
646
+ def dec_ref(key = Thread.current.object_id)
647
+ @mutex.synchronize do
648
+ if @active_socks[key]
649
+ @active_socks[key].ref -= 1
650
+ elsif @inactive_socks[key]
651
+ @inactive_socks[key].ref -= 1
652
+ else
653
+ @log.warn("Not found key for dec_ref: #{key}")
654
+ end
655
+ end
656
+ end
657
+
658
+ # This method is expected to be called in class which doesn't call #inc_ref
659
+ def dec_ref_by_value(val)
660
+ @mutex.synchronize do
661
+ sock = @active_socks.detect { |_, v| v.sock == val }
662
+ if sock
663
+ key = sock.first
664
+ @active_socks[key].ref -= 1
665
+ return
666
+ end
667
+
668
+ sock = @inactive_socks.detect { |_, v| v.sock == val }
669
+ if sock
670
+ key = sock.first
671
+ @inactive_socks[key].ref -= 1
672
+ return
673
+ else
674
+ @log.warn("Not found key for dec_ref_by_value: #{key}")
675
+ end
676
+ end
677
+ end
678
+
679
+ # This method is expected to be called in class which doesn't call #fetch_or
680
+ def revoke_by_value(val)
681
+ @mutex.synchronize do
682
+ sock = @active_socks.detect { |_, v| v.sock == val }
683
+ if sock
684
+ key = sock.first
685
+ @inactive_socks[key] = @active_socks.delete(key)
686
+ @inactive_socks[key].ref = 0
687
+ else
688
+ @log.debug("Not found for revoke_by_value :#{val}")
689
+ end
690
+ end
691
+ end
692
+
693
+ private
694
+
695
+ def timeout
696
+ @timeout && Time.now + @timeout
697
+ end
698
+
699
+ # This method is thread unsafe
700
+ def expired?(key = Thread.current.object_id)
701
+ @active_socks[key].timeout ? @active_socks[key].timeout < Time.now : false
702
+ end
703
+ end
704
+
705
+ # @param keepalive [Bool]
706
+ # @param keepalive_timeout [Integer | nil]
707
+ def initialize(sender, server, failure:, keepalive: false, keepalive_timeout: nil)
542
708
  @sender = sender
543
709
  @log = sender.log
544
710
  @compress = sender.compress
@@ -571,6 +737,11 @@ module Fluent::Plugin
571
737
  @resolved_host = nil
572
738
  @resolved_time = 0
573
739
  @resolved_once = false
740
+
741
+ @keepalive = keepalive
742
+ if @keepalive
743
+ @socket_cache = SocketCache.new(keepalive_timeout, @log)
744
+ end
574
745
  end
575
746
 
576
747
  attr_accessor :usock
@@ -578,6 +749,7 @@ module Fluent::Plugin
578
749
  attr_reader :name, :host, :port, :weight, :standby, :state
579
750
  attr_reader :sockaddr # used by on_heartbeat
580
751
  attr_reader :failure, :available # for test
752
+ attr_reader :socket_cache # for ack
581
753
 
582
754
  RequestInfo = Struct.new(:state, :shared_key_nonce, :auth)
583
755
 
@@ -598,15 +770,12 @@ module Fluent::Plugin
598
770
  end
599
771
 
600
772
  def verify_connection
601
- sock = @sender.create_transfer_socket(resolved_host, port, @hostname)
602
- begin
773
+ connect do |sock|
603
774
  ri = RequestInfo.new(@sender.security ? :helo : :established)
604
775
  if ri.state != :established
605
776
  establish_connection(sock, ri)
606
777
  raise if ri.state != :established
607
778
  end
608
- ensure
609
- sock.close
610
779
  end
611
780
  end
612
781
 
@@ -672,11 +841,16 @@ module Fluent::Plugin
672
841
  end
673
842
 
674
843
  def send_data(tag, chunk)
675
- sock = @sender.create_transfer_socket(resolved_host, port, @hostname)
844
+ sock = connect
845
+
676
846
  begin
677
847
  send_data_actual(sock, tag, chunk)
678
848
  rescue
679
- sock.close rescue nil
849
+ if @keepalive
850
+ @socket_cache.revoke
851
+ else
852
+ sock.close rescue nil
853
+ end
680
854
  raise
681
855
  end
682
856
 
@@ -684,12 +858,27 @@ module Fluent::Plugin
684
858
  return sock # to read ACK from socket
685
859
  end
686
860
 
687
- sock.close_write rescue nil
688
- sock.close rescue nil
861
+ if @keepalive
862
+ @socket_cache.dec_ref
863
+ else
864
+ sock.close_write rescue nil
865
+ sock.close rescue nil
866
+ end
689
867
  heartbeat(false)
690
868
  nil
691
869
  end
692
870
 
871
+ def clear
872
+ @keepalive && @socket_cache.clear
873
+ end
874
+
875
+ def purge_obsolete_socks
876
+ unless @keepalive
877
+ raise "Don not call this method without keepalive option"
878
+ end
879
+ @socket_cache.purge_obsolete_socks
880
+ end
881
+
693
882
  # FORWARD_TCP_HEARTBEAT_DATA = FORWARD_HEADER + ''.to_msgpack + [].to_msgpack
694
883
  def send_heartbeat
695
884
  begin
@@ -705,7 +894,7 @@ module Fluent::Plugin
705
894
 
706
895
  case @sender.heartbeat_type
707
896
  when :transport
708
- @sender.create_transfer_socket(dest_addr, port, @hostname) do |sock|
897
+ connect(dest_addr) do |sock|
709
898
  ## don't send any data to not cause a compatibility problem
710
899
  # sock.write FORWARD_TCP_HEARTBEAT_DATA
711
900
 
@@ -880,6 +1069,32 @@ module Fluent::Plugin
880
1069
  raise "BUG: unknown session state: #{ri.state}"
881
1070
  end
882
1071
  end
1072
+
1073
+ private
1074
+
1075
+ def connect(host = nil)
1076
+ sock = if @keepalive
1077
+ @socket_cache.fetch_or { @sender.create_transfer_socket(host || resolved_host, port, @hostname) }
1078
+ else
1079
+ @log.debug('connect new socket')
1080
+ @sender.create_transfer_socket(host || resolved_host, port, @hostname)
1081
+ end
1082
+
1083
+ if block_given?
1084
+ begin
1085
+ yield(sock)
1086
+ rescue
1087
+ @socket_cache.revoke(sock) if @keepalive
1088
+ raise
1089
+ else
1090
+ @socket_cache.dec_ref(sock) if @keepalive
1091
+ ensure
1092
+ sock.close unless @keepalive
1093
+ end
1094
+ else
1095
+ sock
1096
+ end
1097
+ end
883
1098
  end
884
1099
 
885
1100
  # Override Node to disable heartbeat
@@ -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
@@ -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.rc1'
20
20
 
21
21
  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: {
@@ -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
@@ -111,4 +111,28 @@ class JsonParserTest < ::Test::Unit::TestCase
111
111
  assert_equal text, record['time']
112
112
  end
113
113
  end
114
+
115
+ def test_yajl_parse_io_with_buffer_smaller_than_input
116
+ parser = Fluent::Test::Driver::Parser.new(Fluent::Plugin::JSONParser)
117
+ parser.configure(
118
+ 'keep_time_key' => 'true',
119
+ 'json_parser' => 'yajl',
120
+ 'stream_buffer_size' => 1,
121
+ )
122
+ text = "100"
123
+
124
+ waiting(5) do
125
+ rd, wr = IO.pipe
126
+ wr.write "{\"time\":\"#{text}\"}"
127
+
128
+ parser.instance.parse_io(rd) do |time, record|
129
+ assert_equal text.to_i, time.sec
130
+ assert_equal text, record['time']
131
+
132
+ # Once a record has been received the 'write' end of the pipe must be
133
+ # closed, otherwise the test will block waiting for more input.
134
+ wr.close
135
+ end
136
+ end
137
+ end
114
138
  end
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.4.2
4
+ version: 1.5.0.rc1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Sadayuki Furuhashi
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2019-04-02 00:00:00.000000000 Z
11
+ date: 2019-05-09 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: msgpack
@@ -772,9 +772,9 @@ required_ruby_version: !ruby/object:Gem::Requirement
772
772
  version: '2.1'
773
773
  required_rubygems_version: !ruby/object:Gem::Requirement
774
774
  requirements:
775
- - - ">="
775
+ - - ">"
776
776
  - !ruby/object:Gem::Version
777
- version: '0'
777
+ version: 1.3.1
778
778
  requirements: []
779
779
  rubyforge_project:
780
780
  rubygems_version: 2.6.14.1