fluentd 0.12.0.pre.3 → 0.12.0

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.

@@ -93,9 +93,9 @@ module Fluent
93
93
  super
94
94
 
95
95
  if @@buffer_paths.has_key?(@buffer_path)
96
- raise ConfigError, "Other '#{@@buffer_paths[@buffer_path]}' plugin already use same buffer_path: type = #{conf['type']}, buffer_path = #{@buffer_path}"
96
+ raise ConfigError, "Other '#{@@buffer_paths[@buffer_path]}' plugin already use same buffer_path: type = #{conf['@type'] || conf['type']}, buffer_path = #{@buffer_path}"
97
97
  else
98
- @@buffer_paths[@buffer_path] = conf['type']
98
+ @@buffer_paths[@buffer_path] = conf['@type'] || conf['type']
99
99
  end
100
100
 
101
101
  if pos = @buffer_path.index('*')
@@ -1,3 +1,19 @@
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
+
1
17
  module Fluent
2
18
  class GrepFilter < Filter
3
19
  Fluent::Plugin.register_filter('grep', self)
@@ -1,3 +1,19 @@
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
+
1
17
  require 'ostruct'
2
18
 
3
19
  module Fluent
@@ -28,6 +28,8 @@ module Fluent
28
28
  super
29
29
  end
30
30
 
31
+ EMPTY_GIF_IMAGE = "GIF89a\u0001\u0000\u0001\u0000\x80\xFF\u0000\xFF\xFF\xFF\u0000\u0000\u0000,\u0000\u0000\u0000\u0000\u0001\u0000\u0001\u0000\u0000\u0002\u0002D\u0001\u0000;".force_encoding("UTF-8")
32
+
31
33
  config_param :port, :integer, :default => 9880
32
34
  config_param :bind, :string, :default => '0.0.0.0'
33
35
  config_param :body_size_limit, :size, :default => 32*1024*1024 # TODO default
@@ -38,6 +40,7 @@ module Fluent
38
40
  config_param :format, :string, :default => 'default'
39
41
  config_param :blocking_timeout, :time, :default => 0.5
40
42
  config_param :cors_allow_origins, :array, :default => nil
43
+ config_param :respond_with_empty_img, :bool, :default => false
41
44
 
42
45
  def configure(conf)
43
46
  super
@@ -121,7 +124,11 @@ module Fluent
121
124
 
122
125
  # Skip nil record
123
126
  if record.nil?
124
- return ["200 OK", {'Content-type'=>'text/plain'}, ""]
127
+ if @respond_with_empty_img
128
+ return ["200 OK", {'Content-type'=>'image/gif; charset=utf-8'}, EMPTY_GIF_IMAGE]
129
+ else
130
+ return ["200 OK", {'Content-type'=>'text/plain'}, ""]
131
+ end
125
132
  end
126
133
 
127
134
  if @add_http_headers
@@ -163,7 +170,11 @@ module Fluent
163
170
  return ["500 Internal Server Error", {'Content-type'=>'text/plain'}, "500 Internal Server Error\n#{$!}\n"]
164
171
  end
165
172
 
166
- return ["200 OK", {'Content-type'=>'text/plain'}, ""]
173
+ if @respond_with_empty_img
174
+ return ["200 OK", {'Content-type'=>'image/gif; charset=utf-8'}, EMPTY_GIF_IMAGE]
175
+ else
176
+ return ["200 OK", {'Content-type'=>'text/plain'}, ""]
177
+ end
167
178
  end
168
179
 
169
180
  private
@@ -183,9 +194,10 @@ module Fluent
183
194
 
184
195
  def parse_params_with_parser(params)
185
196
  if content = params[EVENT_RECORD_PARAMETER]
186
- time, record = @parser.call(content)
187
- raise "Received event is not #{@format}: #{content}" if record.nil?
188
- return time, record
197
+ @parser.parse(content) { |time, record|
198
+ raise "Received event is not #{@format}: #{content}" if record.nil?
199
+ return time, record
200
+ }
189
201
  else
190
202
  raise "'#{EVENT_RECORD_PARAMETER}' parameter is required"
191
203
  end
@@ -76,7 +76,7 @@ module Fluent
76
76
  opts[:pretty_json] = true
77
77
  end
78
78
 
79
- if tags = qs['tag'] and tag = tags[0]
79
+ if tag = get_search_parameter(qs, 'tag'.freeze)
80
80
  # ?tag= to search an output plugin by match pattern
81
81
  if obj = @agent.plugin_info_by_tag(tag, opts)
82
82
  list = [obj]
