fluentd 1.10.4-x64-mingw32 → 1.11.4-x64-mingw32

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of fluentd might be problematic. Click here for more details.

Files changed (64) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +100 -0
  3. data/example/copy_roundrobin.conf +3 -3
  4. data/example/counter.conf +1 -1
  5. data/example/filter_stdout.conf +2 -2
  6. data/example/{in_dummy_blocks.conf → in_sample_blocks.conf} +4 -4
  7. data/example/{in_dummy_with_compression.conf → in_sample_with_compression.conf} +3 -3
  8. data/example/logevents.conf +5 -5
  9. data/example/multi_filters.conf +1 -1
  10. data/example/out_exec_filter.conf +2 -2
  11. data/example/out_forward.conf +1 -1
  12. data/example/out_forward_buf_file.conf +1 -1
  13. data/example/out_forward_client.conf +5 -5
  14. data/example/out_forward_heartbeat_none.conf +1 -1
  15. data/example/out_forward_sd.conf +1 -1
  16. data/example/out_forward_shared_key.conf +2 -2
  17. data/example/out_forward_tls.conf +1 -1
  18. data/example/out_forward_users.conf +3 -3
  19. data/example/out_null.conf +4 -4
  20. data/example/secondary_file.conf +1 -1
  21. data/fluentd.gemspec +5 -5
  22. data/lib/fluent/command/fluentd.rb +11 -0
  23. data/lib/fluent/log.rb +33 -3
  24. data/lib/fluent/match.rb +9 -0
  25. data/lib/fluent/plugin/buffer.rb +49 -40
  26. data/lib/fluent/plugin/buffer/chunk.rb +2 -1
  27. data/lib/fluent/plugin/in_dummy.rb +2 -123
  28. data/lib/fluent/plugin/in_exec.rb +4 -2
  29. data/lib/fluent/plugin/in_gc_stat.rb +16 -0
  30. data/lib/fluent/plugin/in_http.rb +148 -77
  31. data/lib/fluent/plugin/in_sample.rb +141 -0
  32. data/lib/fluent/plugin/in_tail.rb +2 -2
  33. data/lib/fluent/plugin/in_unix.rb +77 -77
  34. data/lib/fluent/plugin/out_http.rb +1 -1
  35. data/lib/fluent/plugin/output.rb +8 -5
  36. data/lib/fluent/plugin/parser_json.rb +5 -2
  37. data/lib/fluent/plugin/parser_syslog.rb +215 -54
  38. data/lib/fluent/plugin_helper/cert_option.rb +5 -8
  39. data/lib/fluent/plugin_helper/child_process.rb +3 -2
  40. data/lib/fluent/plugin_helper/inject.rb +2 -1
  41. data/lib/fluent/plugin_helper/socket.rb +1 -1
  42. data/lib/fluent/supervisor.rb +11 -5
  43. data/lib/fluent/system_config.rb +2 -1
  44. data/lib/fluent/version.rb +1 -1
  45. data/test/plugin/test_buffer.rb +4 -0
  46. data/test/plugin/test_in_exec.rb +18 -0
  47. data/test/plugin/test_in_gc_stat.rb +24 -1
  48. data/test/plugin/test_in_http.rb +57 -0
  49. data/test/plugin/{test_in_dummy.rb → test_in_sample.rb} +25 -25
  50. data/test/plugin/test_in_tail.rb +3 -0
  51. data/test/plugin/test_in_unix.rb +128 -72
  52. data/test/plugin/test_output.rb +12 -0
  53. data/test/plugin/test_parser_syslog.rb +66 -29
  54. data/test/plugin_helper/data/cert/empty.pem +0 -0
  55. data/test/plugin_helper/test_cert_option.rb +7 -0
  56. data/test/plugin_helper/test_child_process.rb +15 -0
  57. data/test/plugin_helper/test_http_server_helper.rb +5 -0
  58. data/test/plugin_helper/test_inject.rb +13 -0
  59. data/test/plugin_helper/test_server.rb +34 -0
  60. data/test/plugin_helper/test_socket.rb +8 -0
  61. data/test/test_log.rb +44 -0
  62. data/test/test_match.rb +11 -0
  63. data/test/test_static_config_analysis.rb +2 -2
  64. metadata +19 -16
