fluentd 0.10.57 → 0.10.58

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (78) hide show
  1. checksums.yaml +4 -4
  2. data/.travis.yml +1 -0
  3. data/ChangeLog +18 -0
  4. data/fluent.conf +26 -16
  5. data/fluentd.gemspec +1 -1
  6. data/lib/fluent/config.rb +1 -8
  7. data/lib/fluent/config/basic_parser.rb +1 -0
  8. data/lib/fluent/config/configure_proxy.rb +7 -7
  9. data/lib/fluent/config/types.rb +1 -0
  10. data/lib/fluent/config/v1_parser.rb +2 -1
  11. data/lib/fluent/engine.rb +0 -26
  12. data/lib/fluent/env.rb +1 -0
  13. data/lib/fluent/formatter.rb +96 -26
  14. data/lib/fluent/input.rb +4 -0
  15. data/lib/fluent/output.rb +7 -2
  16. data/lib/fluent/parser.rb +82 -88
  17. data/lib/fluent/plugin.rb +18 -0
  18. data/lib/fluent/plugin/buf_file.rb +3 -3
  19. data/lib/fluent/plugin/in_dummy.rb +103 -0
  20. data/lib/fluent/plugin/in_exec.rb +1 -1
  21. data/lib/fluent/plugin/in_forward.rb +3 -3
  22. data/lib/fluent/plugin/in_gc_stat.rb +1 -1
  23. data/lib/fluent/plugin/in_http.rb +49 -17
  24. data/lib/fluent/plugin/in_monitor_agent.rb +41 -10
  25. data/lib/fluent/plugin/in_object_space.rb +1 -1
  26. data/lib/fluent/plugin/in_status.rb +1 -1
  27. data/lib/fluent/plugin/in_stream.rb +3 -3
  28. data/lib/fluent/plugin/in_syslog.rb +5 -5
  29. data/lib/fluent/plugin/in_tail.rb +6 -6
  30. data/lib/fluent/plugin/out_copy.rb +1 -1
  31. data/lib/fluent/plugin/out_exec_filter.rb +1 -1
  32. data/lib/fluent/plugin/out_file.rb +3 -3
  33. data/lib/fluent/plugin/out_roundrobin.rb +1 -1
  34. data/lib/fluent/plugin/socket_util.rb +2 -2
  35. data/lib/fluent/supervisor.rb +21 -24
  36. data/lib/fluent/test/base.rb +14 -0
  37. data/lib/fluent/test/output_test.rb +7 -1
  38. data/lib/fluent/version.rb +1 -1
  39. data/test/config/test_config_parser.rb +6 -2
  40. data/test/config/test_configurable.rb +1 -1
  41. data/test/config/test_configure_proxy.rb +1 -1
  42. data/test/config/test_dsl.rb +1 -1
  43. data/test/config/test_literal_parser.rb +2 -2
  44. data/test/config/test_section.rb +1 -1
  45. data/test/config/test_system_config.rb +65 -15
  46. data/test/config/test_types.rb +63 -0
  47. data/test/plugin/test_in_dummy.rb +95 -0
  48. data/test/plugin/test_in_exec.rb +1 -1
  49. data/test/plugin/test_in_forward.rb +1 -2
  50. data/test/plugin/test_in_gc_stat.rb +1 -1
  51. data/test/plugin/test_in_http.rb +77 -2
  52. data/test/plugin/test_in_object_space.rb +1 -1
  53. data/test/plugin/test_in_status.rb +1 -1
  54. data/test/plugin/test_in_stream.rb +1 -2
  55. data/test/plugin/test_in_syslog.rb +1 -2
  56. data/test/plugin/test_in_tail.rb +1 -1
  57. data/test/plugin/test_in_tcp.rb +1 -2
  58. data/test/plugin/test_in_udp.rb +1 -2
  59. data/test/plugin/test_out_copy.rb +12 -1
  60. data/test/plugin/test_out_exec.rb +1 -1
  61. data/test/plugin/test_out_exec_filter.rb +1 -1
  62. data/test/plugin/test_out_file.rb +61 -7
  63. data/test/plugin/test_out_forward.rb +1 -2
  64. data/test/plugin/test_out_roundrobin.rb +12 -1
  65. data/test/plugin/test_out_stdout.rb +1 -1
  66. data/test/plugin/test_out_stream.rb +1 -2
  67. data/test/scripts/fluent/plugin/formatter_known.rb +4 -1
  68. data/{lib → test/scripts}/fluent/plugin/out_test.rb +0 -0
  69. data/test/scripts/fluent/plugin/parser_known.rb +2 -1
  70. data/test/test_config.rb +1 -1
  71. data/test/test_configdsl.rb +1 -2
  72. data/test/test_formatter.rb +172 -3
  73. data/test/test_input.rb +21 -0
  74. data/test/test_match.rb +1 -2
  75. data/test/test_mixin.rb +1 -1
  76. data/test/test_output.rb +25 -1
  77. data/test/test_parser.rb +124 -78
  78. metadata +12 -4
