fluentd 0.12.7 → 0.12.8

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: 34615d5421761b2b42cd9831d2d87a96ac401b9d
4
- data.tar.gz: 49ba7a22424ee071c07c166e35a5bff49720327b
3
+ metadata.gz: 67639a7dc51e74c0269b78897487a90bd0acf29b
4
+ data.tar.gz: 43fd091c294a06a0d88eea360a7efd5b215d5577
5
5
  SHA512:
6
- metadata.gz: d71f71d6e0f4267836f6f683698a9dbe12c5447dd95a19a1a251bde03e058913a6b32de0a7bdad98783efb198660cf5a5dfebe76ec5e60ebf19d698654ded6b6
7
- data.tar.gz: 6a09feb33ef26f30c0901d3f0e2e6b046d4a837b1679c69b7c87abf12f2de2cd5ce3a88ff86a220c81ee5a0e0cd8fcb5aea9d9899aa6b757be24ee79d63df2a3
6
+ metadata.gz: 1357f9b5a372172f9e6441dafdb106d8fa2e6756d3bb23c54b0d16c1e8888fc97e75b5ebbf94aac7ae589d48d42af6b08968653de0309cc8a9efe6b2a9ade81d
7
+ data.tar.gz: 00d88127b76af84ba10b1830c0be4861047a571cb953dfd63c7887d145ca8752fb8a4a978b586159918356ee57f6b7b00190295d9a6509467bd41791e8b63dfb
data/ChangeLog CHANGED
@@ -1,5 +1,23 @@
1
1
  # v0.12
2
2
 
3
+ ## Release 0.12.8 - 2015/04/22
4
+
5
+ ### New features / Enhancement
6
+
7
+ * output: Support millisecond for try_flush_interval and queued_chunk_flush_interval
8
+ https://github.com/fluent/fluentd/pull/568
9
+ * filter_record_transformer: Support fields which start with @
10
+ https://github.com/fluent/fluentd/pull/574
11
+ * config: Add final attribute to prevent config overwritten by subclass
12
+ https://github.com/fluent/fluentd/pull/583
13
+
14
+ ### Bug fixes
15
+
16
+ * config: fix embedded code parsing
17
+ https://github.com/fluent/fluentd/pull/564
18
+ * out_forward: Resend chunks on dropped connection when wait ACK
19
+ https://github.com/fluent/fluentd/pull/580
20
+
3
21
  ## Release 0.12.7 - 2015/03/22
4
22
 
5
23
  ### New features / Enhancement
@@ -17,7 +17,7 @@
17
17
  module Fluent
18
18
  module Config
19
19
  class ConfigureProxy
20
- attr_accessor :name, :param_name, :required, :multi, :alias, :argument, :params, :defaults, :sections
20
+ attr_accessor :name, :final, :param_name, :required, :multi, :alias, :argument, :params, :defaults, :sections
21
21
  # config_param :desc, :string, :default => '....'
22
22
  # config_set_default :buffer_type, :memory
23
23
  #
@@ -38,6 +38,7 @@ module Fluent
38
38
 
39
39
  def initialize(name, opts = {})
40
40
  @name = name.to_sym
41
+ @final = opts.fetch(:final, false)
41
42
 
42
43
  @param_name = (opts[:param_name] || @name).to_sym
43
44
  @required = opts[:required]
@@ -58,7 +59,13 @@ module Fluent
58
59
  @multi.nil? ? true : @multi
59
60
  end
60
61
 
62
+ def final?
63
+ @final
64
+ end
65
+
61
66
  def merge(other) # self is base class, other is subclass