@@ -0,0 +1,141 @@
1
+ #
2
+ # Fluentd
3
+ #
4
+ # Licensed under the Apache License, Version 2.0 (the "License");
5
+ # you may not use this file except in compliance with the License.
6
+ # You may obtain a copy of the License at
7
+ #
8
+ # http://www.apache.org/licenses/LICENSE-2.0
9
+ #
10
+ # Unless required by applicable law or agreed to in writing, software
11
+ # distributed under the License is distributed on an "AS IS" BASIS,
12
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ # See the License for the specific language governing permissions and
14
+ # limitations under the License.
15
+ #
16
+
17
+ require 'json'
18
+
19
+ require 'fluent/plugin/input'
20
+ require 'fluent/config/error'
21
+
22
+ module Fluent::Plugin
23
+ class SampleInput < Input
24
+ Fluent::Plugin.register_input('sample', self)
25
+ Fluent::Plugin.register_input('dummy', self)
26
+
27
+ helpers :thread, :storage
28
+
29
+ BIN_NUM = 10
30
+ DEFAULT_STORAGE_TYPE = 'local'
31
+
32
+ desc "The value is the tag assigned to the generated events."
33
+ config_param :tag, :string
34
+ desc "The number of events in event stream of each emits."
35
+ config_param :size, :integer, default: 1
36
+ desc "It configures how many events to generate per second."
37
+ config_param :rate, :integer, default: 1
38
+ desc "If specified, each generated event has an auto-incremented key field."
39
+ config_param :auto_increment_key, :string, default: nil
40
+ desc "The boolean to suspend-and-resume incremental value after restart"
41
+ config_param :suspend, :bool, default: false,deprecated: 'This parameters is ignored'
42
+ desc "The sample data to be generated. An array of JSON hashes or a single JSON hash."
43
+ config_param :sample, alias: :dummy, default: [{"message" => "sample"}] do |val|
44
+ begin
45
+ parsed = JSON.parse(val)
46
+ rescue JSON::ParserError => ex
47
+ # Fluent::ConfigParseError, "got incomplete JSON" will be raised
48
+ # at literal_parser.rb with --use-v1-config, but I had to
49
+ # take care at here for the case of --use-v0-config.
50
+ raise Fluent::ConfigError, "#{ex.class}: #{ex.message}"
51
+ end
52
+ sample = parsed.is_a?(Array) ? parsed : [parsed]
53
+ sample.each_with_index do |e, i|
54
+ raise Fluent::ConfigError, "#{i}th element of sample, #{e}, is not a hash" unless e.is_a?(Hash)
55
+ end
56
+ sample
57
+ end
58
+
59
+ def initialize
60
+ super
61
+ @storage = nil
62
+ end
63
+
64
+ def configure(conf)
65
+ super
66
+ @sample_index = 0
67
+ config = conf.elements.select{|e| e.name == 'storage' }.first
68
+ @storage = storage_create(usage: 'suspend', conf: config, default_type: DEFAULT_STORAGE_TYPE)
69
+ end
70
+
71
+ def multi_workers_ready?
72
+ true
73
+ end
74
+
75
+ def start
76
+ super
77
+
78
+ @storage.put(:increment_value, 0) unless @storage.get(:increment_value)
79
+ # keep 'dummy' to avoid breaking changes for existing environment. Change it in fluentd v2
80
+ @storage.put(:dummy_index, 0) unless @storage.get(:dummy_index)
81
+
82
+ if @auto_increment_key && !@storage.get(:auto_increment_value)
83
+ @storage.put(:auto_increment_value, -1)
84
+ end
85
+
86
+ thread_create(:sample_input, &method(:run))
87
+ end
88
+
89
+ def run
90
+ batch_num = (@rate / BIN_NUM).to_i
91
+ residual_num = (@rate % BIN_NUM)
92
+ while thread_current_running?
93
+ current_time = Time.now.to_i
94
+ BIN_NUM.times do
95
+ break unless (thread_current_running? && Time.now.to_i <= current_time)
96
+ wait(0.1) { emit(batch_num) }
97
+ end
98
+ emit(residual_num) if thread_current_running?
99
+ # wait for next second
100
+ while thread_current_running? && Time.now.to_i <= current_time
101
+ sleep 0.01
102
+ end
103
+ end
104
+ end
105
+
106
+ def emit(num)
107
+ begin
108
+ if @size > 1
109
+ num.times do
110
+ router.emit_array(@tag, Array.new(@size) { [Fluent::EventTime.now, generate] })
111
+ end
112
+ else
113
+ num.times { router.emit(@tag, Fluent::EventTime.now, generate) }
114
+ end
115
+ rescue => _
116
+ # ignore all errors not to stop emits by emit errors
117
+ end
118
+ end
119
+
120
+ def generate
121
+ d = @sample[@sample_index]
122
+ unless d
123
+ @sample_index = 0
124
+ d = @sample[@sample_index]
125
+ end
126
+ @sample_index += 1
127
+ if @auto_increment_key
128
+ d = d.dup
129
+ d[@auto_increment_key] = @storage.update(:auto_increment_value){|v| v + 1 }
130
+ end
131
+ d
132
+ end
133
+
134
+ def wait(time)
135
+ start_time = Time.now
136
+ yield
137
+ sleep_time = time - (Time.now - start_time)
138
+ sleep sleep_time if sleep_time > 0
139
+ end
140
+ end
141
+ end
@@ -271,9 +271,9 @@ module Fluent::Plugin
271
271
  end
