fluentd 0.12.37 → 0.12.38

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: f88c1cef594f4ca5773a92310fd32f0fbf728a89
4
- data.tar.gz: e9fc666c618c2b9bf35d10c21466a0539bb45d10
3
+ metadata.gz: 8fc381ec2e8ffc7a3f6e196b597a324f3e6e553e
4
+ data.tar.gz: 2f1832c98553174c7fe96f87832898e669f985cb
5
5
  SHA512:
6
- metadata.gz: 5652613563369d7f73bd290c5e550a9fd02bff462502bf9074a399a4fa2df89e3da69a4f63e34480751d3411938f57346aa1d1e049a26fb921b40609c7d7d311
7
- data.tar.gz: 641ea08ba43f5f646a6444f91939c7f99fc95d5d59a562f59123e64f2d79bf120adb17e32e3b9dea3daa51bc432619db688567c74319b131743ad6c1b475606b
6
+ metadata.gz: cabc4f4b85ff50f0421b63f3ddceb2046a8354fac5097af1cbee43b32124e110ce8b12bf23fc55682c91266a63643ae7f38d142af146f903a4b049b162eb3073
7
+ data.tar.gz: bd375fc4feee2fa831c4bb03d1ed3118c3807434e250a0be635dadf5cac1654cdab666b069b1754d07d45c91c0207785ca083424968071c7baadb5d6080cb222
data/ChangeLog CHANGED
@@ -1,5 +1,24 @@
1
1
  # v0.12
2
2
 
3
+ ## Release 0.12.38 - 2017/07/13
4
+
5
+ ### New features / Enhancement
6
+
7
+ * in_syslog: Add Add allow_without_priority and default_priority parameters
8
+ https://github.com/fluent/fluentd/pull/1608
9
+ * in_syslog: More characters are available in tag part of syslog format
10
+ https://github.com/fluent/fluentd/pull/1609
11
+ * in_syslog: Add resolve_hostname and source_address_key parameters
12
+ https://github.com/fluent/fluentd/pull/1615
13
+ * filter_grep: Support new configuration format by config_section
14
+ https://github.com/fluent/fluentd/pull/1627
15
+
16
+ ### Bug fixes
17
+
18
+ * log: Capture more fluentd log events during shutdown
19
+ https://github.com/fluent/fluentd/pull/1618
20
+ https://github.com/fluent/fluentd/pull/1625
21
+
3
22
  ## Release 0.12.37 - 2017/06/21
4
23
 
5
24
  ### New features / Enhancement
@@ -38,6 +38,11 @@ module Fluent
38
38
  @started_outputs = []
39
39
  @started_filters = []
40
40
 
41
+ @outputs_for_log_event = []
42
+ @filters_for_log_event = []
43
+ @started_outputs_for_log_event = []
44
+ @started_filters_for_log_event = []
45
+
41
46
  @log = Engine.log
42
47
  @event_router = EventRouter.new(NoMatchMatch.new(log), self)
43
48
  @error_collector = nil
@@ -70,16 +75,47 @@ module Fluent
70
75
  @outputs.each { |o|
71
76
  o.start
72
77
  @started_outputs << o
78
+ @started_outputs_for_log_event << o if @outputs_for_log_event.include?(o)
73
79
  }
74
80
 
75
81
  @filters.each { |f|
76
82
  f.start
77
83
  @started_filters << f
84
+ @started_filters_for_log_event << f if @filters_for_log_event.include?(f)
78
85
  }
79
86
  end
80
87
 
81
88
  def shutdown
