fluentd 1.7.4-x64-mingw32 → 1.8.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.

Files changed (101) hide show
  1. checksums.yaml +4 -4
  2. data/.github/ISSUE_TEMPLATE/bug_report.md +0 -1
  3. data/.travis.yml +4 -0
  4. data/CHANGELOG.md +70 -0
  5. data/MAINTAINERS.md +1 -0
  6. data/example/out_forward_sd.conf +17 -0
  7. data/example/sd.yaml +8 -0
  8. data/fluentd.gemspec +1 -1
  9. data/lib/fluent/agent.rb +3 -1
  10. data/lib/fluent/command/cat.rb +1 -2
  11. data/lib/fluent/command/fluentd.rb +16 -8
  12. data/lib/fluent/compat/call_super_mixin.rb +9 -0
  13. data/lib/fluent/compat/exec_util.rb +1 -1
  14. data/lib/fluent/config/configure_proxy.rb +4 -4
  15. data/lib/fluent/config/element.rb +28 -15
  16. data/lib/fluent/config/error.rb +6 -0
  17. data/lib/fluent/config/literal_parser.rb +24 -2
  18. data/lib/fluent/config/section.rb +43 -6
  19. data/lib/fluent/config/types.rb +98 -26
  20. data/lib/fluent/configurable.rb +2 -2
  21. data/lib/fluent/counter/base_socket.rb +2 -4
  22. data/lib/fluent/engine.rb +41 -122
  23. data/lib/fluent/event.rb +5 -7
  24. data/lib/fluent/fluent_log_event_router.rb +141 -0
  25. data/lib/fluent/msgpack_factory.rb +19 -2
  26. data/lib/fluent/plugin.rb +10 -1
  27. data/lib/fluent/plugin/base.rb +2 -2
  28. data/lib/fluent/plugin/buf_file.rb +11 -7
  29. data/lib/fluent/plugin/buf_file_single.rb +8 -5
  30. data/lib/fluent/plugin/buffer/chunk.rb +1 -1
  31. data/lib/fluent/plugin/buffer/file_chunk.rb +4 -6
  32. data/lib/fluent/plugin/buffer/file_single_chunk.rb +3 -5
  33. data/lib/fluent/plugin/formatter_csv.rb +23 -1
  34. data/lib/fluent/plugin/formatter_stdout.rb +1 -1
  35. data/lib/fluent/plugin/in_forward.rb +1 -1
  36. data/lib/fluent/plugin/in_monitor_agent.rb +4 -2
  37. data/lib/fluent/plugin/in_tail.rb +6 -0
  38. data/lib/fluent/plugin/in_unix.rb +1 -1
  39. data/lib/fluent/plugin/out_forward.rb +77 -28
  40. data/lib/fluent/plugin/out_forward/ack_handler.rb +1 -1
  41. data/lib/fluent/plugin/out_forward/load_balancer.rb +5 -2
  42. data/lib/fluent/plugin/out_stream.rb +1 -1
  43. data/lib/fluent/plugin/output.rb +11 -3
  44. data/lib/fluent/plugin/parser.rb +1 -0
  45. data/lib/fluent/plugin/sd_file.rb +155 -0
  46. data/lib/fluent/plugin/sd_static.rb +58 -0
  47. data/lib/fluent/plugin/service_discovery.rb +80 -0
  48. data/lib/fluent/plugin_helper.rb +1 -0
  49. data/lib/fluent/plugin_helper/child_process.rb +3 -3
  50. data/lib/fluent/plugin_helper/compat_parameters.rb +11 -1
  51. data/lib/fluent/plugin_helper/extract.rb +1 -1
  52. data/lib/fluent/plugin_helper/inject.rb +1 -1
  53. data/lib/fluent/plugin_helper/record_accessor.rb +10 -19
  54. data/lib/fluent/plugin_helper/server.rb +8 -4
  55. data/lib/fluent/plugin_helper/service_discovery.rb +80 -0
  56. data/lib/fluent/plugin_helper/service_discovery/manager.rb +132 -0
  57. data/lib/fluent/plugin_helper/service_discovery/round_robin_balancer.rb +43 -0
  58. data/lib/fluent/plugin_id.rb +7 -0
  59. data/lib/fluent/root_agent.rb +7 -9
  60. data/lib/fluent/supervisor.rb +192 -211
  61. data/lib/fluent/system_config.rb +26 -52
  62. data/lib/fluent/test/driver/base_owned.rb +15 -2
  63. data/lib/fluent/time.rb +8 -6
  64. data/lib/fluent/version.rb +1 -1
  65. data/test/command/test_fluentd.rb +12 -7
  66. data/test/config/test_configurable.rb +154 -0
  67. data/test/config/test_element.rb +18 -0
  68. data/test/config/test_literal_parser.rb +4 -0
  69. data/test/config/test_system_config.rb +48 -91
  70. data/test/config/test_types.rb +293 -120
  71. data/test/counter/test_client.rb +8 -4
  72. data/test/plugin/data/sd_file/config +11 -0
  73. data/test/plugin/data/sd_file/config.json +17 -0
  74. data/test/plugin/data/sd_file/config.yaml +11 -0
  75. data/test/plugin/data/sd_file/config.yml +11 -0
  76. data/test/plugin/data/sd_file/invalid_config.yml +7 -0
  77. data/test/plugin/out_forward/test_handshake_protocol.rb +2 -2
  78. data/test/plugin/out_forward/test_load_balancer.rb +1 -1
  79. data/test/plugin/out_forward/test_socket_cache.rb +2 -2
  80. data/test/plugin/test_buf_file.rb +40 -0
  81. data/test/plugin/test_buf_file_single.rb +32 -0
  82. data/test/plugin/test_buffer_file_chunk.rb +0 -11
  83. data/test/plugin/test_buffer_file_single_chunk.rb +0 -10
  84. data/test/plugin/test_formatter_csv.rb +9 -0
  85. data/test/plugin/test_in_forward.rb +9 -9
  86. data/test/plugin/test_in_monitor_agent.rb +37 -10
  87. data/test/plugin/test_in_unix.rb +5 -5
  88. data/test/plugin/test_out_forward.rb +45 -1
  89. data/test/plugin/test_out_stdout.rb +36 -1
  90. data/test/plugin/test_out_stream.rb +3 -3
  91. data/test/plugin/test_output.rb +25 -1
  92. data/test/plugin/test_sd_file.rb +211 -0
  93. data/test/plugin_helper/service_discovery/test_manager.rb +93 -0
  94. data/test/plugin_helper/service_discovery/test_round_robin_balancer.rb +21 -0
  95. data/test/plugin_helper/test_server.rb +13 -0
  96. data/test/plugin_helper/test_service_discovery.rb +72 -0
  97. data/test/test_event.rb +15 -15
  98. data/test/test_fluent_log_event_router.rb +99 -0
  99. data/test/test_logger_initializer.rb +26 -0
  100. data/test/test_supervisor.rb +30 -59
  101. metadata +43 -6
