fluentd 0.12.15 → 0.12.16

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: 4e290b6bd8327f7f9fd6a0de891b535de5dbfeb8
4
- data.tar.gz: 65429f3882fdcc0bde69322bdc52ff1aaee29e28
3
+ metadata.gz: 2bc899dd92a360462c8ab40bdec4dfa955918c74
4
+ data.tar.gz: 0c93a1e695baf87d2d01c0ff10821a2733115c44
5
5
  SHA512:
6
- metadata.gz: c99e6469d461655c2e43403a6ccb64eb72357853953b8140d1e5ff05a332ada1f3b4a21f74ffd218153cbe68445dc9cb2e0a3972d7a37f3ff9b2f22d26fdbcbb
7
- data.tar.gz: f650ce569e09dcf28dc2113a573c88fd35e7f239028159c0f3971e397f32eca694f2ccd0c096084cb90345e0f39b3a4ccb0f48e22ef87a135d490fb7c2e7bc5d
6
+ metadata.gz: 35585caea38a587df230faa0027cbd5820c6e43e48669d35755055f73a185509e08bf0089c0ff7b2e4c4eaacfe361406f14709161d81f7e3ebc6354c5a203fd1
7
+ data.tar.gz: 9f3e64fedf79a5609c54e7387a14d2a4c03705b7be00d4382d554a4ff357b5617dcc10689f6e4067ecd8e9e99cf6158583c88ac32761433f04fb091229db6fdc
data/.gitignore CHANGED
@@ -21,3 +21,4 @@ test/config/tmp/*
21
21
  make_dist.sh
22
22
  Gemfile.local
23
23
  .ruby-version
24
+ coverage/*
data/ChangeLog CHANGED
@@ -1,5 +1,23 @@
1
1
  # v0.12
2
2
 
3
+ ## Release 0.12.16 - 2015/09/30
4
+
5
+ ### New features / Enhancement
6
+
7
+ * parser: Add null_value_pattern and null_empty_string option for ltsv, csv and tsv.
8
+ https://github.com/fluent/fluentd/pull/657
9
+ * RPC: Implement /api/config.dump and /api/config.getDump APIs to dump in memory config
10
+ https://github.com/fluent/fluentd/pull/666
11
+ * Add --show-plugin-config option
12
+ https://github.com/fluent/fluentd/pull/663
13
+
14
+ ### Bug fixes
15
+
16
+ * in_http: Fix add_remote_addr and add_http_headers options not working on bulk request
17
+ https://github.com/fluent/fluentd/pull/673
18
+ * parser: RegexpParser#initialize should wrap only Hash configuration
19
+ https://github.com/fluent/fluentd/pull/647
20
+
3
21
  ## Release 0.12.15 - 2015/07/29
4
22
 
5
23
  ### New features / Enhancement
@@ -30,7 +30,7 @@ Gem::Specification.new do |gem|
30
30
  gem.add_runtime_dependency("string-scrub", [">= 0.0.3"])
31
31
 
32
32
  gem.add_development_dependency("rake", [">= 0.9.2"])
33
- gem.add_development_dependency("flexmock")
33
+ gem.add_development_dependency("flexmock", ["~> 1.3.3"])
34
34
  gem.add_development_dependency("parallel_tests", [">= 0.15.3"])
35
35
  gem.add_development_dependency("simplecov", ["~> 0.6.4"])
36
36
  gem.add_development_dependency("rr", [">= 1.0.0"])
@@ -34,6 +34,10 @@ op.on('--dry-run', "Check fluentd setup is correct or not", TrueClass) {|b|
34
34
  opts[:dry_run] = b
35
35
  }
36
36
 
37
+ op.on('--show-plugin-config=PLUGIN', "Show PLUGIN configuration and exit(ex: input:dummy)") {|plugin|
38
+ opts[:show_plugin_config] = plugin
39
+ }
40
+
37
41
  op.on('-p', '--plugin DIR', "add plugin directory") {|s|
38
42
  opts[:plugin_dirs] << s
39
43
  }
@@ -17,7 +17,7 @@
17
17
  module Fluent
18
18
  module Config
19
19
  class ConfigureProxy
20
- attr_accessor :name, :final, :param_name, :required, :multi, :alias, :argument, :params, :defaults, :sections
20
+ attr_accessor :name, :final, :param_name, :required, :multi, :alias, :argument, :params, :defaults, :descriptions, :sections
21
21
  # config_param :desc, :string, :default => '....'
22
22
  # config_set_default :buffer_type, :memory
23
23
  #
@@ -48,6 +48,7 @@ module Fluent
48
48
  @argument = nil # nil: ignore argument
49
49
  @params = {}
50
50
  @defaults = {}
51
+ @descriptions = {}
51
52
  @sections = {}
52
53
  end
53
54
 
@@ -157,6 +158,10 @@ module Fluent
157
158
  config_set_default(name, opts[:default])
158
159
  end
159
160
 
161
+ if opts.has_key?(:desc)
162
+ config_set_desc(name, opts[:desc])
163
+ end
164
+
160
165
  [name, block, opts]
161
166
  end
162
167
 
@@ -189,6 +194,17 @@ module Fluent
189
194
  nil
190
195
  end
191
196
 
197
+ def config_set_desc(name, description)
198
+ name = name.to_sym
199
+
200
+ if @descriptions.has_key?(name)
201
+ raise ArgumentError, "#{self.name}: description specified twice for #{name}"
202
+ end
203
+
204
+ @descriptions[name] = description
205
+ nil
206
+ end
207
+
192
208
  def config_section(name, *args, &block)
193
209
  unless block_given?
194
210
  raise ArgumentError, "#{self.name}: config_section requires block parameter"
@@ -208,6 +224,20 @@ module Fluent
208
224
 
209
225
  name
210
226
  end
227
+
228
+ def dump(level = 0)
229
+ dumped_config = "\n"
230
+ indent = " " * level
231
+ @params.each do |name, config|
232
+ dumped_config << "#{indent}#{name}: #{config[1][:type]}: <#{@defaults[name].inspect}>"
233
+ dumped_config << " # #{@descriptions[name]}" if @descriptions[name]
234
+ dumped_config << "\n"
235
+ end
236
+ @sections.each do |section_name, sub_proxy|
237
+ dumped_config << "#{indent}#{section_name}#{sub_proxy.dump(level + 1)}"
238
+ end
239
+ dumped_config
240
+ end
211
241
  end
212
242
  end
213
243
  end
@@ -121,6 +121,10 @@ module Fluent
121
121
  # p AnyGreatClass.dup.name #=> nil
122
122
  configurables.map{ |a| a.configure_proxy(a.name || a.object_id.to_s) }.reduce(:merge)
123
123
  end
124
+
125
+ def dump
126
+ configure_proxy_map[self.to_s].dump
127
+ end
124
128
  end
125
129
  end
126
130
 
@@ -527,7 +527,7 @@ module Fluent
527
527
  else
528
528
  @flush_interval = [60, @time_slice_cache_interval].min
529
529
  @enqueue_buffer_proc = Proc.new do
530
- nowslice = @time_slicer.call(Engine.now.to_i - @time_slice_wait)
530
+ nowslice = @time_slicer.call(Engine.now - @time_slice_wait)
531
531
  @buffer.keys.each {|key|
532
532
  if key < nowslice
533
533
  @buffer.push(key)
@@ -169,7 +169,7 @@ module Fluent
169
169
  super()
170
170
  @regexp = regexp
171
171
  unless conf.empty?
172
- conf = Config::Element.new('default_regexp_conf', '', conf, [])
172
+ conf = Config::Element.new('default_regexp_conf', '', conf, []) unless conf.is_a?(Config::Element)
173
173
  configure(conf)
174
174
  end
175
175
 
@@ -295,6 +295,8 @@ module Fluent
295
295
  end
296
296
  config_param :time_key, :string, :default => nil
297
297
  config_param :time_format, :string, :default => nil
298
+ config_param :null_value_pattern, :string, :default => nil
299
+ config_param :null_empty_string, :bool, :default => false
298
300
 
299
301
  def configure(conf)
300
302
  super
@@ -308,11 +310,16 @@ module Fluent
308
310
  end
309
311
 
310
312
  @time_parser = TimeParser.new(@time_format)
313
+
314
+ if @null_value_pattern
315
+ @null_value_pattern = Regexp.new(@null_value_pattern)
316
+ end
317
+
311
318
  @mutex = Mutex.new
312
319
  end
313
320
 
314
321
  def values_map(values)
315
- record = Hash[keys.zip(values)]
322
+ record = Hash[keys.zip(values.map { |value| convert_value_to_nil(value) })]
316
323
 
317
324
  if @time_key
318
325
  value = @keep_time_key ? record[@time_key] : record.delete(@time_key)
@@ -345,6 +352,16 @@ module Fluent
345
352
  end
346
353
  }
347
354
  end
355
+
356
+ def convert_value_to_nil(value)
357
+ if value and @null_empty_string
358
+ value = (value == '') ? nil : value
359
+ end
360
+ if value and @null_value_pattern
361
+ value = ::Fluent::StringUtil.match_regexp(@null_value_pattern, value) ? nil : value
362
+ end
363
+ value
364
+ end
348
365
  end
349
366
 
350
367
  class TSVParser < ValuesParser
@@ -18,6 +18,11 @@ module Fluent
18
18
  class GrepFilter < Filter
19
19
  Fluent::Plugin.register_filter('grep', self)
20
20
 
21
+ def initialize
22
+ super
23
+ require 'fluent/plugin/string_util'
24
+ end
25
+
21
26
  REGEXP_MAX_NUM = 20
22
27
 
23
28
  (1..REGEXP_MAX_NUM).each {|i| config_param :"regexp#{i}", :string, :default => nil }
@@ -54,10 +59,10 @@ module Fluent
54
59
  begin
55
60
  catch(:break_loop) do
56
61
  @regexps.each do |key, regexp|
57
- throw :break_loop unless match(regexp, record[key].to_s)
62
+ throw :break_loop unless ::Fluent::StringUtil.match_regexp(regexp, record[key].to_s)
58
63
  end
59
64
  @excludes.each do |key, exclude|
60
- throw :break_loop if match(exclude, record[key].to_s)
65
+ throw :break_loop if ::Fluent::StringUtil.match_regexp(exclude, record[key].to_s)
61
66
  end
62
67
  result = record
63
68
  end
@@ -67,19 +72,5 @@ module Fluent
67
72
  end
68
73
  result
69
74
  end
70
-
71
- private
72
-
73
- def match(regexp, string)
74
- begin
75
- return regexp.match(string)
76
- rescue ArgumentError => e
77
- raise e unless e.message.index("invalid byte sequence in".freeze).zero?
78
- log.info "invalid byte sequence is replaced in `#{string}`"
79
- string = string.scrub('?')
80
- retry
81
- end
82
- return true
83
- end
84
75
  end
85
76
  end
@@ -131,18 +131,18 @@ module Fluent
131
131
  end
132
132
  end
133
133
 
134
- if @add_http_headers
135
- params.each_pair { |k,v|
136
- if k.start_with?("HTTP_")
137
- record[k] = v
138
- end
139
- }
140
- end
141
-
142
- if @add_remote_addr
143
- record['REMOTE_ADDR'] = params['REMOTE_ADDR']
134
+ unless record.is_a?(Array)
135
+ if @add_http_headers
136
+ params.each_pair { |k,v|
137
+ if k.start_with?("HTTP_")
138
+ record[k] = v
139
+ end
140
+ }
141
+ end
142
+ if @add_remote_addr
143
+ record['REMOTE_ADDR'] = params['REMOTE_ADDR']
144
+ end
144
145
  end
145
-
146
146
  time = if param_time = params['time']
147
147
  param_time = param_time.to_i
148
148
  param_time.zero? ? Engine.now : param_time
@@ -159,11 +159,21 @@ module Fluent
159
159
  if record.is_a?(Array)
160
160
  mes = MultiEventStream.new
161
161
  record.each do |single_record|
162
+ if @add_http_headers
163
+ params.each_pair { |k,v|
164
+ if k.start_with?("HTTP_")
165
+ single_record[k] = v
166
+ end
167
+ }
168
+ end
169
+ if @add_remote_addr
170
+ single_record['REMOTE_ADDR'] = params['REMOTE_ADDR']
171
+ end
162
172
  single_time = single_record.delete("time") || time
163
173
  mes.add(single_time, single_record)
164
174
  end
165
175
  router.emit_stream(tag, mes)
166
- else
176
+ else
167
177
  router.emit(tag, time, record)
168
178
  end
169
179
  rescue
@@ -0,0 +1,32 @@
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
+ module StringUtil
19
+ def match_regexp(regexp, string)
20
+ begin
21
+ return regexp.match(string)
22
+ rescue ArgumentError => e
23
+ raise e unless e.message.index("invalid byte sequence in".freeze).zero?
24
+ log.info "invalid byte sequence is replaced in `#{string}`"
25
+ string = string.scrub('?')
26
+ retry
27
+ end
28
+ return true
29
+ end
30
+ module_function :match_regexp
31
+ end
32
+ end
@@ -41,7 +41,7 @@ module Fluent
41
41
  def mount_proc(path, &block)
42
42
  @server.mount_proc(path) { |req, res|
43
43
  begin
44
- code, header, body = block.call(req, res)
44
+ code, header, response = block.call(req, res)
45
45
  rescue => e
46
46
  @log.warn "failed to handle RPC request", :path => path, :error => e.to_s
47
47
  @log.warn_backtrace e.backtrace
@@ -56,11 +56,11 @@ module Fluent
56
56
 
57
57
  code = 200 if code.nil?
58
58
  header = {'Content-Type' => 'application/json'} if header.nil?
59
- body = if body.nil?
59
+ body = if response.nil?
60
60
  '{"ok":true}'
61
61
  else
62
- body['ok'] = code == 200
63
- body.to_json
62
+ response.body['ok'] = code == 200
63
+ response.body.to_json
64
64
  end
65
65
 
66
66
  res.status = code
@@ -105,6 +105,7 @@ module Fluent
105
105
  @use_v1_config = opt[:use_v1_config]
106
106
  @log_path = opt[:log_path]
107
107
  @dry_run = opt[:dry_run]
108
+ @show_plugin_config = opt[:show_plugin_config]
108
109
  @libs = opt[:libs]
109
110
  @plugin_dirs = opt[:plugin_dirs]
110
111
  @chgroup = opt[:chgroup]
@@ -124,12 +125,15 @@ module Fluent
124
125
 
125
126
  def start
126
127
  @log.init
128
+ show_plugin_config if @show_plugin_config
127
129
  read_config
128
130
  apply_system_config
129
131
 
130
132
  dry_run if @dry_run
131
133
  start_daemonize if @daemonize
132
134
  setup_rpc_server if @rpc_endpoint
135
+ setup_rpc_get_dump if @enable_get_dump
136
+
133
137
  if @supervise
134
138
  install_supervisor_signal_handlers
135
139
  run_rpc_server if @rpc_endpoint
@@ -185,6 +189,17 @@ module Fluent
185
189
  exit 1
186
190
  end
187
191
 
192
+ def show_plugin_config
193
+ $log.info "Show config for #{@show_plugin_config}"
194
+ name, type = @show_plugin_config.split(":")
195
+ plugin = Plugin.__send__("new_#{name}", type)
196
+ $log.info plugin.class.dump
197
+ exit 0
198
+ rescue => e
199
+ $log.error "show config failed: #{e}"
200
+ exit 1
201
+ end
202
+
188
203
  def start_daemonize
189
204
  @wait_daemonize_pipe_r, @wait_daemonize_pipe_w = IO.pipe
190
205
 
@@ -263,6 +278,21 @@ module Fluent
263
278
  supervisor_sighup_handler
264
279
  nil
265
280
  }
281
+ @rpc_server.mount_proc('/api/config.dump') { |req, res|
282
+ $log.debug "fluentd RPC got /api/config.dump request"
283
+ $log.info "dump in-memory config"
284
+ supervisor_dump_config_handler
285
+ nil
286
+ }
287
+ end
288
+
289
+ def setup_rpc_get_dump
290
+ @rpc_server.mount_proc('/api/config.getDump') { |req, res|
291
+ $log.debug "fluentd RPC got /api/config.dump request"
292
+ $log.info "get dump in-memory config via HTTP"
293
+ res.body = supervisor_get_dump_config_handler
294
+ [nil, nil, res]
295
+ }
266
296
  end
267
297
 
268
298
  def run_rpc_server
@@ -399,6 +429,14 @@ module Fluent
399
429
  end
400
430
  end
401
431
 
432
+ def supervisor_dump_config_handler
433
+ $log.info @conf.to_s
434
+ end
435
+
436
+ def supervisor_get_dump_config_handler
437
+ {conf: @conf.to_s}
438
+ end
439
+
402
440
  def read_config
403
441
  $log.info "reading config file", :path => @config_path
404
442
  @config_fname = File.basename(@config_path)
@@ -423,6 +461,7 @@ module Fluent
423
461
  config_param :suppress_config_dump, :bool, :default => nil
424
462
  config_param :without_source, :bool, :default => nil
425
463
  config_param :rpc_endpoint, :string, :default => nil
464
+ config_param :enable_get_dump, :bool, :default => nil
426
465
 
427
466
  def initialize(conf)
428
467
  super()
@@ -438,6 +477,7 @@ module Fluent
438
477
  @suppress_repeated_stacktrace = system.suppress_repeated_stacktrace unless system.suppress_repeated_stacktrace.nil?
439
478
  @without_source = system.without_source unless system.without_source.nil?
440
479
  @rpc_endpoint = system.rpc_endpoint unless system.rpc_endpoint.nil?
480
+ @enable_get_dump = system.enable_get_dump unless system.enable_get_dump.nil?
441
481
  }
442
482
  end
443
483
  end
@@ -16,6 +16,6 @@
16
16
 
17
17
  module Fluent
18
18
 
19
- VERSION = '0.12.15'
19
+ VERSION = '0.12.16'
20
20
 
21
21
  end
@@ -94,6 +94,128 @@ module Fluent::Config
94
94
  assert_raise(ArgumentError) { proxy.config_argument(:name, default: "name2") }
95
95
  end
96
96
  end
97
+
98
+ sub_test_case '#config_set_desc' do
99
+ setup do
100
+ @proxy = Fluent::Config::ConfigureProxy.new(:section)
101
+ end
102
+
103
+ test 'does not permit description specification twice w/ :desc option' do
104
+ @proxy.config_param(:name, :string, desc: "description")
105
+ assert_raise(ArgumentError) { @proxy.config_set_desc(:name, "description2") }
106
+ end
107
+
108
+ test 'does not permit description specification twice' do
109
+ @proxy.config_param(:name, :string)
110
+ @proxy.config_set_desc(:name, "description")
111
+ assert_raise(ArgumentError) { @proxy.config_set_desc(:name, "description2") }
112
+ end
113
+ end
114
+
115
+ sub_test_case '#dump' do
116
+ setup do
117
+ @proxy = Fluent::Config::ConfigureProxy.new(:section)
118
+ end
119
+
120
+ test 'empty proxy' do
121
+ assert_equal("\n", @proxy.dump)
122
+ end
123
+
124
+ test 'plain proxy w/o default value' do
125
+ @proxy.config_param(:name, :string)
126
+ assert_equal(<<CONFIG, @proxy.dump)
127
+
128
+ name: string: <nil>
129
+ CONFIG
130
+ end
131
+
132
+ test 'plain proxy w/ default value' do
133
+ @proxy.config_param(:name, :string, default: "name1")
134
+ assert_equal(<<CONFIG, @proxy.dump)
135
+
136
+ name: string: <"name1">
137
+ CONFIG
138
+ end
139
+
140
+ test 'plain proxy w/ default value using config_set_default' do
141
+ @proxy.config_param(:name, :string)
142
+ @proxy.config_set_default(:name, "name1")
143
+ assert_equal(<<CONFIG, @proxy.dump)
144
+
145
+ name: string: <"name1">
146
+ CONFIG
147
+ end
148
+
149
+ test 'single sub proxy' do
150
+ @proxy.config_section(:sub) do
151
+ config_param(:name, :string, default: "name1")
152
+ end
153
+ assert_equal(<<CONFIG, @proxy.dump)
154
+
155
+ sub
156
+ name: string: <"name1">
157
+ CONFIG
158
+ end
159
+
160
+ test 'nested sub proxy' do
161
+ @proxy.config_section(:sub) do
162
+ config_param(:name1, :string, default: "name1")
163
+ config_param(:name2, :string, default: "name2")
164
+ config_section(:sub2) do
165
+ config_param(:name3, :string, default: "name3")
166
+ config_param(:name4, :string, default: "name4")
167
+ end
168
+ end
169
+ assert_equal(<<CONFIG, @proxy.dump)
170
+
171
+ sub
172
+ name1: string: <"name1">
173
+ name2: string: <"name2">
174
+ sub2
175
+ name3: string: <"name3">
176
+ name4: string: <"name4">
177
+ CONFIG
178
+ end
179
+
180
+ sub_test_case 'w/ description' do
181
+ test 'single proxy' do
182
+ @proxy.config_param(:name, :string, desc: "description for name")
183
+ assert_equal(<<CONFIG, @proxy.dump)
184
+
185
+ name: string: <nil> # description for name
186
+ CONFIG
187
+ end
188
+
189
+ test 'single proxy using config_set_desc' do
190
+ @proxy.config_param(:name, :string)
191
+ @proxy.config_set_desc(:name, "description for name")
192
+ assert_equal(<<CONFIG, @proxy.dump)
193
+
194
+ name: string: <nil> # description for name
195
+ CONFIG
196
+ end
197
+
198
+ test 'sub proxy' do
199
+ @proxy.config_section(:sub) do
200
+ config_param(:name1, :string, default: "name1", desc: "desc1")
201
+ config_param(:name2, :string, default: "name2", desc: "desc2")
202
+ config_section(:sub2) do
203
+ config_param(:name3, :string, default: "name3")
204
+ config_param(:name4, :string, default: "name4", desc: "desc4")
205
+ end
206
+ end
207
+ assert_equal(<<CONFIG, @proxy.dump)
208
+
209
+ sub
210
+ name1: string: <"name1"> # desc1
211
+ name2: string: <"name2"> # desc2
212
+ sub2
213
+ name3: string: <"name3">
214
+ name4: string: <"name4"> # desc4
215
+ CONFIG
216
+ end
217
+ end
218
+ end
97
219
  end
98
220
  end
99
221
  end
@@ -85,6 +85,56 @@ class HttpInputTest < Test::Unit::TestCase
85
85
 
86
86
  end
87
87
 
88
+ def test_json_with_add_remote_addr
89
+ d = create_driver(CONFIG + "add_remote_addr true")
90
+ time = Time.parse("2011-01-02 13:14:15 UTC").to_i
91
+
92
+ d.expect_emit "tag1", time, {"REMOTE_ADDR"=>"127.0.0.1", "a"=>1}
93
+ d.expect_emit "tag2", time, {"REMOTE_ADDR"=>"127.0.0.1", "a"=>2}
94
+
95
+ d.run do
96
+ d.expected_emits.each {|tag,time,record|
97
+ res = post("/#{tag}", {"json"=>record.to_json, "time"=>time.to_s})
98
+ assert_equal "200", res.code
99
+ }
100
+ end
101
+
102
+ end
103
+
104
+ def test_multi_json_with_add_remote_addr
105
+ d = create_driver(CONFIG + "add_remote_addr true")
106
+
107
+ time = Time.parse("2011-01-02 13:14:15 UTC").to_i
108
+ events = [{"a"=>1},{"a"=>2}]
109
+ tag = "tag1"
110
+
111
+ d.expect_emit "tag1", time, {"REMOTE_ADDR"=>"127.0.0.1", "a"=>1}
112
+ d.expect_emit "tag1", time, {"REMOTE_ADDR"=>"127.0.0.1", "a"=>2}
113
+
114
+ d.run do
115
+ res = post("/#{tag}", {"json"=>events.to_json, "time"=>time.to_s})
116
+ assert_equal "200", res.code
117
+ end
118
+
119
+ end
120
+
121
+ def test_multi_json_with_add_http_headers
122
+ d = create_driver(CONFIG + "add_http_headers true")
123
+
124
+ time = Time.parse("2011-01-02 13:14:15 UTC").to_i
125
+ events = [{"a"=>1},{"a"=>2}]
126
+ tag = "tag1"
127
+
128
+ d.run do
129
+ res = post("/#{tag}", {"json"=>events.to_json, "time"=>time.to_s})
130
+ assert_equal "200", res.code
131
+ end
132
+
133
+ d.emit_streams.each { |tag, es|
134
+ assert include_http_header?(es.first[1])
135
+ }
136
+ end
137
+
88
138
  def test_json_with_add_http_headers
89
139
  d = create_driver(CONFIG + "add_http_headers true")
90
140
 
@@ -213,7 +213,7 @@ module FluentOutputTest
213
213
  FileUtils.mkdir_p(TMP_DIR)
214
214
  end
215
215
 
216
- TMP_DIR = File.expand_path(File.dirname(__FILE__) + "/../tmp/time_sliced_output")
216
+ TMP_DIR = File.expand_path(File.dirname(__FILE__) + "/tmp/time_sliced_output")
217
217
 
218
218
  CONFIG = %[]
219
219
 
@@ -529,6 +529,38 @@ module ParserTest
529
529
  assert_equal text, record['time']
530
530
  end
531
531
  end
532
+
533
+ data('array param' => '["a","b","c","d","e","f"]', 'string param' => 'a,b,c,d,e,f')
534
+ def test_parse_with_null_value_pattern
535
+ parser = TextParser::TSVParser.new
536
+ parser.configure(
537
+ 'keys'=>param,
538
+ 'time_key'=>'time',
539
+ 'null_value_pattern'=>'^(-|null|NULL)$'
540
+ )
541
+ parser.parse("-\tnull\tNULL\t\t--\tnuLL") do |time, record|
542
+ assert_nil record['a']
543
+ assert_nil record['b']
544
+ assert_nil record['c']
545
+ assert_equal record['d'], ''
546
+ assert_equal record['e'], '--'
547
+ assert_equal record['f'], 'nuLL'
548
+ end
549
+ end
550
+
551
+ data('array param' => '["a","b"]', 'string param' => 'a,b')
552
+ def test_parse_with_null_empty_string
553
+ parser = TextParser::TSVParser.new
554
+ parser.configure(
555
+ 'keys'=>param,
556
+ 'time_key'=>'time',
557
+ 'null_empty_string'=>true
558
+ )
559
+ parser.parse("\t ") do |time, record|
560
+ assert_nil record['a']
561
+ assert_equal record['b'], ' '
562
+ end
563
+ end
532
564
  end
533
565
 
534
566
  class CSVParserTest < ::Test::Unit::TestCase
@@ -586,6 +618,38 @@ module ParserTest
586
618
  assert_equal text, record['time']
587
619
  end
588
620
  end
621
+
622
+ data('array param' => '["a","b","c","d","e","f"]', 'string param' => 'a,b,c,d,e,f')
623
+ def test_parse_with_null_value_pattern
624
+ parser = TextParser::CSVParser.new
625
+ parser.configure(
626
+ 'keys'=>param,
627
+ 'time_key'=>'time',
628
+ 'null_value_pattern'=>'^(-|null|NULL)$'
629
+ )
630
+ parser.parse("-,null,NULL,,--,nuLL") do |time, record|
631
+ assert_nil record['a']
632
+ assert_nil record['b']
633
+ assert_nil record['c']
634
+ assert_equal record['d'], ''
635
+ assert_equal record['e'], '--'
636
+ assert_equal record['f'], 'nuLL'
637
+ end
638
+ end
639
+
640
+ data('array param' => '["a","b"]', 'string param' => 'a,b')
641
+ def test_parse_with_null_empty_string
642
+ parser = TextParser::CSVParser.new
643
+ parser.configure(
644
+ 'keys'=>param,
645
+ 'time_key'=>'time',
646
+ 'null_empty_string'=>true
647
+ )
648
+ parser.parse(", ") do |time, record|
649
+ assert_nil record['a']
650
+ assert_equal record['b'], ' '
651
+ end
652
+ end
589
653
  end
590
654
 
591
655
  class LabeledTSVParserTest < ::Test::Unit::TestCase
@@ -684,6 +748,32 @@ module ParserTest
684
748
  assert_equal text, record['time']
685
749
  end
686
750
  end
751
+
752
+ def test_parse_with_null_value_pattern
753
+ parser = TextParser::LabeledTSVParser.new
754
+ parser.configure(
755
+ 'null_value_pattern'=>'^(-|null|NULL)$'
756
+ )
757
+ parser.parse("a:-\tb:null\tc:NULL\td:\te:--\tf:nuLL") do |time, record|
758
+ assert_nil record['a']
759
+ assert_nil record['b']
760
+ assert_nil record['c']
761
+ assert_equal record['d'], ''
762
+ assert_equal record['e'], '--'
763
+ assert_equal record['f'], 'nuLL'
764
+ end
765
+ end
766
+
767
+ def test_parse_with_null_empty_string
768
+ parser = TextParser::LabeledTSVParser.new
769
+ parser.configure(
770
+ 'null_empty_string'=>true
771
+ )
772
+ parser.parse("a:\tb: ") do |time, record|
773
+ assert_nil record['a']
774
+ assert_equal record['b'], ' '
775
+ end
776
+ end
687
777
  end
688
778
 
689
779
  class NoneParserTest < ::Test::Unit::TestCase
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.15
4
+ version: 0.12.16
5
5
  platform: ruby
6
6
  authors:
7
7
  - Sadayuki Furuhashi
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2015-07-29 00:00:00.000000000 Z
11
+ date: 2015-09-30 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: msgpack
@@ -172,16 +172,16 @@ dependencies:
172
172
  name: flexmock
173
173
  requirement: !ruby/object:Gem::Requirement
174
174
  requirements:
175
- - - ">="
175
+ - - "~>"
176
176
  - !ruby/object:Gem::Version
177
- version: '0'
177
+ version: 1.3.3
178
178
  type: :development
179
179
  prerelease: false
180
180
  version_requirements: !ruby/object:Gem::Requirement
181
181
  requirements:
182
- - - ">="
182
+ - - "~>"
183
183
  - !ruby/object:Gem::Version
184
- version: '0'
184
+ version: 1.3.3
185
185
  - !ruby/object:Gem::Dependency
186
186
  name: parallel_tests
187
187
  requirement: !ruby/object:Gem::Requirement
@@ -370,6 +370,7 @@ files:
370
370
  - lib/fluent/plugin/out_stdout.rb
371
371
  - lib/fluent/plugin/out_stream.rb
372
372
  - lib/fluent/plugin/socket_util.rb
373
+ - lib/fluent/plugin/string_util.rb
373
374
  - lib/fluent/process.rb
374
375
  - lib/fluent/registry.rb
375
376
  - lib/fluent/root_agent.rb