82
- @started_filters.map { |f|
89
+ (@started_filters - @started_filters_for_log_event).map { |f|
90
+ Thread.new do
91
+ begin
92
+ log.info "shutting down filter#{@context.nil? ? '' : " in #{@context}"}", type: Plugin.lookup_name_from_class(f.class), plugin_id: f.plugin_id
93
+ f.shutdown
94
+ rescue => e
95
+ log.warn "unexpected error while shutting down filter plugins", plugin: f.class, plugin_id: f.plugin_id, error_class: e.class, error: e
96
+ log.warn_backtrace
97
+ end
98
+ end
99
+ }.each { |t| t.join }
100
+
101
+ # Output plugin as filter emits records at shutdown so emit problem still exist.
102
+ # This problem will be resolved after actual filter mechanizm.
103
+ (@started_outputs - @started_outputs_for_log_event).map { |o|
104
+ Thread.new do
105
+ begin
106
+ log.info "shutting down output#{@context.nil? ? '' : " in #{@context}"}", type: Plugin.lookup_name_from_class(o.class), plugin_id: o.plugin_id
107
+ o.shutdown
108
+ rescue => e
109
+ log.warn "unexpected error while shutting down output plugins", plugin: o.class, plugin_id: o.plugin_id, error_class: e.class, error: e
110
+ log.warn_backtrace
111
+ end
112
+ end
113
+ }.each { |t| t.join }
114
+
115
+ ## execute callback from Engine to flush log event queue before shutting down corresponding filters and outputs
116
+ yield if block_given?
117
+
118
+ @started_filters_for_log_event.map { |f|
83
119
  Thread.new do
84
120
  begin
85
121
  log.info "shutting down filter#{@context.nil? ? '' : " in #{@context}"}", type: Plugin.lookup_name_from_class(f.class), plugin_id: f.plugin_id
@@ -93,7 +129,7 @@ module Fluent
93
129
 
94
130
  # Output plugin as filter emits records at shutdown so emit problem still exist.
95
131
  # This problem will be resolved after actual filter mechanizm.
96
- @started_outputs.map { |o|
132
+ @started_outputs_for_log_event.map { |o|
97
133
  Thread.new do
98
134
  begin
99
135
  log.info "shutting down output#{@context.nil? ? '' : " in #{@context}"}", type: Plugin.lookup_name_from_class(o.class), plugin_id: o.plugin_id
@@ -134,6 +170,10 @@ module Fluent
134
170
  @outputs << output
135
171
  @event_router.add_rule(pattern, output)
136
172
 
173
+ if match_event_log_tag?(pattern)
174
+ @outputs_for_log_event << output
175
+ end
176
+
137
177
  output
138
178
  end
139
179
 
@@ -146,9 +186,17 @@ module Fluent
146
186
  @filters << filter
147
187
  @event_router.add_rule(pattern, filter)
148
188
 
189
+ if match_event_log_tag?(pattern)
190
+ @filters_for_log_event << filter
191
+ end
192
+
149
193
  filter
150
194
  end
151
195
 
196
+ def match_event_log_tag?(pattern)
197
+ EventRouter::Rule.new(pattern, nil).match?($log.tag)
198
+ end
199
+
152
200
  # For handling invalid record
153
201
  def emit_error_event(tag, time, record, error)
154
202
  end
@@ -165,7 +165,6 @@ module Fluent
165
165
  $log.disable_events(Thread.current)
166
166
 
167
167
  while sleep(LOG_EMIT_INTERVAL)
168
- break if @log_event_loop_stop
169
168
  next if @log_event_queue.empty?
170
169
 
171
170
  # NOTE: thead-safe of slice! depends on GVL
@@ -179,6 +178,7 @@ module Fluent
179
178
  $log.error "failed to emit fluentd's log event", tag: tag, event: record, error_class: e.class, error: e
180
179
  end
181
180
  }
181
+ break if @log_event_loop_stop
182
182
  end
183
183
  end
184
184
 
@@ -209,10 +209,13 @@ module Fluent
209
209
  $log.error_backtrace
210
210
  ensure
211
211
  $log.info "shutting down fluentd"
212
- shutdown
213
212
  if @log_emit_thread
214
- @log_event_loop_stop = true
215
- @log_emit_thread.join
213
+ shutdown do
214
+ @log_event_loop_stop = true
215
+ @log_emit_thread.join
216
+ end
217
+ else
218
+ shutdown
216
219
  end
217
220
  end
218
221
  end
@@ -237,8 +240,8 @@ module Fluent
237
240
  @root_agent.start
238
241
  end
239
242
 
240
- def shutdown
241
- @root_agent.shutdown
243
+ def shutdown(&block)
244
+ @root_agent.shutdown(&block)
242
245
  end
243
246
  end
244
247
 
@@ -537,9 +537,9 @@ module Fluent
537
537
 
538
538
  class SyslogParser < Parser
539
539
  # From existence TextParser pattern
540
- REGEXP = /^(?<time>[^ ]*\s*[^ ]* [^ ]*) (?<host>[^ ]*) (?<ident>[a-zA-Z0-9_\/\.\-]*)(?:\[(?<pid>[0-9]+)\])?(?:[^\:]*\:)? *(?<message>.*)$/
540
+ REGEXP = /^(?<time>[^ ]*\s*[^ ]* [^ ]*) (?<host>[^ ]*) (?<ident>[^ :\[]*)(?:\[(?<pid>[0-9]+)\])?(?:[^\:]*\:)? *(?<message>.*)$/
541
541
  # From in_syslog default pattern
542
- REGEXP_WITH_PRI = /^\<(?<pri>[0-9]+)\>(?<time>[^ ]* {1,2}[^ ]* [^ ]*) (?<host>[^ ]*) (?<ident>[a-zA-Z0-9_\/\.\-]*)(?:\[(?<pid>[0-9]+)\])?(?:[^\:]*\:)? *(?<message>.*)$/
542
+ REGEXP_WITH_PRI = /^\<(?<pri>[0-9]+)\>(?<time>[^ ]* {1,2}[^ ]* [^ ]*) (?<host>[^ ]*) (?<ident>[^ :\[]*)(?:\[(?<pid>[0-9]+)\])?(?:[^\:]*\:)? *(?<message>.*)$/
543
543
  REGEXP_RFC5424 = /\A^(?<time>[^ ]+) (?<host>[^ ]+) (?<ident>[^ ]+) (?<pid>[-0-9]+) (?<msgid>[^ ]+) (?<extradata>(\[(.*)\]|[^ ])) (?<message>.+)$\z/
544
544
  REGEXP_RFC5424_WITH_PRI = /\A^\<(?<pri>[0-9]{1,3})\>[1-9]\d{0,2} (?<time>[^ ]+) (?<host>[^ ]+) (?<ident>[^ ]+) (?<pid>[-0-9]+) (?<msgid>[^ ]+) (?<extradata>(\[(.*)\]|[^ ])) (?<message>.+)$\z/
545
545
  REGEXP_DETECT_RFC5424 = /^\<.*\>[1-9]\d{0,2}/
@@ -28,32 +28,59 @@ module Fluent
28
28
 
29
29
  REGEXP_MAX_NUM = 20
30
30
 
31
- (1..REGEXP_MAX_NUM).each {|i| config_param :"regexp#{i}", :string, default: nil }
32
- (1..REGEXP_MAX_NUM).each {|i| config_param :"exclude#{i}", :string, default: nil }
31
+ (1..REGEXP_MAX_NUM).each {|i| config_param :"regexp#{i}", :string, default: nil, deprecated: "Use <regexp> section" }
32
+ (1..REGEXP_MAX_NUM).each {|i| config_param :"exclude#{i}", :string, default: nil, deprecated: "Use <exclude> section" }
33
+
34
+ config_section :regexp, param_name: :regexps, multi: true do
35
+ desc "The field name to which the regular expression is applied."
36
+ config_param :key, :string
37
+ desc "The regular expression."
38
+ config_param :pattern do |value|
39
+ Regexp.compile(value)
40
+ end
41
+ end
42
+
43
+ config_section :exclude, param_name: :excludes, multi: true do
44
+ desc "The field name to which the regular expression is applied."
45
+ config_param :key, :string
46
+ desc "The regular expression."
47
+ config_param :pattern do |value|
48
+ Regexp.compile(value)
49
+ end
50
+ end
33
51
 
34
52
  # for test
35
- attr_reader :regexps
36
- attr_reader :excludes
53
+ attr_reader :_regexps
54
+ attr_reader :_excludes
37
55
 
38
56
  def configure(conf)
39
57
  super
40
58
 
41
- @regexps = {}
59
+ @_regexps = {}
42
60
  (1..REGEXP_MAX_NUM).each do |i|
43
61
  next unless conf["regexp#{i}"]
44
62
  key, regexp = conf["regexp#{i}"].split(/ /, 2)
45
63
  raise ConfigError, "regexp#{i} does not contain 2 parameters" unless regexp
46
- raise ConfigError, "regexp#{i} contains a duplicated key, #{key}" if @regexps[key]
47
- @regexps[key] = Regexp.compile(regexp)
64
+ raise ConfigError, "regexp#{i} contains a duplicated key, #{key}" if @_regexps[key]
65
+ @_regexps[key] = Regexp.compile(regexp)
48
66
  end
49
67
 
50
- @excludes = {}
68
+ @_excludes = {}
51
69
  (1..REGEXP_MAX_NUM).each do |i|
52
70
  next unless conf["exclude#{i}"]
53
71
  key, exclude = conf["exclude#{i}"].split(/ /, 2)
54
72
  raise ConfigError, "exclude#{i} does not contain 2 parameters" unless exclude
55
- raise ConfigError, "exclude#{i} contains a duplicated key, #{key}" if @excludes[key]
56
- @excludes[key] = Regexp.compile(exclude)
73
+ raise ConfigError, "exclude#{i} contains a duplicated key, #{key}" if @_excludes[key]
74
+ @_excludes[key] = Regexp.compile(exclude)
75
+ end
76
+
77
+ @regexps.each do |e|
78
+ raise Fluent::ConfigError, "Duplicate key: #{e.key}" if @_regexps.key?(e.key)
79
+ @_regexps[e.key] = e.pattern
80
+ end
81
+ @excludes.each do |e|
82
+ raise Fluent::ConfigError, "Duplicate key: #{e.key}" if @_excludes.key?(e.key)
83
+ @_excludes[e.key] = e.pattern
57
84
  end
58
85
  end
59
86
 
@@ -61,10 +88,10 @@ module Fluent
61
88
  result = nil
62
89
  begin
63
90
  catch(:break_loop) do
64
- @regexps.each do |key, regexp|
91
+ @_regexps.each do |key, regexp|
65
92
  throw :break_loop unless ::Fluent::StringUtil.match_regexp(regexp, record[key].to_s)
66
93
  end
67
- @excludes.each do |key, exclude|
94
+ @_excludes.each do |key, exclude|
68
95
  throw :break_loop if ::Fluent::StringUtil.match_regexp(exclude, record[key].to_s)
69
96
  end
70
97
  result = record
@@ -93,6 +93,10 @@ module Fluent
93
93
  config_param :source_host_key, :string, default: 'source_host'.freeze, deprecated: "use source_hostname_key instead"
94
94
  desc "The field name of the client's hostname."
95
95
  config_param :source_hostname_key, :string, default: nil
96
+ desc "The field name of the client's source address."
97
+ config_param :source_address_key, :string, default: nil
98
+ desc 'Try to resolve hostname from IP addresses or not.'
99
+ config_param :resolve_hostname, :bool, default: nil
96
100
  desc 'The field name of the priority.'
97
101
  config_param :priority_key, :string, default: nil
98
102
  desc 'The field name of the facility.'
@@ -101,14 +105,28 @@ module Fluent
101
105
  config_param :message_length_limit, :size, default: 2048
102
106
  config_param :blocking_timeout, :time, default: 0.5
103
107
 
108
+ desc 'If true, accept syslog message without PRI part'
109
+ config_param :allow_without_priority, :bool, default: false
110
+ # 13 is the default value of rsyslog and syslog-ng
111
+ desc 'The default PRI value (0 - 191 are available)'
112
+ config_param :default_priority, :integer, default: 13
113
+
104
114
  def configure(conf)
105
115
  super
106
116
 
117
+ if @default_priority < 0 && @default_priority > 191
118
+ raise ConfigError, "default_priority must be 0 ~ 191"
119
+ end
120
+
121
+ if @allow_without_priority && conf['message_format'] == 'auto'
122
+ raise ConfigError, "message_format auto isn't allowed when allow_without_priority is true"
123
+ end
124
+
125
+ conf['with_priority'] = !@allow_without_priority
107
126
  if conf.has_key?('format')
108
127
  @parser = Plugin.new_parser(conf['format'])
109
128
  @parser.configure(conf)
110
129
  else
111
- conf['with_priority'] = true
112
130
  @parser = TextParser::SyslogParser.new
113
131
  @parser.configure(conf)
114
132
  @use_default = true
@@ -117,15 +135,25 @@ module Fluent
117
135
  if @source_hostname_key.nil? && @include_source_host
118
136
  @source_hostname_key = @source_host_key
119
137
  end
138
+ if @source_hostname_key
139
+ if @resolve_hostname.nil?
140
+ @resolve_hostname = true
141
+ elsif !@resolve_hostname # user specifies "false" in configure
142
+ raise Fluent::ConfigError, "resolve_hostname must be true with source_hostname_key"
143
+ end
144
+ end
120
145
  end
121
146
 
122
147
  def start
123
- callback = if @use_default
124
- method(:receive_data)
148
+ callback = if @allow_without_priority
149
+ method(:receive_data_allow_without_priority)
125
150
  else
126
- method(:receive_data_parser)
151
+ if @use_default
152
+ method(:receive_data_default)
153
+ else
154
+ method(:receive_data_with_format)
155
+ end
127
156
  end
128
-
129
157
  @loop = Coolio::Loop.new
130
158
  @handler = listen(callback)
131
159
  @loop.attach(@handler)
@@ -149,7 +177,16 @@ module Fluent
149
177
 
150
178
  private
151
179
 
152
- def receive_data_parser(data, addr)
180
+ ## SyslogParser with PRI part
181
+ def receive_data_default(data, addr)
182
+ parse_text(data, addr)
183
+ rescue => e
184
+ log.error data.dump, error: e.to_s
185
+ log.error_backtrace
186
+ end
187
+
188
+ ## PRI part parser + custom parser without PRI part
189
+ def receive_data_with_format(data, addr)
153
190
  m = SYSLOG_REGEXP.match(data)
154
191
  unless m
155
192
  log.warn "invalid syslog message: #{data.dump}"
@@ -158,48 +195,50 @@ module Fluent
158
195
  pri = m[1].to_i
159
196
  text = m[2]
160
197
 
161
- @parser.parse(text) { |time, record|
162
- unless time && record
163
- log.warn "pattern not match: #{text.inspect}"
164
- return
165
- end
166
-
167
- facility = FACILITY_MAP[pri >> 3]
168
- priority = PRIORITY_MAP[pri & 0b111]
198
+ parse_text(text, addr, pri)
199
+ rescue => e
200
+ log.error data.dump, error: e.to_s
201
+ log.error_backtrace
202
+ end
169
203
 
170
- record[@priority_key] = priority if @priority_key
171
- record[@facility_key] = facility if @facility_key
172
- record[@source_hostname_key] = addr[2] if @source_hostname_key
204
+ ## PRI part parser + SyslogParser without PRI part | custom parser without PRI part
205
+ def receive_data_allow_without_priority(data, addr)
206
+ m = SYSLOG_REGEXP.match(data)
207
+ if m
208
+ pri = m[1].to_i
209
+ text = m[2]
210
+ else
211
+ pri = @default_priority
212
+ text = data
213
+ end
173
214
 
174
- tag = "#{@tag}.#{facility}.#{priority}"
175
- emit(tag, time, record)
176
- }
215
+ parse_text(text, addr, pri)
177
216
  rescue => e
178
217
  log.error data.dump, error: e.to_s
179
218
  log.error_backtrace
180
219
  end
181
220
 
182
- def receive_data(data, addr)
183
- @parser.parse(data) { |time, record|
221
+ def parse_text(text, addr, pri = nil)
222
+ @parser.parse(text) { |time, record|
184
223
  unless time && record
185
- log.warn "invalid syslog message", data: data
224
+ log.warn "pattern not match: #{text.inspect}"
186
225
  return
187
226
  end
188
227
 
189
- pri = record.delete('pri'.freeze)
228
+ ## from receive_data_default
229
+ pri = record.delete('pri'.freeze) unless pri
230
+
190
231
  facility = FACILITY_MAP[pri >> 3]
191
232
  priority = PRIORITY_MAP[pri & 0b111]
192
233
 
193
234
  record[@priority_key] = priority if @priority_key
194
235
  record[@facility_key] = facility if @facility_key
195
236
  record[@source_hostname_key] = addr[2] if @source_hostname_key
237
+ record[@source_address_key] = addr[3] if @source_address_key
196
238
 
197
239
  tag = "#{@tag}.#{facility}.#{priority}"
198
240
  emit(tag, time, record)
199
241
  }
200
- rescue => e
201
- log.error data.dump, error: e.to_s
202
- log.error_backtrace
203
242
  end
204
243
 
205
244
  private
@@ -209,10 +248,10 @@ module Fluent
209
248
  if @protocol_type == :udp
210
249
  @usock = SocketUtil.create_udp_socket(@bind)
211
250
  @usock.bind(@bind, @port)
212
- SocketUtil::UdpHandler.new(@usock, log, @message_length_limit, callback, !!@source_hostname_key)
251
+ SocketUtil::UdpHandler.new(@usock, log, @message_length_limit, callback, @resolve_hostname)
213
252
  else
214
253
  # syslog family add "\n" to each message and this seems only way to split messages in tcp stream
215
- Coolio::TCPServer.new(@bind, @port, SocketUtil::TcpHandler, log, "\n", callback, !!@source_hostname_key)
254
+ Coolio::TCPServer.new(@bind, @port, SocketUtil::TcpHandler, log, "\n", callback, @resolve_hostname)
216
255
  end
217
256
  end
218
257
 
@@ -16,6 +16,6 @@
16
16
 
17
17
  module Fluent
18
18
 
19
- VERSION = '0.12.37'
19
+ VERSION = '0.12.38'
20
20
 
21
21
  end
@@ -22,12 +22,51 @@ class GrepFilterTest < Test::Unit::TestCase
22
22
 
23
23
  test "regexpN can contain a space" do
24
24
  d = create_driver(%[regexp1 message foo])
25
- assert_equal(Regexp.compile(/ foo/), d.instance.regexps['message'])
25
+ assert_equal(Regexp.compile(/ foo/), d.instance._regexps['message'])
26
26
  end
27
27
 
28
28
  test "excludeN can contain a space" do
29
29
  d = create_driver(%[exclude1 message foo])
30
- assert_equal(Regexp.compile(/ foo/), d.instance.excludes['message'])
30
+ assert_equal(Regexp.compile(/ foo/), d.instance._excludes['message'])
31
+ end
32
+
33
+ sub_test_case "duplicate key" do
34
+ test "flat" do
35
+ conf = %[
36
+ regexp1 message test
37
+ regexp2 message test2
38
+ ]
39
+ assert_raise(Fluent::ConfigError) do
40
+ create_driver(conf)
41
+ end
42
+ end
43
+ test "section" do
44
+ conf = %[
45
+ <regexp>
46
+ key message
47
+ pattern test
48
+ </regexp>
49
+ <regexp>
50
+ key message
51
+ pattern test2
52
+ </regexp>
53
+ ]
54
+ assert_raise(Fluent::ConfigError) do
55
+ create_driver(conf)
56
+ end
57
+ end
58
+ test "mix" do
59
+ conf = %[
60
+ regexp1 message test
61
+ <regexp>
62
+ key message
63
+ pattern test
64
+ </regexp>
65
+ ]
66
+ assert_raise(Fluent::ConfigError) do
67
+ create_driver(conf)
68
+ end
69
+ end
31
70
  end
32
71
  end
33
72
 
@@ -75,6 +114,38 @@ class GrepFilterTest < Test::Unit::TestCase
75
114
  end
76
115
  end
77
116
 
117
+ test 'regexps' do
118
+ conf = %[
119
+ <regexp>
120
+ key message
121
+ pattern WARN
122
+ </regexp>
123
+ ]
124
+ es = emit(conf, messages)
125
+ assert_equal(3, es.instance_variable_get(:@record_array).size)
126
+ assert_block('only WARN logs') do
127
+ es.all? { |t, r|
128
+ !r['message'].include?('INFO')
129
+ }
130
+ end
131
+ end
132
+
133
+ test 'excludes' do
134
+ conf = %[
135
+ <exclude>
136
+ key message
137
+ pattern favicon
138
+ </exclude>
139
+ ]
140
+ es = emit(conf, messages)
141
+ assert_equal(3, es.instance_variable_get(:@record_array).size)
142
+ assert_block('remove favicon logs') do
143
+ es.all? { |t, r|
144
+ !r['message'].include?('favicon')
145
+ }
146
+ end
147
+ end
148
+
78
149
  sub_test_case 'with invalid sequence' do
79
150
  def messages
80
151
  [
@@ -35,6 +35,74 @@ class SyslogInputTest < Test::Unit::TestCase
35
35
  }
36
36
  end
37
37
 
38
+ sub_test_case 'source_hostname_key and source_address_key features' do
39
+ test 'resolve_hostname must be true with source_hostname_key' do
40
+ assert_raise(Fluent::ConfigError) {
41
+ create_driver(CONFIG + <<EOS)
42
+ resolve_hostname false
43
+ source_hostname_key hostname
44
+ EOS
45
+ }
46
+ end
47
+
48
+ 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] }
49
+ LOCALHOST_HOSTNAME = LOCALHOST_HOSTNAME_GETTER.call
50
+ DUMMY_SOCK = Struct.new(:remote_host, :remote_addr, :remote_port).new(LOCALHOST_HOSTNAME, "127.0.0.1", 0)
51
+ data(
52
+ both: [:hostname, :address],
53
+ hostname: [:hostname],
54
+ address: [:address],
55
+ )
56
+ test 'source_hostname_key and source_address_key parameter feature should add record(s)' do |keys|
57
+ conf = CONFIG.dup
58
+ if keys.include?(:hostname)
59
+ conf << <<EOL
60
+ source_hostname_key source_hostname
61
+ EOL
62
+ end
63
+ if keys.include?(:address)
64
+ conf << <<EOL
65
+ source_address_key source_address
66
+ EOL
67
+ end
68
+ tests = create_test_case
69
+ d = create_driver(conf)
70
+
71
+ d.run do
72
+ u = UDPSocket.new
73
+ u.connect('127.0.0.1', PORT)
74
+ tests.each {|test|
75
+ u.send(test['msg'], 0)
76
+ }
77
+ sleep 1
78
+ end
79
+
80
+ d.emits.each { |tag, _time, record|
81
+ if keys.include?(:hostname)
82
+ assert_true record.has_key?('source_hostname')
83
+ assert_equal DUMMY_SOCK.remote_host, record['source_hostname']
84
+ unless keys.include?(:address)
85
+ assert_false record.has_key?('source_address')
86
+ end
87
+ end
88
+ if keys.include?(:address)
89
+ assert_true record.has_key?('source_address')
90
+ assert_equal DUMMY_SOCK.remote_addr, record['source_address']
91
+ unless keys.include?(:hostname)
92
+ assert_false record.has_key?('source_hostname')
93
+ end
94
+ end
95
+ }
96
+ end
97
+
98
+ data('resolve_hostname' => 'resolve_hostname true',
99
+ 'source_hostname_key' => 'source_hostname_key source_host')
100
+ def test_configure_reslove_hostname(param)
101
+ d = create_driver([CONFIG, param].join("\n"))
102
+ assert_true d.instance.resolve_hostname
103
+ end
104
+ end
105
+
38
106
  def test_time_format
39
107
  configs = {'127.0.0.1' => CONFIG}
40
108
  configs.merge!('::1' => IPv6_CONFIG) if ipv6_enabled?
@@ -213,6 +281,77 @@ class SyslogInputTest < Test::Unit::TestCase
213
281
  compare_test_result(d.emits, tests, {facility: facility})
214
282
  end
215
283
 
284
+ def test_allow_without_priority_with_default_format
285
+ d = create_driver([CONFIG, 'allow_without_priority true'].join("\n"))
286
+
287
+ tests = [
288
+ {'msg' => '<6>Sep 10 00:00:00 localhost logger: ' + 'x' * 100 + "\n", 'expected' => 'x' * 100},
289
+ {'msg' => 'Sep 10 00:00:00 localhost logger: ' + 'x' * 1024 + "\n", 'expected' => 'x' * 1024, 'tag' => 'syslog.user.notice'},
290
+ ]
291
+
292
+ d.run do
293
+ u = UDPSocket.new
294
+ u.connect('127.0.0.1', PORT)
295
+ tests.each {|test|
296
+ u.send(test['msg'], 0)
297
+ }
298
+ sleep 1
299
+ end
300
+
301
+ assert_equal 2, d.emits.size
302
+ compare_test_result(d.emits, tests)
303
+ end
304
+
305
+ def test_allow_without_priority_with_json_format
306
+ d = create_driver([CONFIG, %[
307
+ allow_without_priority true
308
+ format json
309
+ ]].join("\n"))
310
+
311
+ message = 'foo'
312
+ tests = [
313
+ {'msg' => '<6>' + {'message' => message}.to_json + "\n", 'expected' => message},
314
+ {'msg' => {'message' => message}.to_json + "\n", 'expected' => message, 'tag' => 'syslog.user.notice'},
315
+ ]
316
+
317
+ d.run do
318
+ u = UDPSocket.new
319
+ u.connect('127.0.0.1', PORT)
320
+ tests.each {|test|
321
+ u.send(test['msg'], 0)
322
+ }
323
+ sleep 1
324
+ end
325
+
326
+ assert_equal 2, d.emits.size
327
+ compare_test_result(d.emits, tests)
328
+ end
329
+
330
+ def test_default_priority
331
+ d = create_driver([CONFIG, %[
332
+ allow_without_priority true
333
+ default_priority 100
334
+ ]].join("\n"))
335
+
336
+ tests = [
337
+ {'msg' => '<6>Sep 10 00:00:00 localhost logger: ' + 'x' * 100 + "\n", 'expected' => 'x' * 100},
338
+ {'msg' => 'Sep 10 00:00:00 localhost logger: ' + 'x' * 1024 + "\n", 'expected' => 'x' * 1024, 'tag' => 'syslog.ntp.warn'},
339
+ ]
340
+
341
+ d.run do
342
+ u = UDPSocket.new
343
+ u.connect('127.0.0.1', PORT)
344
+ tests.each {|test|
345
+ u.send(test['msg'], 0)
346
+ }
347
+ sleep 1
348
+ end
349
+
350
+ assert_equal 2, d.emits.size
351
+ compare_test_result(d.emits, tests)
352
+
353
+ end
354
+
216
355
  def create_test_case(large_message = false)
217
356
  # actual syslog message has "\n"
218
357
  if large_message
@@ -231,7 +370,7 @@ class SyslogInputTest < Test::Unit::TestCase
231
370
 
232
371
  def compare_test_result(events, tests, options = {})
233
372
  events.each_index { |i|
234
- assert_equal('syslog.kern.info', events[i][0]) # <6> means kern.info
373
+ assert_equal((tests[i]['tag'] || 'syslog.kern.info'), events[i][0]) # <6> means kern.info
235
374
  assert_equal(tests[i]['expected'], events[i][2]['message'])
236
375
  assert_equal(options[:host], events[i][2]['source_host']) if options[:host]
237
376
  assert_equal(options[:priority], events[i][2]['priority']) if options[:priority]
@@ -374,6 +374,24 @@ module ParserTest
374
374
  end
375
375
  end
376
376
 
377
+ def test_parse_various_characters_for_tag
378
+ ident = '~!@#$%^&*()_+=-`]{};"\'/?\\,.<>'
379
+ @parser.configure({})
380
+ @parser.parse("Feb 28 12:00:00 192.168.0.1 #{ident}[11111]: [error] Syslog test") { |time, record|
381
+ assert_equal_event_time(event_time('Feb 28 12:00:00', format: '%b %d %H:%M:%S'), time)
382
+ assert_equal(@expected.merge('ident' => ident), record)
383
+ }
384
+ end
385
+
386
+ def test_parse_various_characters_for_tag_with_priority
387
+ ident = '~!@#$%^&*()_+=-`]{};"\'/?\\,.<>'
388
+ @parser.configure({'with_priority' => true})
389
+ @parser.parse("<6>Feb 28 12:00:00 192.168.0.1 #{ident}[11111]: [error] Syslog test") { |time, record|
390
+ assert_equal_event_time(event_time('Feb 28 12:00:00', format: '%b %d %H:%M:%S'), time)
391
+ assert_equal(@expected.merge('pri' => 6, 'ident' => ident), record)
392
+ }
393
+ end
394
+
377
395
  class TestRFC5424Regexp < self
378
396
  def test_parse_with_rfc5424_message
379
397
  @parser.configure(
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.37
4
+ version: 0.12.38
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-06-22 00:00:00.000000000 Z
11
+ date: 2017-07-13 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: msgpack