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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 158d62963384d98d44438e5f3a8e31c97f3124e4
4
- data.tar.gz: 59a98cfbf508e67e462849f8ada07a509255f8bf
3
+ metadata.gz: e1ebffe0eb082e2cf131d53ad1481cff9ef4648a
4
+ data.tar.gz: 85603a42d5d04d6b72d7d7a7829c9139d7a3bd67
5
5
  SHA512:
6
- metadata.gz: bf8ab978620b5472988d8483828a9b6e5004e5028b86165b31628810c93d15f591b07fd24e0f6f9929b9390bd8de7ccb1035eeb6d389c8b862638a1325f8fc6a
7
- data.tar.gz: 52257e481b34bddce80b2c7f7442328f704f0b11ee1c3495fa895dea4902040ff3eec6ad65c62a261dcb659c5f48bd3cd0455abe045c860259dad420d1b2f9e5
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.
@@ -64,7 +64,7 @@ module Fluent
64
64
  end
65
65
  end
66
66
 
67
- STRING_TYPE = Proc.new { |val, opts| val.to_s.encode(Encoding::UTF_8) }
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.encode(Encoding::UTF_8)
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)
@@ -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
- IS_WINDOWS
26
+ ServerEngine.windows?
27
27
  end
28
28
  end
@@ -357,6 +357,9 @@ module Fluent
357
357
  def write(data)
358
358
  @out.write(data)
359
359
  end
360
+ # We need `#<<` method to use this logger class with other
361
+ # libraries such as aws-sdk
362
+ alias << write
360
363
 
361
364
  def flush
362
365
  @out.flush
@@ -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
- router.emit_error_event(tag, time, record, ArgumentError.new("#{@key_name} does not exist"))
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
- router.emit_error_event(tag, time, record, Fluent::Plugin::Parser::ParserError.new("pattern not match with data '#{raw_value}'"))
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
- router.emit_error_event(tag, time, record, e)
82
- return FAILED_RESULT
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
- router.emit_error_event(tag, time, record, Fluent::Plugin::Parser::ParserError.new("parse failed #{e.message}"))
91
- return FAILED_RESULT
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
 
@@ -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 '#{@name}' doesn't have timestamp placeholder for #{title}('#{example}') for timekey #{timekey.to_i}"
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 '#{@name}' doesn't have tag placeholder"
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 '#{@name}' has tag placeholders, but chunk key 'tag' is not configured"
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 '#{@name}' doesn't have enough placeholders for keys #{not_specified.join(',')}"
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 '#{@name}' has placeholders, but chunk keys doesn't have keys #{not_satisfied.join(',')}"
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
- @regexp = @with_priority ? REGEXP_WITH_PRI : REGEXP
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]*$/
@@ -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
- end
140
- lifecycle(desc: true) do |i|
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", pluguin: instance.class, plugin_id: instance.plugin_id, error: e
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
@@ -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
@@ -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}")
@@ -16,6 +16,6 @@
16
16
 
17
17
  module Fluent
18
18
 
19
- VERSION = '0.14.13'
19
+ VERSION = '0.14.14.pre.1'
20
20
 
21
21
  end
@@ -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 = nil
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
- assert_nil(s.instance_variable_get(:@log_level))
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
- assert_nil(s.instance_variable_get(:@log_level))
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
- test 'log_level' do
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 warn
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(Fluent::Log::LEVEL_WARN, s.instance_variable_get("@log").level)
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))
@@ -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
@@ -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
- res = Net::HTTP.start(url.host, url.port) {|http|
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
@@ -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
@@ -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())
@@ -720,6 +720,10 @@ class PluginLoggerTest < Test::Unit::TestCase
720
720
  @log.write("log")
721
721
  end
722
722
 
723
+ def test_write_alias
724
+ assert(@log.respond_to?(:<<))
725
+ end
726
+
723
727
  def test_out
724
728
  assert_equal(@log.out, @logger.out)
725
729
  @log.out = Object.new
@@ -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
@@ -174,10 +174,15 @@ EOC
174
174
  end
175
175
 
176
176
  sub_test_case 'start/shutdown' do
177
- setup do
178
- @ra = RootAgent.new(log: $log)
179
- stub(Engine).root_agent { @ra }
180
- @ra.configure(Config.parse(<<-EOC, "(test)", "(test_dir)", true))
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
- @ra
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 status' do
198
- @ra.start
199
- assert_true @ra.inputs.first.started
200
- assert_true @ra.filters.first.started
201
- assert_true @ra.outputs.first.started
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
- @ra.shutdown
204
- assert_false @ra.inputs.first.started
205
- assert_false @ra.filters.first.started
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.13
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-02-17 00:00:00.000000000 Z
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: '0'
713
+ version: 1.3.1
714
714
  requirements: []
715
715
  rubyforge_project:
716
716
  rubygems_version: 2.5.2