fluentd 0.12.31 → 0.12.32

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: 0aa9f8f4d0ebedebdba427af533a3b6b67eb7bcd
4
- data.tar.gz: f69ae9d212c29b4c68303460aa10a9c21aa2080a
3
+ metadata.gz: 29eef51ecd637ecb6cf5cc7d41bc31527513afff
4
+ data.tar.gz: 2077e5585e911081e2f76225ff97995aaa1b5100
5
5
  SHA512:
6
- metadata.gz: 4a89d78f39a8f3d5ced5460274db3e0c8ac7923fe68e9dc0d119225f912aa30a37a067f5d7c403f156f12491d9bd3440eeb82913cf7e3092ce49199b9c7ddb4b
7
- data.tar.gz: a3009318c51d0f8dcd774521c67b0935c6d619d5cd8b506e126cd3bf2e55c24c39c8150fca88cfb0eb25eaa0fae4fbf778f482d6fd26e52ea03d990c49c6fddb
6
+ metadata.gz: 416d42c9f2d15131fbe0b01c91a11b5cb22f3e54d052ebc948304e4f9a02ca1445f5c88882e9af01778e553fcb708c83b24a42b3442582a9e1ee8e6ad1ebf9e0
7
+ data.tar.gz: fee4ebcca6c1ee8eb452043552bc720fb8850641414bbf4586d9d668a93a9230c1ed6283220255d4150b7ae135af4010590082af962871bfae4ca8d0c37c2325
@@ -3,10 +3,10 @@ language: ruby
3
3
  rvm:
4
4
  - 2.0.0
5
5
  - 2.1
6
- - 2.2.3
7
- - 2.3.0
6
+ - 2.2.6
7
+ - 2.3.3
8
+ - 2.4.0
8
9
  - ruby-head
9
- - rbx-2
10
10
 
11
11
  os:
12
12
  - linux
@@ -15,7 +15,6 @@ os:
15
15
  branches:
16
16
  only:
17
17
  - master
18
- - v0.10
19
18
  - v0.12
20
19
  - v0.14
21
20
 
@@ -29,6 +28,6 @@ sudo: false
29
28
  matrix:
30
29
  allow_failures:
31
30
  - rvm: ruby-head
32
- - rvm: rbx-2
33
- - rvm: 2.3.0
34
- - rvm: 1.9.3
31
+ exclude:
32
+ - rvm: 2.2.6
33
+ os: osx
data/ChangeLog CHANGED
@@ -1,5 +1,26 @@
1
1
  # v0.12
2
2
 
3
+ ## Release 0.12.32 - 2017/02/02
4
+
5
+ ### New features / Enhancement
6
+
7
+ * formatter: Port add_newline parameter to v012
8
+ https://github.com/fluent/fluentd/pull/1447
9
+ * record_transformer: remove_keys processing should be last
10
+ https://github.com/fluent/fluentd/pull/1433
11
+ * in_tail: Capture unmatched lines
12
+ https://github.com/fluent/fluentd/pull/1421
13
+ * in_monitor_agent: Port #1393 to v0.12 for with_ivars query paretemer
14
+ https://github.com/fluent/fluentd/pull/1402
15
+ * in_monitor_agent: Port #1387 to v0.12 for retry field
16
+ https://github.com/fluent/fluentd/pull/1387
17
+ * Allow msgpack-ruby v1
18
+
19
+ ### Bug fixes
20
+
21
+ * in_exec: Ensure to sleep for prevending fork bomb
22
+ https://github.com/fluent/fluentd/pull/1348
23
+
3
24
  ## Release 0.12.31 - 2016/12/14
4
25
 
5
26
  ### New features / Enhancement
@@ -19,7 +19,7 @@ Gem::Specification.new do |gem|
19
19
 
20
20
  gem.required_ruby_version = '>= 1.9.3'
21
21
 
22
- gem.add_runtime_dependency("msgpack", [">= 0.5.11", "< 0.6.0"])
22
+ gem.add_runtime_dependency("msgpack", [">= 0.5.11", "< 2"])
23
23
  gem.add_runtime_dependency("json", [">= 1.4.3"])