272
272
  else
273
273
  if is_file
274
- unless @ignore_list.include?(path)
274
+ unless @ignore_list.include?(p)
275
275
  log.warn "#{p} unreadable. It is excluded and would be examined next time."
276
- @ignore_list << path if @ignore_repeated_permission_error
276
+ @ignore_list << p if @ignore_repeated_permission_error
277
277
  end
278
278
  end
279
279
  false
@@ -14,50 +14,68 @@
14
14
  # limitations under the License.
15
15
  #
16
16
 
17
- require 'fileutils'
18
- require 'socket'
17
+ require 'fluent/env'
18
+ require 'fluent/plugin/input'
19
+ require 'fluent/msgpack_factory'
19
20
 
20
21
  require 'cool.io'
21
22
  require 'yajl'
23
+ require 'fileutils'
24
+ require 'socket'
25
+
26
+ module Fluent::Plugin
27
+ # TODO: This plugin will be 3rd party plugin
28
+ class UnixInput < Input
29
+ Fluent::Plugin.register_input('unix', self)
30
+
31
+ helpers :event_loop
32
+
33
+ def initialize
34
+ super
22
35
 
23
- require 'fluent/input'
24
- require 'fluent/event'
36
+ @lsock = nil
37
+ end
25
38
 
26
- module Fluent
27
- # obsolete
28
- class StreamInput < Input
29
- config_param :blocking_timeout, :time, default: 0.5
39
+ desc 'The path to your Unix Domain Socket.'
40
+ config_param :path, :string, default: Fluent::DEFAULT_SOCKET_PATH
41
+ desc 'The backlog of Unix Domain Socket.'
42
+ config_param :backlog, :integer, default: nil
43
+ desc "New tag instead of incoming tag"
44
+ config_param :tag, :string, default: nil
45
+
46
+ def configure(conf)
47
+ super
48
+ end
30
49
 
31
50
  def start
32
51
  super
33
52
 
34
- @loop = Coolio::Loop.new
35
53
  @lsock = listen
36
- @loop.attach(@lsock)
37
- @thread = Thread.new(&method(:run))
54
+ event_loop_attach(@lsock)
38
55
  end
39
56
 
40
57
  def shutdown
41
- @loop.watchers.each {|w| w.detach }
42
- @loop.stop
43
- @lsock.close
44
- @thread.join
58
+ if @lsock
59
+ event_loop_detach(@lsock)
60
+ @lsock.close
61
+ end
45
62
 
46
63
  super
47
64
  end
48
65
 
49
- #def listen
50
- #end
66
+ def listen
67
+ if File.exist?(@path)
68
+ log.warn "Found existing '#{@path}'. Remove this file for in_unix plugin"
69
+ File.unlink(@path)
70
+ end
71
+ FileUtils.mkdir_p(File.dirname(@path))
51
72
 
52
- def run
53
- @loop.run(@blocking_timeout)
54
- rescue
55
- log.error "unexpected error", error: $!.to_s
56
- log.error_backtrace
73
+ log.info "listening fluent socket on #{@path}"
74
+ s = Coolio::UNIXServer.new(@path, Handler, log, method(:on_message))
75
+ s.listen(@backlog) unless @backlog.nil?
76
+ s
57
77
  end
58
78
 
59
- private
60
-
61
79
  # message Entry {
62
80
  # 1: long time
63
81
  # 2: object record
@@ -79,23 +97,27 @@ module Fluent
79
97
  # 3: object record
80
98
  # }
81
99
  def on_message(msg)
82
- # TODO format error
83
- tag = msg[0].to_s
100
+ unless msg.is_a?(Array)
101
+ log.warn "incoming data is broken:", msg: msg
102
+ return
103
+ end
104
+
105
+ tag = @tag || (msg[0].to_s)
84
106
  entries = msg[1]
85
107
 
86
- if entries.class == String
108
+ case entries
109
+ when String
87
110
  # PackedForward
