fluentd 1.10.1-x86-mingw32 → 1.11.1-x86-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.

Files changed (62) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +112 -1
  3. data/CONTRIBUTING.md +1 -1
  4. data/lib/fluent/command/debug.rb +1 -0
  5. data/lib/fluent/command/fluentd.rb +12 -1
  6. data/lib/fluent/config.rb +1 -0
  7. data/lib/fluent/log.rb +45 -6
  8. data/lib/fluent/match.rb +1 -1
  9. data/lib/fluent/plugin/in_dummy.rb +2 -2
  10. data/lib/fluent/plugin/in_forward.rb +2 -2
  11. data/lib/fluent/plugin/in_gc_stat.rb +16 -0
  12. data/lib/fluent/plugin/in_http.rb +146 -75
  13. data/lib/fluent/plugin/in_monitor_agent.rb +1 -1
  14. data/lib/fluent/plugin/in_syslog.rb +4 -4
  15. data/lib/fluent/plugin/in_tail.rb +4 -4
  16. data/lib/fluent/plugin/in_unix.rb +77 -77
  17. data/lib/fluent/plugin/out_copy.rb +1 -1
  18. data/lib/fluent/plugin/out_file.rb +1 -1
  19. data/lib/fluent/plugin/out_forward.rb +23 -18
  20. data/lib/fluent/plugin/out_forward/load_balancer.rb +1 -1
  21. data/lib/fluent/plugin/out_http.rb +15 -2
  22. data/lib/fluent/plugin/parser_multiline.rb +1 -1
  23. data/lib/fluent/plugin/parser_syslog.rb +215 -54
  24. data/lib/fluent/plugin_helper/child_process.rb +3 -2
  25. data/lib/fluent/plugin_helper/record_accessor.rb +14 -0
  26. data/lib/fluent/plugin_helper/service_discovery.rb +7 -0
  27. data/lib/fluent/plugin_helper/service_discovery/manager.rb +8 -0
  28. data/lib/fluent/plugin_helper/socket.rb +20 -2
  29. data/lib/fluent/plugin_helper/socket_option.rb +2 -2
  30. data/lib/fluent/supervisor.rb +21 -9
  31. data/lib/fluent/system_config.rb +2 -1
  32. data/lib/fluent/test/filter_test.rb +2 -2
  33. data/lib/fluent/test/output_test.rb +3 -3
  34. data/lib/fluent/version.rb +1 -1
  35. data/test/command/test_fluentd.rb +57 -10
  36. data/test/config/test_system_config.rb +2 -0
  37. data/test/plugin/out_forward/test_load_balancer.rb +46 -0
  38. data/test/plugin/test_in_gc_stat.rb +24 -1
  39. data/test/plugin/test_in_http.rb +57 -0
  40. data/test/plugin/test_in_syslog.rb +1 -1
  41. data/test/plugin/test_in_tail.rb +20 -16
  42. data/test/plugin/test_in_unix.rb +128 -73
  43. data/test/plugin/test_out_forward.rb +11 -2
  44. data/test/plugin/test_out_http.rb +38 -0
  45. data/test/plugin/test_out_null.rb +1 -1
  46. data/test/plugin/test_output_as_buffered_retries.rb +12 -4
  47. data/test/plugin/test_parser_syslog.rb +66 -29
  48. data/test/plugin_helper/data/cert/cert_chains/ca-cert-key.pem +27 -0
  49. data/test/plugin_helper/data/cert/cert_chains/ca-cert.pem +20 -0
  50. data/test/plugin_helper/data/cert/cert_chains/cert-key.pem +27 -0
  51. data/test/plugin_helper/data/cert/cert_chains/cert.pem +40 -0
  52. data/test/plugin_helper/data/cert/generate_cert.rb +38 -0
  53. data/test/plugin_helper/http_server/test_app.rb +1 -1
  54. data/test/plugin_helper/http_server/test_route.rb +1 -1
  55. data/test/plugin_helper/test_child_process.rb +15 -0
  56. data/test/plugin_helper/test_http_server_helper.rb +2 -2
  57. data/test/plugin_helper/test_record_accessor.rb +41 -0
  58. data/test/plugin_helper/test_server.rb +1 -1
  59. data/test/plugin_helper/test_service_discovery.rb +37 -4
  60. data/test/plugin_helper/test_socket.rb +131 -0
  61. data/test/test_log.rb +44 -0
  62. metadata +12 -2
@@ -223,7 +223,7 @@ module Fluent::Plugin
223
223
  opts = {with_config: false, with_retry: false}