24
24
  gem.add_runtime_dependency("yajl-ruby", ["~> 1.0"])
25
25
  gem.add_runtime_dependency("cool.io", [">= 1.2.2", "< 2.0.0"])
@@ -135,6 +135,7 @@ module Fluent
135
135
  include StructuredFormatMixin
136
136
 
137
137
  config_param :json_parser, :string, default: 'oj'
138
+ config_param :add_newline, :bool, default: true
138
139
 
139
140
  def configure(conf)
140
141
  super
@@ -147,19 +148,34 @@ module Fluent
147
148
  rescue LoadError
148
149
  @dump_proc = Yajl.method(:dump)
149
150
  end
151
+
152
+ # format json is used on various highload environment, so re-define method to skip if check
153
+ unless @add_newline
154
+ define_singleton_method(:format, method(:format_without_nl))
155
+ end
150
156
  end
151
157
 
152
158
  def format_record(record)
153
159
  "#{@dump_proc.call(record)}\n"
154
160
  end
161
+
162
+ def format_without_nl(tag, time, record)
163
+ @dump_proc.call(record)
164
+ end
155
165
  end
156
166
 
157
167
  class HashFormatter < Formatter
158
168
  include HandleTagAndTimeMixin
159
169
  include StructuredFormatMixin
160
170
 
171
+ config_param :add_newline, :bool, default: true
172
+
161
173
  def format_record(record)
162
- "#{record.to_s}\n"
174
+ if @add_newline
175
+ "#{record.to_s}\n"
176
+ else
177
+ record.to_s
178
+ end
163
179
  end
164
180
  end
165
181
 
@@ -177,6 +193,7 @@ module Fluent
177
193
 
178
194
  config_param :delimiter, :string, default: "\t"
179
195
  config_param :label_delimiter, :string, default: ":"
196
+ config_param :add_newline, :bool, default: true
180
197
 
181
198
  def format(tag, time, record)
182
199
  filter_record(tag, time, record)
@@ -184,7 +201,7 @@ module Fluent
184
201
  result << @delimiter if result.length.nonzero?
185
202
  result << "#{pair.first}#{@label_delimiter}#{pair.last}"
186
203
  }
187
- formatted << "\n"
204
+ formatted << "\n".freeze if @add_newline
188
205
  formatted
189
206
  end
190
207
  end
@@ -197,6 +214,7 @@ module Fluent
197
214
  end
198
215
  config_param :force_quotes, :bool, default: true
199
216
  config_param :fields, :array, value_type: :string
217
+ config_param :add_newline, :bool, default: true
200
218
 
201
219
  def initialize
202
220
  super
@@ -217,7 +235,9 @@ module Fluent
217
235
  memo << record[key]
218
236
  memo
219
237
  end
220
- CSV.generate_line(row, @generate_opts)
238
+ line = CSV.generate_line(row, @generate_opts)
239
+ line.chomp! unless @add_newline
240
+ line
221
241
  end
222
242
  end
223
243
 
@@ -95,14 +95,13 @@ module Fluent
95
95
  last_record = nil
96
96
  es.each do |time, record|
97
97
  last_record = record # for debug log
98
- placeholder_values.merge!({
99
- 'time' => @placeholder_expander.time_value(time),
100
- 'record' => record,
101
- })
98
+ placeholder_values['time'] = @placeholder_expander.time_value(time)
99
+ placeholder_values['record'] = record
102
100
  new_record = reform(record, placeholder_values)
103
101
  if @renew_time_key && new_record.has_key?(@renew_time_key)
104
102
  time = new_record[@renew_time_key].to_i
105
103
  end
104
+ @remove_keys.each { |k| new_record.delete(k) } if @remove_keys
106
105
  new_es.add(time, new_record)
107
106
  end
108
107
  new_es
@@ -131,7 +130,6 @@ module Fluent
131
130
  new_record = @renew_record ? {} : record.dup
132
131
  @keep_keys.each {|k| new_record[k] = record[k]} if @keep_keys and @renew_record
133
132
  new_record.merge!(expand_placeholders(@map, placeholders))