@@ -28,7 +28,7 @@ module Fluent
28
28
  def configure(conf)
29
29
  super
30
30
 
31
- @time_formatter = Strftime.new(TIME_FORMAT)
31
+ @time_formatter = Strftime.new(@time_format || TIME_FORMAT)
32
32
  @sub_formatter = Plugin.new_formatter(@output_type, parent: self.owner)
33
33
  @sub_formatter.configure(conf)
34
34
  end
@@ -256,7 +256,7 @@ module Fluent::Plugin
256
256
  serializer = :to_json.to_proc
257
257
  feeder = ->(d){ parser << d }
258
258
  else # msgpack
259
- parser = Fluent::Engine.msgpack_factory.unpacker
259
+ parser = Fluent::MessagePackFactory.msgpack_unpacker
260
260
  serializer = :to_msgpack.to_proc
261
261
  feeder = ->(d){
262
262
  parser.feed_each(d){|obj|
@@ -64,7 +64,8 @@ module Fluent::Plugin
64
64
  def config_ltsv(_req)
65
65
  obj = {
66
66
  'pid' => Process.pid,
67
- 'ppid' => Process.ppid
67
+ 'ppid' => Process.ppid,
68
+ 'version' => Fluent::VERSION,
68
69
  }.merge(@agent.fluentd_opts)
69
70
 
70
71
  render_ltsv([obj])
@@ -73,7 +74,8 @@ module Fluent::Plugin
73
74
  def config_json(req)
74
75
  obj = {
75
76
  'pid' => Process.pid,
76
- 'ppid' => Process.ppid
77
+ 'ppid' => Process.ppid,
78
+ 'version' => Fluent::VERSION,
77
79
  }.merge(@agent.fluentd_opts)
78
80
  opts = build_option(req)
79
81
 
@@ -204,6 +204,12 @@ module Fluent::Plugin
204
204
  timer_execute(:in_tail_refresh_watchers, @refresh_interval, &method(:refresh_watchers))
205
205
  end
206
206
 
207
+ def stop
208
+ @@pos_file_paths.delete(@pos_file)
209
+
210
+ super
211
+ end
212
+
207
213
  def shutdown
208
214
  # during shutdown phase, don't close io. It should be done in close after all threads are stopped. See close.
209
215
  stop_watchers(@tails.keys, immediate: true, remove_watcher: false)
@@ -143,7 +143,7 @@ module Fluent
143
143
  @y.on_parse_complete = @on_message
144
144
  else
145
145
  m = method(:on_read_msgpack)
146
- @u = Fluent::Engine.msgpack_factory.unpacker
146
+ @u = Fluent::MessagePackFactory.msgpack_unpacker
147
147
  end
148
148
 
149
149
  singleton_class.module_eval do
@@ -18,6 +18,7 @@ require 'fluent/output'
18
18
  require 'fluent/config/error'
19
19
  require 'fluent/clock'
20
20
  require 'base64'
21
+ require 'forwardable'
21
22
 
22
23
  require 'fluent/compat/socket_util'
23
24
  require 'fluent/plugin/out_forward/handshake_protocol'
@@ -32,7 +33,7 @@ module Fluent::Plugin
32
33
  class ForwardOutput < Output
33
34
  Fluent::Plugin.register_output('forward', self)
34
35
 
35
- helpers :socket, :server, :timer, :thread, :compat_parameters
36
+ helpers :socket, :server, :timer, :thread, :compat_parameters, :service_discovery
36
37
 
37
38
  LISTEN_PORT = 24224
38
39
 
@@ -224,23 +225,41 @@ module Fluent::Plugin
224
225
  socket_cache: socket_cache,
225
226
  )
226
227
 
227
- @servers.each do |server|
228
- failure = FailureDetector.new(@heartbeat_interval, @hard_timeout, Time.now.to_i.to_f)
229
- name = server.name || "#{server.host}:#{server.port}"
228
+ configs = []
230
229
 
231
- log.info "adding forwarding server '#{name}'", host: server.host, port: server.port, weight: server.weight, plugin_id: plugin_id
232
- if @heartbeat_type == :none
233
- @nodes << NoneHeartbeatNode.new(self, server, failure: failure, connection_manager: @connection_manager, ack_handler: @ack_handler)
234
- else
235
- node = Node.new(self, server, failure: failure, connection_manager: @connection_manager, ack_handler: @ack_handler)
230
+ # rewrite for using server as sd_static
231
+ conf.elements(name: 'server').each do |s|
232
+ s.name = 'service'
233
+ end
234
+
235
+ unless conf.elements(name: 'service').empty?
236
+ # To copy `services` element only
237
+ new_elem = Fluent::Config::Element.new('static_service_discovery', {}, {}, conf.elements(name: 'service'))
238
+ configs << { type: :static, conf: new_elem }
239
+ end
240
+
241
+ conf.elements(name: 'service_discovery').each_with_index do |c, i|
242
+ configs << { type: @service_discovery[i][:@type], conf: c }
243
+ end
244
+
245
+ service_discovery_create_manager(
246
+ :out_forward_service_discovery_watcher,
247
+ configurations: configs,
248
+ load_balancer: LoadBalancer.new(log),
249
+ custom_build_method: method(:build_node),
250
+ )
251
+
252
+ discovery_manager.services.each do |server|
253
+ # it's only for test
254
+ @nodes << server
255
+ unless @heartbeat_type == :none
236
256
  begin
237
- node.validate_host_resolution!
257
+ server.validate_host_resolution!
238
258
  rescue => e
239
259
  raise unless @ignore_network_errors_at_startup
240
260
  log.warn "failed to resolve node name when configured", server: (server.name || server.host), error: e
241
- node.disable!
261
+ server.disable!
242
262
  end
243
- @nodes << node
244
263
  end
245
264
  end
246
265
 
@@ -252,8 +271,8 @@ module Fluent::Plugin
252
271
  end
253
272
  end
254
273
 
255
- if @nodes.empty?
256
- raise Fluent::ConfigError, "forward output plugin requires at least one <server> is required"
274
+ if discovery_manager.services.empty?
275
+ raise Fluent::ConfigError, "forward output plugin requires at least one node is required. Add <server> or <service_discovery>"
257
276
  end
258
277
 
259
278
  if !@keepalive && @keepalive_timeout
@@ -274,12 +293,9 @@ module Fluent::Plugin
274
293
  def start
275
294
  super
276
295
 
277
- @load_balancer = LoadBalancer.new(log)
278
- @load_balancer.rebuild_weight_array(@nodes)
279
-
280
296
  unless @heartbeat_type == :none
281
297
  if @heartbeat_type == :udp
282
- @usock = socket_create_udp(@nodes.first.host, @nodes.first.port, nonblock: true)
298
+ @usock = socket_create_udp(discovery_manager.services.first.host, discovery_manager.services.first.port, nonblock: true)
283
299
  server_create_udp(:out_forward_heartbeat_receiver, 0, socket: @usock, max_bytes: @read_length, &method(:on_udp_heatbeat_response_recv))
284
300
  end
285
301
  timer_execute(:out_forward_heartbeat_request, @heartbeat_interval, &method(:on_heartbeat_timer))
@@ -297,7 +313,7 @@ module Fluent::Plugin
297
313
  end
298
314
 
299
315
  if @verify_connection_at_startup
300
- @nodes.each do |node|
316
+ discovery_manager.services.each do |node|
301
317
  begin
302
318
  node.verify_connection
303
319
  rescue StandardError => e
@@ -333,7 +349,7 @@ module Fluent::Plugin
333
349
  return if chunk.empty?
334
350
  tag = chunk.metadata.tag
335
351
 
336
- @load_balancer.select_healthy_node { |node| node.send_data(tag, chunk) }
352
+ discovery_manager.select_service { |node| node.send_data(tag, chunk) }
337
353
  end
338
354
 
339
355
  def try_write(chunk)
@@ -343,7 +359,7 @@ module Fluent::Plugin
343
359
  return
344
360
  end
345
361
  tag = chunk.metadata.tag
346
- @load_balancer.select_healthy_node { |n| n.send_data(tag, chunk) }
362
+ discovery_manager.select_service { |node| node.send_data(tag, chunk) }
347
363
  end
348
364
 
349
365
  def create_transfer_socket(host, port, hostname, &block)
@@ -387,6 +403,23 @@ module Fluent::Plugin
387
403
  end
388
404
  end
389
405
 
406
+ def statistics
407
+ stats = super
408
+ services = discovery_manager.services
409
+ healthy_nodes_count = 0
410
+ registed_nodes_count = services.size
411
+ services.each do |s|
412
+ if s.available?
413
+ healthy_nodes_count += 1
414
+ end
415
+ end
416
+
417
+ stats.merge(
418
+ 'healthy_nodes_count' => healthy_nodes_count,
419
+ 'registered_nodes_count' => registed_nodes_count,
420
+ )
421
+ end
422
+
390
423
  # MessagePack FixArray length is 3
391
424
  FORWARD_HEADER = [0x93].pack('C').freeze
392
425
  def forward_header
@@ -395,9 +428,21 @@ module Fluent::Plugin
395
428
 
396
429
  private
397
430
 
431
+ def build_node(server)
432
+ name = server.name || "#{server.host}:#{server.port}"
433
+ log.info "adding forwarding server '#{name}'", host: server.host, port: server.port, weight: server.weight, plugin_id: plugin_id
434
+
435
+ failure = FailureDetector.new(@heartbeat_interval, @hard_timeout, Time.now.to_i.to_f)
436
+ if @heartbeat_type == :none
437
+ NoneHeartbeatNode.new(self, server, failure: failure, connection_manager: @connection_manager, ack_handler: @ack_handler)
438
+ else
439
+ Node.new(self, server, failure: failure, connection_manager: @connection_manager, ack_handler: @ack_handler)
440
+ end
441
+ end
442
+
398
443
  def on_heartbeat_timer
399
444
  need_rebuild = false
400
- @nodes.each do |n|
445
+ discovery_manager.services.each do |n|
401
446
  begin
402
447
  log.trace "sending heartbeat", host: n.host, port: n.port, heartbeat_type: @heartbeat_type
403
448
  n.usock = @usock if @usock
@@ -412,19 +457,19 @@ module Fluent::Plugin
412
457
  end
413
458
 
414
459
  if need_rebuild
415
- @load_balancer.rebuild_weight_array(@nodes)
460
+ discovery_manager.rebalance
416
461
  end
417
462
  end
418
463
 
419
464
  def on_udp_heatbeat_response_recv(data, sock)
420
465
  sockaddr = Socket.pack_sockaddr_in(sock.remote_port, sock.remote_host)
421
- if node = @nodes.find { |n| n.sockaddr == sockaddr }
466
+ if node = discovery_manager.services.find { |n| n.sockaddr == sockaddr }
422
467
  # log.trace "heartbeat arrived", name: node.name, host: node.host, port: node.port
423
468
  if node.heartbeat
424
- @load_balancer.rebuild_weight_array(@nodes)
469
+ discovery_manager.rebalance
425
470
  end
426
471
  else
427
- log.warn("Unknown heartbeat response received from #{sock.remote_host}:#{sock.remote_port}")
472
+ log.warn("Unknown heartbeat response received from #{sock.remote_host}:#{sock.remote_port}. It may service out")
428
473
  end
429
474
  end
430
475
 
@@ -463,12 +508,16 @@ module Fluent::Plugin
463
508
  end
464
509
 
465
510
  class Node
511
+ extend Forwardable
512
+ def_delegators :@server, :discovery_id, :host, :port, :name, :weight, :standby, :username, :password, :shared_key
513
+
466
514
  # @param connection_manager [Fluent::Plugin::ForwardOutput::ConnectionManager]
467
515
  # @param ack_handler [Fluent::Plugin::ForwardOutput::AckHandler]
468
516
  def initialize(sender, server, failure:, connection_manager:, ack_handler:)
469
517
  @sender = sender
470
518
  @log = sender.log
471
519
  @compress = sender.compress
520
+ @server = server
472
521
 
473
522
  @name = server.name
474
523
  @host = server.host
@@ -496,7 +545,7 @@ module Fluent::Plugin
496
545
  username: server.username,
497
546
  )