@@ -0,0 +1,103 @@
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
+ module Fluent
18
+ class DummyInput < Input
19
+ Fluent::Plugin.register_input('dummy', self)
20
+
21
+ BIN_NUM = 10
22
+
23
+ config_param :tag, :string
24
+ config_param :rate, :integer, :default => 1
25
+ config_param :auto_increment_key, :string, :default => nil
26
+ config_param :dummy, :default => [{"message"=>"dummy"}] do |val|
27
+ begin
28
+ parsed = JSON.parse(val)
29
+ rescue JSON::ParserError => e
30
+ # Fluent::ConfigParseError, "got incomplete JSON" will be raised
31
+ # at literal_parser.rb with --use-v1-config, but I had to
32
+ # take care at here for the case of --use-v0-config.
33
+ raise Fluent::ConfigError, "#{e.class}: #{e.message}"
34
+ end
35
+ dummy = parsed.is_a?(Array) ? parsed : [parsed]
36
+ dummy.each_with_index do |e, i|
37
+ raise Fluent::ConfigError, "#{i}th element of dummy, #{e}, is not a hash" unless e.is_a?(Hash)
38
+ end
39
+ dummy
40
+ end
41
+
42
+ def configure(conf)
43
+ super
44
+
45
+ @increment_value = 0
46
+ @dummy_index = 0
47
+ end
48
+
49
+ def start
50
+ super
51
+ @running = true
52
+ @thread = Thread.new(&method(:run))
53
+ end
54
+
55
+ def shutdown
56
+ @running = false
57
+ @thread.join
58
+ end
59
+
60
+ def run
61
+ batch_num = (@rate / BIN_NUM).to_i
62
+ residual_num = (@rate % BIN_NUM)
63
+ while @running
64
+ current_time = Time.now.to_i
65
+ BIN_NUM.times do
66
+ break unless (@running && Time.now.to_i <= current_time)
67
+ wait(0.1) { emit(batch_num) }
68
+ end
69
+ emit(residual_num)
70
+ # wait for next second
71
+ while @running && Time.now.to_i <= current_time
72
+ sleep 0.01
73
+ end
74
+ end
75
+ end
76
+
77
+ def emit(num)
78
+ num.times { router.emit(@tag, Fluent::Engine.now, generate()) }
79
+ end
80
+
81
+ def generate
82
+ d = @dummy[@dummy_index]
83
+ unless d
84
+ @dummy_index = 0
85
+ d = @dummy[0]
86
+ end
87
+ @dummy_index += 1
88
+ if @auto_increment_key
89
+ d = d.dup
90
+ d[@auto_increment_key] = @increment_value
91
+ @increment_value += 1
92
+ end
93
+ d
94
+ end
95
+
96
+ def wait(time)
97
+ start_time = Time.now
98
+ yield
99
+ sleep_time = time - (Time.now - start_time)
100
+ sleep sleep_time if sleep_time > 0
101
+ end
102
+ end
103
+ end
@@ -151,7 +151,7 @@ module Fluent
151
151
  time = Engine.now