@@ -84,17 +84,29 @@ module Fluent
84
84
  list = []
85
85
  end
86
86
 
87
- elsif plugin_ids = qs['id'] and plugin_id = plugin_ids[0]
88
- # ?id= to search a plugin by 'id <plugin_id>' config param
87
+ elsif plugin_id = get_search_parameter(qs, '@id'.freeze)
88
+ # ?@id= to search a plugin by 'id <plugin_id>' config param
89
89
  if obj = @agent.plugin_info_by_id(plugin_id, opts)
90
90
  list = [obj]
91
91
  else
92
92
  list = []
93
93
  end
94
94
 
95
- elsif types = qs['type'] and type = types[0]
96
- # ?type= to search plugins by 'type <type>' config param
97
- list = @agent.plugins_info_by_type(type, opts)
95
+ elsif plugin_id = get_search_parameter(qs, 'id'.freeze)
96
+ # Without @ version of ?@id= for backward compatibility
97
+ if obj = @agent.plugin_info_by_id(plugin_id, opts)
98
+ list = [obj]
99
+ else
100
+ list = []
101
+ end
102
+
103
+ elsif plugin_type = get_search_parameter(qs, '@type'.freeze)
104
+ # ?@type= to search plugins by 'type <type>' config param
105
+ list = @agent.plugins_info_by_type(plugin_type, opts)
106
+
107
+ elsif plugin_type = get_search_parameter(qs, 'type'.freeze)
108
+ # Without @ version of ?@type= for backward compatibility
109
+ list = @agent.plugins_info_by_type(plugin_type, opts)
98
110
 
99
111
  else
100
112
  # otherwise show all plugins
@@ -104,6 +116,11 @@ module Fluent
104
116
  return list, opts
105
117
  end
106
118
 
119
+ def get_search_parameter(qs, param_name)
120
+ return nil unless qs.has_key?(param_name)
121
+ qs[param_name].first
122
+ end
123
+
107
124
  def render_json(obj, opts={})
108
125
  render_json_error(200, obj, opts)
109
126
  end
@@ -211,13 +228,10 @@ module Fluent
211
228
  end
212
229
 
213
230
  MONITOR_INFO = {
214
- 'plugin_id' => 'plugin_id',
215
- 'type' => 'config["type"]',
216
231
  'output_plugin' => 'is_a?(::Fluent::Output)',
217
232
  'buffer_queue_length' => '@buffer.queue_size',
218
233
  'buffer_total_queued_size' => '@buffer.total_queued_chunk_size',
219
234
  'retry_count' => '@num_errors',
220
- 'config' => 'config',
221
235
  }
222
236
 
223
237
  def all_plugins
@@ -252,16 +266,18 @@ module Fluent
252
266
  array
253
267
  end
254
268
 
255
- # try to match the tag and get the info from the
256
- # matched output plugin
269
+ # try to match the tag and get the info from the matched output plugin
270
+ # TODO: Support output in label
257
271
  def plugin_info_by_tag(tag, opts={})
258
- m = Engine.match(tag)
259
- if m
260
- pe = m.output
261
- get_monitor_info(pe, opts)
262
- else
263
- nil
264
- end
272
+ matches = Engine.root_agent.event_router.instance_variable_get(:@match_rules)
273
+ matches.each { |rule|
274
+ if rule.match?(tag)
275
+ if rule.collector.is_a?(Output)
276
+ return get_monitor_info(rule.collector, opts)
277
+ end
278
+ end
279
+ }
280
+ nil
265
281
  end
266
282
 
267
283
  # search a plugin by plugin_id
@@ -280,7 +296,7 @@ module Fluent
280
296
  # multiple plugins could have the same type
281
297
  def plugins_info_by_type(type, opts={})
282
298
  array = all_plugins.select {|pe|
283
- pe.config['type'] == type rescue nil
299
+ (pe.config['@type'] == type || pe.config['type'] == type) rescue nil
284
300
  }