134
- @remove_keys.each {|k| new_record.delete(k) } if @remove_keys
135
133
 
136
134
  new_record
137
135
  end
@@ -140,10 +140,11 @@ module Fluent
140
140
  io = IO.popen(@command, "r")
141
141
  @parser.call(io)
142
142
  Process.waitpid(io.pid)
143
- sleep @run_interval
144
143
  rescue
145
144
  log.error "exec failed to run or shutdown child process", error: $!.to_s, error_class: $!.class.to_s
146
145
  log.warn_backtrace $!.backtrace
146
+ ensure
147
+ sleep @run_interval
147
148
  end
148
149
  end
149
150
  end
@@ -34,6 +34,7 @@ module Fluent
34
34
  config_param :emit_interval, :time, default: 60
35
35
  config_param :emit_config, :bool, default: false
36
36
  config_param :include_config, :bool, default: true
37
+ config_param :include_retry, :bool, default: true
37
38
 
38
39
  class MonitorServlet < WEBrick::HTTPServlet::AbstractServlet
39
40
  def initialize(server, agent)
@@ -77,16 +78,24 @@ module Fluent
77
78
 
78
79
  # if ?debug=1 is set, set :with_debug_info for get_monitor_info
79
80
  # and :pretty_json for render_json_error
80
- opts = {with_config: @agent.include_config}
81
+ opts = {with_config: @agent.include_config, with_retry: @agent.include_retry}
81
82
  if s = qs['debug'] and s[0]
82
83
  opts[:with_debug_info] = true
83
84
  opts[:pretty_json] = true
84
85
  end
85
86
 
87
+ if ivars = (qs['with_ivars'] || []).first
88
+ opts[:ivars] = ivars.split(',')
89
+ end
90
+
86
91
  if with_config = get_search_parameter(qs, 'with_config'.freeze)
87
92
  opts[:with_config] = Fluent::Config.bool_value(with_config)
88
93
  end
89
94
 
95
+ if with_retry = get_search_parameter(qs, 'with_retry'.freeze)
96
+ opts[:with_retry] = Fluent::Config.bool_value(with_retry)
97
+ end
98
+
90
99
  if tag = get_search_parameter(qs, 'tag'.freeze)
91
100
  # ?tag= to search an output plugin by match pattern
92
101
  if obj = @agent.plugin_info_by_tag(tag, opts)
@@ -259,7 +268,7 @@ module Fluent
259
268
  log.debug "tag parameter is specified. Emit plugins info to '#{@tag}'"
260
269
 
261
270
  @loop = Coolio::Loop.new
262
- opts = {with_config: @emit_config}
271
+ opts = {with_config: @emit_config, with_retry: false}
263
272
  timer = TimerWatcher.new(@emit_interval, log) {
264
273
  es = MultiEventStream.new
265
274
  now = Engine.now
@@ -385,6 +394,7 @@ module Fluent
385
394
 
386
395
  # TODO: use %i() after drop ruby v1.9.3 support.
387
396
  IGNORE_ATTRIBUTES = %W(@config_root_section @config @masked_config).map(&:to_sym)
397
+ EMPTY_RESULT = {}
388
398
 
389
399
  # get monitor info from the plugin `pe` and return a hash object
390
400
  def get_monitor_info(pe, opts={})
@@ -404,6 +414,13 @@ module Fluent
404
414
  end
405
415
  }
406
416
 
417
+ if opts[:with_retry]
418
+ num_errors = pe.instance_variable_get(:@num_errors)
419
+ if num_errors
420
+ obj['retry'] = num_errors.zero? ? EMPTY_RESULT : get_retry_info(pe, num_errors)
421
+ end
422
+ end
423
+
407
424
  # include all instance variables if :with_debug_info is set
408
425
  if opts[:with_debug_info]
409
426
  iv = {}
@@ -415,11 +432,25 @@ module Fluent
415
432
  }
416
433
  end
417
434
  obj['instance_variables'] = iv
