fluentd 0.14.13 → 0.14.14.pre.1
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/README.md +25 -0
- data/lib/fluent/config/types.rb +2 -2
- data/lib/fluent/env.rb +3 -3
- data/lib/fluent/log.rb +3 -0
- data/lib/fluent/plugin/filter_parser.rb +17 -6
- data/lib/fluent/plugin/in_http.rb +2 -0
- data/lib/fluent/plugin/in_monitor_agent.rb +6 -1
- data/lib/fluent/plugin/in_tail.rb +3 -1
- data/lib/fluent/plugin/output.rb +7 -7
- data/lib/fluent/plugin/parser_apache2.rb +1 -1
- data/lib/fluent/plugin/parser_syslog.rb +40 -1
- data/lib/fluent/plugin_helper/storage.rb +1 -1
- data/lib/fluent/root_agent.rb +7 -3
- data/lib/fluent/supervisor.rb +10 -0
- data/lib/fluent/system_config.rb +7 -0
- data/lib/fluent/version.rb +1 -1
- data/test/config/test_system_config.rb +36 -6
- data/test/config/test_types.rb +19 -0
- data/test/plugin/test_filter_parser.rb +35 -0
- data/test/plugin/test_in_http.rb +22 -0
- data/test/plugin/test_in_monitor_agent.rb +36 -9
- data/test/plugin/test_in_tail.rb +15 -0
- data/test/plugin/test_output.rb +15 -15
- data/test/plugin/test_parser_apache2.rb +8 -0
- data/test/plugin/test_parser_syslog.rb +176 -0
- data/test/plugin_helper/test_storage.rb +9 -0
- data/test/test_log.rb +4 -0
- data/test/test_plugin_classes.rb +35 -0
- data/test/test_root_agent.rb +41 -14
- metadata +4 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: e1ebffe0eb082e2cf131d53ad1481cff9ef4648a
|
4
|
+
data.tar.gz: 85603a42d5d04d6b72d7d7a7829c9139d7a3bd67
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 6ccaff3d1a6c1d0cf31760cac64e907951d319d624a2033870d070658e62368b137ed6e8aecde67909ede472b25b862c70f4845710b62c5aa30ec7c3560331cb
|
7
|
+
data.tar.gz: 722a6b02123610def0f9dc3e2d7f806148e88dca697d3c75ccf70fc36ad5db06ff391b702442952717f42e48e92f8ecc1bdf9f8be958fdf74def5d1ca2f324ff
|
data/README.md
CHANGED
@@ -26,6 +26,31 @@ Mobile/Web Application Logging | Fluentd can function as middleware to enable as
|
|
26
26
|
$ fluentd -c conf/fluent.conf &
|
27
27
|
$ echo '{"json":"message"}' | fluent-cat debug.test
|
28
28
|
|
29
|
+
## Development
|
30
|
+
|
31
|
+
### Prerequisites
|
32
|
+
|
33
|
+
- Ruby 2.1 or later
|
34
|
+
- git
|
35
|
+
|
36
|
+
`git` should be in `PATH`. On Windows, you can use `Github for Windows` and `GitShell` for easy setup.
|
37
|
+
|
38
|
+
### Install dependent gems
|
39
|
+
|
40
|
+
Use bundler:
|
41
|
+
|
42
|
+
$ gem install bundler
|
43
|
+
$ bundle install --path vendor/bundle
|
44
|
+
|
45
|
+
### Run test
|
46
|
+
|
47
|
+
$ bundle exec rake test
|
48
|
+
|
49
|
+
You can run specified test via `TEST` environment variable:
|
50
|
+
|
51
|
+
$ bundle exec rake test TEST=test/test_specified_path.rb
|
52
|
+
$ bundle exec rake test TEST=test/test_*.rb
|
53
|
+
|
29
54
|
## Fluentd UI: Admin GUI
|
30
55
|
|
31
56
|
[Fluentd UI](https://github.com/fluent/fluentd-ui) is a graphical user interface to start/stop/configure Fluentd.
|
data/lib/fluent/config/types.rb
CHANGED
@@ -64,7 +64,7 @@ module Fluent
|
|
64
64
|
end
|
65
65
|
end
|
66
66
|
|
67
|
-
STRING_TYPE = Proc.new { |val, opts| val.to_s.
|
67
|
+
STRING_TYPE = Proc.new { |val, opts| val.to_s.force_encoding(Encoding::UTF_8) }
|
68
68
|
ENUM_TYPE = Proc.new { |val, opts|
|
69
69
|
s = val.to_sym
|
70
70
|
list = opts[:list]
|
@@ -85,7 +85,7 @@ module Fluent
|
|
85
85
|
value
|
86
86
|
else
|
87
87
|
case type
|
88
|
-
when :string then value.to_s.
|
88
|
+
when :string then value.to_s.force_encoding(Encoding::UTF_8)
|
89
89
|
when :integer then value.to_i
|
90
90
|
when :float then value.to_f
|
91
91
|
when :size then Config.size_value(value)
|
data/lib/fluent/env.rb
CHANGED
@@ -14,15 +14,15 @@
|
|
14
14
|
# limitations under the License.
|
15
15
|
#
|
16
16
|
|
17
|
+
require 'serverengine/utils'
|
18
|
+
|
17
19
|
module Fluent
|
18
20
|
DEFAULT_CONFIG_PATH = ENV['FLUENT_CONF'] || '/etc/fluent/fluent.conf'
|
19
21
|
DEFAULT_PLUGIN_DIR = ENV['FLUENT_PLUGIN'] || '/etc/fluent/plugin'
|
20
22
|
DEFAULT_SOCKET_PATH = ENV['FLUENT_SOCKET'] || '/var/run/fluent/fluent.sock'
|
21
23
|
DEFAULT_OJ_OPTIONS = {bigdecimal_load: :float, mode: :compat, use_to_json: true}
|
22
|
-
IS_WINDOWS = /mswin|mingw/ === RUBY_PLATFORM
|
23
|
-
private_constant :IS_WINDOWS
|
24
24
|
|
25
25
|
def self.windows?
|
26
|
-
|
26
|
+
ServerEngine.windows?
|
27
27
|
end
|
28
28
|
end
|
data/lib/fluent/log.rb
CHANGED
@@ -32,6 +32,7 @@ module Fluent::Plugin
|
|
32
32
|
config_param :inject_key_prefix, :string, default: nil
|
33
33
|
config_param :replace_invalid_sequence, :bool, default: false
|
34
34
|
config_param :hash_value_field, :string, default: nil
|
35
|
+
config_param :emit_invalid_record_to_error, :bool, default: true
|
35
36
|
|
36
37
|
attr_reader :parser
|
37
38
|
|
@@ -49,7 +50,9 @@ module Fluent::Plugin
|
|
49
50
|
def filter_with_time(tag, time, record)
|
50
51
|
raw_value = record[@key_name]
|
51
52
|
if raw_value.nil?
|
52
|
-
|
53
|
+
if @emit_invalid_record_to_error
|
54
|
+
router.emit_error_event(tag, time, record, ArgumentError.new("#{@key_name} does not exist"))
|
55
|
+
end
|
53
56
|
if @reserve_data
|
54
57
|
return time, handle_parsed(tag, record, time, {})
|
55
58
|
else
|
@@ -67,7 +70,9 @@ module Fluent::Plugin
|
|
67
70
|
r = handle_parsed(tag, record, t, values)
|
68
71
|
return t, r
|
69
72
|
else
|
70
|
-
|
73
|
+
if @emit_invalid_record_to_error
|
74
|
+
router.emit_error_event(tag, time, record, Fluent::Plugin::Parser::ParserError.new("pattern not match with data '#{raw_value}'"))
|
75
|
+
end
|
71
76
|
if @reserve_data
|
72
77
|
t = time
|
73
78
|
r = handle_parsed(tag, record, time, {})
|
@@ -78,8 +83,11 @@ module Fluent::Plugin
|
|
78
83
|
end
|
79
84
|
end
|
80
85
|
rescue Fluent::Plugin::Parser::ParserError => e
|
81
|
-
|
82
|
-
|
86
|
+
if @emit_invalid_record_to_error
|
87
|
+
raise e
|
88
|
+
else
|
89
|
+
return FAILED_RESULT
|
90
|
+
end
|
83
91
|
rescue ArgumentError => e
|
84
92
|
raise unless @replace_invalid_sequence
|
85
93
|
raise unless e.message.index("invalid byte sequence in") == 0
|
@@ -87,8 +95,11 @@ module Fluent::Plugin
|
|
87
95
|
raw_value = raw_value.scrub(REPLACE_CHAR)
|
88
96
|
retry
|
89
97
|
rescue => e
|
90
|
-
|
91
|
-
|
98
|
+
if @emit_invalid_record_to_error
|
99
|
+
raise Fluent::Plugin::Parser::ParserError, "parse failed #{e.message}"
|
100
|
+
else
|
101
|
+
return FAILED_RESULT
|
102
|
+
end
|
92
103
|
end
|
93
104
|
end
|
94
105
|
|
@@ -373,6 +373,8 @@ module Fluent::Plugin
|
|
373
373
|
params.update WEBrick::HTTPUtils.parse_form_data(@body, boundary)
|
374
374
|
elsif @content_type =~ /^application\/json/
|
375
375
|
params['json'] = @body
|
376
|
+
elsif @content_type =~ /^application\/msgpack/
|
377
|
+
params['msgpack'] = @body
|
376
378
|
end
|
377
379
|
path_info = uri.path
|
378
380
|
|
@@ -226,6 +226,11 @@ module Fluent::Plugin
|
|
226
226
|
@first_warn = false
|
227
227
|
end
|
228
228
|
|
229
|
+
def configure(conf)
|
230
|
+
super
|
231
|
+
@port += fluentd_worker_id
|
232
|
+
end
|
233
|
+
|
229
234
|
def multi_workers_ready?
|
230
235
|
true
|
231
236
|
end
|
@@ -233,7 +238,7 @@ module Fluent::Plugin
|
|
233
238
|
def start
|
234
239
|
super
|
235
240
|
|
236
|
-
log.debug "listening monitoring http server on http://#{@bind}:#{@port}/api/plugins"
|
241
|
+
log.debug "listening monitoring http server on http://#{@bind}:#{@port}/api/plugins for worker#{fluentd_worker_id}"
|
237
242
|
@srv = WEBrick::HTTPServer.new({
|
238
243
|
BindAddress: @bind,
|
239
244
|
Port: @port,
|
@@ -79,6 +79,8 @@ module Fluent::Plugin
|
|
79
79
|
config_param :open_on_every_update, :bool, default: false
|
80
80
|
desc 'Limit the watching files that the modification time is within the specified time range (when use \'*\' in path).'
|
81
81
|
config_param :limit_recently_modified, :time, default: nil
|
82
|
+
desc 'Enable the option to skip the refresh of watching list on startup.'
|
83
|
+
config_param :skip_refresh_on_startup, :bool, default: false
|
82
84
|
|
83
85
|
attr_reader :paths
|
84
86
|
|
@@ -160,7 +162,7 @@ module Fluent::Plugin
|
|
160
162
|
@pf = PositionFile.parse(@pf_file)
|
161
163
|
end
|
162
164
|
|
163
|
-
refresh_watchers
|
165
|
+
refresh_watchers unless @skip_refresh_on_startup
|
164
166
|
timer_execute(:in_tail_refresh_watchers, @refresh_interval, &method(:refresh_watchers))
|
165
167
|
end
|
166
168
|
|
data/lib/fluent/plugin/output.rb
CHANGED
@@ -589,13 +589,13 @@ module Fluent
|
|
589
589
|
example = @argument[:example]
|
590
590
|
timekey = @argument[:timekey]
|
591
591
|
if !sec && timekey
|
592
|
-
raise Fluent::ConfigError, "Parameter '#{name}' doesn't have timestamp placeholders for timekey #{timekey.to_i}"
|
592
|
+
raise Fluent::ConfigError, "Parameter '#{name}: #{string}' doesn't have timestamp placeholders for timekey #{timekey.to_i}"
|
593
593
|
end
|
594
594
|
if sec && !timekey
|
595
|
-
raise Fluent::ConfigError, "Parameter '#{name}' has timestamp placeholders, but chunk key 'time' is not configured"
|
595
|
+
raise Fluent::ConfigError, "Parameter '#{name}: #{string}' has timestamp placeholders, but chunk key 'time' is not configured"
|
596
596
|
end
|
597
597
|
if sec && timekey && timekey < sec
|
598
|
-
raise Fluent::ConfigError, "Parameter '#{
|
598
|
+
raise Fluent::ConfigError, "Parameter '#{name}: #{string}' doesn't have timestamp placeholder for #{title}('#{example}') for timekey #{timekey.to_i}"
|
599
599
|
end
|
600
600
|
end
|
601
601
|
|
@@ -603,10 +603,10 @@ module Fluent
|
|
603
603
|
parts = @argument[:parts]
|
604
604
|
tagkey = @argument[:tagkey]
|
605
605
|
if tagkey && parts.empty?
|
606
|
-
raise Fluent::ConfigError, "Parameter '#{
|
606
|
+
raise Fluent::ConfigError, "Parameter '#{name}: #{string}' doesn't have tag placeholder"
|
607
607
|
end
|
608
608
|
if !tagkey && !parts.empty?
|
609
|
-
raise Fluent::ConfigError, "Parameter '#{
|
609
|
+
raise Fluent::ConfigError, "Parameter '#{name}: #{string}' has tag placeholders, but chunk key 'tag' is not configured"
|
610
610
|
end
|
611
611
|
end
|
612
612
|
|
@@ -615,11 +615,11 @@ module Fluent
|
|
615
615
|
chunk_keys = @argument[:chunkkeys]
|
616
616
|
if (chunk_keys - keys).size > 0
|
617
617
|
not_specified = (chunk_keys - keys).sort
|
618
|
-
raise Fluent::ConfigError, "Parameter '#{
|
618
|
+
raise Fluent::ConfigError, "Parameter '#{name}: #{string}' doesn't have enough placeholders for keys #{not_specified.join(',')}"
|
619
619
|
end
|
620
620
|
if (keys - chunk_keys).size > 0
|
621
621
|
not_satisfied = (keys - chunk_keys).sort
|
622
|
-
raise Fluent::ConfigError, "Parameter '#{
|
622
|
+
raise Fluent::ConfigError, "Parameter '#{name}: #{string}' has placeholders, but chunk keys doesn't have keys #{not_satisfied.join(',')}"
|
623
623
|
end
|
624
624
|
end
|
625
625
|
end
|
@@ -21,7 +21,7 @@ module Fluent
|
|
21
21
|
class Apache2Parser < Parser
|
22
22
|
Plugin.register_parser('apache2', self)
|
23
23
|
|
24
|
-
REGEXP = /^(?<host>[^ ]*) [^ ]* (?<user>[^ ]*) \[(?<time>[^\]]*)\] "(?<method>\S+)(?: +(?<path>[^\"]*?)(?: +\S*)?)?" (?<code>[^ ]*) (?<size>[^ ]*)(?: "(?<referer>[^\"]*)" "(?<agent>[^\"]*)")?$/
|
24
|
+
REGEXP = /^(?<host>[^ ]*) [^ ]* (?<user>[^ ]*) \[(?<time>[^\]]*)\] "(?<method>\S+)(?: +(?<path>(?:[^\"]|\\.)*?)(?: +\S*)?)?" (?<code>[^ ]*) (?<size>[^ ]*)(?: "(?<referer>(?:[^\"]|\\.)*)" "(?<agent>(?:[^\"]|\\.)*)")?$/
|
25
25
|
TIME_FORMAT = "%d/%b/%Y:%H:%M:%S %z"
|
26
26
|
|
27
27
|
def initialize
|
@@ -27,9 +27,13 @@ module Fluent
|
|
27
27
|
REGEXP = /^(?<time>[^ ]*\s*[^ ]* [^ ]*) (?<host>[^ ]*) (?<ident>[a-zA-Z0-9_\/\.\-]*)(?:\[(?<pid>[0-9]+)\])?(?:[^\:]*\:)? *(?<message>.*)$/
|
28
28
|
# From in_syslog default pattern
|
29
29
|
REGEXP_WITH_PRI = /^\<(?<pri>[0-9]+)\>(?<time>[^ ]* {1,2}[^ ]* [^ ]*) (?<host>[^ ]*) (?<ident>[a-zA-Z0-9_\/\.\-]*)(?:\[(?<pid>[0-9]+)\])?(?:[^\:]*\:)? *(?<message>.*)$/
|
30
|
+
REGEXP_RFC5424 = /\A^\<(?<pri>[0-9]{1,3})\>[1-9]\d{0,2} (?<time>[^ ]+) (?<host>[^ ]+) (?<ident>[^ ]+) (?<pid>[-0-9]+) (?<msgid>[^ ]+) (?<extradata>(\[(.*)\]|[^ ])) (?<message>.+)$\z/
|
31
|
+
REGEXP_DETECT_RFC5424 = /^\<.*\>[1-9]\d{0,2}/
|
30
32
|
|
31
33
|
config_set_default :time_format, "%b %d %H:%M:%S"
|
32
34
|
config_param :with_priority, :bool, default: false
|
35
|
+
config_param :message_format, :enum, list: [:rfc3164, :rfc5424, :auto], default: :rfc3164
|
36
|
+
config_param :rfc5424_time_format, :string, default: "%Y-%m-%dT%H:%M:%S.%L%z"
|
33
37
|
|
34
38
|
def initialize
|
35
39
|
super
|
@@ -39,7 +43,27 @@ module Fluent
|
|
39
43
|
def configure(conf)
|
40
44
|
super
|
41
45
|
|
42
|
-
@
|
46
|
+
@time_parser_rfc3164 = @time_parser_rfc5424 = nil
|
47
|
+
@regexp = case @message_format
|
48
|
+
when :rfc3164
|
49
|
+
class << self
|
50
|
+
alias_method :parse, :parse_plain
|
51
|
+
end
|
52
|
+
@with_priority ? REGEXP_WITH_PRI : REGEXP
|
53
|
+
when :rfc5424
|
54
|
+
class << self
|
55
|
+
alias_method :parse, :parse_plain
|
56
|
+
end
|
57
|
+
@time_format = @rfc5424_time_format unless conf.has_key?('time_format')
|
58
|
+
REGEXP_RFC5424
|
59
|
+
when :auto
|
60
|
+
class << self
|
61
|
+
alias_method :parse, :parse_auto
|
62
|
+
end
|
63
|
+
@time_parser_rfc3164 = time_parser_create(format: @time_format)
|
64
|
+
@time_parser_rfc5424 = time_parser_create(format: @rfc5424_time_format)
|
65
|
+
nil
|
66
|
+
end
|
43
67
|
@time_parser = time_parser_create
|
44
68
|
end
|
45
69
|
|
@@ -48,6 +72,21 @@ module Fluent
|
|
48
72
|
end
|
49
73
|
|
50
74
|
def parse(text)
|
75
|
+
# This is overwritten in configure
|
76
|
+
end
|
77
|
+
|
78
|
+
def parse_auto(text, &block)
|
79
|
+
if REGEXP_DETECT_RFC5424.match(text)
|
80
|
+
@regexp = REGEXP_RFC5424
|
81
|
+
@time_parser = @time_parser_rfc5424
|
82
|
+
else
|
83
|
+
@regexp = @with_priority ? REGEXP_WITH_PRI : REGEXP
|
84
|
+
@time_parser = @time_parser_rfc3164
|
85
|
+
end
|
86
|
+
parse_plain(text, &block)
|
87
|
+
end
|
88
|
+
|
89
|
+
def parse_plain(text, &block)
|
51
90
|
m = @regexp.match(text)
|
52
91
|
unless m
|
53
92
|
yield nil, nil
|
@@ -30,7 +30,7 @@ module Fluent
|
|
30
30
|
StorageState = Struct.new(:storage, :running)
|
31
31
|
|
32
32
|
def storage_create(usage: '', type: nil, conf: nil, default_type: nil)
|
33
|
-
if conf && !conf.arg.empty?
|
33
|
+
if conf && conf.respond_to?(:arg) && !conf.arg.empty?
|
34
34
|
usage = conf.arg
|
35
35
|
end
|
36
36
|
if !usage.empty? && usage !~ /^[a-zA-Z][-_.a-zA-Z0-9]*$/
|
data/lib/fluent/root_agent.rb
CHANGED
@@ -136,8 +136,12 @@ module Fluent
|
|
136
136
|
def start
|
137
137
|
lifecycle(desc: true) do |i| # instance
|
138
138
|
i.start unless i.started?
|
139
|
-
|
140
|
-
|
139
|
+
# Input#start sometimes emits lots of evetns with in_tail/`read_from_head true` case
|
140
|
+
# and it causes deadlock for small buffer/queue output. To avoid such problem,
|
141
|
+
# buffer related output threads should be run before `Input#start`.
|
142
|
+
# This is why after_start should be called immediately after start call.
|
143
|
+
# This depends on `desc: true` because calling plugin order of `desc: true` is
|
144
|
+
# Output, Filter, Label, Output with Router, then Input.
|
141
145
|
i.after_start unless i.after_started?
|
142
146
|
end
|
143
147
|
end
|
@@ -173,7 +177,7 @@ module Fluent
|
|
173
177
|
log.debug "calling #{method} on #{kind} plugin", type: Plugin.lookup_type_from_class(instance.class), plugin_id: instance.plugin_id
|
174
178
|
instance.send(method) unless instance.send(checker)
|
175
179
|
rescue Exception => e
|
176
|
-
log.warn "unexpected error while calling #{method} on #{kind} plugin",
|
180
|
+
log.warn "unexpected error while calling #{method} on #{kind} plugin", plugin: instance.class, plugin_id: instance.plugin_id, error: e
|
177
181
|
log.warn_backtrace
|
178
182
|
end
|
179
183
|
end
|
data/lib/fluent/supervisor.rb
CHANGED
@@ -61,6 +61,7 @@ module Fluent
|
|
61
61
|
|
62
62
|
def after_run
|
63
63
|
stop_rpc_server if @rpc_endpoint
|
64
|
+
Fluent::Supervisor.cleanup_resources
|
64
65
|
end
|
65
66
|
|
66
67
|
def run_rpc_server
|
@@ -387,6 +388,14 @@ module Fluent
|
|
387
388
|
}
|
388
389
|
end
|
389
390
|
|
391
|
+
def self.cleanup_resources
|
392
|
+
unless Fluent.windows?
|
393
|
+
if ENV.has_key?('SERVERENGINE_SOCKETMANAGER_PATH')
|
394
|
+
FileUtils.rm_f(ENV['SERVERENGINE_SOCKETMANAGER_PATH'])
|
395
|
+
end
|
396
|
+
end
|
397
|
+
end
|
398
|
+
|
390
399
|
def initialize(opt)
|
391
400
|
@daemonize = opt[:daemonize]
|
392
401
|
@supervise = opt[:supervise]
|
@@ -493,6 +502,7 @@ module Fluent
|
|
493
502
|
init_engine
|
494
503
|
run_configure
|
495
504
|
run_engine
|
505
|
+
self.class.cleanup_resources if @standalone_worker
|
496
506
|
exit 0
|
497
507
|
end
|
498
508
|
end
|
data/lib/fluent/system_config.rb
CHANGED
@@ -98,6 +98,13 @@ module Fluent
|
|
98
98
|
next # doesn't exist in command line options
|
99
99
|
when :emit_error_log_interval
|
100
100
|
system.emit_error_log_interval = @suppress_interval if @suppress_interval
|
101
|
+
when :log_level
|
102
|
+
ll_value = instance_variable_get("@log_level")
|
103
|
+
# info level can't be specified via command line option.
|
104
|
+
# log_level is info here, it is default value and <system>'s log_level should be applied if exists.
|
105
|
+
if ll_value != Fluent::Log::LEVEL_INFO
|
106
|
+
system.log_level = ll_value
|
107
|
+
end
|
101
108
|
else
|
102
109
|
next unless instance_variable_defined?("@#{param}")
|
103
110
|
supervisor_value = instance_variable_get("@#{param}")
|
data/lib/fluent/version.rb
CHANGED
@@ -13,11 +13,13 @@ module Fluent::Config
|
|
13
13
|
end
|
14
14
|
|
15
15
|
class FakeSupervisor
|
16
|
+
attr_writer :log_level
|
17
|
+
|
16
18
|
def initialize
|
17
19
|
@workers = nil
|
18
20
|
@root_dir = nil
|
19
21
|
@log = FakeLoggerInitializer.new
|
20
|
-
@log_level =
|
22
|
+
@log_level = Fluent::Log::LEVEL_INFO
|
21
23
|
@suppress_interval = nil
|
22
24
|
@suppress_config_dump = nil
|
23
25
|
@suppress_repeated_stacktrace = nil
|
@@ -55,7 +57,7 @@ module Fluent::Config
|
|
55
57
|
assert_nil(sc.without_source)
|
56
58
|
assert_equal(1, s.instance_variable_get(:@workers))
|
57
59
|
assert_nil(s.instance_variable_get(:@root_dir))
|
58
|
-
|
60
|
+
assert_equal(Fluent::Log::LEVEL_INFO, s.instance_variable_get(:@log_level))
|
59
61
|
assert_nil(s.instance_variable_get(:@suppress_repeated_stacktrace))
|
60
62
|
assert_nil(s.instance_variable_get(:@emit_error_log_interval))
|
61
63
|
assert_nil(s.instance_variable_get(:@suppress_config_dump))
|
@@ -99,7 +101,7 @@ module Fluent::Config
|
|
99
101
|
sc.apply(s)
|
100
102
|
assert_equal(1, s.instance_variable_get(:@workers))
|
101
103
|
assert_nil(s.instance_variable_get(:@root_dir))
|
102
|
-
|
104
|
+
assert_equal(Fluent::Log::LEVEL_INFO, s.instance_variable_get(:@log_level))
|
103
105
|
assert_nil(s.instance_variable_get(:@suppress_repeated_stacktrace))
|
104
106
|
assert_nil(s.instance_variable_get(:@emit_error_log_interval))
|
105
107
|
assert_nil(s.instance_variable_get(:@suppress_config_dump))
|
@@ -109,16 +111,43 @@ module Fluent::Config
|
|
109
111
|
assert_nil(s.instance_variable_get(:@dir_permission))
|
110
112
|
end
|
111
113
|
|
112
|
-
|
114
|
+
data('trace' => Fluent::Log::LEVEL_TRACE,
|
115
|
+
'debug' => Fluent::Log::LEVEL_DEBUG,
|
116
|
+
'info' => Fluent::Log::LEVEL_INFO,
|
117
|
+
'warn' => Fluent::Log::LEVEL_WARN,
|
118
|
+
'error' => Fluent::Log::LEVEL_ERROR,
|
119
|
+
'fatal' => Fluent::Log::LEVEL_FATAL)
|
120
|
+
test 'log_level is applied when log_level related command line option is not passed' do |level|
|
121
|
+
conf = parse_text(<<-EOS)
|
122
|
+
<system>
|
123
|
+
log_level #{Fluent::Log::LEVEL_TEXT[level]}
|
124
|
+
</system>
|
125
|
+
EOS
|
126
|
+
s = FakeSupervisor.new
|
127
|
+
sc = Fluent::SystemConfig.new(conf)
|
128
|
+
sc.attach(s)
|
129
|
+
sc.apply(s)
|
130
|
+
assert_equal(level, s.instance_variable_get("@log").level)
|
131
|
+
end
|
132
|
+
|
133
|
+
# info is removed because info level can't be specified via command line
|
134
|
+
data('trace' => Fluent::Log::LEVEL_TRACE,
|
135
|
+
'debug' => Fluent::Log::LEVEL_DEBUG,
|
136
|
+
'warn' => Fluent::Log::LEVEL_WARN,
|
137
|
+
'error' => Fluent::Log::LEVEL_ERROR,
|
138
|
+
'fatal' => Fluent::Log::LEVEL_FATAL)
|
139
|
+
test 'log_level is ignored when log_level related command line option is passed' do |level|
|
113
140
|
conf = parse_text(<<-EOS)
|
114
141
|
<system>
|
115
|
-
log_level
|
142
|
+
log_level info
|
116
143
|
</system>
|
117
144
|
EOS
|
118
145
|
s = FakeSupervisor.new
|
146
|
+
s.log_level = level
|
119
147
|
sc = Fluent::SystemConfig.new(conf)
|
148
|
+
sc.attach(s)
|
120
149
|
sc.apply(s)
|
121
|
-
assert_equal(
|
150
|
+
assert_equal(level, s.instance_variable_get("@log").level)
|
122
151
|
end
|
123
152
|
|
124
153
|
test 'process global overridable variables' do
|
@@ -130,6 +159,7 @@ module Fluent::Config
|
|
130
159
|
EOS
|
131
160
|
s = FakeSupervisor.new
|
132
161
|
sc = Fluent::SystemConfig.new(conf)
|
162
|
+
sc.attach(s)
|
133
163
|
sc.apply(s)
|
134
164
|
assert_equal(0655, s.instance_variable_get(:@file_permission))
|
135
165
|
assert_equal(0765, s.instance_variable_get(:@dir_permission))
|
data/test/config/test_types.rb
CHANGED
@@ -69,6 +69,17 @@ class TestConfigTypes < ::Test::Unit::TestCase
|
|
69
69
|
assert_equal Encoding::UTF_8, Config::STRING_TYPE.call('test', {}).encoding
|
70
70
|
end
|
71
71
|
|
72
|
+
data('latin' => 'Märch',
|
73
|
+
'ascii' => 'ascii',
|
74
|
+
'space' => ' ',
|
75
|
+
'number' => '1',
|
76
|
+
'Hiragana' => 'あいうえお')
|
77
|
+
test 'string w/ binary' do |str|
|
78
|
+
actual = Config::STRING_TYPE.call(str.b, {})
|
79
|
+
assert_equal str, actual
|
80
|
+
assert_equal Encoding::UTF_8, actual.encoding
|
81
|
+
end
|
82
|
+
|
72
83
|
test 'enum' do
|
73
84
|
assert_equal :val, Config::ENUM_TYPE.call('val', {list: [:val, :value, :v]})
|
74
85
|
assert_equal :v, Config::ENUM_TYPE.call('v', {list: [:val, :value, :v]})
|
@@ -143,6 +154,14 @@ class TestConfigTypes < ::Test::Unit::TestCase
|
|
143
154
|
assert_raise(RuntimeError.new("unknown type in REFORMAT: foo")){ Config::HASH_TYPE.call("x:1,y:2", {value_type: :foo}) }
|
144
155
|
end
|
145
156
|
|
157
|
+
data('latin' => ['3:Märch', {"3"=>"Märch"}],
|
158
|
+
'ascii' => ['ascii:ascii', {"ascii"=>"ascii"}],
|
159
|
+
'number' => ['number:1', {"number"=>"1"}],
|
160
|
+
'Hiragana' => ['hiragana:あいうえお', {"hiragana"=>"あいうえお"}])
|
161
|
+
test 'hash w/ binary' do |(target, expected)|
|
162
|
+
assert_equal(expected, Config::HASH_TYPE.call(target.b, { value_type: :string }))
|
163
|
+
end
|
164
|
+
|
146
165
|
test 'array' do
|
147
166
|
assert_equal(["1","2",1], Config::ARRAY_TYPE.call('["1","2",1]', {}))
|
148
167
|
assert_equal(["1","2","1"], Config::ARRAY_TYPE.call('1,2,1', {}))
|
@@ -662,4 +662,39 @@ class ParserFilterTest < Test::Unit::TestCase
|
|
662
662
|
end
|
663
663
|
end
|
664
664
|
end
|
665
|
+
|
666
|
+
class EmitInvalidRecordToErrorTest < self
|
667
|
+
def test_pattern_is_mismached_with_emit_invalid_record_to_error
|
668
|
+
d = create_driver(CONFIG_UNMATCHED_PATTERN_LOG + "emit_invalid_record_to_error false")
|
669
|
+
flexmock(d.instance.router).should_receive(:emit_error_event).never
|
670
|
+
assert_nothing_raised {
|
671
|
+
d.run do
|
672
|
+
d.feed(@tag, Fluent::EventTime.now.to_i, {'message' => INVALID_MESSAGE})
|
673
|
+
end
|
674
|
+
}
|
675
|
+
assert_equal 0, d.filtered.length
|
676
|
+
end
|
677
|
+
|
678
|
+
def test_parser_error_with_emit_invalid_record_to_error
|
679
|
+
d = create_driver(CONFIG_INVALID_TIME_VALUE + "emit_invalid_record_to_error false")
|
680
|
+
flexmock(d.instance.router).should_receive(:emit_error_event).never
|
681
|
+
assert_nothing_raised {
|
682
|
+
d.run do
|
683
|
+
d.feed(@tag, Fluent::EventTime.now.to_i, {'data' => '{"time":[], "f1":"v1"}'})
|
684
|
+
end
|
685
|
+
}
|
686
|
+
assert_equal 0, d.filtered.length
|
687
|
+
end
|
688
|
+
|
689
|
+
def test_key_not_exist_with_emit_invalid_record_to_error
|
690
|
+
d = create_driver(CONFIG_NOT_IGNORE + "emit_invalid_record_to_error false")
|
691
|
+
flexmock(d.instance.router).should_receive(:emit_error_event).never
|
692
|
+
assert_nothing_raised {
|
693
|
+
d.run do
|
694
|
+
d.feed(@tag, Fluent::EventTime.now.to_i, {'foo' => 'bar'})
|
695
|
+
end
|
696
|
+
}
|
697
|
+
assert_equal 0, d.filtered.length
|
698
|
+
end
|
699
|
+
end
|
665
700
|
end
|
data/test/plugin/test_in_http.rb
CHANGED
@@ -313,6 +313,28 @@ class HttpInputTest < Test::Unit::TestCase
|
|
313
313
|
assert_equal_event_time time, d.events[1][1]
|
314
314
|
end
|
315
315
|
|
316
|
+
def test_application_msgpack
|
317
|
+
d = create_driver
|
318
|
+
time = event_time("2011-01-02 13:14:15 UTC")
|
319
|
+
time_i = time.to_i
|
320
|
+
events = [
|
321
|
+
["tag1", time, {"a"=>1}],
|
322
|
+
["tag2", time, {"a"=>2}],
|
323
|
+
]
|
324
|
+
res_codes = []
|
325
|
+
|
326
|
+
d.run(expect_records: 2) do
|
327
|
+
events.each do |tag, t, record|
|
328
|
+
res = post("/#{tag}?time=#{time_i.to_s}", record.to_msgpack, {"Content-Type"=>"application/msgpack"})
|
329
|
+
res_codes << res.code
|
330
|
+
end
|
331
|
+
end
|
332
|
+
assert_equal ["200", "200"], res_codes
|
333
|
+
assert_equal events, d.events
|
334
|
+
assert_equal_event_time time, d.events[0][1]
|
335
|
+
assert_equal_event_time time, d.events[1][1]
|
336
|
+
end
|
337
|
+
|
316
338
|
def test_msgpack
|
317
339
|
d = create_driver
|
318
340
|
time = event_time("2011-01-02 13:14:15 UTC")
|
@@ -204,10 +204,9 @@ EOC
|
|
204
204
|
unless header.has_key?('Content-Type')
|
205
205
|
header['Content-Type'] = 'application/octet-stream'
|
206
206
|
end
|
207
|
-
|
207
|
+
Net::HTTP.start(url.host, url.port) {|http|
|
208
208
|
http.request(req)
|
209
209
|
}
|
210
|
-
res.body
|
211
210
|
end
|
212
211
|
|
213
212
|
sub_test_case "servlets" do
|
@@ -268,7 +267,7 @@ plugin_id:test_in\tplugin_category:input\ttype:test_in\toutput_plugin:false\tret
|
|
268
267
|
expected_test_filter_response = "\
|
269
268
|
plugin_id:test_filter\tplugin_category:filter\ttype:test_filter\toutput_plugin:false\tretry_count:"
|
270
269
|
|
271
|
-
response = get("http://127.0.0.1:#{@port}/api/plugins")
|
270
|
+
response = get("http://127.0.0.1:#{@port}/api/plugins").body
|
272
271
|
test_in = response.split("\n")[0]
|
273
272
|
test_filter = response.split("\n")[3]
|
274
273
|
assert_equal(expected_test_in_response, test_in)
|
@@ -306,7 +305,7 @@ plugin_id:test_filter\tplugin_category:filter\ttype:test_filter\toutput_plugin:f
|
|
306
305
|
}
|
307
306
|
expected_null_response.merge!("config" => {"@id" => "null", "@type" => "null"}) if with_config
|
308
307
|
expected_null_response.merge!("retry" => {}) if with_retry
|
309
|
-
response = JSON.parse(get("http://127.0.0.1:#{@port}/api/plugins.json"))
|
308
|
+
response = JSON.parse(get("http://127.0.0.1:#{@port}/api/plugins.json").body)
|
310
309
|
test_in_response = response["plugins"][0]
|
311
310
|
null_response = response["plugins"][5]
|
312
311
|
assert_equal(expected_test_in_response, test_in_response)
|
@@ -343,7 +342,7 @@ plugin_id:test_filter\tplugin_category:filter\ttype:test_filter\toutput_plugin:f
|
|
343
342
|
}
|
344
343
|
expected_null_response.merge!("config" => {"@id" => "null", "@type" => "null"}) if with_config
|
345
344
|
expected_null_response.merge!("retry" => {}) if with_retry
|
346
|
-
response = JSON.parse(get("http://127.0.0.1:#{@port}/api/plugins.json#{query_param}"))
|
345
|
+
response = JSON.parse(get("http://127.0.0.1:#{@port}/api/plugins.json#{query_param}").body)
|
347
346
|
test_in_response = response["plugins"][0]
|
348
347
|
null_response = response["plugins"][5]
|
349
348
|
assert_equal(expected_test_in_response, test_in_response)
|
@@ -376,7 +375,7 @@ plugin_id:test_filter\tplugin_category:filter\ttype:test_filter\toutput_plugin:f
|
|
376
375
|
"type" => "null",
|
377
376
|
"instance_variables" => {"id" => "null", "num_errors" => 0}
|
378
377
|
}
|
379
|
-
response = JSON.parse(get("http://127.0.0.1:#{@port}/api/plugins.json?with_config=no&with_retry=no&with_ivars=id,num_errors"))
|
378
|
+
response = JSON.parse(get("http://127.0.0.1:#{@port}/api/plugins.json?with_config=no&with_retry=no&with_ivars=id,num_errors").body)
|
380
379
|
test_in_response = response["plugins"][0]
|
381
380
|
null_response = response["plugins"][5]
|
382
381
|
assert_equal(expected_test_in_response, test_in_response)
|
@@ -394,7 +393,7 @@ plugin_id:test_filter\tplugin_category:filter\ttype:test_filter\toutput_plugin:f
|
|
394
393
|
expected_response_regex = /pid:\d+\tppid:\d+\tconfig_path:\/etc\/fluent\/fluent.conf\tpid_file:\tplugin_dirs:\[\"\/etc\/fluent\/plugin\"\]\tlog_path:/
|
395
394
|
|
396
395
|
assert_match(expected_response_regex,
|
397
|
-
get("http://127.0.0.1:#{@port}/api/config"))
|
396
|
+
get("http://127.0.0.1:#{@port}/api/config").body)
|
398
397
|
end
|
399
398
|
|
400
399
|
test "/api/config.json" do
|
@@ -405,7 +404,7 @@ plugin_id:test_filter\tplugin_category:filter\ttype:test_filter\toutput_plugin:f
|
|
405
404
|
tag monitor
|
406
405
|
")
|
407
406
|
d.instance.start
|
408
|
-
res = JSON.parse(get("http://127.0.0.1:#{@port}/api/config.json"))
|
407
|
+
res = JSON.parse(get("http://127.0.0.1:#{@port}/api/config.json").body)
|
409
408
|
assert_equal("/etc/fluent/fluent.conf", res["config_path"])
|
410
409
|
assert_nil(res["pid_file"])
|
411
410
|
assert_equal(["/etc/fluent/plugin"], res["plugin_dirs"])
|
@@ -474,7 +473,7 @@ plugin_id:test_filter\tplugin_category:filter\ttype:test_filter\toutput_plugin:f
|
|
474
473
|
output.submit_flush_once
|
475
474
|
sleep 0.1 until output.buffer.queued?
|
476
475
|
end
|
477
|
-
response = JSON.parse(get("http://127.0.0.1:#{@port}/api/plugins.json"))
|
476
|
+
response = JSON.parse(get("http://127.0.0.1:#{@port}/api/plugins.json").body)
|
478
477
|
test_out_fail_write_response = response["plugins"][1]
|
479
478
|
# remove dynamic keys
|
480
479
|
response_retry_count = test_out_fail_write_response.delete("retry_count")
|
@@ -486,4 +485,32 @@ plugin_id:test_filter\tplugin_category:filter\ttype:test_filter\toutput_plugin:f
|
|
486
485
|
assert{ response_retry_count == response_retry["steps"] + 1 }
|
487
486
|
end
|
488
487
|
end
|
488
|
+
|
489
|
+
sub_test_case "check the port number of http server" do
|
490
|
+
test "on single worker environment" do
|
491
|
+
port = unused_port
|
492
|
+
d = create_driver("
|
493
|
+
@type monitor_agent
|
494
|
+
bind '127.0.0.1'
|
495
|
+
port #{port}
|
496
|
+
")
|
497
|
+
d.instance.start
|
498
|
+
assert_equal("200", get("http://127.0.0.1:#{port}/api/plugins").code)
|
499
|
+
end
|
500
|
+
|
501
|
+
test "worker_id = 2 on multi worker environment" do
|
502
|
+
port = unused_port
|
503
|
+
Fluent::SystemConfig.overwrite_system_config('workers' => 4) do
|
504
|
+
d = Fluent::Test::Driver::Input.new(Fluent::Plugin::MonitorAgentInput)
|
505
|
+
d.instance.instance_eval{ @_fluentd_worker_id = 2 }
|
506
|
+
d.configure("
|
507
|
+
@type monitor_agent
|
508
|
+
bind '127.0.0.1'
|
509
|
+
port #{port - 2}
|
510
|
+
")
|
511
|
+
d.instance.start
|
512
|
+
end
|
513
|
+
assert_equal("200", get("http://127.0.0.1:#{port}/api/plugins").code)
|
514
|
+
end
|
515
|
+
end
|
489
516
|
end
|
data/test/plugin/test_in_tail.rb
CHANGED
@@ -1198,4 +1198,19 @@ class TailInputTest < Test::Unit::TestCase
|
|
1198
1198
|
assert_equal expected_files, plugin.expand_paths.sort
|
1199
1199
|
end
|
1200
1200
|
end
|
1201
|
+
|
1202
|
+
def test_skip_refresh_on_startup
|
1203
|
+
FileUtils.touch("#{TMP_DIR}/tail.txt")
|
1204
|
+
config = config_element('', '', {
|
1205
|
+
'format' => 'none',
|
1206
|
+
'refresh_interval' => 1,
|
1207
|
+
'skip_refresh_on_startup' => true
|
1208
|
+
})
|
1209
|
+
d = create_driver(config)
|
1210
|
+
d.run(shutdown: false) {}
|
1211
|
+
assert_equal 0, d.instance.instance_variable_get(:@tails).keys.size
|
1212
|
+
# detect a file at first execution of in_tail_refresh_watchers timer
|
1213
|
+
waiting(5) { sleep 0.1 until d.instance.instance_variable_get(:@tails).keys.size == 1 }
|
1214
|
+
d.instance_shutdown
|
1215
|
+
end
|
1201
1216
|
end
|
data/test/plugin/test_output.rb
CHANGED
@@ -315,7 +315,7 @@ class OutputTest < Test::Unit::TestCase
|
|
315
315
|
validators = @i.placeholder_validators(:path, "/my/path/file.%Y-%m-%d.log")
|
316
316
|
assert_equal 1, validators.size
|
317
317
|
assert_equal 1, validators.select(&:time?).size
|
318
|
-
assert_raise Fluent::ConfigError.new("Parameter 'path' has timestamp placeholders, but chunk key 'time' is not configured") do
|
318
|
+
assert_raise Fluent::ConfigError.new("Parameter 'path: /my/path/file.%Y-%m-%d.log' has timestamp placeholders, but chunk key 'time' is not configured") do
|
319
319
|
validators.first.validate!
|
320
320
|
end
|
321
321
|
end
|
@@ -325,7 +325,7 @@ class OutputTest < Test::Unit::TestCase
|
|
325
325
|
validators = @i.placeholder_validators(:path, "/my/path/to/file.log")
|
326
326
|
assert_equal 1, validators.size
|
327
327
|
assert_equal 1, validators.select(&:time?).size
|
328
|
-
assert_raise Fluent::ConfigError.new("Parameter 'path' doesn't have timestamp placeholders for timekey 30") do
|
328
|
+
assert_raise Fluent::ConfigError.new("Parameter 'path: /my/path/to/file.log' doesn't have timestamp placeholders for timekey 30") do
|
329
329
|
validators.first.validate!
|
330
330
|
end
|
331
331
|
end
|
@@ -335,7 +335,7 @@ class OutputTest < Test::Unit::TestCase
|
|
335
335
|
validators = @i.placeholder_validators(:path, "/my/path/${tag}/file.log")
|
336
336
|
assert_equal 1, validators.size
|
337
337
|
assert_equal 1, validators.select(&:tag?).size
|
338
|
-
assert_raise Fluent::ConfigError.new("Parameter 'path' has tag placeholders, but chunk key 'tag' is not configured") do
|
338
|
+
assert_raise Fluent::ConfigError.new("Parameter 'path: /my/path/${tag}/file.log' has tag placeholders, but chunk key 'tag' is not configured") do
|
339
339
|
validators.first.validate!
|
340
340
|
end
|
341
341
|
end
|
@@ -345,7 +345,7 @@ class OutputTest < Test::Unit::TestCase
|
|
345
345
|
validators = @i.placeholder_validators(:path, "/my/path/file.log")
|
346
346
|
assert_equal 1, validators.size
|
347
347
|
assert_equal 1, validators.select(&:tag?).size
|
348
|
-
assert_raise Fluent::ConfigError.new("Parameter 'path' doesn't have tag placeholder") do
|
348
|
+
assert_raise Fluent::ConfigError.new("Parameter 'path: /my/path/file.log' doesn't have tag placeholder") do
|
349
349
|
validators.first.validate!
|
350
350
|
end
|
351
351
|
end
|
@@ -355,7 +355,7 @@ class OutputTest < Test::Unit::TestCase
|
|
355
355
|
validators = @i.placeholder_validators(:path, "/my/path/${username}/file.${group}.log")
|
356
356
|
assert_equal 1, validators.size
|
357
357
|
assert_equal 1, validators.select(&:keys?).size
|
358
|
-
assert_raise Fluent::ConfigError.new("Parameter 'path' has placeholders, but chunk keys doesn't have keys group,username") do
|
358
|
+
assert_raise Fluent::ConfigError.new("Parameter 'path: /my/path/${username}/file.${group}.log' has placeholders, but chunk keys doesn't have keys group,username") do
|
359
359
|
validators.first.validate!
|
360
360
|
end
|
361
361
|
end
|
@@ -365,7 +365,7 @@ class OutputTest < Test::Unit::TestCase
|
|
365
365
|
validators = @i.placeholder_validators(:path, "/my/path/file.log")
|
366
366
|
assert_equal 1, validators.size
|
367
367
|
assert_equal 1, validators.select(&:keys?).size
|
368
|
-
assert_raise Fluent::ConfigError.new("Parameter 'path' doesn't have enough placeholders for keys group,username") do
|
368
|
+
assert_raise Fluent::ConfigError.new("Parameter 'path: /my/path/file.log' doesn't have enough placeholders for keys group,username") do
|
369
369
|
validators.first.validate!
|
370
370
|
end
|
371
371
|
end
|
@@ -374,14 +374,14 @@ class OutputTest < Test::Unit::TestCase
|
|
374
374
|
sub_test_case '#placeholder_validate!' do
|
375
375
|
test 'raises configuration error for a templace when timestamp placeholders exist but time key is missing' do
|
376
376
|
@i.configure(config_element('ROOT', '', {}, [config_element('buffer', '')]))
|
377
|
-
assert_raise Fluent::ConfigError.new("Parameter 'path' has timestamp placeholders, but chunk key 'time' is not configured") do
|
377
|
+
assert_raise Fluent::ConfigError.new("Parameter 'path: /path/without/timestamp/file.%Y%m%d-%H%M.log' has timestamp placeholders, but chunk key 'time' is not configured") do
|
378
378
|
@i.placeholder_validate!(:path, "/path/without/timestamp/file.%Y%m%d-%H%M.log")
|
379
379
|
end
|
380
380
|
end
|
381
381
|
|
382
382
|
test 'raises configuration error for a template without timestamp placeholders when timekey is configured' do
|
383
383
|
@i.configure(config_element('ROOT', '', {}, [config_element('buffer', 'time', {"timekey" => 180})]))
|
384
|
-
assert_raise Fluent::ConfigError.new("Parameter 'path' doesn't have timestamp placeholders for timekey 180") do
|
384
|
+
assert_raise Fluent::ConfigError.new("Parameter 'path: /my/path/file.log' doesn't have timestamp placeholders for timekey 180") do
|
385
385
|
@i.placeholder_validate!(:path, "/my/path/file.log")
|
386
386
|
end
|
387
387
|
assert_nothing_raised do
|
@@ -391,7 +391,7 @@ class OutputTest < Test::Unit::TestCase
|
|
391
391
|
|
392
392
|
test 'raises configuration error for a template with timestamp placeholders when plugin is configured more fine timekey' do
|
393
393
|
@i.configure(config_element('ROOT', '', {}, [config_element('buffer', 'time', {"timekey" => 180})]))
|
394
|
-
assert_raise Fluent::ConfigError.new("Parameter 'path' doesn't have timestamp placeholder for hour('%H') for timekey 180") do
|
394
|
+
assert_raise Fluent::ConfigError.new("Parameter 'path: /my/path/file.%Y%m%d_%H.log' doesn't have timestamp placeholder for hour('%H') for timekey 180") do
|
395
395
|
@i.placeholder_validate!(:path, "/my/path/file.%Y%m%d_%H.log")
|
396
396
|
end
|
397
397
|
assert_nothing_raised do
|
@@ -401,14 +401,14 @@ class OutputTest < Test::Unit::TestCase
|
|
401
401
|
|
402
402
|
test 'raises configuration error for a template when tag placeholders exist but tag key is missing' do
|
403
403
|
@i.configure(config_element('ROOT', '', {}, [config_element('buffer', '')]))
|
404
|
-
assert_raise Fluent::ConfigError.new("Parameter 'path' has tag placeholders, but chunk key 'tag' is not configured") do
|
404
|
+
assert_raise Fluent::ConfigError.new("Parameter 'path: /my/path/${tag}/file.${tag[2]}.log' has tag placeholders, but chunk key 'tag' is not configured") do
|
405
405
|
@i.placeholder_validate!(:path, "/my/path/${tag}/file.${tag[2]}.log")
|
406
406
|
end
|
407
407
|
end
|
408
408
|
|
409
409
|
test 'raises configuration error for a template without tag placeholders when tagkey is configured' do
|
410
410
|
@i.configure(config_element('ROOT', '', {}, [config_element('buffer', 'tag')]))
|
411
|
-
assert_raise Fluent::ConfigError.new("Parameter 'path' doesn't have tag placeholder") do
|
411
|
+
assert_raise Fluent::ConfigError.new("Parameter 'path: /my/path/file.log' doesn't have tag placeholder") do
|
412
412
|
@i.placeholder_validate!(:path, "/my/path/file.log")
|
413
413
|
end
|
414
414
|
assert_nothing_raised do
|
@@ -418,14 +418,14 @@ class OutputTest < Test::Unit::TestCase
|
|
418
418
|
|
419
419
|
test 'raises configuration error for a template when variable key placeholders exist but chunk keys are missing' do
|
420
420
|
@i.configure(config_element('ROOT', '', {}, [config_element('buffer', '')]))
|
421
|
-
assert_raise Fluent::ConfigError.new("Parameter 'path' has placeholders, but chunk keys doesn't have keys service,username") do
|
421
|
+
assert_raise Fluent::ConfigError.new("Parameter 'path: /my/path/${service}/file.${username}.log' has placeholders, but chunk keys doesn't have keys service,username") do
|
422
422
|
@i.placeholder_validate!(:path, "/my/path/${service}/file.${username}.log")
|
423
423
|
end
|
424
424
|
end
|
425
425
|
|
426
426
|
test 'raises configuration error for a template without variable key placeholders when chunk keys are configured' do
|
427
427
|
@i.configure(config_element('ROOT', '', {}, [config_element('buffer', 'username,service')]))
|
428
|
-
assert_raise Fluent::ConfigError.new("Parameter 'path' doesn't have enough placeholders for keys service,username") do
|
428
|
+
assert_raise Fluent::ConfigError.new("Parameter 'path: /my/path/file.log' doesn't have enough placeholders for keys service,username") do
|
429
429
|
@i.placeholder_validate!(:path, "/my/path/file.log")
|
430
430
|
end
|
431
431
|
assert_nothing_raised do
|
@@ -435,10 +435,10 @@ class OutputTest < Test::Unit::TestCase
|
|
435
435
|
|
436
436
|
test 'raise configuration error for a template and configuration with keys mismatch' do
|
437
437
|
@i.configure(config_element('ROOT', '', {}, [config_element('buffer', 'username,service')]))
|
438
|
-
assert_raise Fluent::ConfigError.new("Parameter 'path' doesn't have enough placeholders for keys service") do
|
438
|
+
assert_raise Fluent::ConfigError.new("Parameter 'path: /my/path/file.${username}.log' doesn't have enough placeholders for keys service") do
|
439
439
|
@i.placeholder_validate!(:path, "/my/path/file.${username}.log")
|
440
440
|
end
|
441
|
-
assert_raise Fluent::ConfigError.new("Parameter 'path' doesn't have enough placeholders for keys username") do
|
441
|
+
assert_raise Fluent::ConfigError.new("Parameter 'path: /my/path/${service}/file.log' doesn't have enough placeholders for keys username") do
|
442
442
|
@i.placeholder_validate!(:path, "/my/path/${service}/file.log")
|
443
443
|
end
|
444
444
|
assert_nothing_raised do
|
@@ -35,4 +35,12 @@ class Apache2ParserTest < ::Test::Unit::TestCase
|
|
35
35
|
assert_equal(@expected, record)
|
36
36
|
}
|
37
37
|
end
|
38
|
+
|
39
|
+
def test_parse_with_escape_sequence
|
40
|
+
@parser.instance.parse('192.168.0.1 - - [28/Feb/2013:12:00:00 +0900] "GET /\" HTTP/1.1" 200 777 "referer \\\ \"" "user agent \\\ \""') { |_, record|
|
41
|
+
assert_equal('/\"', record['path'])
|
42
|
+
assert_equal('referer \\\ \"', record['referer'])
|
43
|
+
assert_equal('user agent \\\ \"', record['agent'])
|
44
|
+
}
|
45
|
+
end
|
38
46
|
end
|
@@ -63,4 +63,180 @@ class SyslogParserTest < ::Test::Unit::TestCase
|
|
63
63
|
assert_equal "Feb 28 00:00:12", record['time']
|
64
64
|
end
|
65
65
|
end
|
66
|
+
|
67
|
+
class TestRFC5424Regexp < self
|
68
|
+
def test_parse_with_rfc5424_message
|
69
|
+
@parser.configure(
|
70
|
+
'time_format' => '%Y-%m-%dT%H:%M:%S.%L%z',
|
71
|
+
'message_format' => 'rfc5424',
|
72
|
+
)
|
73
|
+
text = '<16>1 2017-02-06T13:14:15.003Z 192.168.0.1 fluentd - - - Hi, from Fluentd!'
|
74
|
+
@parser.instance.parse(text) do |time, record|
|
75
|
+
assert_equal(event_time("2017-02-06T13:14:15.003Z", format: '%Y-%m-%dT%H:%M:%S.%L%z'), time)
|
76
|
+
assert_equal "-", record["pid"]
|
77
|
+
assert_equal "-", record["msgid"]
|
78
|
+
assert_equal "-", record["extradata"]
|
79
|
+
assert_equal "Hi, from Fluentd!", record["message"]
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
def test_parse_with_rfc5424_message_without_time_format
|
84
|
+
@parser.configure(
|
85
|
+
'message_format' => 'rfc5424',
|
86
|
+
)
|
87
|
+
text = '<16>1 2017-02-06T13:14:15.003Z 192.168.0.1 fluentd - - - Hi, from Fluentd!'
|
88
|
+
@parser.instance.parse(text) do |time, record|
|
89
|
+
assert_equal(event_time("2017-02-06T13:14:15.003Z", format: '%Y-%m-%dT%H:%M:%S.%L%z'), time)
|
90
|
+
assert_equal "-", record["pid"]
|
91
|
+
assert_equal "-", record["msgid"]
|
92
|
+
assert_equal "-", record["extradata"]
|
93
|
+
assert_equal "Hi, from Fluentd!", record["message"]
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
def test_parse_with_rfc5424_structured_message
|
98
|
+
@parser.configure(
|
99
|
+
'time_format' => '%Y-%m-%dT%H:%M:%S.%L%z',
|
100
|
+
'message_format' => 'rfc5424',
|
101
|
+
)
|
102
|
+
text = '<16>1 2017-02-06T13:14:15.003Z 192.168.0.1 fluentd 11111 ID24224 [exampleSDID@20224 iut="3" eventSource="Application" eventID="11211"] Hi, from Fluentd!'
|
103
|
+
@parser.instance.parse(text) do |time, record|
|
104
|
+
assert_equal(event_time("2017-02-06T13:14:15.003Z", format: '%Y-%m-%dT%H:%M:%S.%L%z'), time)
|
105
|
+
assert_equal "11111", record["pid"]
|
106
|
+
assert_equal "ID24224", record["msgid"]
|
107
|
+
assert_equal "[exampleSDID@20224 iut=\"3\" eventSource=\"Application\" eventID=\"11211\"]",
|
108
|
+
record["extradata"]
|
109
|
+
assert_equal "Hi, from Fluentd!", record["message"]
|
110
|
+
end
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
114
|
+
class TestAutoRegexp < self
|
115
|
+
def test_auto_with_legacy_syslog_message
|
116
|
+
@parser.configure(
|
117
|
+
'time_format' => '%b %d %M:%S:%H',
|
118
|
+
'mseeage_format' => 'auto',
|
119
|
+
)
|
120
|
+
text = 'Feb 28 00:00:12 192.168.0.1 fluentd[11111]: [error] Syslog test'
|
121
|
+
@parser.instance.parse(text) do |time, record|
|
122
|
+
assert_equal(event_time("Feb 28 00:00:12", format: '%b %d %M:%S:%H'), time)
|
123
|
+
assert_equal(@expected, record)
|
124
|
+
end
|
125
|
+
end
|
126
|
+
|
127
|
+
def test_auto_with_legacy_syslog_priority_message
|
128
|
+
@parser.configure(
|
129
|
+
'time_format' => '%b %d %M:%S:%H',
|
130
|
+
'with_priority' => true,
|
131
|
+
'mseeage_format' => 'auto',
|
132
|
+
)
|
133
|
+
text = '<6>Feb 28 12:00:00 192.168.0.1 fluentd[11111]: [error] Syslog test'
|
134
|
+
@parser.instance.parse(text) do |time, record|
|
135
|
+
assert_equal(event_time("Feb 28 12:00:00", format: '%b %d %M:%S:%H'), time)
|
136
|
+
assert_equal(@expected.merge('pri' => 6), record)
|
137
|
+
end
|
138
|
+
end
|
139
|
+
|
140
|
+
def test_parse_with_rfc5424_message
|
141
|
+
@parser.configure(
|
142
|
+
'time_format' => '%Y-%m-%dT%H:%M:%S.%L%z',
|
143
|
+
'message_format' => 'auto',
|
144
|
+
)
|
145
|
+
text = '<16>1 2017-02-06T13:14:15.003Z 192.168.0.1 fluentd - - - Hi, from Fluentd!'
|
146
|
+
@parser.instance.parse(text) do |time, record|
|
147
|
+
assert_equal(event_time("2017-02-06T13:14:15.003Z", format: '%Y-%m-%dT%H:%M:%S.%L%z'), time)
|
148
|
+
assert_equal "-", record["pid"]
|
149
|
+
assert_equal "-", record["msgid"]
|
150
|
+
assert_equal "-", record["extradata"]
|
151
|
+
assert_equal "Hi, from Fluentd!", record["message"]
|
152
|
+
end
|
153
|
+
end
|
154
|
+
|
155
|
+
def test_parse_with_rfc5424_structured_message
|
156
|
+
@parser.configure(
|
157
|
+
'time_format' => '%Y-%m-%dT%H:%M:%S.%L%z',
|
158
|
+
'message_format' => 'auto',
|
159
|
+
)
|
160
|
+
text = '<16>1 2017-02-06T13:14:15.003Z 192.168.0.1 fluentd 11111 ID24224 [exampleSDID@20224 iut="3" eventSource="Application" eventID="11211"] Hi, from Fluentd!'
|
161
|
+
@parser.instance.parse(text) do |time, record|
|
162
|
+
assert_equal(event_time("2017-02-06T13:14:15.003Z", format: '%Y-%m-%dT%H:%M:%S.%L%z'), time)
|
163
|
+
assert_equal "11111", record["pid"]
|
164
|
+
assert_equal "ID24224", record["msgid"]
|
165
|
+
assert_equal "[exampleSDID@20224 iut=\"3\" eventSource=\"Application\" eventID=\"11211\"]",
|
166
|
+
record["extradata"]
|
167
|
+
assert_equal "Hi, from Fluentd!", record["message"]
|
168
|
+
end
|
169
|
+
end
|
170
|
+
|
171
|
+
def test_parse_with_both_message_type
|
172
|
+
@parser.configure(
|
173
|
+
'time_format' => '%b %d %M:%S:%H',
|
174
|
+
'rfc5424_time_format' => '%Y-%m-%dT%H:%M:%S.%L%z',
|
175
|
+
'message_format' => 'auto',
|
176
|
+
)
|
177
|
+
text = 'Feb 28 12:00:00 192.168.0.1 fluentd[11111]: [error] Syslog test'
|
178
|
+
@parser.instance.parse(text) do |time, record|
|
179
|
+
assert_equal(event_time("Feb 28 12:00:00", format: '%b %d %M:%S:%H'), time)
|
180
|
+
assert_equal(@expected, record)
|
181
|
+
end
|
182
|
+
text = '<16>1 2017-02-06T13:14:15.003Z 192.168.0.1 fluentd 11111 ID24224 [exampleSDID@20224 iut="3" eventSource="Application" eventID="11211"] Hi, from Fluentd!'
|
183
|
+
@parser.instance.parse(text) do |time, record|
|
184
|
+
assert_equal(event_time("2017-02-06T13:14:15.003Z", format: '%Y-%m-%dT%H:%M:%S.%L%z'), time)
|
185
|
+
assert_equal "11111", record["pid"]
|
186
|
+
assert_equal "ID24224", record["msgid"]
|
187
|
+
assert_equal "[exampleSDID@20224 iut=\"3\" eventSource=\"Application\" eventID=\"11211\"]",
|
188
|
+
record["extradata"]
|
189
|
+
assert_equal "Hi, from Fluentd!", record["message"]
|
190
|
+
end
|
191
|
+
text = 'Feb 28 12:00:02 192.168.0.1 fluentd[11111]: [error] Syslog test'
|
192
|
+
@parser.instance.parse(text) do |time, record|
|
193
|
+
assert_equal(event_time("Feb 28 12:00:02", format: '%b %d %M:%S:%H'), time)
|
194
|
+
assert_equal(@expected, record)
|
195
|
+
end
|
196
|
+
text = '<16>1 2017-02-06T13:14:15.003Z 192.168.0.1 fluentd - - - Hi, from Fluentd!'
|
197
|
+
@parser.instance.parse(text) do |time, record|
|
198
|
+
assert_equal(event_time("2017-02-06T13:14:15.003Z", format: '%Y-%m-%dT%H:%M:%S.%L%z'), time)
|
199
|
+
assert_equal "-", record["pid"]
|
200
|
+
assert_equal "-", record["msgid"]
|
201
|
+
assert_equal "-", record["extradata"]
|
202
|
+
assert_equal "Hi, from Fluentd!", record["message"]
|
203
|
+
end
|
204
|
+
end
|
205
|
+
|
206
|
+
def test_parse_with_both_message_type_and_priority
|
207
|
+
@parser.configure(
|
208
|
+
'time_format' => '%b %d %M:%S:%H',
|
209
|
+
'rfc5424_time_format' => '%Y-%m-%dT%H:%M:%S.%L%z',
|
210
|
+
'with_priority' => true,
|
211
|
+
'message_format' => 'auto',
|
212
|
+
)
|
213
|
+
text = '<6>Feb 28 12:00:00 192.168.0.1 fluentd[11111]: [error] Syslog test'
|
214
|
+
@parser.instance.parse(text) do |time, record|
|
215
|
+
assert_equal(event_time("Feb 28 12:00:00", format: '%b %d %M:%S:%H'), time)
|
216
|
+
assert_equal(@expected.merge('pri' => 6), record)
|
217
|
+
end
|
218
|
+
text = '<16>1 2017-02-06T13:14:15.003Z 192.168.0.1 fluentd 11111 ID24224 [exampleSDID@20224 iut="3" eventSource="Application" eventID="11211"] Hi, from Fluentd!'
|
219
|
+
@parser.instance.parse(text) do |time, record|
|
220
|
+
assert_equal(event_time("2017-02-06T13:14:15.003Z", format: '%Y-%m-%dT%H:%M:%S.%L%z'), time)
|
221
|
+
assert_equal "11111", record["pid"]
|
222
|
+
assert_equal "ID24224", record["msgid"]
|
223
|
+
assert_equal "[exampleSDID@20224 iut=\"3\" eventSource=\"Application\" eventID=\"11211\"]",
|
224
|
+
record["extradata"]
|
225
|
+
assert_equal "Hi, from Fluentd!", record["message"]
|
226
|
+
end
|
227
|
+
text = '<16>Feb 28 12:00:02 192.168.0.1 fluentd[11111]: [error] Syslog test'
|
228
|
+
@parser.instance.parse(text) do |time, record|
|
229
|
+
assert_equal(event_time("Feb 28 12:00:02", format: '%b %d %M:%S:%H'), time)
|
230
|
+
assert_equal(@expected.merge('pri' => 16), record)
|
231
|
+
end
|
232
|
+
text = '<16>1 2017-02-06T13:14:15.003Z 192.168.0.1 fluentd - - - Hi, from Fluentd!'
|
233
|
+
@parser.instance.parse(text) do |time, record|
|
234
|
+
assert_equal(event_time("2017-02-06T13:14:15.003Z", format: '%Y-%m-%dT%H:%M:%S.%L%z'), time)
|
235
|
+
assert_equal "-", record["pid"]
|
236
|
+
assert_equal "-", record["msgid"]
|
237
|
+
assert_equal "-", record["extradata"]
|
238
|
+
assert_equal "Hi, from Fluentd!", record["message"]
|
239
|
+
end
|
240
|
+
end
|
241
|
+
end
|
66
242
|
end
|
@@ -99,6 +99,15 @@ class StorageHelperTest < Test::Unit::TestCase
|
|
99
99
|
assert_equal 0, d._storages.size
|
100
100
|
end
|
101
101
|
|
102
|
+
test 'can be configured with hash' do
|
103
|
+
d = Dummy.new
|
104
|
+
d.configure(config_element())
|
105
|
+
conf = { '@type' => 'example' }
|
106
|
+
assert_nothing_raised do
|
107
|
+
d.storage_create(conf: conf)
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
102
111
|
test 'can override default configuration parameters, but not overwrite whole definition' do
|
103
112
|
d = Dummy.new
|
104
113
|
d.configure(config_element())
|
data/test/test_log.rb
CHANGED
data/test/test_plugin_classes.rb
CHANGED
@@ -21,6 +21,28 @@ module FluentTest
|
|
21
21
|
end
|
22
22
|
end
|
23
23
|
|
24
|
+
class FluentTestGenInput < ::Fluent::Plugin::Input
|
25
|
+
::Fluent::Plugin.register_input('test_in_gen', self)
|
26
|
+
|
27
|
+
attr_reader :started
|
28
|
+
|
29
|
+
config_param :num, :integer, default: 10000
|
30
|
+
|
31
|
+
def start
|
32
|
+
super
|
33
|
+
@started = true
|
34
|
+
|
35
|
+
@num.times { |i|
|
36
|
+
router.emit("test.evet", Fluent::EventTime.now, {'message' => 'Hello!', 'key' => "value#{i}", 'num' => i})
|
37
|
+
}
|
38
|
+
end
|
39
|
+
|
40
|
+
def shutdown
|
41
|
+
@started = false
|
42
|
+
super
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
24
46
|
class FluentTestOutput < ::Fluent::Plugin::Output
|
25
47
|
::Fluent::Plugin.register_output('test_out', self)
|
26
48
|
|
@@ -112,6 +134,19 @@ module FluentTest
|
|
112
134
|
|
113
135
|
class FluentTestBufferedOutput < ::Fluent::Plugin::Output
|
114
136
|
::Fluent::Plugin.register_output('test_out_buffered', self)
|
137
|
+
|
138
|
+
attr_reader :started
|
139
|
+
|
140
|
+
def start
|
141
|
+
super
|
142
|
+
@started = true
|
143
|
+
end
|
144
|
+
|
145
|
+
def shutdown
|
146
|
+
@started = false
|
147
|
+
super
|
148
|
+
end
|
149
|
+
|
115
150
|
def write(chunk)
|
116
151
|
# drop everything
|
117
152
|
end
|
data/test/test_root_agent.rb
CHANGED
@@ -174,10 +174,15 @@ EOC
|
|
174
174
|
end
|
175
175
|
|
176
176
|
sub_test_case 'start/shutdown' do
|
177
|
-
|
178
|
-
|
179
|
-
stub(Engine).root_agent {
|
180
|
-
|
177
|
+
def setup_root_agent(conf)
|
178
|
+
ra = RootAgent.new(log: $log)
|
179
|
+
stub(Engine).root_agent { ra }
|
180
|
+
ra.configure(Config.parse(conf, "(test)", "(test_dir)", true))
|
181
|
+
ra
|
182
|
+
end
|
183
|
+
|
184
|
+
test 'plugin status' do
|
185
|
+
ra = setup_root_agent(<<-EOC)
|
181
186
|
<source>
|
182
187
|
@type test_in
|
183
188
|
@id test_in
|
@@ -191,19 +196,41 @@ EOC
|
|
191
196
|
@id test_out
|
192
197
|
</match>
|
193
198
|
EOC
|
194
|
-
|
199
|
+
ra.start
|
200
|
+
assert_true ra.inputs.first.started
|
201
|
+
assert_true ra.filters.first.started
|
202
|
+
assert_true ra.outputs.first.started
|
203
|
+
|
204
|
+
ra.shutdown
|
205
|
+
assert_false ra.inputs.first.started
|
206
|
+
assert_false ra.filters.first.started
|
207
|
+
assert_false ra.outputs.first.started
|
195
208
|
end
|
196
209
|
|
197
|
-
test 'plugin
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
|
210
|
+
test 'output plugin threads should run before input plugin is blocked with buffer full' do
|
211
|
+
ra = setup_root_agent(<<-EOC)
|
212
|
+
<source>
|
213
|
+
@type test_in_gen
|
214
|
+
@id test_in_gen
|
215
|
+
</source>
|
216
|
+
<match **>
|
217
|
+
@type test_out_buffered
|
218
|
+
@id test_out_buffered
|
219
|
+
<buffer>
|
220
|
+
chunk_limit_size 1k
|
221
|
+
queue_limit_length 2
|
222
|
+
flush_thread_count 2
|
223
|
+
overflow_action block
|
224
|
+
</buffer>
|
225
|
+
</match>
|
226
|
+
EOC
|
227
|
+
waiting(5) { ra.start }
|
228
|
+
assert_true ra.inputs.first.started
|
229
|
+
assert_true ra.outputs.first.started
|
202
230
|
|
203
|
-
|
204
|
-
assert_false
|
205
|
-
assert_false
|
206
|
-
assert_false @ra.outputs.first.started
|
231
|
+
ra.shutdown
|
232
|
+
assert_false ra.inputs.first.started
|
233
|
+
assert_false ra.outputs.first.started
|
207
234
|
end
|
208
235
|
end
|
209
236
|
|
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: 0.14.
|
4
|
+
version: 0.14.14.pre.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Sadayuki Furuhashi
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2017-
|
11
|
+
date: 2017-03-16 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: msgpack
|
@@ -708,9 +708,9 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
708
708
|
version: '2.1'
|
709
709
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
710
710
|
requirements:
|
711
|
-
- - "
|
711
|
+
- - ">"
|
712
712
|
- !ruby/object:Gem::Version
|
713
|
-
version:
|
713
|
+
version: 1.3.1
|
714
714
|
requirements: []
|
715
715
|
rubyforge_project:
|
716
716
|
rubygems_version: 2.5.2
|