fluentd 0.12.32 → 0.12.33

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: 29eef51ecd637ecb6cf5cc7d41bc31527513afff
4
- data.tar.gz: 2077e5585e911081e2f76225ff97995aaa1b5100
3
+ metadata.gz: daedf4abbe7d43acdd8df2b8ac75f4f93dc8f252
4
+ data.tar.gz: 99ea120dbf1257a4be97fc480a4f022b7cc638bf
5
5
  SHA512:
6
- metadata.gz: 416d42c9f2d15131fbe0b01c91a11b5cb22f3e54d052ebc948304e4f9a02ca1445f5c88882e9af01778e553fcb708c83b24a42b3442582a9e1ee8e6ad1ebf9e0
7
- data.tar.gz: fee4ebcca6c1ee8eb452043552bc720fb8850641414bbf4586d9d668a93a9230c1ed6283220255d4150b7ae135af4010590082af962871bfae4ca8d0c37c2325
6
+ metadata.gz: d9440abbc6e21242e9281fe3c9ca469fa12f9b47abe8115559c4702fdb5f763fa8576d986434ea76829da45b21a05edfead3217d8ab5cfc798f28de2dea0d982
7
+ data.tar.gz: dab81f6a5f102c884c0ab0ec6628934aeb5fff690e87ea24148b421fae70e8efab72425b59e8f558052fb8e7f6dd3a097a1268b9af794abd8e4f5e0397b67176
data/ChangeLog CHANGED
@@ -1,5 +1,29 @@
1
1
  # v0.12
2
2
 