152
152
  end
153
153
 
154
- Engine.emit(tag, time, record)
154
+ router.emit(tag, time, record)
155
155
  rescue => e
156
156
  log.error "exec failed to emit", :error => e.to_s, :error_class => e.class.to_s, :tag => tag, :record => Yajl.dump(record)
157
157
  end
@@ -145,7 +145,7 @@ module Fluent
145
145
  if entries.class == String
146
146
  # PackedForward
147
147
  es = MessagePackEventStream.new(entries, @cached_unpacker)
148
- Engine.emit_stream(tag, es)
148
+ router.emit_stream(tag, es)
149
149
 
150
150
  elsif entries.class == Array
151
151
  # Forward
@@ -157,7 +157,7 @@ module Fluent
157
157
  time = (now ||= Engine.now) if time == 0
158
158
  es.add(time, record)
159
159
  }
160
- Engine.emit_stream(tag, es)
160
+ router.emit_stream(tag, es)
161
161
 
162
162
  else
163
163
  # Message
@@ -165,7 +165,7 @@ module Fluent
165
165
  return if record.nil?
166
166
  time = msg[1]
167
167
  time = Engine.now if time == 0
168
- Engine.emit(tag, time, record)
168
+ router.emit(tag, time, record)
169
169
  end
170
170
  end
171
171
 
@@ -69,7 +69,7 @@ module Fluent
69
69
  def on_timer
70
70
  now = Engine.now
71
71
  record = GC.stat
72
- Engine.emit(@tag, now, record)
72
+ router.emit(@tag, now, record)
73
73
  end
74
74
  end
75
75
  end
@@ -29,13 +29,18 @@ module Fluent
29
29
  super
30
30
  end
31
31
 
32
+ 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")
33
+
32
34
  config_param :port, :integer, :default => 9880
33
35
  config_param :bind, :string, :default => '0.0.0.0'
34
36
  config_param :body_size_limit, :size, :default => 32*1024*1024 # TODO default
35
37
  config_param :keepalive_timeout, :time, :default => 10 # TODO default
36
38
  config_param :backlog, :integer, :default => nil
37
39
  config_param :add_http_headers, :bool, :default => false
40
+ config_param :add_remote_addr, :bool, :default => false
38
41
  config_param :format, :string, :default => 'default'
42
+ config_param :cors_allow_origins, :array, :default => nil
43
+ config_param :respond_with_empty_img, :bool, :default => false
39
44
 
40
45
  def configure(conf)
41
46
  super
@@ -43,9 +48,8 @@ module Fluent
43
48
  m = if @format == 'default'
44
49
  method(:parse_params_default)
45
50
  else
46
- parser = TextParser.new
47
- parser.configure(conf)
48
- @parser = parser
51
+ @parser = Plugin.new_parser(@format)
52
+ @parser.configure(conf)
49
53
  method(:parse_params_with_parser)
50
54
  end
51
55
  (class << self; self; end).module_eval do
@@ -85,7 +89,9 @@ module Fluent
85
89
  super
86
90
  @km = KeepaliveManager.new(@keepalive_timeout)
87
91
  #@lsock = Coolio::TCPServer.new(@bind, @port, Handler, @km, method(:on_request), @body_size_limit)
88
- @lsock = Coolio::TCPServer.new(lsock, nil, Handler, @km, method(:on_request), @body_size_limit, @format, log)
92
+ @lsock = Coolio::TCPServer.new(lsock, nil, Handler, @km, method(:on_request),
93
+ @body_size_limit, @format, log,
94
+ @cors_allow_origins)
89
95
  @lsock.listen(@backlog) unless @backlog.nil?