88
- es = MessagePackEventStream.new(entries)
111
+ es = Fluent::MessagePackEventStream.new(entries)
89
112
  router.emit_stream(tag, es)
90
113
 
91
- elsif entries.class == Array
114
+ when Array
92
115
  # Forward
93
- es = MultiEventStream.new
116
+ es = Fluent::MultiEventStream.new
94
117
  entries.each {|e|
95
118
  record = e[1]
96
119
  next if record.nil?
97
- time = e[0]
98
- time = (now ||= EventTime.now) if time.to_i == 0
120
+ time = convert_time(e[0])
99
121
  es.add(time, record)
100
122
  }
101
123
  router.emit_stream(tag, es)
@@ -105,25 +127,28 @@ module Fluent
105
127
  record = msg[2]
106
128
  return if record.nil?
107
129
 
108
- time = msg[1]
109
- time = EventTime.now if time.to_i == 0
130
+ time = convert_time(msg[1])
110
131
  router.emit(tag, time, record)
111
132
  end
112
133
  end
113
134
 
135
+ def convert_time(time)
136
+ case time
137
+ when nil, 0
138
+ Fluent::EventTime.now
139
+ when Fluent::EventTime
140
+ time
141
+ else
142
+ Fluent::EventTime.from_time(Time.at(time))
143
+ end
144
+ end
145
+
114
146
  class Handler < Coolio::Socket
115
147
  def initialize(io, log, on_message)
116
148
  super(io)
117
- if io.is_a?(TCPSocket)
118
- opt = [1, @timeout.to_i].pack('I!I!') # { int l_onoff; int l_linger; }
119
- io.setsockopt(Socket::SOL_SOCKET, Socket::SO_LINGER, opt)
120
- end
149
+
121
150
  @on_message = on_message
122
151
  @log = log
123
- @log.trace {
124
- remote_port, remote_addr = *Socket.unpack_sockaddr_in(@_io.getpeername) rescue nil
125
- "accepted fluent socket from '#{remote_addr}:#{remote_port}': object_id=#{self.object_id}"
126
- }
127
152
  end
128
153
 
129
154
  def on_connect
@@ -131,13 +156,13 @@ module Fluent
131
156
 
132
157
  def on_read(data)
133
158
  first = data[0]
134
- if first == '{' || first == '['
159
+ if first == '{'.freeze || first == '['.freeze
135
160
  m = method(:on_read_json)
136
- @y = Yajl::Parser.new
137
- @y.on_parse_complete = @on_message
161
+ @parser = Yajl::Parser.new
162
+ @parser.on_parse_complete = @on_message
138
163
  else
139
164
  m = method(:on_read_msgpack)
140
- @u = Fluent::MessagePackFactory.msgpack_unpacker
165
+ @parser = Fluent::MessagePackFactory.msgpack_unpacker
141
166
  end
142
167
 
143
168
  singleton_class.module_eval do
@@ -147,17 +172,17 @@ module Fluent
147
172
  end
148
173
 
149
174
  def on_read_json(data)
150
- @y << data
151
- rescue
152
- @log.error "unexpected error", error: $!.to_s
175
+ @parser << data
176
+ rescue => e
177
+ @log.error "unexpected error in json payload", error: e.to_s
153
178
  @log.error_backtrace
154
179
  close
155
180
  end
156
181
 
157
182
  def on_read_msgpack(data)
158
- @u.feed_each(data, &@on_message)
159
- rescue
160
- @log.error "unexpected error", error: $!.to_s
183
+ @parser.feed_each(data, &@on_message)
184
+ rescue => e
185
+ @log.error "unexpected error in msgpack payload", error: e.to_s
161
186
  @log.error_backtrace
162
187
  close
163
188
  end
@@ -167,29 +192,4 @@ module Fluent
167
192
  end
168
193
  end
169
194
  end
170
-
171
- class UnixInput < StreamInput
172
- Plugin.register_input('unix', self)
173
-
174
- desc 'The path to your Unix Domain Socket.'
175
- config_param :path, :string, default: DEFAULT_SOCKET_PATH
176
- desc 'The backlog of Unix Domain Socket.'
177
- config_param :backlog, :integer, default: nil
178
-
179
- def configure(conf)
180
- super
181
- #log.warn "'unix' input is obsoleted and will be removed. Use 'forward' instead."
182
- end
183
-
184
- def listen
185
- if File.exist?(@path)
186
- File.unlink(@path)
187
- end
188
- FileUtils.mkdir_p File.dirname(@path)
189
- log.info "listening fluent socket on #{@path}"
190
- s = Coolio::UNIXServer.new(@path, Handler, log, method(:on_message))
191
- s.listen(@backlog) unless @backlog.nil?
192
- s
193
- end
194
- end
195
195
  end
