fluentd 0.10.45 → 0.10.46
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 +4 -4
- data/.travis.yml +3 -1
- data/ChangeLog +13 -0
- data/Rakefile +18 -2
- data/fluentd.gemspec +3 -1
- data/lib/fluent/command/fluentd.rb +5 -0
- data/lib/fluent/config.rb +17 -333
- data/lib/fluent/config/basic_parser.rb +108 -0
- data/lib/fluent/config/configure_proxy.rb +145 -0
- data/lib/fluent/{config_dsl.rb → config/dsl.rb} +5 -1
- data/lib/fluent/config/element.rb +82 -0
- data/lib/fluent/config/error.rb +7 -0
- data/lib/fluent/config/literal_parser.rb +158 -0
- data/lib/fluent/config/parser.rb +96 -0
- data/lib/fluent/config/section.rb +115 -0
- data/lib/fluent/config/types.rb +86 -0
- data/lib/fluent/config/v1_parser.rb +156 -0
- data/lib/fluent/configurable.rb +108 -0
- data/lib/fluent/engine.rb +4 -3
- data/lib/fluent/load.rb +0 -1
- data/lib/fluent/parser.rb +15 -5
- data/lib/fluent/plugin/buf_memory.rb +13 -5
- data/lib/fluent/plugin/in_forward.rb +18 -5
- data/lib/fluent/plugin/in_http.rb +4 -2
- data/lib/fluent/plugin/in_tail.rb +1 -1
- data/lib/fluent/plugin/out_forward.rb +33 -29
- data/lib/fluent/registry.rb +76 -0
- data/lib/fluent/supervisor.rb +2 -1
- data/lib/fluent/test/base.rb +3 -1
- data/lib/fluent/version.rb +1 -1
- data/spec/config/config_parser_spec.rb +176 -0
- data/spec/config/configurable_spec.rb +373 -0
- data/spec/config/configure_proxy_spec.rb +96 -0
- data/spec/config/dsl_spec.rb +239 -0
- data/spec/config/helper.rb +50 -0
- data/spec/config/literal_parser_spec.rb +190 -0
- data/spec/config/section_spec.rb +97 -0
- data/spec/spec_helper.rb +60 -0
- data/test/plugin/{in_exec.rb → test_in_exec.rb} +0 -0
- data/test/plugin/{in_forward.rb → test_in_forward.rb} +5 -0
- data/test/plugin/{in_gc_stat.rb → test_in_gc_stat.rb} +0 -0
- data/test/plugin/{in_http.rb → test_in_http.rb} +0 -0
- data/test/plugin/{in_object_space.rb → test_in_object_space.rb} +0 -0
- data/test/plugin/{in_status.rb → test_in_status.rb} +0 -0
- data/test/plugin/{in_stream.rb → test_in_stream.rb} +0 -0
- data/test/plugin/{in_syslog.rb → test_in_syslog.rb} +0 -0
- data/test/plugin/{in_tail.rb → test_in_tail.rb} +0 -0
- data/test/plugin/{out_copy.rb → test_out_copy.rb} +0 -0
- data/test/plugin/{out_exec.rb → test_out_exec.rb} +0 -0
- data/test/plugin/{out_exec_filter.rb → test_out_exec_filter.rb} +0 -0
- data/test/plugin/{out_file.rb → test_out_file.rb} +0 -0
- data/test/plugin/{out_forward.rb → test_out_forward.rb} +15 -0
- data/test/plugin/{out_roundrobin.rb → test_out_roundrobin.rb} +0 -0
- data/test/plugin/{out_stdout.rb → test_out_stdout.rb} +0 -0
- data/test/plugin/{out_stream.rb → test_out_stream.rb} +0 -0
- data/test/scripts/fluent/plugin/parser_known.rb +3 -0
- data/test/{config.rb → test_config.rb} +1 -0
- data/test/{configdsl.rb → test_configdsl.rb} +1 -1
- data/test/{match.rb → test_match.rb} +0 -0
- data/test/{mixin.rb → test_mixin.rb} +0 -0
- data/test/{output.rb → test_output.rb} +0 -0
- data/test/{parser.rb → test_parser.rb} +22 -5
- metadata +114 -51
@@ -0,0 +1,108 @@
|
|
1
|
+
module Fluent
|
2
|
+
require 'fluent/config/configure_proxy'
|
3
|
+
require 'fluent/config/section'
|
4
|
+
require 'fluent/config/error'
|
5
|
+
require 'fluent/registry'
|
6
|
+
|
7
|
+
module Configurable
|
8
|
+
attr_reader :config
|
9
|
+
|
10
|
+
def self.included(mod)
|
11
|
+
mod.extend(ClassMethods)
|
12
|
+
end
|
13
|
+
|
14
|
+
def initialize
|
15
|
+
# to simulate implicit 'attr_accessor' by config_param / config_section and its value by config_set_default
|
16
|
+
proxy = self.class.merged_configure_proxy
|
17
|
+
proxy.params.keys.each do |name|
|
18
|
+
if proxy.defaults.has_key?(name)
|
19
|
+
instance_variable_set("@#{name}".to_sym, proxy.defaults[name])
|
20
|
+
end
|
21
|
+
end
|
22
|
+
proxy.sections.keys.each do |name|
|
23
|
+
subproxy = proxy.sections[name]
|
24
|
+
if subproxy.multi?
|
25
|
+
instance_variable_set("@#{subproxy.param_name}".to_sym, [])
|
26
|
+
else
|
27
|
+
instance_variable_set("@#{subproxy.param_name}".to_sym, nil)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
def configure(conf)
|
33
|
+
@config = conf
|
34
|
+
|
35
|
+
logger = self.respond_to?(:log) ? log : $log
|
36
|
+
proxy = self.class.merged_configure_proxy
|
37
|
+
|
38
|
+
root = Fluent::Config::SectionGenerator.generate(proxy, conf, logger)
|
39
|
+
@config_root_section = root
|
40
|
+
|
41
|
+
root.instance_eval{ @params.keys }.each do |param_name|
|
42
|
+
varname = "@#{param_name}".to_sym
|
43
|
+
if (! root[param_name].nil?) || instance_variable_get(varname).nil?
|
44
|
+
instance_variable_set(varname, root[param_name])
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
self
|
49
|
+
end
|
50
|
+
|
51
|
+
CONFIG_TYPE_REGISTRY = Registry.new(:config_type, 'fluent/plugin/type_')
|
52
|
+
|
53
|
+
def self.register_type(type, callable = nil, &block)
|
54
|
+
callable ||= block
|
55
|
+
CONFIG_TYPE_REGISTRY.register(type, callable)
|
56
|
+
end
|
57
|
+
|
58
|
+
def self.lookup_type(type)
|
59
|
+
CONFIG_TYPE_REGISTRY.lookup(type)
|
60
|
+
end
|
61
|
+
|
62
|
+
module ClassMethods
|
63
|
+
def configure_proxy_map
|
64
|
+
map = {}
|
65
|
+
self.define_singleton_method(:configure_proxy_map){ map }
|
66
|
+
map
|
67
|
+
end
|
68
|
+
|
69
|
+
def configure_proxy(mod_name)
|
70
|
+
map = configure_proxy_map
|
71
|
+
unless map[mod_name]
|
72
|
+
proxy = Fluent::Config::ConfigureProxy.new(mod_name, required: true, multi: false)
|
73
|
+
map[mod_name] = proxy
|
74
|
+
end
|
75
|
+
map[mod_name]
|
76
|
+
end
|
77
|
+
|
78
|
+
def config_param(name, *args, &block)
|
79
|
+
configure_proxy(self.name).config_param(name, *args, &block)
|
80
|
+
attr_accessor name
|
81
|
+
end
|
82
|
+
|
83
|
+
def config_set_default(name, defval)
|
84
|
+
configure_proxy(self.name).config_set_default(name, defval)
|
85
|
+
end
|
86
|
+
|
87
|
+
def config_section(name, *args, &block)
|
88
|
+
configure_proxy(self.name).config_section(name, *args, &block)
|
89
|
+
attr_accessor configure_proxy(self.name).sections[name].param_name
|
90
|
+
end
|
91
|
+
|
92
|
+
def merged_configure_proxy
|
93
|
+
configurables = ancestors.reverse.select{ |a| a.respond_to?(:configure_proxy) }
|
94
|
+
|
95
|
+
# 'a.object_id.to_s' is to support anonymous class
|
96
|
+
# which created in tests to overwrite original behavior temporally
|
97
|
+
#
|
98
|
+
# p Module.new.name #=> nil
|
99
|
+
# p Class.new.name #=> nil
|
100
|
+
# p AnyGreatClass.dup.name #=> nil
|
101
|
+
configurables.map{ |a| a.configure_proxy(a.name || a.object_id.to_s) }.reduce(:merge)
|
102
|
+
end
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
# load default types
|
107
|
+
require 'fluent/config/types'
|
108
|
+
end
|
data/lib/fluent/engine.rb
CHANGED
@@ -68,11 +68,12 @@ module Fluent
|
|
68
68
|
}
|
69
69
|
end
|
70
70
|
|
71
|
-
def parse_config(io, fname, basepath=Dir.pwd)
|
71
|
+
def parse_config(io, fname, basepath = Dir.pwd, v1_config = false)
|
72
72
|
conf = if fname =~ /\.rb$/
|
73
|
+
require 'fluent/config/dsl'
|
73
74
|
Config::DSL::Parser.parse(io, File.join(basepath, fname))
|
74
75
|
else
|
75
|
-
Config.parse(io, fname, basepath)
|
76
|
+
Config.parse(io, fname, basepath, v1_config)
|
76
77
|
end
|
77
78
|
configure(conf)
|
78
79
|
conf.check_not_fetched {|key,e|
|
@@ -144,7 +145,7 @@ module Fluent
|
|
144
145
|
# this is not thread-safe but inconsistency doesn't
|
145
146
|
# cause serious problems while locking causes.
|
146
147
|
if @match_cache_keys.size >= MATCH_CACHE_SIZE
|
147
|
-
@
|
148
|
+
@match_cache.delete @match_cache_keys.shift
|
148
149
|
end
|
149
150
|
@match_cache[tag] = target
|
150
151
|
@match_cache_keys << tag
|
data/lib/fluent/load.rb
CHANGED
data/lib/fluent/parser.rb
CHANGED
@@ -16,6 +16,8 @@
|
|
16
16
|
# limitations under the License.
|
17
17
|
#
|
18
18
|
module Fluent
|
19
|
+
require 'fluent/registry'
|
20
|
+
|
19
21
|
class TextParser
|
20
22
|
class TimeParser
|
21
23
|
def initialize(time_format)
|
@@ -236,7 +238,11 @@ module Fluent
|
|
236
238
|
|
237
239
|
if @time_key
|
238
240
|
value = record.delete(@time_key)
|
239
|
-
time =
|
241
|
+
time = if value.nil?
|
242
|
+
Engine.now
|
243
|
+
else
|
244
|
+
@mutex.synchronize { @time_parser.parse(value) }
|
245
|
+
end
|
240
246
|
else
|
241
247
|
time = Engine.now
|
242
248
|
end
|
@@ -444,7 +450,8 @@ module Fluent
|
|
444
450
|
end
|
445
451
|
end
|
446
452
|
|
447
|
-
|
453
|
+
TEMPLATE_REGISTRY = Registry.new(:config_type, 'fluent/plugin/parser_')
|
454
|
+
{
|
448
455
|
'apache' => Proc.new { RegexpParser.new(/^(?<host>[^ ]*) [^ ]* (?<user>[^ ]*) \[(?<time>[^\]]*)\] "(?<method>\S+)(?: +(?<path>[^ ]*) +\S*)?" (?<code>[^ ]*) (?<size>[^ ]*)(?: "(?<referer>[^\"]*)" "(?<agent>[^\"]*)")?$/, {'time_format'=>"%d/%b/%Y:%H:%M:%S %z"}) },
|
449
456
|
'apache2' => Proc.new { ApacheParser.new },
|
450
457
|
'syslog' => Proc.new { RegexpParser.new(/^(?<time>[^ ]*\s*[^ ]* [^ ]*) (?<host>[^ ]*) (?<ident>[a-zA-Z0-9_\/\.\-]*)(?:\[(?<pid>[0-9]+)\])?[^\:]*\: *(?<message>.*)$/, {'time_format'=>"%b %d %H:%M:%S"}) },
|
@@ -455,6 +462,8 @@ module Fluent
|
|
455
462
|
'nginx' => Proc.new { RegexpParser.new(/^(?<remote>[^ ]*) (?<host>[^ ]*) (?<user>[^ ]*) \[(?<time>[^\]]*)\] "(?<method>\S+)(?: +(?<path>[^\"]*) +\S*)?" (?<code>[^ ]*) (?<size>[^ ]*)(?: "(?<referer>[^\"]*)" "(?<agent>[^\"]*)")?$/, {'time_format'=>"%d/%b/%Y:%H:%M:%S %z"}) },
|
456
463
|
'none' => Proc.new { NoneParser.new },
|
457
464
|
'multiline' => Proc.new { MultilineParser.new },
|
465
|
+
}.each { |name, factory|
|
466
|
+
TEMPLATE_REGISTRY.register(name, factory)
|
458
467
|
}
|
459
468
|
|
460
469
|
def self.register_template(name, regexp_or_proc, time_format=nil)
|
@@ -465,7 +474,7 @@ module Fluent
|
|
465
474
|
factory = regexp_or_proc
|
466
475
|
end
|
467
476
|
|
468
|
-
|
477
|
+
TEMPLATE_REGISTRY.register(name, factory)
|
469
478
|
end
|
470
479
|
|
471
480
|
def initialize
|
@@ -500,8 +509,9 @@ module Fluent
|
|
500
509
|
|
501
510
|
else
|
502
511
|
# built-in template
|
503
|
-
|
504
|
-
|
512
|
+
begin
|
513
|
+
factory = TEMPLATE_REGISTRY.lookup(format)
|
514
|
+
rescue ConfigError => e # keep same error message
|
505
515
|
raise ConfigError, "Unknown format template '#{format}'"
|
506
516
|
end
|
507
517
|
@parser = factory.call
|
@@ -71,20 +71,28 @@ module Fluent
|
|
71
71
|
super
|
72
72
|
end
|
73
73
|
|
74
|
+
config_param :flush_at_shutdown, :bool, :default => true
|
74
75
|
# Overwrite default BasicBuffer#buffer_queue_limit
|
75
76
|
# to limit total memory usage upto 512MB.
|
76
77
|
config_set_default :buffer_queue_limit, 64
|
77
78
|
|
78
79
|
def configure(conf)
|
79
80
|
super
|
81
|
+
|
82
|
+
unless @flush_at_shutdown
|
83
|
+
$log.warn "When flush_at_shutdown is false, buf_memory discards buffered chunks at shutdown."
|
84
|
+
$log.warn "Please confirm 'flush_at_shutdown false' configuration is correct or not."
|
85
|
+
end
|
80
86
|
end
|
81
87
|
|
82
88
|
def before_shutdown(out)
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
89
|
+
if @flush_at_shutdown
|
90
|
+
synchronize do
|
91
|
+
@map.each_key {|key|
|
92
|
+
push(key)
|
93
|
+
}
|
94
|
+
while pop(out)
|
95
|
+
end
|
88
96
|
end
|
89
97
|
end
|
90
98
|
end
|
@@ -31,6 +31,8 @@ module Fluent
|
|
31
31
|
config_param :backlog, :integer, :default => nil
|
32
32
|
# SO_LINGER 0 to send RST rather than FIN to avoid lots of connections sitting in TIME_WAIT at src
|
33
33
|
config_param :linger_timeout, :integer, :default => 0
|
34
|
+
# This option is for Cool.io's loop wait timeout to avoid loop stuck at shutdown. Almost users don't need to change this value.
|
35
|
+
config_param :blocking_timeout, :time, :default => 0.5
|
34
36
|
|
35
37
|
def configure(conf)
|
36
38
|
super
|
@@ -56,10 +58,12 @@ module Fluent
|
|
56
58
|
@loop.watchers.each {|w| w.detach }
|
57
59
|
@loop.stop
|
58
60
|
@usock.close
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
61
|
+
unless support_blocking_timeout?
|
62
|
+
listen_address = (@bind == '0.0.0.0' ? '127.0.0.1' : @bind)
|
63
|
+
# This line is for connecting listen socket to stop the event loop.
|
64
|
+
# We should use more better approach, e.g. using pipe, fixing cool.io with timeout, etc.
|
65
|
+
TCPSocket.open(listen_address, @port) {|sock| } # FIXME @thread.join blocks without this line
|
66
|
+
end
|
63
67
|
@thread.join
|
64
68
|
@lsock.close
|
65
69
|
end
|
@@ -82,13 +86,22 @@ module Fluent
|
|
82
86
|
#end
|
83
87
|
|
84
88
|
def run
|
85
|
-
|
89
|
+
if support_blocking_timeout?
|
90
|
+
@loop.run(@blocking_timeout)
|
91
|
+
else
|
92
|
+
@loop.run
|
93
|
+
end
|
86
94
|
rescue => e
|
87
95
|
log.error "unexpected error", :error => e, :error_class => e.class
|
88
96
|
log.error_backtrace
|
89
97
|
end
|
90
98
|
|
91
99
|
protected
|
100
|
+
|
101
|
+
def support_blocking_timeout?
|
102
|
+
@loop.method(:run).arity.nonzero?
|
103
|
+
end
|
104
|
+
|
92
105
|
# message Entry {
|
93
106
|
# 1: long time
|
94
107
|
# 2: object record
|
@@ -25,6 +25,7 @@ module Fluent
|
|
25
25
|
|
26
26
|
def initialize
|
27
27
|
require 'webrick/httputils'
|
28
|
+
require 'uri'
|
28
29
|
super
|
29
30
|
end
|
30
31
|
|
@@ -246,7 +247,8 @@ module Fluent
|
|
246
247
|
|
247
248
|
@env['REMOTE_ADDR'] = @remote_addr if @remote_addr
|
248
249
|
|
249
|
-
|
250
|
+
uri = URI.parse(@parser.request_url)
|
251
|
+
params = WEBrick::HTTPUtils.parse_query(uri.query)
|
250
252
|
|
251
253
|
if @content_type =~ /^application\/x-www-form-urlencoded/
|
252
254
|
params.update WEBrick::HTTPUtils.parse_query(@body)
|
@@ -256,7 +258,7 @@ module Fluent
|
|
256
258
|
elsif @content_type =~ /^application\/json/
|
257
259
|
params['json'] = @body
|
258
260
|
end
|
259
|
-
path_info =
|
261
|
+
path_info = uri.path
|
260
262
|
|
261
263
|
params.merge!(@env)
|
262
264
|
@env.clear
|
@@ -43,6 +43,7 @@ module Fluent
|
|
43
43
|
config_param :hard_timeout, :time, :default => 60
|
44
44
|
config_param :expire_dns_cache, :time, :default => nil # 0 means disable cache
|
45
45
|
config_param :phi_threshold, :integer, :default => 16
|
46
|
+
config_param :phi_failure_detector, :bool, :default => true
|
46
47
|
attr_reader :nodes
|
47
48
|
|
48
49
|
# backward compatibility
|
@@ -82,8 +83,10 @@ module Fluent
|
|
82
83
|
end
|
83
84
|
|
84
85
|
failure = FailureDetector.new(@heartbeat_interval, @hard_timeout, Time.now.to_i.to_f)
|
85
|
-
|
86
|
-
|
86
|
+
|
87
|
+
node_conf = NodeConfig.new(name, host, port, weight, standby, failure,
|
88
|
+
@phi_threshold, recover_sample_size, @expire_dns_cache, @phi_failure_detector)
|
89
|
+
@nodes << Node.new(log, node_conf)
|
87
90
|
log.info "adding forwarding server '#{name}'", :host=>host, :port=>port, :weight=>weight
|
88
91
|
}
|
89
92
|
end
|
@@ -324,40 +327,40 @@ module Fluent
|
|
324
327
|
end
|
325
328
|
end
|
326
329
|
|
330
|
+
NodeConfig = Struct.new("NodeConfig", :name, :host, :port, :weight, :standby, :failure,
|
331
|
+
:phi_threshold, :recover_sample_size, :expire_dns_cache, :phi_failure_detector)
|
332
|
+
|
327
333
|
class Node
|
328
|
-
def initialize(
|
329
|
-
phi_threshold, recover_sample_size, expire_dns_cache, log)
|
330
|
-
@name = name
|
331
|
-
@host = host
|
332
|
-
@port = port
|
333
|
-
@weight = weight
|
334
|
-
@standby = standby
|
335
|
-
@failure = failure
|
336
|
-
@phi_threshold = phi_threshold
|
337
|
-
@recover_sample_size = recover_sample_size
|
338
|
-
@expire_dns_cache = expire_dns_cache
|
339
|
-
@available = true
|
334
|
+
def initialize(log, conf)
|
340
335
|
@log = log
|
336
|
+
@conf = conf
|
337
|
+
@name = @conf.name
|
338
|
+
@host = @conf.host
|
339
|
+
@port = @conf.port
|
340
|
+
@weight = @conf.weight
|
341
|
+
@failure = @conf.failure
|
342
|
+
@available = true
|
341
343
|
|
342
344
|
@resolved_host = nil
|
343
345
|
@resolved_time = 0
|
344
346
|
resolved_host # check dns
|
345
347
|
end
|
346
348
|
|
349
|
+
attr_reader :conf
|
347
350
|
attr_reader :name, :host, :port, :weight
|
348
|
-
attr_writer :weight, :standby, :available
|
349
351
|
attr_reader :sockaddr # used by on_heartbeat
|
352
|
+
attr_reader :failure, :available # for test
|
350
353
|
|
351
354
|
def available?
|
352
355
|
@available
|
353
356
|
end
|
354
357
|
|
355
358
|
def standby?
|
356
|
-
@standby
|
359
|
+
@conf.standby
|
357
360
|
end
|
358
361
|
|
359
362
|
def resolved_host
|
360
|
-
case @expire_dns_cache
|
363
|
+
case @conf.expire_dns_cache
|
361
364
|
when 0
|
362
365
|
# cache is disabled
|
363
366
|
return resolve_dns!
|
@@ -369,7 +372,7 @@ module Fluent
|
|
369
372
|
else
|
370
373
|
now = Engine.now
|
371
374
|
rh = @resolved_host
|
372
|
-
if !rh || now - @resolved_time >= @expire_dns_cache
|
375
|
+
if !rh || now - @resolved_time >= @conf.expire_dns_cache
|
373
376
|
rh = @resolved_host = resolve_dns!
|
374
377
|
@resolved_time = now
|
375
378
|
end
|
@@ -401,24 +404,25 @@ module Fluent
|
|
401
404
|
return true
|
402
405
|
end
|
403
406
|
|
404
|
-
|
405
|
-
|
406
|
-
|
407
|
-
|
408
|
-
|
409
|
-
|
410
|
-
|
411
|
-
|
412
|
-
|
413
|
-
|
407
|
+
if @conf.phi_failure_detector
|
408
|
+
phi = @failure.phi(now)
|
409
|
+
#$log.trace "phi '#{@name}'", :host=>@host, :port=>@port, :phi=>phi
|
410
|
+
if phi > @conf.phi_threshold
|
411
|
+
@log.warn "detached forwarding server '#{@name}'", :host=>@host, :port=>@port, :phi=>phi
|
412
|
+
@available = false
|
413
|
+
@resolved_host = nil # expire cached host
|
414
|
+
@failure.clear
|
415
|
+
return true
|
416
|
+
end
|
414
417
|
end
|
418
|
+
return false
|
415
419
|
end
|
416
420
|
|
417
421
|
def heartbeat(detect=true)
|
418
422
|
now = Time.now.to_f
|
419
423
|
@failure.add(now)
|
420
424
|
#@log.trace "heartbeat from '#{@name}'", :host=>@host, :port=>@port, :available=>@available, :sample_size=>@failure.sample_size
|
421
|
-
if detect && !@available && @failure.sample_size > @recover_sample_size
|
425
|
+
if detect && !@available && @failure.sample_size > @conf.recover_sample_size
|
422
426
|
@available = true
|
423
427
|
@log.warn "recovered forwarding server '#{@name}'", :host=>@host, :port=>@port
|
424
428
|
return true
|
@@ -0,0 +1,76 @@
|
|
1
|
+
module Fluent
|
2
|
+
require 'fluent/config/error'
|
3
|
+
|
4
|
+
class Registry
|
5
|
+
def initialize(kind, search_prefix)
|
6
|
+
@kind = kind
|
7
|
+
@search_prefix = search_prefix
|
8
|
+
@map = {}
|
9
|
+
end
|
10
|
+
|
11
|
+
attr_reader :kind
|
12
|
+
|
13
|
+
def register(type, value)
|
14
|
+
type = type.to_sym
|
15
|
+
@map[type] = value
|
16
|
+
end
|
17
|
+
|
18
|
+
def lookup(type)
|
19
|
+
type = type.to_sym
|
20
|
+
if value = @map[type]
|
21
|
+
return value
|
22
|
+
end
|
23
|
+
search(type)
|
24
|
+
if value = @map[type]
|
25
|
+
return value
|
26
|
+
end
|
27
|
+
raise ConfigError, "Unknown #{@kind} plugin '#{type}'. Run 'gem search -rd fluentd-plugin' to find plugins" # TODO error class
|
28
|
+
end
|
29
|
+
|
30
|
+
def search(type)
|
31
|
+
path = "#{@search_prefix}#{type}"
|
32
|
+
|
33
|
+
# prefer LOAD_PATH than gems
|
34
|
+
files = $LOAD_PATH.map { |lp|
|
35
|
+
lpath = File.expand_path(File.join(lp, "#{path}.rb"))
|
36
|
+
File.exist?(lpath) ? lpath : nil
|
37
|
+
}.compact
|
38
|
+
unless files.empty?
|
39
|
+
# prefer newer version
|
40
|
+
require files.sort.last
|
41
|
+
return
|
42
|
+
end
|
43
|
+
|
44
|
+
# search gems
|
45
|
+
if defined?(::Gem::Specification) && ::Gem::Specification.respond_to?(:find_all)
|
46
|
+
specs = Gem::Specification.find_all { |spec|
|
47
|
+
spec.contains_requirable_file? path
|
48
|
+
}
|
49
|
+
|
50
|
+
# prefer newer version
|
51
|
+
specs = specs.sort_by { |spec| spec.version }
|
52
|
+
if spec = specs.last
|
53
|
+
spec.require_paths.each { |lib|
|
54
|
+
file = "#{spec.full_gem_path}/#{lib}/#{path}"
|
55
|
+
require file
|
56
|
+
}
|
57
|
+
end
|
58
|
+
|
59
|
+
# backward compatibility for rubygems < 1.8
|
60
|
+
elsif defined?(::Gem) && ::Gem.respond_to?(:searcher)
|
61
|
+
#files = Gem.find_files(path).sort
|
62
|
+
specs = Gem.searcher.find_all(path)
|
63
|
+
|
64
|
+
# prefer newer version
|
65
|
+
specs = specs.sort_by { |spec| spec.version }
|
66
|
+
specs.reverse_each { |spec|
|
67
|
+
files = Gem.searcher.matching_files(spec, path)
|
68
|
+
unless files.empty?
|
69
|
+
require files.first
|
70
|
+
break
|
71
|
+
end
|
72
|
+
}
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|