3
+ ## Release 0.12.33 - 2017/03/09
4
+
5
+ ### New features / Enhancement
6
+
7
+ * in_syslog: Backport 'Support rfc5424 syslog format'
8
+ https://github.com/fluent/fluentd/pull/1495
9
+ * in_forward: Add source_address_key and fix source_hostname_key parameters
10
+ https://github.com/fluent/fluentd/pull/1490
11
+ * in_tail: Skip directories when use **/* in path
12
+ https://github.com/fluent/fluentd/pull/1464
13
+ * in_tail: Add limit_recently_modified parameter to limit watching files
14
+ https://github.com/fluent/fluentd/pull/1474
15
+ * in_tail: Skip the refresh of watching list on startup
16
+ https://github.com/fluent/fluentd/pull/1487
17
+ * parser: Allow escape sequence in Apache access log
18
+ https://github.com/fluent/fluentd/pull/1479
19
+ * log: Add Fluent::Log#<< to support some SDKs
20
+ https://github.com/fluent/fluentd/pull/1478
21
+
22
+ ### Bug fixes
23
+
24
+ * config: Set encoding forcefully to avoid UndefinedConversionError
25
+ https://github.com/fluent/fluentd/pull/1477
26
+
3
27
  ## Release 0.12.32 - 2017/02/02
4
28
 
5
29
  ### New features / Enhancement
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.
@@ -65,7 +65,7 @@ module Fluent
65
65
  end
66
66
  end
67
67
 
68
- STRING_TYPE = Proc.new { |val, opts| val }
68
+ STRING_TYPE = Proc.new { |val, opts| val.to_s.force_encoding(Encoding::UTF_8) }
69
69
  ENUM_TYPE = Proc.new { |val, opts|
70
70
  s = val.to_sym
71
71
  list = opts[:list]
@@ -86,7 +86,7 @@ module Fluent
86
86
  value
87
87
  else
88
88
  case type
89
- when :string then value.to_s
89
+ when :string then value.to_s.force_encoding(Encoding::UTF_8)
90
90
  when :integer then value.to_i
91
91
  when :float then value.to_f
92
92
  when :size then Config.size_value(value)
@@ -240,6 +240,9 @@ module Fluent
240
240
  def write(data)
241
241
  @out.write(data)
242
242
  end
243
+ # We need `#<<` method to use this logger class with other
244
+ # libraries such as aws-sdk
245
+ alias << write
243
246
 
244
247
  def flush
245
248
  @out.flush
@@ -463,7 +463,7 @@ module Fluent
463
463
  end
464
464
 
465
465
  class ApacheParser < Parser
466
- REGEXP = /^(?<host>[^ ]*) [^ ]* (?<user>[^ ]*) \[(?<time>[^\]]*)\] "(?<method>\S+)(?: +(?<path>[^\"]*?)(?: +\S*)?)?" (?<code>[^ ]*) (?<size>[^ ]*)(?: "(?<referer>[^\"]*)" "(?<agent>[^\"]*)")?$/
466
+ REGEXP = /^(?<host>[^ ]*) [^ ]* (?<user>[^ ]*) \[(?<time>[^\]]*)\] "(?<method>\S+)(?: +(?<path>(?:[^\"]|\\.)*?)(?: +\S*)?)?" (?<code>[^ ]*) (?<size>[^ ]*)(?: "(?<referer>(?:[^\"]|\\.)*)" "(?<agent>(?:[^\"]|\\.)*)")?$/
467
467
  TIME_FORMAT = "%d/%b/%Y:%H:%M:%S %z"
468
468
 
469
469
  def initialize
@@ -536,9 +536,13 @@ module Fluent
536
536
  REGEXP = /^(?<time>[^ ]*\s*[^ ]* [^ ]*) (?<host>[^ ]*) (?<ident>[a-zA-Z0-9_\/\.\-]*)(?:\[(?<pid>[0-9]+)\])?(?:[^\:]*\:)? *(?<message>.*)$/
537
537
  # From in_syslog default pattern
538
538
  REGEXP_WITH_PRI = /^\<(?<pri>[0-9]+)\>(?<time>[^ ]* {1,2}[^ ]* [^ ]*) (?<host>[^ ]*) (?<ident>[a-zA-Z0-9_\/\.\-]*)(?:\[(?<pid>[0-9]+)\])?(?:[^\:]*\:)? *(?<message>.*)$/
539
+ REGEXP_RFC5424 = /\A^\<(?<pri>[0-9]{1,3})\>[1-9]\d{0,2} (?<time>[^ ]+) (?<host>[^ ]+) (?<ident>[^ ]+) (?<pid>[-0-9]+) (?<msgid>[^ ]+) (?<extradata>(\[(.*)\]|[^ ])) (?<message>.+)$\z/
540
+ REGEXP_DETECT_RFC5424 = /^\<.*\>[1-9]\d{0,2}/
539
541
 
540
542
  config_param :time_format, :string, default: "%b %d %H:%M:%S"
541
543
  config_param :with_priority, :bool, default: false
544
+ config_param :message_format, :enum, list: [:rfc3164, :rfc5424, :auto], default: :rfc3164
545
+ config_param :rfc5424_time_format, :string, default: "%Y-%m-%dT%H:%M:%S.%L%z"
542
546
 
543
547
  def initialize
544
548
  super
@@ -548,7 +552,27 @@ module Fluent
548
552
  def configure(conf)
549
553
  super
550
554
 
551
- @regexp = @with_priority ? REGEXP_WITH_PRI : REGEXP
555
+ @time_parser_rfc3164 = @time_parser_rfc5424 = nil
556
+ @regexp = case @message_format
557
+ when :rfc3164
558
+ class << self
559
+ alias_method :parse, :parse_plain
560
+ end
561
+ @with_priority ? REGEXP_WITH_PRI : REGEXP
562
+ when :rfc5424
563
+ class << self
564
+ alias_method :parse, :parse_plain
565
+ end
566
+ @time_format = @rfc5424_time_format unless conf.has_key?('time_format')
567
+ REGEXP_RFC5424
568
+ when :auto
569
+ class << self
570
+ alias_method :parse, :parse_auto
571
+ end
572
+ @time_parser_rfc3164 = TextParser::TimeParser.new(@time_format)
573
+ @time_parser_rfc5424 = TextParser::TimeParser.new(@rfc5424_time_format)
574
+ nil
575
+ end
552
576
  @time_parser = TextParser::TimeParser.new(@time_format)
553
577
  end
554
578
 
@@ -557,6 +581,21 @@ module Fluent
557
581
  end
558
582
 
559
583
  def parse(text)
584
+ # This is overwritten in configure
585
+ end
586
+
587
+ def parse_auto(text, &block)
588
+ if REGEXP_DETECT_RFC5424.match(text)
589
+ @regexp = REGEXP_RFC5424
590
+ @time_parser = @time_parser_rfc5424
591
+ else
592
+ @regexp = @with_priority ? REGEXP_WITH_PRI : REGEXP
593
+ @time_parser = @time_parser_rfc3164
594
+ end
595
+ parse_plain(text, &block)
596
+ end
597
+
598
+ def parse_plain(text, &block)
560
599
  m = @regexp.match(text)
561
600
  unless m
562
601
  if block_given?
@@ -47,11 +47,24 @@ module Fluent
47
47
  config_param :chunk_size_limit, :size, default: nil
48
48
  desc 'Skip an event if incoming event is invalid.'
49
49
  config_param :skip_invalid_event, :bool, default: false
50
+ desc 'Try to resolve hostname from IP addresses or not.'
51
+ config_param :resolve_hostname, :bool, default: nil
52
+ desc "The field name of the client's source address."
53
+ config_param :source_address_key, :string, default: nil
50
54
  desc "The field name of the client's hostname."
51
55
  config_param :source_hostname_key, :string, default: nil
52
56
 
53
57
  def configure(conf)
54
58
  super
59
+
60
+ if @source_hostname_key
61
+ if @resolve_hostname.nil?
62
+ @resolve_hostname = true
63
+ elsif !@resolve_hostname # user specifies "false" in configure
64
+ raise Fluent::ConfigError, "resolve_hostname must be true with source_hostname_key"
65
+ end
66
+ end
67
+ @enable_field_injection = @source_address_key || @source_hostname_key
55
68
  end
56
69
 
57
70
  def start
@@ -88,7 +101,7 @@ module Fluent
88
101
 
89
102
  def listen
90
103
  log.info "listening fluent socket on #{@bind}:#{@port}"
91
- s = Coolio::TCPServer.new(@bind, @port, Handler, @linger_timeout, log, method(:on_message))
104
+ s = Coolio::TCPServer.new(@bind, @port, Handler, @linger_timeout, log, @resolve_hostname, method(:on_message))
92
105
  s.listen(@backlog) unless @backlog.nil?
93
106
  s
94
107
  end
@@ -161,7 +174,7 @@ module Fluent
161
174
  # PackedForward
162
175
  es = MessagePackEventStream.new(entries)
163
176
  es = check_and_skip_invalid_event(tag, es, peeraddr) if @skip_invalid_event
164
- es = add_source_host(es, peeraddr[2]) if @source_hostname_key
177
+ es = add_source_host(es, peeraddr) if @enable_field_injection
165
178
  router.emit_stream(tag, es)
166
179
  option = msg[2]
167
180
 
@@ -180,7 +193,7 @@ module Fluent
180
193
  }
181
194
  es
182
195
  end
183
- es = add_source_host(es, peeraddr[2]) if @source_hostname_key
196
+ es = add_source_host(es, peeraddr) if @enable_field_injection
184
197
  router.emit_stream(tag, es)
185
198
  option = msg[2]
186
199
 
@@ -194,7 +207,10 @@ module Fluent
194
207
  end
195
208
  return if record.nil?
196
209
  time = Engine.now if time == 0
197
- record[@source_hostname_key] = peeraddr[2] if @source_hostname_key
210
+ if @enable_field_injection
211
+ record[@source_hostname_key] = peeraddr[2] if @source_hostname_key
212
+ record[@source_address_key] = peeraddr[3] if @source_address_key
213
+ end
198
214
  router.emit(tag, time, record)
199
215
  option = msg[3]
200
216
  end
@@ -219,12 +235,31 @@ module Fluent
219
235
  new_es
220
236
  end
221
237
 
222
- def add_source_host(es, host)
238
+ def add_source_host(es, peeraddr)
223
239
  new_es = MultiEventStream.new
224
- es.each { |time, record|
225
- record[@source_hostname_key] = host
226
- new_es.add(time, record)
227
- }
240
+ if @source_address_key && @source_hostname_key
241
+ address = peeraddr[3]
242
+ hostname = peeraddr[2]
243
+ es.each { |time, record|
244
+ record[@source_address_key] = address
245
+ record[@source_hostname_key] = hostname
246
+ new_es.add(time, record)
247
+ }
248
+ elsif @source_address_key
249
+ address = peeraddr[3]
250
+ es.each { |time, record|
251
+ record[@source_address_key] = address
252
+ new_es.add(time, record)
253
+ }
254
+ elsif @source_hostname_key
255
+ hostname = peeraddr[2]
256
+ es.each { |time, record|
257
+ record[@source_hostname_key] = hostname
258
+ new_es.add(time, record)
259
+ }
260
+ else
261
+ raise "BUG: don't call this method in this case"
262
+ end
228
263
  new_es
229
264
  end
230
265
 
@@ -236,11 +271,13 @@ module Fluent
236
271
  class Handler < Coolio::Socket
237
272
  PEERADDR_FAILED = ["?", "?", "name resolusion failed", "?"]
238
273
 
239
- def initialize(io, linger_timeout, log, on_message)
274
+ def initialize(io, linger_timeout, log, resolve_hostname, on_message)
240
275
  super(io)
241
276
 
242
277
  @peeraddr = nil
243
278
  if io.is_a?(TCPSocket) # for unix domain socket support in the future
279
+ io.do_not_reverse_lookup = !resolve_hostname unless resolve_hostname.nil?
280
+
244
281
  @peeraddr = (io.peeraddr rescue PEERADDR_FAILED)
245
282
  opt = [1, linger_timeout].pack('I!I!') # { int l_onoff; int l_linger; }
246
283
  io.setsockopt(Socket::SOL_SOCKET, Socket::SO_LINGER, opt)
@@ -58,6 +58,10 @@ module Fluent
58
58
  config_param :from_encoding, :string, default: nil
59
59
  desc 'Add the log path being tailed to records. Specify the field name to be used.'
60
60
  config_param :path_key, :string, default: nil
61
+ desc 'Limit the watching files that the modification time is within the specified time range (when use \'*\' in path).'
62
+ config_param :limit_recently_modified, :time, default: nil
63
+ desc 'Enable the option to skip the refresh of watching list on startup.'
64
+ config_param :skip_refresh_on_startup, :bool, default: false
61
65
 
62
66
  attr_reader :paths
63
67
 
@@ -128,7 +132,7 @@ module Fluent
128
132
  end
129
133
 
130
134
  @loop = Coolio::Loop.new
131
- refresh_watchers
135
+ refresh_watchers unless @skip_refresh_on_startup
132
136
 
133
137
  @refresh_trigger = TailWatcher::TimerWatcher.new(@refresh_interval, true, log, &method(:refresh_watchers))
134
138
  @refresh_trigger.attach(@loop)
@@ -153,8 +157,12 @@ module Fluent
153
157
  path = date.strftime(path)
154
158
  if path.include?('*')
155
159
  paths += Dir.glob(path).select { |p|
156
- if File.readable?(p)
157
- true
160
+ if File.readable?(p) && !File.directory?(p)
161
+ if @limit_recently_modified && File.mtime(p) < (date - @limit_recently_modified)
162
+ false
163
+ else
164
+ true
165
+ end
158
166
  else
159
167
  log.warn "#{p} unreadable. It is excluded and would be examined next time."
160
168
  false
@@ -16,6 +16,6 @@
16
16
 
17
17
  module Fluent
18
18
 
19
- VERSION = '0.12.32'
19
+ VERSION = '0.12.33'
20
20
 
21
21
  end
@@ -68,6 +68,17 @@ class TestConfigTypes < ::Test::Unit::TestCase
68
68
  assert_equal ' ', Config::STRING_TYPE.call(' ', {})
69
69
  end
70
70
 
71
+ data('latin' => 'Märch',
72
+ 'ascii' => 'ascii',
73
+ 'space' => ' ',
74
+ 'number' => '1',
75
+ 'Hiragana' => 'あいうえお')
76
+ test 'string w/ binary' do |str|
77
+ actual = Config::STRING_TYPE.call(str.b, {})
78
+ assert_equal str, actual
79
+ assert_equal Encoding::UTF_8, actual.encoding
80
+ end
81
+
71
82
  test 'enum' do
72
83
  assert_equal :val, Config::ENUM_TYPE.call('val', {list: [:val, :value, :v]})
73
84
  assert_equal :v, Config::ENUM_TYPE.call('v', {list: [:val, :value, :v]})
@@ -142,6 +153,14 @@ class TestConfigTypes < ::Test::Unit::TestCase
142
153
  assert_raise(RuntimeError.new("unknown type in REFORMAT: foo")){ Config::HASH_TYPE.call("x:1,y:2", {value_type: :foo}) }
143
154
  end
144
155
 
156
+ data('latin' => ['3:Märch', {"3"=>"Märch"}],
157
+ 'ascii' => ['ascii:ascii', {"ascii"=>"ascii"}],
158
+ 'number' => ['number:1', {"number"=>"1"}],
159
+ 'Hiragana' => ['hiragana:あいうえお', {"hiragana"=>"あいうえお"}])
160
+ test 'hash w/ binary' do |(target, expected)|
161
+ assert_equal(expected, Config::HASH_TYPE.call(target.b, { value_type: :string }))
162
+ end
163
+
145
164
  test 'array' do
146
165
  assert_equal(["1","2",1], Config::ARRAY_TYPE.call('["1","2",1]', {}))
147
166
  assert_equal(["1","2","1"], Config::ARRAY_TYPE.call('1,2,1', {}))
File without changes
@@ -498,52 +498,104 @@ class ForwardInputTest < Test::Unit::TestCase
498
498
  @responses << res if try_to_receive_response
499
499
  end
500
500
 
501
- # TODO: Use sub_test_case. Currently Errno::EADDRINUSE happens inside sub_test_case
502
- test 'message protocol with source_hostname_key' do
503
- execute_test { |events|
504
- events.each { |tag, time, record|
505
- send_data [tag, time, record].to_msgpack
501
+ LOCALHOST_HOSTNAME_GETTER = ->(){sock = UDPSocket.new(::Socket::AF_INET); sock.do_not_reverse_lookup = false; sock.connect("127.0.0.1", 2048); sock.peeraddr[2] }
502
+ LOCALHOST_HOSTNAME = LOCALHOST_HOSTNAME_GETTER.call
503
+ DUMMY_SOCK = Struct.new(:remote_host, :remote_addr, :remote_port).new(LOCALHOST_HOSTNAME, "127.0.0.1", 0)
504
+
505
+ sub_test_case 'source_hostname_key and source_address_key features' do
506
+ test 'resolve_hostname must be true with source_hostname_key' do
507
+ assert_raise(Fluent::ConfigError) {
508
+ create_driver(CONFIG + <<EOS)
509
+ resolve_hostname false
510
+ source_hostname_key hostname
511
+ EOS
506
512
  }
507
- }
508
- end
513
+ end
514
+ data(
515
+ both: [:hostname, :address],
516
+ hostname: [:hostname],
517
+ address: [:address],
518
+ )
519
+ test 'message protocol' do |keys|
520
+ execute_test(*keys) { |events|
521
+ events.each { |tag, time, record|
522
+ send_data [tag, time, record].to_msgpack
523
+ }
524
+ }
525
+ end
509
526
 
510
- test 'forward protocol with source_hostname_key' do
511
- execute_test { |events|
512
- entries = []
513
- events.each {|tag,time,record|
514
- entries << [time, record]
527
+ data(
528
+ both: [:hostname, :address],
529
+ hostname: [:hostname],
530
+ address: [:address],
531
+ )
532
+ test 'forward protocol' do |keys|
533
+ execute_test(*keys) { |events|
534
+ entries = []
535
+ events.each {|tag,time,record|
536
+ entries << [time, record]
537
+ }
538
+ send_data ['tag1', entries].to_msgpack
515
539
  }
516
- send_data ['tag1', entries].to_msgpack
517
- }
518
- end
540
+ end
519
541
 
520
- test 'packed forward protocol with source_hostname_key' do
521
- execute_test { |events|
522
- entries = ''
523
- events.each { |tag, time, record|
524
- Fluent::Engine.msgpack_factory.packer(entries).write([time, record]).flush
542
+ data(
543
+ both: [:hostname, :address],
544
+ hostname: [:hostname],
545
+ address: [:address],
546
+ )
547
+ test 'packed forward protocol' do |keys|
548
+ execute_test(*keys) { |events|
549
+ entries = ''
550
+ events.each { |tag, time, record|
551
+ Fluent::Engine.msgpack_factory.packer(entries).write([time, record]).flush
552
+ }
553
+ send_data Fluent::Engine.msgpack_factory.packer.write(["tag1", entries]).to_s
525
554
  }
526
- send_data Fluent::Engine.msgpack_factory.packer.write(["tag1", entries]).to_s
527
- }
528
- end
555
+ end
529
556
 
530
- def execute_test(&block)
531
- d = create_driver(CONFIG + 'source_hostname_key source')
557
+ def execute_test(*keys, &block)
558
+ conf = CONFIG.dup
559
+ if keys.include?(:hostname)
560
+ conf << <<EOL
561
+ source_hostname_key source_hostname
562
+ EOL
563
+ end
564
+ if keys.include?(:address)
565
+ conf << <<EOL
566
+ source_address_key source_address
567
+ EOL
568
+ end
569
+ d = create_driver(conf)
532
570
 
533
- time = Time.parse("2011-01-02 13:14:15 UTC").to_i
534
- events = [
535
- ["tag1", time, {"a"=>1}],
536
- ["tag1", time, {"a"=>2}]
537
- ]
538
- d.expected_emits_length = events.length
571
+ time = Time.parse("2011-01-02 13:14:15 UTC").to_i
572
+ events = [
573
+ ["tag1", time, {"a"=>1}],
574
+ ["tag1", time, {"a"=>2}]
575
+ ]
576
+ d.expected_emits_length = events.length
539
577
 
540
- d.run do
541
- block.call(events)
542
- end
578
+ d.run do
579
+ block.call(events)
580
+ end
543
581
 
544
- d.emits.each { |tag, _time, record|
545
- assert_true record.has_key?('source')
546
- }
582
+ d.emits.each { |tag, _time, record|
583
+ if keys.include?(:hostname)
584
+ assert_true record.has_key?('source_hostname')
585
+ assert_equal DUMMY_SOCK.remote_host, record['source_hostname']
586
+ unless keys.include?(:address)
587
+ assert_false record.has_key?('source_address')
588
+ end
589
+ end
590
+ if keys.include?(:address)
591
+ assert_true record.has_key?('source_address')
592
+ assert_equal DUMMY_SOCK.remote_addr, record['source_address']
593
+ unless keys.include?(:hostname)
594
+ assert_false record.has_key?('source_hostname')
595
+ end
596
+ end
597
+ }
598
+ end
547
599
  end
548
600
 
549
601
  # TODO heartbeat
@@ -2,6 +2,7 @@ require_relative '../helper'
2
2
  require 'fluent/test'
3
3
  require 'net/http'
4
4
  require 'flexmock/test_unit'
5
+ require 'timecop'
5
6
 
6
7
  class TailInputTest < Test::Unit::TestCase
7
8
  include FlexMock::TestCase
@@ -697,6 +698,25 @@ class TailInputTest < Test::Unit::TestCase
697
698
  assert_equal EX_PATHS - [EX_PATHS.last], plugin.expand_paths.sort
698
699
  end
699
700
 
701
+ def test_log_file_without_extension
702
+ expected_files = [
703
+ 'test/plugin/data/log/bar',
704
+ 'test/plugin/data/log/foo/bar.log',
705
+ 'test/plugin/data/log/foo/bar2',
706
+ 'test/plugin/data/log/test.log'
707
+ ]
708
+
709
+ config = config_element("", "", {
710
+ "tag" => "tail",
711
+ "path" => "test/plugin/data/log/**/*",
712
+ "format" => "none",
713
+ "pos_file" => "#{TMP_DIR}/tail.pos"
714
+ })
715
+
716
+ plugin = create_driver(config, false).instance
717
+ assert_equal expected_files, plugin.expand_paths.sort
718
+ end
719
+
700
720
  def test_refresh_watchers