435
+ elsif ivars = opts[:ivars]
436
+ iv = {}
437
+ ivars.each {|name|
438
+ iname = "@#{name}"
439
+ iv[name] = pe.instance_variable_get(iname) if pe.instance_variable_defined?(iname)
440
+ }
441
+ obj['instance_variables'] = iv
418
442
  end
419
443
 
420
444
  obj
421
445
  end
422
446
 
447
+ def get_retry_info(pe, num_errors)
448
+ retry_variables = {}
449
+ retry_variables['steps'] = num_errors
450
+ retry_variables['next_time'] = Time.at(pe.instance_variable_get('@next_retry_time'.freeze))
451
+ retry_variables
452
+ end
453
+
423
454
  def plugin_category(pe)
424
455
  case pe
425
456
  when Fluent::Input
@@ -48,6 +48,8 @@ module Fluent
48
48
  config_param :read_lines_limit, :integer, default: 1000
49
49
  desc 'The interval of flushing the buffer for multiline format'
50
50
  config_param :multiline_flush_interval, :time, default: nil
51
+ desc 'Enable the option to emit unmatched lines.'
52
+ config_param :emit_unmatched_lines, :bool, default: false
51
53
  desc 'Enable the additional watch timer.'
52
54
  config_param :enable_watch_timer, :bool, default: true
53
55
  desc 'The encoding after conversion of the input.'
@@ -315,6 +317,11 @@ module Fluent
315
317
  record[@path_key] ||= tail_watcher.path unless @path_key.nil?
316
318
  es.add(time, record)
317
319
  else
320
+ if @emit_unmatched_lines
321
+ record = {'unmatched_line' => line}
322
+ record[@path_key] ||= tail_watcher.path unless @path_key.nil?
323
+ es.add(::Fluent::Engine.now, record)
324
+ end
318
325
  log.warn "pattern not match: #{line.inspect}"
319
326
  end
320
327
  }
@@ -345,6 +352,9 @@ module Fluent
345
352
  lb = line
346
353
  else
347
354
  if lb.nil?
355
+ if @emit_unmatched_lines
356
+ convert_line_to_event(line, es, tail_watcher)
357
+ end
348
358
  log.warn "got incomplete line before first line from #{tail_watcher.path}: #{line.inspect}"
349
359
  else
350
360
  lb << line
@@ -16,6 +16,6 @@
16
16
 
17
17
  module Fluent
18
18
 
19
- VERSION = '0.12.31'
19
+ VERSION = '0.12.32'
20
20
 
21
21
  end
@@ -99,6 +99,25 @@ class RecordTransformerFilterTest < Test::Unit::TestCase
99
99
  es = emit(config, msgs)
100
100
  es.each_with_index do |(time, record), i|
101
101
  assert_equal(times[i].to_i, time)
102
+ assert_true(record.has_key?('message'))
103
+ end
104
+ end
105
+
106
+ test 'renew_time_key and remove_keys' do
107
+ config = %[
108
+ renew_time_key event_time_key
109
+ remove_keys event_time_key
110
+ auto_typecast true
111
+ <record>
112
+ event_time_key ${record["message"]}
113
+ </record>
114
+ ]
115
+ times = [Time.local(2, 2, 3, 4, 5, 2010, nil, nil, nil, nil), Time.local(3, 2, 3, 4, 5, 2010, nil, nil, nil, nil)]
116
+ msgs = times.map { |t| t.to_f.to_s }
117
+ filtered = emit(config, msgs)
118
+ filtered.each_with_index do |(time, _record), i|
119
+ assert_equal(times[i].to_i, time)
120
+ assert_false(_record.has_key?('event_time_key'))
102
121
  end
103
122
  end
104
123
 
@@ -53,6 +53,17 @@ class ExecInputTest < Test::Unit::TestCase
53
53
  ]
54
54
  end
55
55
 
56
+ def invalid_json_config
57
+ # For counting command execution, redirect stderr to file
58
+ %[
59
+ command ruby #{@script} #{@test_time} 4
60
+ format json
61
+ tag_key tag
62
+ time_key time
63
+ run_interval 0.5
64
+ ]
65
+ end
66
+
56
67
  def test_configure
57
68
  d = create_driver
58
69
  assert_equal 'tsv', d.instance.format