90
96
 
91
97
  @loop = Coolio::Loop.new
@@ -126,7 +132,11 @@ module Fluent
126
132
 
127
133
  # Skip nil record
128
134
  if record.nil?
129
- return ["200 OK", {'Content-type'=>'text/plain'}, ""]
135
+ if @respond_with_empty_img
136
+ return ["200 OK", {'Content-type'=>'image/gif; charset=utf-8'}, EMPTY_GIF_IMAGE]
137
+ else
138
+ return ["200 OK", {'Content-type'=>'text/plain'}, ""]
139
+ end
130
140
  end
131
141
 
132
142
  if @add_http_headers
@@ -137,6 +147,10 @@ module Fluent
137
147
  }
138
148
  end
139
149
 
150
+ if @add_remote_addr
151
+ record['REMOTE_ADDR'] = params['REMOTE_ADDR']
152
+ end
153
+
140
154
  time = if param_time = params['time']
141
155
  param_time = param_time.to_i
142
156
  param_time.zero? ? Engine.now : param_time
@@ -150,21 +164,25 @@ module Fluent
150
164
  # TODO server error
151
165
  begin
152
166
  # Support batched requests
153
- if record.is_a?(Array)
167
+ if record.is_a?(Array)
154
168
  mes = MultiEventStream.new
155
169
  record.each do |single_record|
156
- single_time = single_record.delete("time") || time
170
+ single_time = single_record.delete("time") || time
157
171
  mes.add(single_time, single_record)
158
172
  end
159
- Engine.emit_stream(tag, mes)
173
+ router.emit_stream(tag, mes)
160
174
  else
161
- Engine.emit(tag, time, record)
175
+ router.emit(tag, time, record)
162
176
  end
163
177
  rescue
164
178
  return ["500 Internal Server Error", {'Content-type'=>'text/plain'}, "500 Internal Server Error\n#{$!}\n"]
165
179
  end
166
180
 
167
- return ["200 OK", {'Content-type'=>'text/plain'}, ""]
181
+ if @respond_with_empty_img
182
+ return ["200 OK", {'Content-type'=>'image/gif; charset=utf-8'}, EMPTY_GIF_IMAGE]
183
+ else
184
+ return ["200 OK", {'Content-type'=>'text/plain'}, ""]
185
+ end
168
186
  end
169
187
 
170
188
  private
@@ -184,25 +202,25 @@ module Fluent
184
202
 
185
203
  def parse_params_with_parser(params)
186
204
  if content = params[EVENT_RECORD_PARAMETER]
187
- time, record = @parser.parse(content)
188
- raise "Received event is not #{@format}: #{content}" if record.nil?
189
- return time, record
205
+ @parser.parse(content) { |time, record|
206
+ raise "Received event is not #{@format}: #{content}" if record.nil?
207
+ return time, record
208
+ }
190
209
  else
191
210
  raise "'#{EVENT_RECORD_PARAMETER}' parameter is required"
192
211
  end
193
212
  end
194
213
 
195
214
  class Handler < Coolio::Socket
196
- def initialize(io, km, callback, body_size_limit, format, log)
215
+ def initialize(io, km, callback, body_size_limit, format, log, cors_allow_origins)
197
216
  super(io)
198
217
  @km = km
199
218
  @callback = callback
200
219
  @body_size_limit = body_size_limit
201
- @content_type = ""
202
220
  @next_close = false
203
221
  @format = format
204
222
  @log = log
205
-
223
+ @cors_allow_origins = cors_allow_origins
206
224
  @idle = 0
207
225
  @km.add(self)
208
226
 
@@ -237,12 +255,14 @@ module Fluent
237
255
  def on_headers_complete(headers)
238
256
  expect = nil
239
257
  size = nil
258
+
240
259
  if @parser.http_version == [1, 1]