701
721
  plugin = create_driver(EX_CONFIG, false).instance
702
722
  sio = StringIO.new
@@ -992,4 +1012,43 @@ class TailInputTest < Test::Unit::TestCase
992
1012
  assert_equal(files, [emits[2][2]["path"], emits[3][2]["path"]].sort)
993
1013
  end
994
1014
  end
1015
+
1016
+ def test_limit_recently_modified
1017
+ now = Time.new(2010, 1, 2, 3, 4, 5)
1018
+ FileUtils.touch("#{TMP_DIR}/tail_unwatch.txt", mtime: (now - 3601))
1019
+ FileUtils.touch("#{TMP_DIR}/tail_watch1.txt", mtime: (now - 3600))
1020
+ FileUtils.touch("#{TMP_DIR}/tail_watch2.txt", mtime: now)
1021
+
1022
+ config = config_element('', '', {
1023
+ 'tag' => 'tail',
1024
+ 'path' => "#{TMP_DIR}/*.txt",
1025
+ 'format' => 'none',
1026
+ 'limit_recently_modified' => '3600s'
1027
+ })
1028
+
1029
+ expected_files = [
1030
+ "#{TMP_DIR}/tail_watch1.txt",
1031
+ "#{TMP_DIR}/tail_watch2.txt"
1032
+ ]
1033
+
1034
+ Timecop.freeze(now) do
1035
+ plugin = create_driver(config, false).instance
1036
+ assert_equal expected_files, plugin.expand_paths.sort
1037
+ end
1038
+ end
1039
+
1040
+ def test_skip_refresh_on_startup
1041
+ FileUtils.touch("#{TMP_DIR}/tail.txt")
1042
+ config = config_element('', '', {
1043
+ 'tag' => 'tail',
1044
+ 'path' => "#{TMP_DIR}/*.txt",
1045
+ 'format' => 'none',
1046
+ 'refresh_interval' => 1,
1047
+ 'skip_refresh_on_startup' => true
1048
+ })
1049
+ d = create_driver(config, false)
1050
+ d.run {
1051
+ sleep 0.1 until d.instance.instance_variable_get(:@tails).keys.size == 1
1052
+ }
1053
+ end
995
1054
  end