67
+ return merge_for_finalized(other) if self.final?
68
+
62
69
  options = {
63
70
  param_name: other.param_name,
64
71
  required: (other.required.nil? ? self.required : other.required),
@@ -69,7 +76,52 @@ module Fluent
69
76
  merged.argument = other.argument || self.argument
70
77
  merged.params = self.params.merge(other.params)
71
78
  merged.defaults = self.defaults.merge(other.defaults)
72
- merged.sections = self.sections.merge(other.sections)
79
+ merged.sections = {}
80
+ (self.sections.keys + other.sections.keys).uniq.each do |section_key|
81
+ self_section = self.sections[section_key]
82
+ other_section = other.sections[section_key]
83
+ merged_section = if self_section && other_section
84
+ self_section.merge(other_section)
85
+ elsif self_section || other_section
86
+ self_section || other_section
87
+ else
88
+ raise "BUG: both of self and other section are nil"
89
+ end
90
+ merged.sections[section_key] = merged_section
91
+ end
92
+
93
+ merged
94
+ end
95
+
96
+ def merge_for_finalized(other)
97
+ # list what subclass can do for finalized section
98
+ # * overwrite param_name to escape duplicated name of instance variable
99
+ # * append params/defaults/sections which are missing in superclass
100
+
101
+ options = {
102
+ param_name: other.param_name,
103
+ required: (self.required.nil? ? other.required : self.required),
104
+ multi: (self.multi.nil? ? other.multi : self.multi),
105
+ final: true,
106
+ }
107
+ merged = self.class.new(other.name, options)
108
+
109
+ merged.argument = self.argument || other.argument
110
+ merged.params = other.params.merge(self.params)
111
+ merged.defaults = other.defaults.merge(self.defaults)
112
+ merged.sections = {}
113
+ (self.sections.keys + other.sections.keys).uniq.each do |section_key|
114
+ self_section = self.sections[section_key]
115
+ other_section = other.sections[section_key]
116
+ merged_section = if self_section && other_section
117
+ other_section.merge(self_section)
118
+ elsif self_section || other_section
119
+ self_section || other_section
120
+ else
121
+ raise "BUG: both of self and other section are nil"
122
+ end
123
+ merged.sections[section_key] = merged_section
124
+ end
73
125
 
74
126
  merged
75
127
  end
@@ -147,7 +147,7 @@ module Fluent
147
147
 
148
148
  @ss.pos += code.length
149
149
 
150
- code
150
+ '"#{' + code + '}"'
151
151
  end
152
152
 
153
153
  def eval_embedded_code(code)
@@ -98,7 +98,7 @@ module Fluent
98
98
  def initialize(output)
99
99
  @output = output
100
100
  @finish = false
101
- @next_time = Engine.now + 1.0
101
+ @next_time = Time.now.to_f + 1.0
102
102
  end
103
103
 
104
104
  def configure(conf)
@@ -132,7 +132,7 @@ module Fluent
132
132
  @mutex.lock
133
133
  begin
134
134
  until @finish
135
- time = Engine.now
135
+ time = Time.now.to_f
136
136
 
137
137
  if @next_time <= time
138
138
  @mutex.unlock
@@ -141,7 +141,7 @@ module Fluent
141
141
  ensure
142
142
  @mutex.lock
143
143
  end
144
- next_wait = @next_time - Engine.now
144
+ next_wait = @next_time - Time.now.to_f
145
145
  else
146
146
  next_wait = @next_time - time
147
147
  end
@@ -231,7 +231,7 @@ module Fluent
231
231
  end
232
232
 
233
233
  def start
234
- @next_flush_time = Engine.now + @flush_interval
234
+ @next_flush_time = Time.now.to_f + @flush_interval
235
235
  @buffer.start
236
236
  @secondary.start if @secondary
237
237
  @writers.each {|writer| writer.start }
@@ -280,10 +280,10 @@ module Fluent
280
280
  end
281
281
 
282
282
  def try_flush
283
- time = Engine.now
283
+ time = Time.now.to_f
284
284
 
285
285
  empty = @buffer.queue_size == 0
286
- if empty && @next_flush_time < (now = Engine.now)
286
+ if empty && @next_flush_time < (now = Time.now.to_f)
287
287
  @buffer.synchronize do
288
288
  if @next_flush_time < now
289
289
  enqueue_buffer
@@ -330,7 +330,7 @@ module Fluent
330
330
  end
331
331
 
332
332
  if has_next
333
- return Engine.now + @queued_chunk_flush_interval
333
+ return Time.now.to_f + @queued_chunk_flush_interval
334
334
  else
335
335
  return time + @try_flush_interval
336
336
  end
@@ -385,7 +385,7 @@ module Fluent
385
385
 
386
386
  def force_flush
387
387
  @num_errors_lock.synchronize do
388
- @next_retry_time = Engine.now - 1
388
+ @next_retry_time = Time.now.to_f - 1
389
389
  end
390
390
  enqueue_buffer(true)
391
391
  submit_flush
@@ -179,7 +179,7 @@ module Fluent
179
179
  end
180
180
 
181
181
  def expand(str)
182
- str.gsub(/(\${[a-zA-Z0-9_]+(\[-?[0-9]+\])?}|__[A-Z_]+__)/) {
182
+ str.gsub(/(\${[^}]+}|__[A-Z_]+__)/) {
183
183
  log.warn "unknown placeholder `#{$1}` found" unless @placeholders.include?($1)
184
184
  @placeholders[$1]
185
185
  }
@@ -298,10 +298,12 @@ module Fluent
298
298
  raw_data = sock.recv(1024)
299
299
 
300
300
  # When connection is closed by remote host, socket is ready to read and #recv returns an empty string that means EOF.
301
- # In this case, the node is available and successfully close connection without sending responses.
302
- # ForwardInput is not expected to do so, but some alternatives may do so.
303
- # Therefore do not send the chunk again.
304
- unless raw_data.empty?
301
+ # If this happens we assume the data wasn't delivered and retry it.
302
+ if raw_data.empty?
303
+ @log.warn "node #{node.host}:#{node.port} closed the connection. regard it as unavailable."
304
+ node.disable!
305
+ raise ForwardOutputConnectionClosedError, "node #{node.host}:#{node.port} closed connection"
306
+ else
305
307
  # Serialization type of the response is same as sent data.
306
308
  res = MessagePack.unpack(raw_data)
307
309
 
@@ -16,6 +16,6 @@
16
16
 
17
17
  module Fluent
18
18
 
19
- VERSION = '0.12.7'
19
+ VERSION = '0.12.8'
20
20
 
21
21
  end
@@ -100,14 +100,45 @@ module ConfigurableSpec
100
100
 
101
101
  config_param :name, :string, alias: :fullname
102
102
  config_param :bool, :bool, alias: :flag
103
- config_section :detail, required: true, multi: false, alias: "information" do
104
- config_param :address, :string
103
+ config_section :detail, required: false, multi: false, alias: "information" do
104
+ config_param :address, :string, default: "x"
105
105
  end
106
106
 
107
107
  def get_all
108
108
  [@name, @detail]
109
109
  end
110
110
  end
111
+
112
+ class Example2 < Example1
113
+ config_section :detail, required: true, multi: false, alias: "information2" do
114
+ config_param :address, :string, default: "y"
115
+ config_param :phone_no, :string
116
+ end
117
+ end
118
+
119
+ class Example3
120
+ include Fluent::Configurable
121
+
122
+ config_param :age, :integer, default: 10
123
+
124
+ config_section :appendix, required: true, multi: false, final: true do
125
+ config_param :type, :string
126
+ config_param :name, :string, default: "x"
127
+ end
128
+
129
+ def get_all
130
+ [@name, @detail]
131
+ end
132
+ end
133
+
134
+ class Example4 < Example3
135
+ config_param :age, :integer, default: 20
136
+
137
+ config_section :appendix, required: false, multi: false, final: false do
138
+ config_param :name, :string, default: "y"
139
+ config_param :age, :integer, default: 10
140
+ end
141
+ end
111
142
  end
112
143
 
113
144
  module Fluent::Config
@@ -493,6 +524,97 @@ module Fluent::Config
493
524
  assert_false(ex5.boolvalue)
494
525
  end
495
526
  end
527
+
528
+ sub_test_case '.config_section' do
529
+ def e(name, arg = '', attrs = {}, elements = [])
530
+ attrs_str_keys = {}
531
+ attrs.each{|key, value| attrs_str_keys[key.to_s] = value }
532
+ Fluent::Config::Element.new(name, arg, attrs_str_keys, elements)
533
+ end
534
+
535
+ test 'subclass configuration spec can overwrite superclass specs' do
536
+ # conf0 = e('ROOT', '', {}, [])
537
+
538
+ conf1 = e('ROOT', '', {
539
+ 'name' => 'tagomoris',
540
+ 'bool' => true,
541
+ },
542
+ )
543
+ # <detail> section is required by overwriting of Example2 config_section spec
544
+ assert_nothing_raised { ConfigurableSpec::Example1.new.configure(conf1) }
545
+ assert_raise(Fluent::ConfigError.new("'<detail>' sections are required")) { ConfigurableSpec::Example2.new.configure(conf1) }
546
+
547
+ conf2 = e('ROOT', '', {
548
+ 'name' => 'tagomoris',
549
+ 'bool' => true,
550
+ },
551
+ [e('detail', '', { 'phone_no' => "+81-00-0000-0000" }, [])],
552
+ )
553
+ # <detail> address </detail> default is overwritten by Example2
554
+ assert_nothing_raised { ConfigurableSpec::Example1.new.configure(conf2) }
555
+ assert_nothing_raised { ConfigurableSpec::Example2.new.configure(conf2) }
556
+ ex1 = ConfigurableSpec::Example1.new.configure(conf2)
557
+ assert_equal "x", ex1.detail.address
558
+ ex2 = ConfigurableSpec::Example2.new.configure(conf2)
559
+ assert_equal "y", ex2.detail.address
560
+
561
+ conf3 = e('ROOT', '', {
562
+ 'name' => 'tagomoris',
563
+ 'bool' => true,
564
+ },
565
+ [e('detail', '', { 'address' => "Chiyoda Tokyo Japan" }, [])],
566
+ )
567
+ # <detail> phone_no </detail> is required by Example2 config_param spec
568
+ assert_nothing_raised { ConfigurableSpec::Example1.new.configure(conf3) }
569
+ assert_raise(Fluent::ConfigError.new("'phone_no' parameter is required, in section detail")) { ConfigurableSpec::Example2.new.configure(conf3) }
570
+
571
+ conf4 = e('ROOT', '', {
572
+ 'name' => 'tagomoris',
573
+ 'bool' => true,
574
+ },
575
+ [e('detail', '', { 'address' => "Chiyoda Tokyo Japan", 'phone_no' => '+81-00-0000-0000' }, [])],
576
+ )
577
+ assert_nothing_raised { ConfigurableSpec::Example1.new.configure(conf4) } # phone_no is not used
578
+ assert_nothing_raised { ConfigurableSpec::Example2.new.configure(conf4) }
579
+
580
+ ex2 = ConfigurableSpec::Example2.new.configure(conf4)
581
+ assert_equal "Chiyoda Tokyo Japan", ex2.detail.address
582
+ assert_equal "+81-00-0000-0000", ex2.detail.phone_no
583
+ end
584
+
585
+ test 'adds only config_param definitions into configuration without overwriting existing finalized configuration elements' do
586
+
587
+ conf1 = e('ROOT', '', {}, [])
588
+ # <appendix> is required by Example3 and its not be overwritten by Example4
589
+ assert_raise(Fluent::ConfigError.new("'<appendix>' sections are required")) { ConfigurableSpec::Example3.new.configure(conf1) }
590
+ assert_raise(Fluent::ConfigError.new("'<appendix>' sections are required")) { ConfigurableSpec::Example4.new.configure(conf1) }
591
+
592
+ conf2 = e('ROOT', '', {
593
+ },
594
+ [e('appendix', '', {'type' => '1'}, [])],
595
+ )
596
+ # default value of age is overwritten by Example4, because root proxy is not finalized
597
+ ex3 = ConfigurableSpec::Example3.new.configure(conf2)
598
+ assert_equal 10, ex3.age
599
+ assert_equal '1', ex3.appendix.type
600
+ ex4 = ConfigurableSpec::Example4.new.configure(conf2)
601
+ assert_equal 20, ex4.age
602
+ assert_equal '1', ex4.appendix.type
603
+
604
+ conf3 = e('ROOT', '', {},
605
+ [e('appendix', '', {'type' => '2'}, [])],
606
+ )
607
+ # default value of <appendix> name </appendix> cannot be overwritten because it is finalized
608
+ ex3 = ConfigurableSpec::Example3.new.configure(conf2)
609
+ assert_equal 10, ex3.age
610
+ assert_equal '1', ex3.appendix.type
611
+ ex4 = ConfigurableSpec::Example4.new.configure(conf2)
612
+ assert_equal 20, ex4.age
613
+ assert_equal '1', ex4.appendix.type
614
+ # <appendix> age </appendix> can be added because it is missing in superclass spec
615
+ assert_equal 10, ex4.appendix.age
616
+ end
617
+ end
496
618
  end
497
619
 
498
620
  sub_test_case 'class defined with config_param/config_section having :alias' do
@@ -228,6 +228,8 @@ module Fluent::Config
228
228
  test('"#{"}"}"') { assert_text_parsed_as("}", '"#{"}"}"') }
229
229
  test('"#{#}"') { assert_parse_error('"#{#}"') } # error in embedded ruby code
230
230
  test("\"\#{\n=begin\n}\"") { assert_parse_error("\"\#{\n=begin\n}\"") } # error in embedded ruby code
231
+ test('"#{v1}foo#{v2}"') { assert_text_parsed_as("#{v1}foo#{v2}", '"#{v1}foo#{v2}"') }
232
+ test('"#{1+1}foo#{2+2}bar"') { assert_text_parsed_as("#{1+1}foo#{2+2}bar", '"#{1+1}foo#{2+2}bar"') }
231
233
  end
232
234
 
233
235
  sub_test_case 'array parsing' do
@@ -343,5 +343,35 @@ class RecordTransformerFilterTest < Test::Unit::TestCase
343
343
  assert_nil(r['message'])
344
344
  end
345
345
  end
346
+
347
+ test 'expand fields starting with @ (enable_ruby no)' do
348
+ config = %[
349
+ enable_ruby no
350
+ <record>
351
+ foo ${@timestamp}
352
+ </record>
353
+ ]
354
+ d = create_driver(config)
355
+ message = {"@timestamp" => "foo"}
356
+ es = d.run { d.emit(message, @time) }.filtered
357
+ es.each do |t, r|
358
+ assert_equal(message["@timestamp"], r['foo'])
359
+ end
360
+ end
361
+
362
+ test 'expand fields starting with @ (enable_ruby yes)' do
363
+ config = %[
364
+ enable_ruby yes
365
+ <record>
366
+ foo ${__send__("@timestamp")}
367
+ </record>
368
+ ]
369
+ d = create_driver(config)
370
+ message = {"@timestamp" => "foo"}
371
+ es = d.run { d.emit(message, @time) }.filtered
372
+ es.each do |t, r|
373
+ assert_equal(message["@timestamp"], r['foo'])
374
+ end
375
+ end
346
376
  end
347
377
  end
@@ -186,6 +186,7 @@ class ForwardOutputTest < Test::Unit::TestCase
186
186
  end
187
187
 
188
188
  def test_require_a_node_not_supporting_responses_to_respond_with_ack
189
+ # in_forward, that doesn't support ack feature, and keep connection alive
189
190
  target_input_driver = create_target_input_driver
190
191
 
191
192
  d = create_driver(CONFIG + %[
@@ -203,6 +204,7 @@ class ForwardOutputTest < Test::Unit::TestCase
203
204
  d.register_run_post_condition do
204
205
  d.instance.responses.length == 1
205
206
  end
207
+ d.run_timeout = 2
206
208
 
207
209
  target_input_driver.run do
208
210
  d.run do
@@ -223,7 +225,48 @@ class ForwardOutputTest < Test::Unit::TestCase
223
225
  assert_equal 1, d.instance.exceptions.size
224
226
  end
225
227
 
226
- def create_target_input_driver(do_respond=false, conf=TARGET_CONFIG)
228
+ # bdf1f4f104c00a791aa94dc20087fe2011e1fd83
229
+ def test_require_a_node_not_supporting_responses_2_to_respond_with_ack
230
+ # in_forward, that doesn't support ack feature, and disconnect immediately
231
+ target_input_driver = create_target_input_driver(false, true)
232
+
233
+ d = create_driver(CONFIG + %[
234
+ flush_interval 1s
235
+ require_ack_response true
236
+ ack_response_timeout 5s
237
+ ])
238
+
239
+ time = Time.parse("2011-01-02 13:14:15 UTC").to_i
240
+
241
+ records = [
242
+ {"a" => 1},
243
+ {"a" => 2}
244
+ ]
245
+ d.register_run_post_condition do
246
+ d.instance.responses.length == 1
247
+ end
248
+ d.run_timeout = 2
249
+
250
+ target_input_driver.run do
251
+ d.run do
252
+ records.each do |record|
253
+ d.emit record, time
254
+ end
255
+ end
256
+ end
257
+
258
+ emits = target_input_driver.emits
259
+ assert_equal ['test', time, records[0]], emits[0]
260
+ assert_equal ['test', time, records[1]], emits[1]
261
+
262
+ assert_equal 0, d.instance.responses.size
263
+ assert_equal 1, d.instance.exceptions.size # send_data() fails and to be retried
264
+
265
+ node = d.instance.nodes.first
266
+ assert_equal false, node.available # node is regarded as unavailable when unexpected EOF
267
+ end
268
+
269
+ def create_target_input_driver(do_respond=false, disconnect=false, conf=TARGET_CONFIG)
227
270
  require 'fluent/plugin/in_forward'
228
271
 
229
272
  DummyEngineDriver.new(Fluent::ForwardInput) {
@@ -265,9 +308,13 @@ class ForwardOutputTest < Test::Unit::TestCase
265
308
  # chunk_counter is reset to zero only after all the data have been received and successfully deserialized.
266
309
  break if handler.chunk_counter == 0
267
310
  end
311
+ if disconnect
312
+ handler.close
313
+ sock = nil
314
+ end
268
315
  sleep # wait for connection to be closed by client
269
316
  ensure
270
- sock.close
317
+ sock.close if sock
271
318
  end
272
319
  end
273
320
  end
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.7
4
+ version: 0.12.8
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-03-21 00:00:00.000000000 Z
11
+ date: 2015-04-21 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: msgpack