224
224
  timer_execute(:in_monitor_agent_emit, @emit_interval, repeat: true) {
225
225
  es = Fluent::MultiEventStream.new
226
- now = Fluent::Engine.now
226
+ now = Fluent::EventTime.now
227
227
  plugins_info_all(opts).each { |record|
228
228
  es.add(now, record)
229
229
  }
@@ -181,12 +181,12 @@ module Fluent::Plugin
181
181
  if octet_count_frame
182
182
  while idx = buffer.index(delimiter, pos)
183
183
  num = Integer(buffer[pos..idx])
184
- pos = idx + num
185
- msg = buffer[idx + 1...pos]
186
- if msg.size < num - 1
187
- pos = pos - num - num.to_s.size
184
+ msg = buffer[idx + delimiter_size, num]
185
+ if msg.size != num
188
186
  break
189
187
  end
188
+
189
+ pos = idx + delimiter_size + num
190
190
  message_handler(msg, conn)
191
191
  end
192
192
  else
@@ -136,7 +136,7 @@ module Fluent::Plugin
136
136
  raise Fluent::ConfigError, "#{rc} are reserved words: #{@path_delimiter}"
137
137
  end
138
138
 
139
- @paths = @path.split(@path_delimiter).map(&:strip)
139
+ @paths = @path.split(@path_delimiter).map(&:strip).uniq
140
140
  if @paths.empty?
141
141
  raise Fluent::ConfigError, "tail: 'path' parameter is required on tail input"
142
142
  end
@@ -271,9 +271,9 @@ module Fluent::Plugin
271
271
  end
272
272
  else
273
273
  if is_file
274
- unless @ignore_list.include?(path)
274
+ unless @ignore_list.include?(p)
275
275
  log.warn "#{p} unreadable. It is excluded and would be examined next time."
276
- @ignore_list << path if @ignore_repeated_permission_error
276
+ @ignore_list << p if @ignore_repeated_permission_error
277
277
  end
278
278
  end
279
279
  false
@@ -296,7 +296,7 @@ module Fluent::Plugin
296
296
  end
297
297
  path.include?('*') ? Dir.glob(path) : path
298
298
  }.flatten.uniq
299
- paths - excluded
299
+ paths.uniq - excluded
300
300
  end
301
301
 
302
302
  # in_tail with '*' path doesn't check rotation file equality at refresh phase.
@@ -14,50 +14,68 @@
14
14
  # limitations under the License.
15
15
  #
16
16
 
17
- require 'fileutils'
18
- require 'socket'
17
+ require 'fluent/env'
18
+ require 'fluent/plugin/input'
19
+ require 'fluent/msgpack_factory'
19
20
 
20
21
  require 'cool.io'
21
22
  require 'yajl'
23
+ require 'fileutils'
24
+ require 'socket'
25
+
26
+ module Fluent::Plugin
27
+ # TODO: This plugin will be 3rd party plugin
28
+ class UnixInput < Input
29
+ Fluent::Plugin.register_input('unix', self)
30
+
31
+ helpers :event_loop
32
+
33
+ def initialize
34
+ super
22
35
 
23
- require 'fluent/input'
24
- require 'fluent/event'
36
+ @lsock = nil
37
+ end
25
38
 
26
- module Fluent
27
- # obsolete
28
- class StreamInput < Input
29
- config_param :blocking_timeout, :time, default: 0.5
39
+ desc 'The path to your Unix Domain Socket.'
40
+ config_param :path, :string, default: Fluent::DEFAULT_SOCKET_PATH
41
+ desc 'The backlog of Unix Domain Socket.'
42
+ config_param :backlog, :integer, default: nil
43
+ desc "New tag instead of incoming tag"
44
+ config_param :tag, :string, default: nil
45
+
46
+ def configure(conf)
47
+ super
48
+ end
30
49
 
31
50
  def start
32
51
  super
33
52
 
34
- @loop = Coolio::Loop.new
35
53
  @lsock = listen
36
- @loop.attach(@lsock)
37
- @thread = Thread.new(&method(:run))
54
+ event_loop_attach(@lsock)
38
55
  end
39
56
 
40
57
  def shutdown
41
- @loop.watchers.each {|w| w.detach }
42
- @loop.stop
43
- @lsock.close
44
- @thread.join
58
+ if @lsock
59
+ event_loop_detach(@lsock)
60
+ @lsock.close
61
+ end
45
62
 
46
63
  super
47
64
  end
48
65
 
49
- #def listen
50
- #end
66
+ def listen
67
+ if File.exist?(@path)
68
+ log.warn "Found existing '#{@path}'. Remove this file for in_unix plugin"
69
+ File.unlink(@path)
70
+ end
71
+ FileUtils.mkdir_p(File.dirname(@path))
51
72
 