@@ -305,6 +305,10 @@ class PluginLoggerTest < Test::Unit::TestCase
305
305
  @log.write("log")
306
306
  end
307
307
 
308
+ def test_write_alias
309
+ assert(@log.respond_to?(:<<))
310
+ end
311
+
308
312
  def test_out
309
313
  assert_equal(@log.out, @logger.out)
310
314
  @log.out = Object.new
@@ -292,6 +292,14 @@ module ParserTest
292
292
  assert_equal(@expected, record)
293
293
  }
294
294
  end
295
+
296
+ def test_parse_with_escape_sequence
297
+ @parser.parse('192.168.0.1 - - [28/Feb/2013:12:00:00 +0900] "GET /\" HTTP/1.1" 200 777 "referer \\\ \"" "user agent \\\ \""') { |_, record|
298
+ assert_equal('/\"', record['path'])
299
+ assert_equal('referer \\\ \"', record['referer'])
300
+ assert_equal('user agent \\\ \"', record['agent'])
301
+ }
302
+ end
295
303
  end
296
304
 
297
305
  class SyslogParserTest < ::Test::Unit::TestCase
@@ -356,6 +364,182 @@ module ParserTest
356
364
  assert_equal "Feb 28 00:00:12", record['time']
357
365
  end
358
366
  end