@@ -215,7 +215,7 @@ module Fluent::Plugin
215
215
  req.basic_auth(@auth.username, @auth.password)
216
216
  end
217
217
  set_headers(req)
218
- req.body = @json_array ? "[#{chunk.read.chop!}]" : chunk.read
218
+ req.body = @json_array ? "[#{chunk.read.chop}]" : chunk.read
219
219
  req
220
220
  end
221
221
 
@@ -340,6 +340,7 @@ module Fluent
340
340
  buffer_conf = conf.elements(name: 'buffer').first || Fluent::Config::Element.new('buffer', '', {}, [])
341
341
  @buffer = Plugin.new_buffer(buffer_type, parent: self)
342
342
  @buffer.configure(buffer_conf)
343
+ @buffer.enable_update_timekeys if @chunk_key_time
343
344
 
344
345
  @flush_at_shutdown = @buffer_config.flush_at_shutdown
345
346
  if @flush_at_shutdown.nil?
@@ -768,17 +769,19 @@ module Fluent
768
769
  end
769
770
  end
770
771
 
771
- if rvalue =~ CHUNK_KEY_PLACEHOLDER_PATTERN
772
- log.warn "chunk key placeholder '#{$1}' not replaced. template:#{str}"
773
- end
774
-
775
- rvalue.sub(CHUNK_ID_PLACEHOLDER_PATTERN) {
772
+ rvalue = rvalue.sub(CHUNK_ID_PLACEHOLDER_PATTERN) {
776
773
  if chunk_passed
777
774
  dump_unique_id_hex(chunk.unique_id)
778
775
  else
779
776
  log.warn "${chunk_id} is not allowed in this plugin. Pass Chunk instead of metadata in extract_placeholders's 2nd argument"
780
777
  end
781
778
  }
779
+
780
+ if rvalue =~ CHUNK_KEY_PLACEHOLDER_PATTERN
781
+ log.warn "chunk key placeholder '#{$1}' not replaced. template:#{str}"
782
+ end
783
+
784
+ rvalue
782
785
  end
783
786
  end
784
787
 
@@ -71,8 +71,11 @@ module Fluent
71
71
  end
72
72
 
73
73
  def parse(text)
74
- r = @load_proc.call(text)
75
- time, record = convert_values(parse_time(r), r)
74
+ record = @load_proc.call(text)
75
+ time = parse_time(record)
76
+ if @execute_convert_values
77
+ time, record = convert_values(time, record)
78
+ end
76
79
  yield time, record
77
80
  rescue @error_class, EncodingError # EncodingError is for oj 3.x or later
78
81
  yield nil, nil
@@ -63,16 +63,22 @@ module Fluent
63
63
  def initialize
64
64
  super
65
65
  @mutex = Mutex.new
66
- @space_count = nil
66
+ @regexp = nil
67
+ @regexp3164 = nil
68
+ @regexp5424 = nil
69
+ @regexp_parser = nil
70
+ @time_parser_rfc3164 = nil
71
+ @time_parser_rfc5424 = nil
72
+ @space_count_rfc3164 = nil
67
73
  @space_count_rfc5424 = nil
74
+ @skip_space_count_rfc3164 = false
75
+ @skip_space_count_rfc5424 = false
76
+ @time_parser_rfc5424_without_subseconds = nil
68
77
  end
69
78
 
70
79
  def configure(conf)
71
80
  super
72
81
 
73
- @time_parser_rfc3164 = @time_parser_rfc5424 = nil
74
- @time_parser_rfc5424_without_subseconds = nil
75
- @support_rfc5424_without_subseconds = false
76
82
  @regexp_parser = @parser_type == :regexp
77
83
  @regexp = case @message_format
78
84
  when :rfc3164
@@ -85,27 +91,49 @@ module Fluent
85
91
  alias_method :parse, :parse_rfc3164
86
92
  end
87
93
  end
94
+ setup_time_parser_3164(@time_format)
88
95
  RFC3164_WITHOUT_TIME_AND_PRI_REGEXP
89
96
  when :rfc5424
90
- class << self
91
- alias_method :parse, :parse_rfc5424_regex
97
+ if @regexp_parser
98
+ class << self
99
+ alias_method :parse, :parse_rfc5424_regex
100
+ end
101
+ else
102
+ class << self
103
+ alias_method :parse, :parse_rfc5424
104
+ end
92
105
  end
93
106
  @time_format = @rfc5424_time_format unless conf.has_key?('time_format')
94
- @support_rfc5424_without_subseconds = true
107
+ setup_time_parser_5424(@time_format)
95
108
  RFC5424_WITHOUT_TIME_AND_PRI_REGEXP
96
109
  when :auto
97
110
  class << self
98
111
  alias_method :parse, :parse_auto
99
112
  end
100
- @time_parser_rfc3164 = time_parser_create(format: @time_format)
101
- @time_parser_rfc5424 = time_parser_create(format: @rfc5424_time_format)
113
+ setup_time_parser_3164(@time_format)
114
+ setup_time_parser_5424(@rfc5424_time_format)
102
115
  nil
103
116
  end
104
117
 
105
- @space_count = @time_format.squeeze(' ').count(' ') + 1
106
- @space_count_rfc5424 = @rfc5424_time_format.squeeze(' ').count(' ') + 1
107
- @time_parser = time_parser_create
118
+ if @regexp_parser
119
+ @regexp3164 = RFC3164_WITHOUT_TIME_AND_PRI_REGEXP
120
+ @regexp5424 = RFC5424_WITHOUT_TIME_AND_PRI_REGEXP
121
+ end
122
+ end
123
+
124
+ def setup_time_parser_3164(time_fmt)
125
+ @time_parser_rfc3164 = time_parser_create(format: time_fmt)
126
+ if ['%b %d %H:%M:%S', '%b %d %H:%M:%S.%N'].include?(time_fmt)
127
+ @skip_space_count_rfc3164 = true
128
+ end
129
+ @space_count_rfc3164 = time_fmt.squeeze(' ').count(' ') + 1
130
+ end
131
+
132
+ def setup_time_parser_5424(time_fmt)
133
+ @time_parser_rfc5424 = time_parser_create(format: time_fmt)
108
134
  @time_parser_rfc5424_without_subseconds = time_parser_create(format: "%Y-%m-%dT%H:%M:%S%z")
135
+ @skip_space_count_rfc5424 = time_fmt.count(' ').zero?
136
+ @space_count_rfc5424 = time_fmt.squeeze(' ').count(' ') + 1
109
137
  end
110
138
 
111
139
  # this method is for tests
@@ -118,14 +146,13 @@ module Fluent
118
146
  end
119
147
 
120
148
  def parse_auto(text, &block)
121
- if REGEXP_DETECT_RFC5424.match(text)
122
- @regexp = RFC5424_WITHOUT_TIME_AND_PRI_REGEXP
123
- @time_parser = @time_parser_rfc5424
124
- @support_rfc5424_without_subseconds = true
125
- parse_rfc5424_regex(text, &block)
149
+ if REGEXP_DETECT_RFC5424.match?(text)
150
+ if @regexp_parser
151
+ parse_rfc5424_regex(text, &block)
152
+ else
153
+ parse_rfc5424(text, &block)
154
+ end
126
155
  else
127
- @regexp = RFC3164_WITHOUT_TIME_AND_PRI_REGEXP
128
- @time_parser = @time_parser_rfc3164
129
156
  if @regexp_parser
130
157
  parse_rfc3164_regex(text, &block)
131
158
  else
@@ -134,6 +161,8 @@ module Fluent
134
161
  end
135
162
  end
136
163
 
164
+ SPLIT_CHAR = ' '.freeze
165
+
137
166
  def parse_rfc3164_regex(text, &block)
138
167
  idx = 0
139
168
  record = {}
@@ -151,22 +180,22 @@ module Fluent
151
180
 
152
181
  i = idx - 1
153
182
  sq = false
154
- @space_count.times do
155
- while text[i + 1] == ' '.freeze
183
+ @space_count_rfc3164.times do
184
+ while text[i + 1] == SPLIT_CHAR
156
185
  sq = true
157
186
  i += 1
158
187
  end
159
188
 
160
- i = text.index(' '.freeze, i + 1)
189
+ i = text.index(SPLIT_CHAR, i + 1)
161
190
  end
162
191
 
163
- time_str = sq ? text.slice(idx, i - idx).squeeze(' ') : text.slice(idx, i - idx)
164
- time = @mutex.synchronize { @time_parser.parse(time_str) }
192
+ time_str = sq ? text.slice(idx, i - idx).squeeze(SPLIT_CHAR) : text.slice(idx, i - idx)
193
+ time = @mutex.synchronize { @time_parser_rfc3164.parse(time_str) }
165
194
  if @keep_time_key
166
195
  record['time'] = time_str
167
196
  end
168
197
 
169
- parse_plain(time, text, i + 1, record, RFC3164_CAPTURES, &block)
198
+ parse_plain(@regexp3164, time, text, i + 1, record, RFC3164_CAPTURES, &block)
170
199
  end
171
200
 
172
201
  def parse_rfc5424_regex(text, &block)
@@ -186,40 +215,36 @@ module Fluent
186
215
  i = idx - 1
187
216
  sq = false
188
217
  @space_count_rfc5424.times {
189
- while text[i + 1] == ' '.freeze
218
+ while text[i + 1] == SPLIT_CHAR
190
219
  sq = true
191
220
  i += 1
192
221
  end
193
222
 
194
- i = text.index(' '.freeze, i + 1)
223
+ i = text.index(SPLIT_CHAR, i + 1)
195
224
  }
196
225
 
197
- time_str = sq ? text.slice(idx, i - idx).squeeze(' '.freeze) : text.slice(idx, i - idx)
226
+ time_str = sq ? text.slice(idx, i - idx).squeeze(SPLIT_CHAR) : text.slice(idx, i - idx)
198
227
  time = @mutex.synchronize do
199
228
  begin
200
- @time_parser.parse(time_str)
229
+ @time_parser_rfc5424.parse(time_str)
201
230
  rescue Fluent::TimeParser::TimeParseError => e
202
- if @support_rfc5424_without_subseconds
203
- log.trace(e)
204
- @time_parser_rfc5424_without_subseconds.parse(time_str)
205
- else
206
- raise
207
- end
231
+ log.trace(e)
232
+ @time_parser_rfc5424_without_subseconds.parse(time_str)
208
233
  end
209
234
  end
210
235
 
211
236
  if @keep_time_key
212
237
  record['time'] = time_str
213
238
  end
214
- parse_plain(time, text, i + 1, record, RFC5424_CAPTURES, &block)
239
+ parse_plain(@regexp5424, time, text, i + 1, record, RFC5424_CAPTURES, &block)
215
240
  end
216
241
 
217
242
  # @param time [EventTime]
218
243
  # @param idx [Integer] note: this argument is needed to avoid string creation
219
244
  # @param record [Hash]
220
245
  # @param capture_list [Array] for performance
221
- def parse_plain(time, text, idx, record, capture_list, &block)
222
- m = @regexp.match(text, idx)
246
+ def parse_plain(re, time, text, idx, record, capture_list, &block)
247
+ m = re.match(text, idx)
223
248
  if m.nil?
224
249
  yield nil, nil
225
250
  return
@@ -244,8 +269,6 @@ module Fluent
244
269
  yield time, record
245
270
  end
246
271
 
247
- SPLIT_CHAR = ' '.freeze
248
-
249
272
  def parse_rfc3164(text, &block)
250
273
  pri = nil
251
274
  cursor = 0
@@ -264,20 +287,35 @@ module Fluent
264
287
  end
265
288
  end
266
289
 
267
- # header part
268
- time_size = 15 # skip Mmm dd hh:mm:ss
269
- time_end = text[cursor + time_size]
270
- if time_end == SPLIT_CHAR
271
- time_str = text.slice(cursor, time_size)
272
- cursor += 16 # time + ' '
273
- elsif time_end == '.'.freeze
274
- # support subsecond time
275
- i = text.index(SPLIT_CHAR, time_size)
276
- time_str = text.slice(cursor, i - cursor)
277
- cursor = i + 1
290
+ if @skip_space_count_rfc3164
291
+ # header part
292
+ time_size = 15 # skip Mmm dd hh:mm:ss
293
+ time_end = text[cursor + time_size]
294
+ if time_end == SPLIT_CHAR
295
+ time_str = text.slice(cursor, time_size)
296
+ cursor += 16 # time + ' '
297
+ elsif time_end == '.'.freeze
298
+ # support subsecond time
299
+ i = text.index(SPLIT_CHAR, time_size)
300
+ time_str = text.slice(cursor, i - cursor)
301
+ cursor = i + 1
302
+ else
303
+ yield nil, nil
304
+ return
305
+ end
278
306
  else
279
- yield nil, nil
280
- return
307
+ i = cursor - 1
308
+ sq = false
309
+ @space_count_rfc3164.times do
310
+ while text[i + 1] == SPLIT_CHAR
311
+ sq = true
312
+ i += 1
313
+ end
314
+ i = text.index(SPLIT_CHAR, i + 1)
315
+ end
316
+
317
+ time_str = sq ? text.slice(idx, i - cursor).squeeze(SPLIT_CHAR) : text.slice(cursor, i - cursor)
318
+ cursor = i + 1
281
319
  end
282
320
 
283
321
  i = text.index(SPLIT_CHAR, cursor)
@@ -325,7 +363,130 @@ module Fluent
325
363
  msg.chomp!
326
364
  record['message'] = msg
327
365
 
328
- time = @time_parser.parse(time_str)
366
+ time = @time_parser_rfc3164.parse(time_str)
367
+ record['time'] = time_str if @keep_time_key
368
+
369
+ yield time, record
370
+ end
371
+
372
+ NILVALUE = '-'.freeze
373
+
374
+ def parse_rfc5424(text, &block)
375
+ pri = nil
376
+ cursor = 0
377
+ if @with_priority
378
+ if text.start_with?('<'.freeze)
379
+ i = text.index('>'.freeze, 1)
380
+ if i < 2
381
+ yield nil, nil
382
+ return
383
+ end
384
+ pri = text.slice(1, i - 1).to_i
385
+ i = text.index(SPLIT_CHAR, i)
386
+ cursor = i + 1
387
+ else
388
+ yield nil, nil
389
+ return
390
+ end
391
+ end
392
+
393
+ # timestamp part
394
+ if @skip_space_count_rfc5424
395
+ i = text.index(SPLIT_CHAR, cursor)
396
+ time_str = text.slice(cursor, i - cursor)
397
+ cursor = i + 1
398
+ else
399
+ i = cursor - 1
400
+ sq = false
401
+ @space_count_rfc5424.times do
402
+ while text[i + 1] == SPLIT_CHAR
403
+ sq = true
404
+ i += 1
405
+ end
406
+ i = text.index(SPLIT_CHAR, i + 1)
407
+ end
408
+
409
+ time_str = sq ? text.slice(idx, i - cursor).squeeze(SPLIT_CHAR) : text.slice(cursor, i - cursor)
410
+ cursor = i + 1
411
+ end
412
+
413
+ # Repeat same code for the performance
414
+
415
+ # host part
416
+ i = text.index(SPLIT_CHAR, cursor)
417
+ unless i
418
+ yield nil, nil
419
+ return
420
+ end
421
+ slice_size = i - cursor
422
+ host = text.slice(cursor, slice_size)
423
+ cursor += slice_size + 1
424
+
425
+ # ident part
426
+ i = text.index(SPLIT_CHAR, cursor)
427
+ unless i
428
+ yield nil, nil
429
+ return
430
+ end
431
+ slice_size = i - cursor
432
+ ident = text.slice(cursor, slice_size)
433
+ cursor += slice_size + 1
434
+
435
+ # pid part
436
+ i = text.index(SPLIT_CHAR, cursor)
437
+ unless i
438
+ yield nil, nil
439
+ return
440
+ end
441
+ slice_size = i - cursor
442
+ pid = text.slice(cursor, slice_size)
443
+ cursor += slice_size + 1
444
+
445
+ # msgid part
446
+ i = text.index(SPLIT_CHAR, cursor)
447
+ unless i
448
+ yield nil, nil
449
+ return
450
+ end
451
+ slice_size = i - cursor
452
+ msgid = text.slice(cursor, slice_size)
453
+ cursor += slice_size + 1
454
+
455
+ record = {'host' => host, 'ident' => ident, 'pid' => pid, 'msgid' => msgid}
456
+ record['pri'] = pri if pri
457
+
458
+ # extradata part
459
+ ed_start = text[cursor]
460
+ if ed_start == NILVALUE
461
+ record['extradata'] = NILVALUE
462
+ cursor += 1
463
+ else
464
+ start = cursor
465
+ i = text.index('] '.freeze, cursor)
466
+ extradata = if i
467
+ diff = i + 1 - start # calculate ']' position
468
+ cursor += diff
469
+ text.slice(start, diff)
470
+ else # No message part case
471
+ cursor = text.bytesize
472
+ text.slice(start, cursor)
473
+ end
474
+ extradata.tr!("\\".freeze, ''.freeze)
475
+ record['extradata'] = extradata
476
+ end
477
+
478
+ # message part
479
+ if cursor != text.bytesize
480
+ msg = text.slice(cursor + 1, text.bytesize)
481
+ msg.chomp!
482
+ record['message'] = msg
483
+ end
484
+
485
+ time = begin
486
+ @time_parser_rfc5424.parse(time_str)
487
+ rescue Fluent::TimeParser::TimeParseError => e
488
+ @time_parser_rfc5424_without_subseconds.parse(time_str)
489
+ end
329
490
  record['time'] = time_str if @keep_time_key
330
491
 
331
492
  yield time, record