52
- def run
53
- @loop.run(@blocking_timeout)
54
- rescue
55
- log.error "unexpected error", error: $!.to_s
56
- log.error_backtrace
73
+ log.info "listening fluent socket on #{@path}"
74
+ s = Coolio::UNIXServer.new(@path, Handler, log, method(:on_message))
75
+ s.listen(@backlog) unless @backlog.nil?
76
+ s
57
77
  end
58
78
 
59
- private
60
-
61
79
  # message Entry {
62
80
  # 1: long time
63
81
  # 2: object record
@@ -79,23 +97,27 @@ module Fluent
79
97
  # 3: object record
80
98
  # }
81
99
  def on_message(msg)
82
- # TODO format error
83
- tag = msg[0].to_s
100
+ unless msg.is_a?(Array)
101
+ log.warn "incoming data is broken:", msg: msg
102
+ return
103
+ end
104
+
105
+ tag = @tag || (msg[0].to_s)
84
106
  entries = msg[1]
85
107
 
86
- if entries.class == String
108
+ case entries
109
+ when String
87
110
  # PackedForward
88
- es = MessagePackEventStream.new(entries)
111
+ es = Fluent::MessagePackEventStream.new(entries)
89
112
  router.emit_stream(tag, es)
90
113
 
91
- elsif entries.class == Array
114
+ when Array
92
115
  # Forward
93
- es = MultiEventStream.new
116
+ es = Fluent::MultiEventStream.new
94
117
  entries.each {|e|
95
118
  record = e[1]
96
119
  next if record.nil?
97
- time = e[0]
98
- time = (now ||= Engine.now) if time.to_i == 0
120
+ time = convert_time(e[0])
99
121
  es.add(time, record)
100
122
  }
101
123
  router.emit_stream(tag, es)
@@ -105,25 +127,28 @@ module Fluent
105
127
  record = msg[2]
106
128
  return if record.nil?
107
129
 
108
- time = msg[1]
109
- time = Engine.now if time.to_i == 0
130
+ time = convert_time(msg[1])
110
131
  router.emit(tag, time, record)
111
132
  end
112
133
  end
113
134
 
135
+ def convert_time(time)
136
+ case time
137
+ when nil, 0
138
+ Fluent::EventTime.now
139
+ when Fluent::EventTime
140
+ time
141
+ else
142
+ Fluent::EventTime.from_time(Time.at(time))
143
+ end
144
+ end
145
+
114
146
  class Handler < Coolio::Socket
115
147
  def initialize(io, log, on_message)
116
148
  super(io)
117
- if io.is_a?(TCPSocket)
118
- opt = [1, @timeout.to_i].pack('I!I!') # { int l_onoff; int l_linger; }
119
- io.setsockopt(Socket::SOL_SOCKET, Socket::SO_LINGER, opt)
120
- end
149
+
121
150
  @on_message = on_message
122
151
  @log = log
123
- @log.trace {
124
- remote_port, remote_addr = *Socket.unpack_sockaddr_in(@_io.getpeername) rescue nil
125
- "accepted fluent socket from '#{remote_addr}:#{remote_port}': object_id=#{self.object_id}"
126
- }
127
152
  end
128
153
 
129
154
  def on_connect
@@ -131,13 +156,13 @@ module Fluent
131
156
 
132
157
  def on_read(data)
133
158
  first = data[0]
134
- if first == '{' || first == '['
159
+ if first == '{'.freeze || first == '['.freeze
135
160
  m = method(:on_read_json)
136
- @y = Yajl::Parser.new
137
- @y.on_parse_complete = @on_message
161
+ @parser = Yajl::Parser.new
162
+ @parser.on_parse_complete = @on_message
138
163
  else
139
164
  m = method(:on_read_msgpack)
140
- @u = Fluent::MessagePackFactory.msgpack_unpacker
165
+ @parser = Fluent::MessagePackFactory.msgpack_unpacker
141
166
  end
142
167
 
143
168
  singleton_class.module_eval do
@@ -147,17 +172,17 @@ module Fluent
147
172
  end
148
173
 
149
174
  def on_read_json(data)
150
- @y << data
151
- rescue
152
- @log.error "unexpected error", error: $!.to_s
175
+ @parser << data
176
+ rescue => e
177
+ @log.error "unexpected error in json payload", error: e.to_s
153
178
  @log.error_backtrace
154
179
  close
155
180
  end
156
181
 
157
182
  def on_read_msgpack(data)