367
+
368
+ class TestRFC5424Regexp < self
369
+ def test_parse_with_rfc5424_message
370
+ @parser.configure(
371
+ 'time_format' => '%Y-%m-%dT%H:%M:%S.%L%z',
372
+ 'message_format' => 'rfc5424',
373
+ )
374
+ text = '<16>1 2017-02-06T13:14:15.003Z 192.168.0.1 fluentd - - - Hi, from Fluentd!'
375
+ @parser.parse(text) do |time, record|
376
+ assert_equal(event_time("2017-02-06T13:14:15.003Z", format: '%Y-%m-%dT%H:%M:%S.%L%z'), time)
377
+ assert_equal "-", record["pid"]
378
+ assert_equal "-", record["msgid"]
379
+ assert_equal "-", record["extradata"]
380
+ assert_equal "Hi, from Fluentd!", record["message"]
381
+ end
382
+ end
383
+
384
+ def test_parse_with_rfc5424_message_without_time_format
385
+ @parser.configure(
386
+ 'message_format' => 'rfc5424',
387
+ )
388
+ text = '<16>1 2017-02-06T13:14:15.003Z 192.168.0.1 fluentd - - - Hi, from Fluentd!'
389
+ @parser.instance.parse(text) do |time, record|
390
+ assert_equal(event_time("2017-02-06T13:14:15.003Z", format: '%Y-%m-%dT%H:%M:%S.%L%z'), time)
391
+ assert_equal "-", record["pid"]
392
+ assert_equal "-", record["msgid"]
393
+ assert_equal "-", record["extradata"]
394
+ assert_equal "Hi, from Fluentd!", record["message"]
395
+ end
396
+ end
397
+
398
+ def test_parse_with_rfc5424_structured_message
399
+ @parser.configure(
400
+ 'time_format' => '%Y-%m-%dT%H:%M:%S.%L%z',
401
+ 'message_format' => 'rfc5424',
402
+ )
403
+ 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!'
404
+ @parser.parse(text) do |time, record|
405
+ assert_equal(event_time("2017-02-06T13:14:15.003Z", format: '%Y-%m-%dT%H:%M:%S.%L%z'), time)
406
+ assert_equal "11111", record["pid"]
407
+ assert_equal "ID24224", record["msgid"]
408
+ assert_equal "[exampleSDID@20224 iut=\"3\" eventSource=\"Application\" eventID=\"11211\"]",
409
+ record["extradata"]
410
+ assert_equal "Hi, from Fluentd!", record["message"]
411
+ end
412
+ end
413
+ end
414
+
415
+ class TestAutoRegexp < self
416
+ def test_auto_with_legacy_syslog_message
417
+ @parser.configure(
418
+ 'time_format' => '%b %d %M:%S:%H',
419
+ 'mseeage_format' => 'auto',
420
+ )
421
+ text = 'Feb 28 00:00:12 192.168.0.1 fluentd[11111]: [error] Syslog test'
422
+ @parser.parse(text) do |time, record|
423
+ assert_equal(event_time("Feb 28 00:00:12", format: '%b %d %M:%S:%H'), time)
424
+ assert_equal(@expected, record)
425
+ end
426
+ end
427
+
428
+ def test_auto_with_legacy_syslog_priority_message
429
+ @parser.configure(
430
+ 'time_format' => '%b %d %M:%S:%H',
431
+ 'with_priority' => true,
432
+ 'mseeage_format' => 'auto',
433
+ )
434
+ text = '<6>Feb 28 12:00:00 192.168.0.1 fluentd[11111]: [error] Syslog test'
435
+ @parser.parse(text) do |time, record|
436
+ assert_equal(event_time("Feb 28 12:00:00", format: '%b %d %M:%S:%H'), time)
437
+ assert_equal(@expected.merge('pri' => 6), record)
438
+ end
439
+ end
440
+
441
+ def test_parse_with_rfc5424_message
442
+ @parser.configure(
443
+ 'time_format' => '%Y-%m-%dT%H:%M:%S.%L%z',
444
+ 'message_format' => 'auto',
445
+ )
446
+ text = '<16>1 2017-02-06T13:14:15.003Z 192.168.0.1 fluentd - - - Hi, from Fluentd!'
447
+ @parser.parse(text) do |time, record|
448
+ assert_equal(event_time("2017-02-06T13:14:15.003Z", format: '%Y-%m-%dT%H:%M:%S.%L%z'), time)
449
+ assert_equal "-", record["pid"]
450
+ assert_equal "-", record["msgid"]
451
+ assert_equal "-", record["extradata"]
452
+ assert_equal "Hi, from Fluentd!", record["message"]
453
+ end
454
+ end
455
+
456
+ def test_parse_with_rfc5424_structured_message
457
+ @parser.configure(
458
+ 'time_format' => '%Y-%m-%dT%H:%M:%S.%L%z',
459
+ 'message_format' => 'auto',
460
+ )
461
+ 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!'
462
+ @parser.parse(text) do |time, record|
463
+ assert_equal(event_time("2017-02-06T13:14:15.003Z", format: '%Y-%m-%dT%H:%M:%S.%L%z'), time)
464
+ assert_equal "11111", record["pid"]
465
+ assert_equal "ID24224", record["msgid"]
466
+ assert_equal "[exampleSDID@20224 iut=\"3\" eventSource=\"Application\" eventID=\"11211\"]",
467
+ record["extradata"]
468
+ assert_equal "Hi, from Fluentd!", record["message"]
469
+ end
470
+ end
471
+
472
+ def test_parse_with_both_message_type
473
+ @parser.configure(
474
+ 'time_format' => '%b %d %M:%S:%H',
475
+ 'rfc5424_time_format' => '%Y-%m-%dT%H:%M:%S.%L%z',
476
+ 'message_format' => 'auto',
477
+ )
478
+ text = 'Feb 28 12:00:00 192.168.0.1 fluentd[11111]: [error] Syslog test'
479
+ @parser.parse(text) do |time, record|
480
+ assert_equal(event_time("Feb 28 12:00:00", format: '%b %d %M:%S:%H'), time)
481
+ assert_equal(@expected, record)
482
+ end
483
+ 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!'
484
+ @parser.parse(text) do |time, record|
485
+ assert_equal(event_time("2017-02-06T13:14:15.003Z", format: '%Y-%m-%dT%H:%M:%S.%L%z'), time)
486
+ assert_equal "11111", record["pid"]
487
+ assert_equal "ID24224", record["msgid"]
488
+ assert_equal "[exampleSDID@20224 iut=\"3\" eventSource=\"Application\" eventID=\"11211\"]",
489
+ record["extradata"]
490
+ assert_equal "Hi, from Fluentd!", record["message"]
491
+ end
492
+ text = 'Feb 28 12:00:02 192.168.0.1 fluentd[11111]: [error] Syslog test'
493
+ @parser.parse(text) do |time, record|
494
+ assert_equal(event_time("Feb 28 12:00:02", format: '%b %d %M:%S:%H'), time)
495
+ assert_equal(@expected, record)
496
+ end
497
+ text = '<16>1 2017-02-06T13:14:15.003Z 192.168.0.1 fluentd - - - Hi, from Fluentd!'
498
+ @parser.parse(text) do |time, record|
499
+ assert_equal(event_time("2017-02-06T13:14:15.003Z", format: '%Y-%m-%dT%H:%M:%S.%L%z'), time)
500
+ assert_equal "-", record["pid"]
501
+ assert_equal "-", record["msgid"]
502
+ assert_equal "-", record["extradata"]
503
+ assert_equal "Hi, from Fluentd!", record["message"]
504
+ end
505
+ end
506
+
507
+ def test_parse_with_both_message_type_and_priority
508
+ @parser.configure(
509
+ 'time_format' => '%b %d %M:%S:%H',
510
+ 'rfc5424_time_format' => '%Y-%m-%dT%H:%M:%S.%L%z',
511
+ 'with_priority' => true,
512
+ 'message_format' => 'auto',
513
+ )
514
+ text = '<6>Feb 28 12:00:00 192.168.0.1 fluentd[11111]: [error] Syslog test'
515
+ @parser.parse(text) do |time, record|
516
+ assert_equal(event_time("Feb 28 12:00:00", format: '%b %d %M:%S:%H'), time)
517
+ assert_equal(@expected.merge('pri' => 6), record)
518
+ end
519
+ 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!'
520
+ @parser.parse(text) do |time, record|
521
+ assert_equal(event_time("2017-02-06T13:14:15.003Z", format: '%Y-%m-%dT%H:%M:%S.%L%z'), time)
522
+ assert_equal "11111", record["pid"]
523
+ assert_equal "ID24224", record["msgid"]
524
+ assert_equal "[exampleSDID@20224 iut=\"3\" eventSource=\"Application\" eventID=\"11211\"]",
525
+ record["extradata"]
526
+ assert_equal "Hi, from Fluentd!", record["message"]
527
+ end
528
+ text = '<16>Feb 28 12:00:02 192.168.0.1 fluentd[11111]: [error] Syslog test'
529
+ @parser.parse(text) do |time, record|
530
+ assert_equal(event_time("Feb 28 12:00:02", format: '%b %d %M:%S:%H'), time)
531
+ assert_equal(@expected.merge('pri' => 16), record)
532
+ end
533
+ text = '<16>1 2017-02-06T13:14:15.003Z 192.168.0.1 fluentd - - - Hi, from Fluentd!'
534
+ @parser.parse(text) do |time, record|
535
+ assert_equal(event_time("2017-02-06T13:14:15.003Z", format: '%Y-%m-%dT%H:%M:%S.%L%z'), time)
536
+ assert_equal "-", record["pid"]
537
+ assert_equal "-", record["msgid"]
538
+ assert_equal "-", record["extradata"]
539
+ assert_equal "Hi, from Fluentd!", record["message"]
540
+ end
541
+ end
542
+ end
359
543
  end