241
260
  @keep_alive = true
242
261
  else
243
262
  @keep_alive = false
244
263
  end
245
264
  @env = {}
265
+ @content_type = ""
246
266
  headers.each_pair {|k,v|
247
267
  @env["HTTP_#{k.gsub('-','_').upcase}"] = v
248
268
  case k
@@ -258,6 +278,8 @@ module Fluent
258
278
  elsif v =~ /Keep-alive/i
259
279
  @keep_alive = true
260
280
  end
281
+ when /Origin/i
282
+ @origin = v
261
283
  end
262
284
  }
263
285
  if expect
@@ -286,6 +308,17 @@ module Fluent
286
308
  def on_message_complete
287
309
  return if closing?
288
310
 
311
+ # CORS check
312
+ # ==========
313
+ # For every incoming request, we check if we have some CORS
314
+ # restrictions and white listed origins through @cors_allow_origins.
315
+ unless @cors_allow_origins.nil?
316
+ unless @cors_allow_origins.include?(@origin)
317
+ send_response_and_close("403 Forbidden", {'Connection' => 'close'}, "")
318
+ return
319
+ end
320
+ end
321
+
289
322
  @env['REMOTE_ADDR'] = @remote_addr if @remote_addr
290
323
 
291
324
  uri = URI.parse(@parser.request_url)
@@ -355,4 +388,3 @@ module Fluent
355
388
  end
356
389
  end
357
390
  end
358
-
@@ -77,7 +77,7 @@ module Fluent
77
77
  opts[:pretty_json] = true
78
78
  end
79
79
 
80
- if tags = qs['tag'] and tag = tags[0]
80
+ if tag = get_search_parameter(qs, 'tag'.freeze)
81
81
  # ?tag= to search an output plugin by match pattern
82
82
  if obj = @agent.plugin_info_by_tag(tag, opts)
83
83
  list = [obj]
@@ -85,17 +85,29 @@ module Fluent
85
85
  list = []
86
86
  end
87
87
 
88
- elsif plugin_ids = qs['id'] and plugin_id = plugin_ids[0]
89
- # ?id= to search a plugin by 'id <plugin_id>' config param
88
+ elsif plugin_id = get_search_parameter(qs, '@id'.freeze)
89
+ # ?@id= to search a plugin by 'id <plugin_id>' config param
90
90
  if obj = @agent.plugin_info_by_id(plugin_id, opts)
91
91
  list = [obj]
92
92
  else
93
93
  list = []
94
94
  end
95
95
 
96
- elsif types = qs['type'] and type = types[0]
97
- # ?type= to search plugins by 'type <type>' config param
98
- list = @agent.plugins_info_by_type(type, opts)
96
+ elsif plugin_id = get_search_parameter(qs, 'id'.freeze)
97
+ # Without @ version of ?@id= for backward compatibility
98
+ if obj = @agent.plugin_info_by_id(plugin_id, opts)
99
+ list = [obj]
100
+ else
101
+ list = []
102
+ end
103
+
104
+ elsif plugin_type = get_search_parameter(qs, '@type'.freeze)
105
+ # ?@type= to search plugins by 'type <type>' config param
106
+ list = @agent.plugins_info_by_type(plugin_type, opts)
107
+
108
+ elsif plugin_type = get_search_parameter(qs, 'type'.freeze)
109
+ # Without @ version of ?@type= for backward compatibility
110
+ list = @agent.plugins_info_by_type(plugin_type, opts)
99
111
 
100
112
  else
101
113
  # otherwise show all plugins
@@ -105,6 +117,11 @@ module Fluent
105
117
  return list, opts
106
118
  end
107
119
 
120
+ def get_search_parameter(qs, param_name)
121
+ return nil unless qs.has_key?(param_name)
122
+ qs[param_name].first
123
+ end
124
+
108
125
  def render_json(obj, opts={})
109
126
  render_json_error(200, obj, opts)