@@ -130,4 +141,16 @@ class ExecInputTest < Test::Unit::TestCase
130
141
  assert_equal ["regex_tag", @test_time, {"message"=>"hello"}], emits[0]
131
142
  assert_equal @test_time, emits[0][1]
132
143
  end
144
+
145
+ def test_emit_with_invalid_script
146
+ d = create_driver invalid_json_config
147
+
148
+ d.run do
149
+ sleep 2
150
+ end
151
+
152
+ assert_equal true, d.emits.empty?
153
+ logs = d.instance.log.logs
154
+ assert_equal true, logs.count { |line| line =~ /exec failed to run or shutdown child process/ }.between?(1, 4)
155
+ end
133
156
  end
@@ -97,7 +97,6 @@ class TailInputTest < Test::Unit::TestCase
97
97
  }
98
98
 
99
99
  d = create_driver
100
-
101
100
  d.run do
102
101
  sleep 1
103
102
 
@@ -115,6 +114,34 @@ class TailInputTest < Test::Unit::TestCase
115
114
  assert_equal(1, d.emit_streams.size)
116
115
  end
117
116
 
117
+ def test_emit_with_emit_unmatched_lines_true
118
+ File.open("#{TMP_DIR}/tail.txt", "wb") { |f| }
119
+
120
+ d = create_driver(%[
121
+ format /^(?<message>test.*)/
122
+ emit_unmatched_lines true
123
+ ])
124
+ d.run do
125
+ sleep 1
126
+
127
+ File.open("#{TMP_DIR}/tail.txt", "ab") { |f|
128
+ f.puts "test line 1"
129
+ f.puts "test line 2"
130
+ f.puts "bad line 1"
131
+ f.puts "test line 3"
132
+ }
133
+
134
+ sleep 1
135
+ end
136
+
137
+ events = d.emits
138
+ assert_equal(4, events.length)
139
+ assert_equal({"message" => "test line 1"}, events[0][2])
140
+ assert_equal({"message" => "test line 2"}, events[1][2])
141
+ assert_equal({"unmatched_line" => "bad line 1"}, events[2][2])
142
+ assert_equal({"message" => "test line 3"}, events[3][2])
143
+ end
144
+
118
145
  data('1' => [1, 2], '10' => [10, 1])
119
146
  def test_emit_with_read_lines_limit(data)
120
147
  limit, num_emits = data
@@ -419,6 +446,44 @@ class TailInputTest < Test::Unit::TestCase
419
446
  assert_equal({"message1" => "test8"}, emits[3][2])
420
447
  end
421
448
 
449
+ def test_multiline_with_emit_unmatched_lines_true
450
+ File.open("#{TMP_DIR}/tail.txt", "wb") { |f| }
451
+
452
+ d = create_driver %[
453
+ format multiline
454
+ format1 /^s (?<message1>[^\\n]+)(\\nf (?<message2>[^\\n]+))?(\\nf (?<message3>.*))?/
455
+ format_firstline /^[s]/
456
+ emit_unmatched_lines true
457
+ ]
458
+ d.run do
459
+ File.open("#{TMP_DIR}/tail.txt", "ab") { |f|
460
+ f.puts "f test1"
461
+ f.puts "s test2"
462
+ f.puts "f test3"
463
+ f.puts "f test4"
464
+ f.puts "s test5"
465
+ f.puts "s test6"
466
+ f.puts "f test7"
467
+ f.puts "s test8"
468
+ }
469
+ sleep 1
470
+
471
+ events = d.emits
472
+ assert_equal(4, events.length)
473
+ assert_equal({"unmatched_line" => "f test1"}, events[0][2])
474
+ assert_equal({"message1" => "test2", "message2" => "test3", "message3" => "test4"}, events[1][2])
475
+ assert_equal({"message1" => "test5"}, events[2][2])
476
+ assert_equal({"message1" => "test6", "message2" => "test7"}, events[3][2])
477
+
478
+ sleep 3
479
+ assert_equal(4, d.emits.length)
480
+ end
481
+
482
+ emits = d.emits
483
+ assert_equal(5, emits.length)
484
+ assert_equal({"message1" => "test8"}, emits[4][2])
485
+ end
486
+
422
487
  def test_multiline_with_flush_interval