498
547
 
499
- @unpacker = Fluent::Engine.msgpack_unpacker
548
+ @unpacker = Fluent::MessagePackFactory.msgpack_unpacker
500
549
 
501
550
  @resolved_host = nil
502
551
  @resolved_time = 0
@@ -508,7 +557,7 @@ module Fluent::Plugin
508
557
 
509
558
  attr_accessor :usock
510
559
 
511
- attr_reader :name, :host, :port, :weight, :standby, :state
560
+ attr_reader :state
512
561
  attr_reader :sockaddr # used by on_udp_heatbeat_response_recv
513
562
  attr_reader :failure # for test
514
563
 
@@ -34,7 +34,7 @@ module Fluent::Plugin
34
34
  @timeout = timeout
35
35
  @log = log
36
36
  @read_length = read_length
37
- @unpacker = Fluent::Engine.msgpack_unpacker
37
+ @unpacker = Fluent::MessagePackFactory.msgpack_unpacker
38
38
  end
39
39
 
40
40
  def collect_response(select_interval)
@@ -36,8 +36,8 @@ module Fluent::Plugin
36
36
  wlen = @weight_array.size
37
37
  wlen.times do
38
38
  node = @mutex.synchronize do
39
- r = @rr
40
- @rr = (@rr + 1) % @weight_array.size
39
+ r = @rr % @weight_array.size
40
+ @rr = (r + 1) % @weight_array.size
41
41
  @weight_array[r]