360
544
 
361
545
  class JsonParserTest < ::Test::Unit::TestCase
@@ -572,11 +756,10 @@ module ParserTest
572
756
  end
573
757
 
574
758
  data('array param' => '["a","b","c","d","e","f"]', 'string param' => 'a,b,c,d,e,f')
575
- def test_parse_with_null_value_pattern
759
+ def test_parse_with_null_value_pattern(param)
576
760
  parser = TextParser::TSVParser.new
577
761
  parser.configure(
578
762
  'keys'=>param,
579
- 'time_key'=>'time',
580
763
  'null_value_pattern'=>'^(-|null|NULL)$'
581
764
  )
582
765
  parser.parse("-\tnull\tNULL\t\t--\tnuLL") do |time, record|
@@ -590,11 +773,10 @@ module ParserTest
590
773
  end
591
774
 
592
775
  data('array param' => '["a","b"]', 'string param' => 'a,b')
593
- def test_parse_with_null_empty_string
776
+ def test_parse_with_null_empty_string(param)
594
777
  parser = TextParser::TSVParser.new
595
778
  parser.configure(
596
779
  'keys'=>param,
597
- 'time_key'=>'time',
598
780
  'null_empty_string'=>true
599
781
  )
600
782
  parser.parse("\t ") do |time, record|