285
301
  array.map {|pe|
286
302
  get_monitor_info(pe, opts)
@@ -297,6 +313,12 @@ module Fluent
297
313
  def get_monitor_info(pe, opts={})
298
314
  obj = {}
299
315
 
316
+ # Common plugin information
317
+ obj['plugin_id'] = pe.plugin_id
318
+ obj['plugin_category'] = plugin_category(pe)
319
+ obj['type'] = pe.config['@type'] || pe.config['type']
320
+ obj['config'] = pe.config
321
+
300
322
  # run MONITOR_INFO in plugins' instance context and store the info to obj
301
323
  MONITOR_INFO.each_pair {|key,code|
302
324
  begin
@@ -320,6 +342,17 @@ module Fluent
320
342
  obj
321
343
  end
322
344
 
345
+ def plugin_category(pe)
346
+ case pe
347
+ when Fluent::Input
348
+ 'input'.freeze
349
+ when Fluent::Output
350
+ 'output'.freeze
351
+ else
352
+ 'unknown'.freeze
353
+ end
354
+ end
355
+
323
356
  def fluentd_opts
324
357
  @fluentd_opts ||= get_fluentd_opts
325
358
  end
@@ -133,7 +133,7 @@ module Fluent
133
133
  pri = m[1].to_i
134
134
  text = m[2]
135
135
 
136
- @parser.call(text) { |time, record|
136
+ @parser.parse(text) { |time, record|
137
137
  unless time && record
138
138
  log.warn "pattern not match: #{text.inspect}"
139
139
  return
@@ -148,7 +148,7 @@ module Fluent
148
148
  end
149
149
 
150
150
  def receive_data(data, addr)
151
- @parser.call(data) { |time, record|
151
+ @parser.parse(data) { |time, record|
152
152
  unless time && record
153
153
  log.warn "invalid syslog message", :data => data
154
154
  return
@@ -192,7 +192,7 @@ module Fluent
192
192
  def flush_buffer(tw)
193
193
  if lb = tw.line_buffer
194
194
  lb.chomp!
195
- @parser.call(lb) { |time, record|
195
+ @parser.parse(lb) { |time, record|
196
196
  if time && record
197
197
  tag = if @tag_prefix || @tag_suffix
198
198
  @tag_prefix + tw.tag + @tag_suffix
@@ -233,7 +233,7 @@ module Fluent
233
233
  def convert_line_to_event(line, es)
234
234
  begin
235
235
  line.chomp! # remove \n
236
- @parser.call(line) { |time, record|
236
+ @parser.parse(line) { |time, record|
237
237
  if time && record
238
238
  es.add(time, record)
239
239
  else
@@ -276,7 +276,7 @@ module Fluent
276
276
  lb ||= ''
277
277
  lines.each do |line|
278
278
  lb << line
279
- @parser.call(lb) { |time, record|
279
+ @parser.parse(lb) { |time, record|
280
280
  if time && record
281
281
  convert_line_to_event(lb, es)
282
282
  lb = ''
@@ -32,7 +32,7 @@ module Fluent
32
32
  conf.elements.select {|e|
33
33
  e.name == 'store'
34
34
  }.each {|e|
35
- type = e['type']
35
+ type = e['@type'] || e['type']
36
36
  unless type
37
37
  raise ConfigError, "Missing 'type' parameter on <store> directive"
38
38
  end
@@ -34,7 +34,7 @@ module Fluent
34
34
  conf.elements.select {|e|
35
35
  e.name == 'store'
36
36
  }.each {|e|
37
- type = e['type']
37
+ type = e['@type'] || e['type']
38
38
  unless type
39
39
  raise ConfigError, "Missing 'type' parameter on <store> directive"
40
40
  end
@@ -132,7 +132,7 @@ module Fluent
132
132
  private
133
133
 
134
134
  def on_message(msg, addr)
135
- @parser.call(msg) { |time, record|
135
+ @parser.parse(msg) { |time, record|
136
136
  unless time && record
137
137
  log.warn "pattern not match: #{msg.inspect}"
138
138
  return
@@ -34,14 +34,14 @@ module Fluent
34
34
  # <filter> <match>
35
35
  #
36
36
  # Relation:
37
- # * RootAgent has many <label>, <source> and <match>
37
+ # * RootAgent has many <label>, <source>, <filter> and <match>
38
38
  # * <label> has many <match> and <filter>
39
39
  #
40
40
  # Next step: `fluentd/agent.rb`
41
41
  # Next step: 'fluentd/label.rb'
42
42
  #
43
43
  class RootAgent < Agent
44
- ERROR_LABEL = "@ERROR".freeze
44
+ ERROR_LABEL = "@ERROR".freeze # @ERROR is built-in error label
45
45
 
46
46
  def initialize(opts = {})
47
47
  super
@@ -60,11 +60,9 @@ module Fluent
60
60
  attr_reader :labels
61
61
 
62
62
  def configure(conf)
63
- super
64
-
65
63
  error_label_config = nil
66
64
 
67
- # initialize <label> elements
65
+ # initialize <label> elements before configuring all plugins to avoid 'label not found' in input, filter and output.
68
66
  label_configs = {}
69
67
  conf.elements.select { |e| e.name == 'label' }.each { |e|
70
68
  name = e.arg
@@ -79,23 +77,23 @@ module Fluent
79
77
  }
80
78
  # Call 'configure' here to avoid 'label not found'
81
79
  label_configs.each { |name, e| @labels[name].configure(e) }
80
+ setup_error_label(error_label_config) if error_label_config
81
+
82
+ super
82
83
 
83
84
  # initialize <source> elements
84
85
  if @without_source
85
86
  log.info "'--without-source' is applied. Ignore <source> sections"
86
87
  else
87
88
  conf.elements.select { |e| e.name == 'source' }.each { |e|
88
- type = e['type']
89
+ type = e['@type'] || e['type']
89
90
  raise ConfigError, "Missing 'type' parameter on <source> directive" unless type
90
91
  add_source(type, e)
91
92
  }
92
93
  end
93
-
94
- setup_error_label(error_label_config) if error_label_config
95
94
  end
96
95
 
97
96
  def setup_error_label(e)
98
- # initialize built-in ERROR label
99
97
  error_label = add_label(ERROR_LABEL)
100
98
  error_label.configure(e)
101
99
  error_label.root_agent = RootAgentProxyWithoutErrorCollector.new(self)
@@ -190,10 +188,7 @@ module Fluent
190
188
  if @suppress_emit_error_log_interval.zero? || now > @next_emit_error_log_time
191
189
  log.warn "emit transaction failed:", error_info
192
190
  log.warn_backtrace
193
- # log.debug "current next_emit_error_log_time: #{Time.at(@next_emit_error_log_time)}"
194
191
  @next_emit_error_log_time = now + @suppress_emit_error_log_interval
195
- # log.debug "next emit failure log suppressed"
196
- # log.debug "next logged time is #{Time.at(@next_emit_error_log_time)}"
197
192
  end
198
193
  raise error
199
194
  end
@@ -16,6 +16,6 @@
16
16
 
17
17
  module Fluent
18
18
 
19
- VERSION = '0.12.0.pre.3'
19
+ VERSION = '0.12.0'
20
20
 
21
21
  end
@@ -13,6 +13,7 @@ class HttpInputTest < Test::Unit::TestCase
13
13
  bind "127.0.0.1"
14
14
  body_size_limit 10m
15
15
  keepalive_timeout 5
16
+ respond_with_empty_img true
16
17
  ]
17
18
 
18
19
  def create_driver(conf=CONFIG)
@@ -199,6 +200,25 @@ class HttpInputTest < Test::Unit::TestCase
199
200
  end
200
201
  end
201
202
 
203
+ def test_resonse_with_empty_img
204
+ d = create_driver(CONFIG + "respond_with_empty_img true")
205
+ assert_equal true, d.instance.respond_with_empty_img
206
+
207
+ time = Time.parse("2011-01-02 13:14:15 UTC").to_i
208
+
209
+ d.expect_emit "tag1", time, {"a"=>1}
210
+ d.expect_emit "tag2", time, {"a"=>2}
211
+
212
+ d.run do
213
+ d.expected_emits.each {|tag,time,record|
214
+ res = post("/#{tag}", {"json"=>record.to_json, "time"=>time.to_s})
215
+ assert_equal "200", res.code
216
+ # Ruby returns ASCII-8 encoded string for GIF.
217
+ assert_equal Fluent::HttpInput::EMPTY_GIF_IMAGE, res.body.force_encoding("UTF-8")
218
+ }
219
+ end
220
+ end
221
+
202
222
  def test_if_content_type_is_initialized_properly
203
223
  # This test is to check if Fluent::HttpInput::Handler's @content_type is initialized properly.
204
224
  # Especially when in Keep-Alive and the second request has no 'Content-Type'.
@@ -214,17 +234,17 @@ class HttpInputTest < Test::Unit::TestCase
214
234
  begin
215
235
  # Create the extended Handler which can store @content_type per request
216
236
  ext_handler = Class.new(Fluent::HttpInput::Handler) do
217
- @@content_types = []
237
+ @@content_types = []
218
238
 
219
- def self.content_types
220
- @@content_types
221
- end
239
+ def self.content_types
240
+ @@content_types
241
+ end
222
242
 
223
- def on_message_complete
224
- @@content_types << @content_type
225
- super
226
- end
243
+ def on_message_complete
244
+ @@content_types << @content_type
245
+ super
227
246
  end
247
+ end
228
248
 
229
249
  # Replace the original Handler temporally with the extended one
230
250
  Fluent::HttpInput.module_eval do