42
42
  end
43
43
  next unless node.available?
@@ -106,6 +106,9 @@ module Fluent::Plugin
106
106
  @weight_array = weight_array
107
107
  end
108
108
  end
109
+
110
+ alias select_service select_healthy_node
111
+ alias rebalance rebuild_weight_array
109
112
  end
110
113
  end
111
114
  end
@@ -68,7 +68,7 @@ module Fluent
68
68
  chain = NullOutputChain.instance
69
69
  chunk.open {|io|
70
70
  # TODO use MessagePackIoEventStream
71
- u = Fluent::Engine.msgpack_factory.unpacker(io)
71
+ u = Fluent::MessagePackFactory.msgpack_unpacker(io)
72
72
  begin
73
73
  u.each {|(tag,entries)|
74
74
  es = MultiEventStream.new
@@ -40,7 +40,7 @@ module Fluent
40
40
  helpers_internal :thread, :retry_state
41
41
 
42
42
  CHUNK_KEY_PATTERN = /^[-_.@a-zA-Z0-9]+$/
43
- CHUNK_KEY_PLACEHOLDER_PATTERN = /\$\{[-_.@$a-zA-Z0-9]+\}/
43
+ CHUNK_KEY_PLACEHOLDER_PATTERN = /\$\{([-_.@$a-zA-Z0-9]+)\}/
44
44
  CHUNK_TAG_PLACEHOLDER_PATTERN = /\$\{(tag(?:\[-?\d+\])?)\}/
45
45
  CHUNK_ID_PLACEHOLDER_PATTERN = /\$\{chunk_id\}/
46
46
 
@@ -707,7 +707,7 @@ module Fluent
707
707
  end
708
708
 
709
709
  def get_placeholders_keys(str)
710
- str.scan(CHUNK_KEY_PLACEHOLDER_PATTERN).map{|ph| ph[2..-2]}.reject{|s| (s == "tag") || (s == 'chunk_id') }.sort
710
+ str.scan(CHUNK_KEY_PLACEHOLDER_PATTERN).map(&:first).reject{|s| (s == "tag") || (s == 'chunk_id') }.sort
711
711
  end
712
712
 
713
713
  # TODO: optimize this code
@@ -759,11 +759,19 @@ module Fluent
759
759
  @chunk_keys.each do |key|
760
760
  hash["${#{key}}"] = metadata.variables[key.to_sym]
761
761
  end
762
- rvalue = rvalue.gsub(CHUNK_KEY_PLACEHOLDER_PATTERN, hash)
762
+
763
+ rvalue = rvalue.gsub(CHUNK_KEY_PLACEHOLDER_PATTERN) do |matched|
764
+ hash.fetch(matched) do
765
+ log.warn "chunk key placeholder '#{matched[2..-2]}' not replaced. template:#{str}"
766
+ ''
767
+ end
768
+ end
763
769
  end
770
+
764
771
  if rvalue =~ CHUNK_KEY_PLACEHOLDER_PATTERN
765
772
  log.warn "chunk key placeholder '#{$1}' not replaced. template:#{str}"
766
773
  end
774
+
767
775
  rvalue.sub(CHUNK_ID_PLACEHOLDER_PATTERN) {
768
776
  if chunk_passed
769
777
  dump_unique_id_hex(chunk.unique_id)
@@ -35,6 +35,7 @@ module Fluent
35
35
  @flag = ServerEngine::BlockingFlag.new
36
36
  @mutex = Mutex.new
37
37
  @timeout = timeout
38
+ @timeout_checker = nil
38
39
  end
39
40
 
40
41
  def start
@@ -0,0 +1,155 @@
1
+ #
2
+ # Fluentd
3
+ #
4
+ # Licensed under the Apache License, Version 2.0 (the "License");
5
+ # you may not use this file except in compliance with the License.
6
+ # You may obtain a copy of the License at
7
+ #
8
+ # http://www.apache.org/licenses/LICENSE-2.0
9
+ #
10
+ # Unless required by applicable law or agreed to in writing, software
11
+ # distributed under the License is distributed on an "AS IS" BASIS,
12
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ # See the License for the specific language governing permissions and
14
+ # limitations under the License.
15
+ #
16
+
17
+ require 'cool.io'
18
+
19
+ require 'fluent/plugin_helper'
20
+ require 'fluent/plugin/service_discovery'
21
+
22
+ module Fluent
23
+ module Plugin
24
+ class FileServiceDiscovery < ServiceDiscovery
25
+ include PluginHelper::Mixin
26
+
27
+ Plugin.register_sd('file', self)
28
+
29
+ DEFAULT_FILE_TYPE = :yaml
30
+ DEFAUT_WEIGHT = 60
31
+ DEFAULT_SD_FILE_PATH = ENV['DEFAULT_SD_FILE_PATH'] || '/etc/fluent/sd.yaml'
32
+
33
+ helpers :event_loop
34
+
35
+ config_param :path, :string, default: DEFAULT_SD_FILE_PATH
36
+ config_param :conf_encoding, :string, default: 'utf-8'
37
+
38
+ def initialize
39
+ super
40
+
41
+ @file_type = nil
42
+ end
43
+
44
+ def configure(conf)
45
+ super
46
+
47
+ unless File.exist?(@path)
48
+ raise Fluent::ConfigError, "sd_file: path=#{@path} not found"
49
+ end
50
+
51
+ @file_type = File.basename(@path).split('.', 2).last.to_sym
52
+ unless %i[yaml yml json].include?(@file_type)
53
+ @file_type = DEFAULT_FILE_TYPE
54
+ end
55
+
56
+ @services = fetch_server_info
57
+ end
58
+
59
+ def start(queue)
60
+ watcher = StatWatcher.new(@path, @log) do |_prev, _cur|
61
+ refresh_file(queue)
62
+ end
63
+ event_loop_attach(watcher)
64
+
65
+ super()
66
+ end
67
+
68
+ private
69
+
70
+ def parser
71
+ @parser ||=
72
+ case @file_type
73
+ when :yaml, :yml
74
+ require 'yaml'
75
+ -> (v) { YAML.safe_load(v).map }
76
+ when :json
77
+ require 'json'
78
+ -> (v) { JSON.parse(v) }
79
+ end
80
+ end
81
+
82
+ def refresh_file(queue)
83
+ s =
84
+ begin
85
+ fetch_server_info
86
+ rescue => e
87
+ @log.error("sd_file: #{e}")
88
+ end
89
+
90
+ if s.nil?
91
+ # if any error occurs, skip this turn
92
+ return
93
+ end
94
+
95
+ diff = []
96
+ join = s - @services
97
+ # Need service_in first to guarantee that server exist at least one all time.
98
+ join.each do |j|
99
+ diff << ServiceDiscovery.service_in_msg(j)
100
+ end
101
+
102
+ drain = @services - s
103
+ drain.each do |d|
104
+ diff << ServiceDiscovery.service_out_msg(d)
105
+ end
106
+
107
+ @services = s
108
+
109
+ diff.each do |a|
110
+ queue.push(a)
111
+ end
112
+ end
113
+
114
+ def fetch_server_info
115
+ config_data =
116
+ begin
117
+ File.open(@path, "r:#{@conf_encoding}:utf-8", &:read)
118
+ rescue => e
119
+ raise Fluent::ConfigError, "sd_file: path=#{@path} couldn't open #{e}"
120
+ end
121
+
122
+ parser.call(config_data).map do |s|
123
+ Service.new(
124
+ :file,
125
+ s.fetch('host'),
126
+ s.fetch('port'),
127
+ s['name'],
128
+ s.fetch('weight', DEFAUT_WEIGHT),
129
+ s['standby'],
130
+ s['username'],
131
+ s['password'],
132
+ s['shared_key'],
133
+ )
134
+ end
135
+ rescue KeyError => e
136
+ raise Fluent::ConfigError, "#{e}. Service must have `host` and `port`"
137
+ end
138
+
139
+ class StatWatcher < Coolio::StatWatcher
140
+ def initialize(path, log, &callback)
141
+ @path = path
142
+ @log = log
143
+ @callback = callback
144
+ super(@path)
145
+ end
146
+
147
+ def on_change(prev_stat, cur_stat)
148
+ @callback.call(prev_stat, cur_stat)
149
+ rescue => e
150
+ @log.error(e)
151
+ end
152
+ end
153
+ end
154
+ end
155
+ end