423
488
  File.open("#{TMP_DIR}/tail.txt", "wb") { |f| }
424
489
 
@@ -17,6 +17,10 @@ def gen_raw_string(time)
17
17
  "#{time} hello"
18
18
  end
19
19
 
20
+ def gen_invalid_json(time)
21
+ %({"tag": "tag1", "time": #{time}, "k1": "ok", })
22
+ end
23
+
20
24
  time = ARGV.first
21
25
  time = Integer(time) rescue time
22
26
 
@@ -29,4 +33,6 @@ when 2
29
33
  print gen_msgpack(time)
30
34
  when 3
31
35
  print gen_raw_string(time)
36
+ when 4
37
+ print gen_invalid_json(time)
32
38
  end
@@ -142,6 +142,14 @@ module FormatterTest
142
142
  assert_equal("#{Yajl.dump(record)}\n", formatted)
143
143
  end
144
144
 
145
+ data('oj' => 'oj', 'yajl' => 'yajl')
146
+ def test_format_without_nl(data)
147
+ @formatter.configure('json_parser' => data, 'add_newline' => false)
148
+ formatted = @formatter.format(tag, @time, record)
149
+
150
+ assert_equal(Yajl.dump(record), formatted)
151
+ end
152
+
145
153
  data('oj' => 'oj', 'yajl' => 'yajl')
146
154
  def test_format_with_symbolic_record(data)
147
155
  @formatter.configure('json_parser' => data)
@@ -252,6 +260,13 @@ module FormatterTest
252
260
  assert_equal("message:awesome\n", formatted)
253
261
  end
254
262
 
263
+ def test_format_without_nl
264
+ @formatter.configure('add_newline' => false)
265
+ formatted = @formatter.format(tag, @time, record)
266
+
267
+ assert_equal("message:awesome", formatted)
268
+ end
269
+
255
270
  def test_format_with_tag
256
271
  @formatter.configure('include_tag_key' => 'true')
257
272
  formatted = @formatter.format(tag, @time, record)
@@ -319,6 +334,15 @@ module FormatterTest
319
334
  assert_equal("\"awesome\",\"awesome2\"\n", formatted)
320
335
  end
321
336
 
337
+ def test_format_without_nl
338
+ @formatter.configure('fields' => 'message,message2', 'add_newline' => false)
339
+ formatted = @formatter.format(tag, @time, {
340
+ 'message' => 'awesome',
341
+ 'message2' => 'awesome2'
342
+ })
343
+ assert_equal("\"awesome\",\"awesome2\"", formatted)
344
+ end
345
+
322
346
  def test_format_with_tag
323
347
  @formatter.configure(
324
348
  'fields' => 'tag,message,message2',
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.31
4
+ version: 0.12.32
5
5
  platform: ruby
6
6
  authors:
7
7
  - Sadayuki Furuhashi
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2016-12-14 00:00:00.000000000 Z
11
+ date: 2017-02-03 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: msgpack
@@ -19,7 +19,7 @@ dependencies:
19
19
  version: 0.5.11
20
20
  - - "<"
21
21
  - !ruby/object:Gem::Version
22
- version: 0.6.0
22
+ version: '2'
23
23
  type: :runtime
24
24
  prerelease: false
25
25
  version_requirements: !ruby/object:Gem::Requirement
@@ -29,7 +29,7 @@ dependencies:
29
29
  version: 0.5.11
30
30
  - - "<"
31
31
  - !ruby/object:Gem::Version
32
- version: 0.6.0
32
+ version: '2'
33
33
  - !ruby/object:Gem::Dependency
34
34
  name: json
35
35
  requirement: !ruby/object:Gem::Requirement
@@ -495,7 +495,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
495
495
  version: '0'
496
496
  requirements: []
497
497
  rubyforge_project:
498
- rubygems_version: 2.5.1
498
+ rubygems_version: 2.5.2
499
499
  signing_key:
500
500
  specification_version: 4
501
501
  summary: Fluentd event collector