158
- @u.feed_each(data, &@on_message)
159
- rescue
160
- @log.error "unexpected error", error: $!.to_s
183
+ @parser.feed_each(data, &@on_message)
184
+ rescue => e
185
+ @log.error "unexpected error in msgpack payload", error: e.to_s
161
186
  @log.error_backtrace
162
187
  close
163
188
  end
@@ -167,29 +192,4 @@ module Fluent
167
192
  end
168
193
  end
169
194
  end
170
-
171
- class UnixInput < StreamInput
172
- Plugin.register_input('unix', self)
173
-
174
- desc 'The path to your Unix Domain Socket.'
175
- config_param :path, :string, default: DEFAULT_SOCKET_PATH
176
- desc 'The backlog of Unix Domain Socket.'
177
- config_param :backlog, :integer, default: nil
178
-
179
- def configure(conf)
180
- super
181
- #log.warn "'unix' input is obsoleted and will be removed. Use 'forward' instead."
182
- end
183
-
184
- def listen
185
- if File.exist?(@path)
186
- File.unlink(@path)
187
- end
188
- FileUtils.mkdir_p File.dirname(@path)
189
- log.info "listening fluent socket on #{@path}"
190
- s = Coolio::UNIXServer.new(@path, Handler, log, method(:on_message))
191
- s.listen(@backlog) unless @backlog.nil?
192
- s
193
- end
194
- end
195
195
  end
@@ -61,7 +61,7 @@ module Fluent::Plugin
61
61
  output.emit_events(tag, @copy_proc ? @copy_proc.call(es) : es)
62
62
  rescue => e
63
63
  if @ignore_errors[i]
64
- log.error "ignore emit error", error: e
64
+ log.error "ignore emit error in #{output.plugin_id}", error: e
65
65
  else
66
66
  raise e
67
67
  end
@@ -155,7 +155,7 @@ module Fluent::Plugin
155
155
  dummy_record_keys = get_placeholders_keys(@path_template) || ['message']
156
156
  dummy_record = Hash[dummy_record_keys.zip(['data'] * dummy_record_keys.size)]
157
157
 
158
- test_chunk1 = chunk_for_test(dummy_tag, Fluent::Engine.now, dummy_record)
158
+ test_chunk1 = chunk_for_test(dummy_tag, Fluent::EventTime.now, dummy_record)
159
159
  test_path = extract_placeholders(@path_template, test_chunk1)
160
160
  unless ::Fluent::FileUtil.writable_p?(test_path)
161
161
  raise Fluent::ConfigError, "out_file: `#{test_path}` is not writable"
@@ -324,7 +324,7 @@ module Fluent::Plugin
324
324
  end
325
325
  end
326
326
 
327
- if @keepalive && @keepalive_timeout
327
+ if @keepalive
328
328
  timer_execute(:out_forward_keep_alived_socket_watcher, @keep_alive_watcher_interval, &method(:on_purge_obsolete_socks))
329
329
  end
330
330
  end
@@ -382,7 +382,12 @@ module Fluent::Plugin
382
382
  cert_logical_store_name: @tls_cert_logical_store_name,
383
383
  cert_use_enterprise_store: @tls_cert_use_enterprise_store,
384
384
 
385
- linger_timeout: @send_timeout,
385
+ # Enabling SO_LINGER causes tcp port exhaustion on Windows.
386
+ # This is because dynamic ports are only 16384 (from 49152 to 65535) and
387
+ # expiring SO_LINGER enabled ports should wait 4 minutes
388
+ # where set by TcpTimeDelay. Its default value is 4 minutes.
389
+ # So, we should disable SO_LINGER on Windows to prevent flood of waiting ports.
390
+ linger_timeout: Fluent.windows? ? nil : @send_timeout,
386
391
  send_timeout: @send_timeout,
387
392
  recv_timeout: @ack_response_timeout,
388
393
  connect_timeout: @connect_timeout,
@@ -578,12 +583,7 @@ module Fluent::Plugin
578
583
 
579
584
  def verify_connection
580
585
  connect do |sock, ri|
581
- if ri.state != :established
582
- establish_connection(sock, ri)
583
- if ri.state != :established
584
- raise "Failed to establish connection to #{@host}:#{@port}"
585
- end
586
- end
586
+ ensure_established_connection(sock, ri)
587
587
  end
588
588
  end
589
589
 
@@ -652,14 +652,7 @@ module Fluent::Plugin
652
652
  def send_data(tag, chunk)
653
653
  ack = @ack_handler && @ack_handler.create_ack(chunk.unique_id, self)
654
654
  connect(nil, ack: ack) do |sock, ri|