@@ -661,29 +843,27 @@ module ParserTest
661
843
  end
662
844
 
663
845
  data('array param' => '["a","b","c","d","e","f"]', 'string param' => 'a,b,c,d,e,f')
664
- def test_parse_with_null_value_pattern
846
+ def test_parse_with_null_value_pattern(param)
665
847
  parser = TextParser::CSVParser.new
666
848
  parser.configure(
667
849
  'keys'=>param,
668
- 'time_key'=>'time',
669
850
  'null_value_pattern'=>'^(-|null|NULL)$'
670
851
  )
671
852
  parser.parse("-,null,NULL,,--,nuLL") do |time, record|
672
853
  assert_nil record['a']
673
854
  assert_nil record['b']
674
855
  assert_nil record['c']
675
- assert_equal record['d'], ''
856
+ assert_nil record['d']
676
857
  assert_equal record['e'], '--'
677
858
  assert_equal record['f'], 'nuLL'
678
859
  end
679
860
  end
680
861
 
681
862
  data('array param' => '["a","b"]', 'string param' => 'a,b')
682
- def test_parse_with_null_empty_string
863
+ def test_parse_with_null_empty_string(param)
683
864
  parser = TextParser::CSVParser.new
684
865
  parser.configure(
685
866
  'keys'=>param,
686
- 'time_key'=>'time',
687
867
  'null_empty_string'=>true
688
868
  )
689
869
  parser.parse(", ") do |time, record|
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.12.32
4
+ version: 0.12.33
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-03 00:00:00.000000000 Z
11
+ date: 2017-03-09 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: msgpack
@@ -425,6 +425,7 @@ files:
425
425
  - test/plugin/data/2010/01/20100102.log
426
426
  - test/plugin/data/log/bar
427
427
  - test/plugin/data/log/foo/bar.log
428
+ - test/plugin/data/log/foo/bar2
428
429
  - test/plugin/data/log/test.log
429
430
  - test/plugin/test_buf_file.rb
430
431
  - test/plugin/test_buf_memory.rb
@@ -515,6 +516,7 @@ test_files:
515
516
  - test/plugin/data/2010/01/20100102.log
516
517
  - test/plugin/data/log/bar
517
518
  - test/plugin/data/log/foo/bar.log
519
+ - test/plugin/data/log/foo/bar2
518
520
  - test/plugin/data/log/test.log
519
521
  - test/plugin/test_buf_file.rb
520
522
  - test/plugin/test_buf_memory.rb