110
127
  end
@@ -212,13 +229,10 @@ module Fluent
212
229
  end
213
230
 
214
231
  MONITOR_INFO = {
215
- 'plugin_id' => 'plugin_id',
216
- 'type' => 'config["type"]',
217
232
  'output_plugin' => 'is_a?(::Fluent::Output)',
218
233
  'buffer_queue_length' => '@buffer.queue_size',
219
234
  'buffer_total_queued_size' => '@buffer.total_queued_chunk_size',
220
235
  'retry_count' => '@num_errors',
221
- 'config' => 'config',
222
236
  }
223
237
 
224
238
  def all_plugins
@@ -275,7 +289,7 @@ module Fluent
275
289
  # multiple plugins could have the same type
276
290
  def plugins_info_by_type(type, opts={})
277
291
  array = all_plugins.select {|pe|
278
- pe.config['type'] == type rescue nil
292
+ (pe.config['@type'] == type || pe.config['type'] == type) rescue nil
279
293
  }
280
294
  array.map {|pe|
281
295
  get_monitor_info(pe, opts)
@@ -292,6 +306,12 @@ module Fluent
292
306
  def get_monitor_info(pe, opts={})
293
307
  obj = {}
294
308
 
309
+ # Common plugin information
310
+ obj['plugin_id'] = pe.plugin_id
311
+ obj['plugin_category'] = plugin_category(pe)
312
+ obj['type'] = pe.config['@type'] || pe.config['type']
313
+ obj['config'] = pe.config
314
+
295
315
  # run MONITOR_INFO in plugins' instance context and store the info to obj
296
316
  MONITOR_INFO.each_pair {|key,code|
297
317
  begin
@@ -315,6 +335,17 @@ module Fluent
315
335
  obj
316
336
  end
317
337
 
338
+ def plugin_category(pe)
339
+ case pe
340
+ when Fluent::Input
341
+ 'input'.freeze
342
+ when Fluent::Output
343
+ 'output'.freeze
344
+ else
345
+ 'unknown'.freeze
346
+ end
347
+ end
348
+
318
349
  def fluentd_opts
319
350
  @fluentd_opts ||= get_fluentd_opts
320
351
  end
@@ -109,7 +109,7 @@ module Fluent
109
109
  record[c.name] = c.count
110
110
  }
111
111
 
112
- Engine.emit(@tag, now, record)
112
+ router.emit(@tag, now, record)
113
113
  rescue => e
114
114
  log.error "object space failed to emit", :error => e.to_s, :error_class => e.class.to_s, :tag => @tag, :record => Yajl.dump(record)
115
115
  end
@@ -69,7 +69,7 @@ module Fluent
69
69
  def on_timer
70
70
  now = Engine.now
71
71
  Status.each {|record|
72
- Engine.emit(@tag, now, record)
72
+ router.emit(@tag, now, record)
73
73
  }
74
74
  end
75
75
  end
@@ -87,7 +87,7 @@ module Fluent
87
87
  if entries.class == String
88
88
  # PackedForward
89
89
  es = MessagePackEventStream.new(entries, @cached_unpacker)
90
- Engine.emit_stream(tag, es)
90
+ router.emit_stream(tag, es)
91
91
 
92
92
  elsif entries.class == Array
93
93
  # Forward
@@ -99,7 +99,7 @@ module Fluent
99
99
  time = (now ||= Engine.now) if time == 0
100
100
  es.add(time, record)
101
101
  }
102
- Engine.emit_stream(tag, es)
102
+ router.emit_stream(tag, es)
103
103
 
104
104
  else
105
105
  # Message
@@ -108,7 +108,7 @@ module Fluent
108
108
 
109
109
  time = msg[1]
110
110
  time = Engine.now if time == 0
111
- Engine.emit(tag, time, record)
111
+ router.emit(tag, time, record)
112
112
  end
113
113
  end
114
114