655
- if ri.state != :established
656
- establish_connection(sock, ri)
657
-
658
- if ri.state != :established
659
- raise ConnectionClosedError, "failed to establish connection with node #{@name}"
660
- end
661
- end
662
-
655
+ ensure_established_connection(sock, ri)
663
656
  send_data_actual(sock, tag, chunk)
664
657
  end
665
658
 
@@ -684,7 +677,9 @@ module Fluent::Plugin
684
677
 
685
678
  case @sender.heartbeat_type
686
679
  when :transport
687
- connect(dest_addr) do |_ri, _sock|
680
+ connect(dest_addr) do |sock, ri|
681
+ ensure_established_connection(sock, ri)
682
+
688
683
  ## don't send any data to not cause a compatibility problem
689
684
  # sock.write FORWARD_TCP_HEARTBEAT_DATA
690
685
 
@@ -714,7 +709,7 @@ module Fluent::Plugin
714
709
  @resolved_host ||= resolve_dns!
715
710
 
716
711
  else
717
- now = Fluent::Engine.now
712
+ now = Fluent::EventTime.now
718
713
  rh = @resolved_host
719
714
  if !rh || now - @resolved_time >= @sender.expire_dns_cache
720
715
  rh = @resolved_host = resolve_dns!
@@ -776,6 +771,16 @@ module Fluent::Plugin
776
771
 
777
772
  private
778
773
 
774
+ def ensure_established_connection(sock, request_info)
775
+ if request_info.state != :established
776
+ establish_connection(sock, request_info)
777
+
778
+ if request_info.state != :established
779
+ raise ConnectionClosedError, "failed to establish connection with node #{@name}"
780
+ end
781
+ end
782
+ end
783
+
779
784
  def connect(host = nil, ack: false, &block)
780
785
  @connection_manager.connect(host: host || resolved_host, port: port, hostname: @hostname, ack: ack, &block)
781
786
  end
@@ -56,7 +56,7 @@ module Fluent::Plugin
56
56
  end
57
57
 
58
58
  def rebuild_weight_array(nodes)
59
- standby_nodes, regular_nodes = nodes.partition {|n|
59
+ standby_nodes, regular_nodes = nodes.select { |e| e.weight > 0 }.partition {|n|
60
60
  n.standby?
61
61
  }
62
62
 
@@ -37,6 +37,8 @@ module Fluent::Plugin
37
37
  config_param :proxy, :string, default: ENV['HTTP_PROXY'] || ENV['http_proxy']
38
38
  desc 'Content-Type for HTTP request'
39
39
  config_param :content_type, :string, default: nil
40
+ desc 'JSON array data format for HTTP request body'
41
+ config_param :json_array, :bool, default: false
40
42
  desc 'Additional headers for HTTP request'
41
43
  config_param :headers, :hash, default: nil
42
44
 
@@ -100,6 +102,13 @@ module Fluent::Plugin
100
102
  @proxy_uri = URI.parse(@proxy) if @proxy
101
103
  @formatter = formatter_create
102
104
  @content_type = setup_content_type unless @content_type
105
+
106
+ if @json_array
107
+ if @formatter_configs.first[:@type] != "json"
108
+ raise Fluent::ConfigError, "json_array option could be used with json formatter only"
109
+ end
110
+ define_singleton_method(:format, method(:format_json_array))
111
+ end
103
112
  end
104
113
 
105
114
  def multi_workers_ready?
@@ -114,6 +123,10 @@ module Fluent::Plugin
114
123
  @formatter.format(tag, time, record)
115
124
  end
116
125
 
126
+ def format_json_array(tag, time, record)
127
+ @formatter.format(tag, time, record) << ","
128
+ end
129
+
117
130
  def write(chunk)
118
131
  uri = parse_endpoint(chunk)
119
132
  req = create_request(chunk, uri)
@@ -128,7 +141,7 @@ module Fluent::Plugin
128
141
  def setup_content_type
129
142
  case @formatter_configs.first[:@type]
130
143
  when 'json'
131
- 'application/x-ndjson'
144
+ @json_array ? 'application/json' : 'application/x-ndjson'
132
145
  when 'csv'
133
146
  'text/csv'
134
147
  when 'tsv', 'ltsv'
@@ -202,7 +215,7 @@ module Fluent::Plugin
202
215
  req.basic_auth(@auth.username, @auth.password)
203
216
  end
204
217
  set_headers(req)
205
- req.body = chunk.read
218
+ req.body = @json_array ? "[#{chunk.read.chop!}]" : chunk.read
206
219
  req
207
220
  end
208
221