fluentd 0.12.23 → 0.12.24

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: 7ddb93865f4a907d91de1d6518b0eecff806c20d
4
- data.tar.gz: cc1925a4af249b8b599adfa0fb2095a39107fd85
3
+ metadata.gz: ab6393d8dfc67b70656190c768de2acb86e8b453
4
+ data.tar.gz: 70f4e1abb3b9d3dda769e4e9ded10dff755602c8
5
5
  SHA512:
6
- metadata.gz: 9dec5f61c9e67be7ca905cae26026f5097cc741fdf2ffc382db9dbfe1e32d64b78b13a30e256bc654c1be6c25543546353ffe910af0c43c1a903a68a6c421d8b
7
- data.tar.gz: 29a02b240b0798f44412d1112f3586a21801636b9e1b91d505c3d7c9fc1051f1ea9f5c2e38faf307512a74743e3282acca361dbe302922b5b150bf0204a1acf3
6
+ metadata.gz: 222628da1465e330bed1f54024845fc9cebd54e80b2c457fb61c8ec46ac8f429a5bd92d43530fb7bc1b34376a00c363ad6b39dedcfd13520a554cafc6d7d7468
7
+ data.tar.gz: 00061a3007fd77070568dd0fb59460cf059fe13bdb9d1ce90823e89ce9f91a121a2adec4961d4978987c3198468229edc0bbd88fc0d0c3b68a67d02afc39d8d8
data/.travis.yml CHANGED
@@ -1,7 +1,6 @@
1
1
  language: ruby
2
2
 
3
3
  rvm:
4
- - 1.9.3
5
4
  - 2.0.0
6
5
  - 2.1
7
6
  - 2.2.3
@@ -32,3 +31,4 @@ matrix:
32
31
  - rvm: ruby-head
33
32
  - rvm: rbx-2
34
33
  - rvm: 2.3.0
34
+ - rvm: 1.9.3
data/ChangeLog CHANGED
@@ -1,5 +1,29 @@
1
1
  # v0.12
2
2
 
3
+ ## Release 0.12.24 - 2016/05/20
4
+
5
+ ### New features / Enhancement
6
+
7
+ * buffer: Add drop_oldest_chunk to buffer_queue_full_action parameter
8
+ https://github.com/fluent/fluentd/pull/934
9
+ * in_tail: Add "encoding" parameter
10
+ https://github.com/fluent/fluentd/pull/889
11
+ * in_tail: Add "path_key" patameter
12
+ https://github.com/fluent/fluentd/pull/951
13
+ * in_monitor_agent: Add emit_config parameter to emit plugin configuration together
14
+ https://github.com/fluent/fluentd/pull/963
15
+ * config: Support multiline string in " quoted string
16
+ https://github.com/fluent/fluentd/pull/929
17
+
18
+ ### Bug fixes
19
+
20
+ * in_http: Add 'Access-Control-Allow-Origin' to response header
21
+ https://github.com/fluent/fluentd/pull/882
22
+ * parser: Fix logger on Fluent::StringUtil to use $log
23
+ https://github.com/fluent/fluentd/pull/926
24
+ * out_forward: Add missing error class definition
25
+ https://github.com/fluent/fluentd/pull/953
26
+
3
27
  ## Release 0.12.23 - 2016/05/03
4
28
 
5
29
  ### Bug fixes
data/fluentd.gemspec CHANGED
@@ -27,7 +27,9 @@ Gem::Specification.new do |gem|
27
27
  gem.add_runtime_dependency("sigdump", ["~> 0.2.2"])
28
28
  gem.add_runtime_dependency("tzinfo", [">= 1.0.0"])
29
29
  gem.add_runtime_dependency("tzinfo-data", [">= 1.0.0"])
30
- gem.add_runtime_dependency("string-scrub", [">= 0.0.3", "<= 0.0.5"])
30
+ unless "".respond_to?(:scrub)
31
+ gem.add_runtime_dependency("string-scrub", [">= 0.1.1"])
32
+ end
31
33
 
32
34
  gem.add_development_dependency("rake", [">= 0.9.2"])
33
35
  gem.add_development_dependency("flexmock", ["~> 1.3.3"])
data/lib/fluent/buffer.rb CHANGED
@@ -150,7 +150,7 @@ module Fluent
150
150
  desc 'The length limit of the chunk queue.'
151
151
  config_param :buffer_queue_limit, :integer, default: 256
152
152
  desc 'The action when the size of buffer queue exceeds the buffer_queue_limit.'
153
- config_param :buffer_queue_full_action, :enum, list: [:exception, :block], default: :exception
153
+ config_param :buffer_queue_full_action, :enum, list: [:exception, :block, :drop_oldest_chunk], default: :exception
154
154
 
155
155
  alias chunk_limit buffer_chunk_limit
156
156
  alias chunk_limit= buffer_chunk_limit=
@@ -212,6 +212,9 @@ module Fluent
212
212
  $log.debug "buffer queue is full. Wait 1 second to re-emit events"
213
213
  sleep 1
214
214
  retry
215
+ when :drop_oldest_chunk
216
+ $log.debug "buffer queue is full. Dropping oldest chunk"
217
+ pop(nil)
215
218
  end
216
219
  end
217
220
 
@@ -326,7 +329,7 @@ module Fluent
326
329
  begin
327
330
  # #push(key) does not push empty chunks into queue.
328
331
  # so this check is nonsense...
329
- if !chunk.empty?
332
+ if !chunk.empty? && !out.nil?
330
333
  write_chunk(chunk, out)
331
334
  end
332
335
 
@@ -28,6 +28,7 @@ module Fluent
28
28
  SPACING = /(?:[ \t\r\n]|\z|\#.*?(?:\z|[\r\n]))+/
29
29
  ZERO_OR_MORE_SPACING = /(?:[ \t\r\n]|\z|\#.*?(?:\z|[\r\n]))*/
30
30
  SPACING_WITHOUT_COMMENT = /(?:[ \t\r\n]|\z)+/
31
+ LINE_END_WITHOUT_SPACING_AND_COMMENT = /(?:\z|[\r\n])/
31
32
 
32
33
  module ClassMethods
33
34
  def symbol(string)
@@ -71,6 +72,10 @@ module Fluent
71
72
  @ss[0]
72
73
  end
73
74
 
75
+ def check(pattern)
76
+ @ss.check(pattern)
77
+ end
78
+
74
79
  def line_end
75
80
  skip(LINE_END)
76
81
  end
@@ -81,6 +81,11 @@ module Fluent
81
81
  while true
82
82
  if skip(/\"/)
83
83
  return string.join
84
+ elsif check(/[^"]#{LINE_END_WITHOUT_SPACING_AND_COMMENT}/)
85
+ if s = check(/[^\\]#{LINE_END_WITHOUT_SPACING_AND_COMMENT}/)
86
+ string << s
87
+ end
88
+ skip(/[^"]#{LINE_END_WITHOUT_SPACING_AND_COMMENT}/)
84
89
  elsif s = scan(/\\./)
85
90
  string << eval_escape_char(s[1,1])
86
91
  elsif skip(/\#\{/)
@@ -362,6 +362,7 @@ module Fluent
362
362
  code, header, body = *@callback.call(path_info, params)
363
363
  body = body.to_s
364
364
 
365
+ header['Access-Control-Allow-Origin'] = @origin if !@cors_allow_origins.nil? && @cors_allow_origins.include?(@origin)
365
366
  if @keep_alive
366
367
  header['Connection'] = 'Keep-Alive'
367
368
  send_response(code, header, body)
@@ -32,6 +32,7 @@ module Fluent
32
32
  config_param :port, :integer, default: 24220
33
33
  config_param :tag, :string, default: nil
34
34
  config_param :emit_interval, :time, default: 60
35
+ config_param :emit_config, :bool, default: false
35
36
 
36
37
  class MonitorServlet < WEBrick::HTTPServlet::AbstractServlet
37
38
  def initialize(server, agent)
@@ -253,7 +254,7 @@ module Fluent
253
254
  log.debug "tag parameter is specified. Emit plugins info to '#{@tag}'"
254
255
 
255
256
  @loop = Coolio::Loop.new
256
- opts = {with_config: false}
257
+ opts = {with_config: @emit_config}
257
258
  timer = TimerWatcher.new(@emit_interval, log) {
258
259
  es = MultiEventStream.new
259
260
  now = Engine.now
@@ -50,6 +50,16 @@ module Fluent
50
50
  config_param :multiline_flush_interval, :time, default: nil
51
51
  desc 'Enable the additional watch timer.'
52
52
  config_param :enable_watch_timer, :bool, default: true
53
+ desc 'The encoding of the input.'
54
+ config_param :encoding, default: nil do |encoding_name|
55
+ begin
56
+ Encoding.find(encoding_name)
57
+ rescue ArgumentError => e
58
+ raise ConfigError, e.message
59
+ end
60
+ end
61
+ desc 'Add the log path being tailed to records. Specify the field name to be used.'
62
+ config_param :path_key, :string, default: nil
53
63
 
54
64
  attr_reader :paths
55
65
 
@@ -222,6 +232,7 @@ module Fluent
222
232
  def flush_buffer(tw)
223
233
  if lb = tw.line_buffer
224
234
  lb.chomp!
235
+ lb.force_encoding(@encoding) if @encoding
225
236
  @parser.parse(lb) { |time, record|
226
237
  if time && record
227
238
  tag = if @tag_prefix || @tag_suffix
@@ -229,6 +240,7 @@ module Fluent
229
240
  else
230
241
  @tag
231
242
  end
243
+ record[@path_key] ||= tw.path unless @path_key.nil?
232
244
  router.emit(tag, time, record)
233
245
  else
234
246
  log.warn "got incomplete line at shutdown from #{tw.path}: #{lb.inspect}"
@@ -266,11 +278,13 @@ module Fluent
266
278
  return true
267
279
  end
268
280
 
269
- def convert_line_to_event(line, es)
281
+ def convert_line_to_event(line, es, tail_watcher)
270
282
  begin
271
283
  line.chomp! # remove \n
284
+ line.force_encoding(@encoding) if @encoding
272
285
  @parser.parse(line) { |time, record|
273
286
  if time && record
287
+ record[@path_key] ||= tail_watcher.path unless @path_key.nil?
274
288
  es.add(time, record)
275
289
  else
276
290
  log.warn "pattern not match: #{line.inspect}"
@@ -285,7 +299,7 @@ module Fluent
285
299
  def parse_singleline(lines, tail_watcher)
286
300
  es = MultiEventStream.new
287
301
  lines.each { |line|
288
- convert_line_to_event(line, es)
302
+ convert_line_to_event(line, es, tail_watcher)
289
303
  }
290
304
  es
291
305
  end
@@ -298,7 +312,7 @@ module Fluent
298
312
  lines.each { |line|
299
313
  if @parser.firstline?(line)
300
314
  if lb
301
- convert_line_to_event(lb, es)
315
+ convert_line_to_event(lb, es, tail_watcher)
302
316
  end
303
317
  lb = line
304
318
  else
@@ -315,7 +329,7 @@ module Fluent
315
329
  lb << line
316
330
  @parser.parse(lb) { |time, record|
317
331
  if time && record
318
- convert_line_to_event(lb, es)
332
+ convert_line_to_event(lb, es, tail_watcher)
319
333
  lb = ''
320
334
  end
321
335
  }
@@ -30,6 +30,9 @@ module Fluent
30
30
  class ForwardOutputResponseError < ForwardOutputError
31
31
  end
32
32
 
33
+ class ForwardOutputConnectionClosedError < ForwardOutputError
34
+ end
35
+
33
36
  class ForwardOutputACKTimeoutError < ForwardOutputResponseError
34
37
  end
35
38
 
@@ -14,6 +14,8 @@
14
14
  # limitations under the License.
15
15
  #
16
16
 
17
+ require "string-scrub" unless "".respond_to?(:scrub)
18
+
17
19
  module Fluent
18
20
  module StringUtil
19
21
  def match_regexp(regexp, string)
@@ -21,7 +23,7 @@ module Fluent
21
23
  return regexp.match(string)
22
24
  rescue ArgumentError => e
23
25
  raise e unless e.message.index("invalid byte sequence in".freeze).zero?
24
- log.info "invalid byte sequence is replaced in `#{string}`"
26
+ $log.info "invalid byte sequence is replaced in `#{string}`"
25
27
  string = string.scrub('?')
26
28
  retry
27
29
  end
@@ -40,7 +40,7 @@ module Fluent
40
40
  if value = @map[type]
41
41
  return value
42
42
  end
43
- raise ConfigError, "Unknown #{@kind} plugin '#{type}'. Run 'gem search -rd fluentd-plugin' to find plugins" # TODO error class
43
+ raise ConfigError, "Unknown #{@kind} plugin '#{type}'. Run 'gem search -rd fluent-plugin' to find plugins" # TODO error class
44
44
  end
45
45
 
46
46
  def search(type)
@@ -16,6 +16,6 @@
16
16
 
17
17
  module Fluent
18
18
 
19
- VERSION = '0.12.23'
19
+ VERSION = '0.12.24'
20
20
 
21
21
  end
@@ -5,6 +5,7 @@ require "fluent/config/error"
5
5
  require "fluent/config/basic_parser"
6
6
  require "fluent/config/literal_parser"
7
7
  require "fluent/config/v1_parser"
8
+ require 'fluent/config/parser'
8
9
 
9
10
  module Fluent::Config
10
11
  module V1TestHelper
@@ -115,6 +116,7 @@ module Fluent::Config
115
116
  test "requires escaping double quote" do
116
117
  assert_text_parsed_as(e('ROOT', '', {"k1" => '"'}), ' k1 "\\""')
117
118
  assert_parse_error(' k1 """')
119
+ assert_parse_error(' k1 ""\'')
118
120
  end
119
121
 
120
122
  test "removes backslash in front of a normal character" do
@@ -124,6 +126,39 @@ module Fluent::Config
124
126
  test "accepts escape characters" do
125
127
  assert_text_parsed_as(e('ROOT', '', {"k1" => "\n"}), ' k1 "\\n"')
126
128
  end
129
+
130
+ test "support multiline string" do
131
+ assert_text_parsed_as(e('ROOT', '',
132
+ {"k1" => %[line1
133
+ line2]
134
+ }),
135
+ %[k1 "line1
136
+ line2"]
137
+ )
138
+ assert_text_parsed_as(e('ROOT', '',
139
+ {"k1" => %[line1 line2]
140
+ }),
141
+ %[k1 "line1\\
142
+ line2"]
143
+ )
144
+ assert_text_parsed_as(e('ROOT', '',
145
+ {"k1" => %[line1
146
+ line2
147
+ line3]
148
+ }),
149
+ %[k1 "line1
150
+ line2
151
+ line3"]
152
+ )
153
+ assert_text_parsed_as(e('ROOT', '',
154
+ {"k1" => %[line1
155
+ line2 line3]
156
+ }),
157
+ %[k1 "line1
158
+ line2\\
159
+ line3"]
160
+ )
161
+ end
127
162
  end
128
163
 
129
164
  sub_test_case 'single quoted string' do
@@ -109,6 +109,8 @@ module Fluent::Config
109
109
  test('"\\0"') { assert_parse_error('"\\0"') } # unknown escaped character
110
110
  test('"\\1"') { assert_parse_error('"\\1"') } # unknown escaped character
111
111
  test('"t') { assert_parse_error('"t') } # non-terminated quoted character
112
+ test("\"t\nt\"") { assert_text_parsed_as("t\nt", "\"t\nt\"" ) } # multiline string
113
+ test("\"t\\\nt\"") { assert_text_parsed_as("tt", "\"t\\\nt\"" ) } # multiline string
112
114
  test('t"') { assert_text_parsed_as('t"', 't"') }
113
115
  test('"."') { assert_text_parsed_as('.', '"."') }
114
116
  test('"*"') { assert_text_parsed_as('*', '"*"') }
@@ -1,7 +1,7 @@
1
1
  require_relative '../helper'
2
2
  require 'fluent/plugin/filter_stdout'
3
3
  require 'timecop'
4
- require 'flexmock'
4
+ require 'flexmock/test_unit'
5
5
 
6
6
  class StdoutFilterTest < Test::Unit::TestCase
7
7
  include Fluent
@@ -13,6 +13,8 @@ class StdoutFilterTest < Test::Unit::TestCase
13
13
  end
14
14
 
15
15
  def teardown
16
+ super # FlexMock::TestCase requires this
17
+ # http://flexmock.rubyforge.org/FlexMock/TestCase.html
16
18
  Timecop.return
17
19
  end
18
20
 
@@ -101,12 +103,12 @@ class StdoutFilterTest < Test::Unit::TestCase
101
103
 
102
104
  # Capture the log output of the block given
103
105
  def capture_log(d, &block)
104
- tmp = d.instance.log
105
- d.instance.log = StringIO.new
106
+ tmp = d.instance.log.out
107
+ d.instance.log.out = StringIO.new
106
108
  yield
107
- return d.instance.log.string
109
+ return d.instance.log.out.string
108
110
  ensure
109
- d.instance.log = tmp
111
+ d.instance.log.out = tmp
110
112
  end
111
113
  end
112
114
 
@@ -1,5 +1,6 @@
1
1
  require_relative '../helper'
2
2
  require 'fluent/test'
3
+ require 'fluent/plugin/in_http'
3
4
  require 'net/http'
4
5
 
5
6
  class HttpInputTest < Test::Unit::TestCase
@@ -269,6 +270,79 @@ class HttpInputTest < Test::Unit::TestCase
269
270
  end
270
271
  end
271
272
 
273
+ def test_cors_allowed
274
+ d = create_driver(CONFIG + "cors_allow_origins [\"http://foo.com\"]")
275
+ assert_equal ["http://foo.com"], d.instance.cors_allow_origins
276
+
277
+ test_in_http_cros_allowed = nil
278
+ acao = nil
279
+
280
+ begin
281
+ d.run do
282
+ Net::HTTP.start("127.0.0.1", PORT) do |http|
283
+ req = Net::HTTP::Post.new("/foo/bar", {"Origin" => "http://foo.com"})
284
+ res = http.request(req)
285
+
286
+ acao = res["Access-Control-Allow-Origin"]
287
+ end
288
+ end
289
+ test_in_http_cros_allowed = true
290
+ rescue
291
+ test_in_http_cros_allowed = false
292
+ end
293
+
294
+ assert_equal true, test_in_http_cros_allowed
295
+ assert_equal "http://foo.com", acao
296
+ end
297
+
298
+ def test_cors_disallowed
299
+ d = create_driver(CONFIG + "cors_allow_origins [\"http://foo.com\"]")
300
+ assert_equal ["http://foo.com"], d.instance.cors_allow_origins
301
+
302
+ test_in_http_cros_disallowed = nil
303
+ response_code = nil
304
+
305
+ begin
306
+ d.run do
307
+ Net::HTTP.start("127.0.0.1", PORT) do |http|
308
+ req = Net::HTTP::Post.new("/foo/bar", {"Origin" => "http://bar.com"})
309
+ res = http.request(req)
310
+
311
+ response_code = res.code
312
+ end
313
+ end
314
+ test_in_http_cros_disallowed = true
315
+ rescue
316
+ test_in_http_cros_disallowed = false
317
+ end
318
+
319
+ assert_equal true, test_in_http_cros_disallowed
320
+ assert_equal "403", response_code
321
+ end
322
+
323
+ $test_in_http_connection_object_ids = []
324
+ $test_in_http_content_types = []
325
+ $test_in_http_content_types_flag = false
326
+ module ContentTypeHook
327
+ def on_headers_complete(headers)
328
+ super
329
+ if $test_in_http_content_types_flag
330
+ $test_in_http_content_types << self.content_type
331
+ end
332
+ end
333
+
334
+ def on_message_begin
335
+ super
336
+ if $test_in_http_content_types_flag
337
+ $test_in_http_connection_object_ids << @io_handler.object_id
338
+ end
339
+ end
340
+ end
341
+
342
+ class Fluent::HttpInput::Handler
343
+ prepend ContentTypeHook
344
+ end
345
+
272
346
  def test_if_content_type_is_initialized_properly
273
347
  # This test is to check if Fluent::HttpInput::Handler's @content_type is initialized properly.
274
348
  # Especially when in Keep-Alive and the second request has no 'Content-Type'.
@@ -1,7 +1,7 @@
1
1
  require_relative '../helper'
2
2
  require 'fluent/test'
3
3
  require 'net/http'
4
- require 'flexmock'
4
+ require 'flexmock/test_unit'
5
5
 
6
6
  class TailInputTest < Test::Unit::TestCase
7
7
  include FlexMock::TestCase
@@ -51,6 +51,17 @@ class TailInputTest < Test::Unit::TestCase
51
51
  assert_equal 1000, d.instance.read_lines_limit
52
52
  end
53
53
 
54
+ def test_configure_encoding
55
+ # valid encoding
56
+ d = create_driver(SINGLE_LINE_CONFIG + 'encoding utf-8')
57
+ assert_equal Encoding::UTF_8, d.instance.encoding
58
+
59
+ # invalid encoding
60
+ assert_raise(Fluent::ConfigError) do
61
+ create_driver(SINGLE_LINE_CONFIG + 'encoding no-such-encoding')
62
+ end
63
+ end
64
+
54
65
  # TODO: Should using more better approach instead of sleep wait
55
66
 
56
67
  def test_emit
@@ -300,6 +311,27 @@ class TailInputTest < Test::Unit::TestCase
300
311
  assert_equal({"message" => "tab "}, emits[5][2])
301
312
  end
302
313
 
314
+ data(
315
+ 'default encoding' => ['', Encoding::ASCII_8BIT],
316
+ 'explicit encoding config' => ['encoding utf-8', Encoding::UTF_8])
317
+ def test_encoding(data)
318
+ encoding_config, encoding = data
319
+
320
+ d = create_driver(SINGLE_LINE_CONFIG + CONFIG_READ_FROM_HEAD + encoding_config)
321
+
322
+ d.run do
323
+ sleep 1
324
+
325
+ File.open("#{TMP_DIR}/tail.txt", "wb") {|f|
326
+ f.puts "test"
327
+ }
328
+ sleep 1
329
+ end
330
+
331
+ emits = d.emits
332
+ assert_equal(encoding, emits[0][2]['message'].encoding)
333
+ end
334
+
303
335
  # multiline mode test
304
336
 
305
337
  def test_multiline
@@ -377,6 +409,33 @@ class TailInputTest < Test::Unit::TestCase
377
409
  end
378
410
  end
379
411
 
412
+ data(
413
+ 'default encoding' => ['', Encoding::ASCII_8BIT],
414
+ 'explicit encoding config' => ['encoding utf-8', Encoding::UTF_8])
415
+ def test_multiline_encoding_of_flushed_record(data)
416
+ encoding_config, encoding = data
417
+
418
+ d = create_driver %[
419
+ format multiline
420
+ format1 /^s (?<message1>[^\\n]+)(\\nf (?<message2>[^\\n]+))?(\\nf (?<message3>.*))?/
421
+ format_firstline /^[s]/
422
+ multiline_flush_interval 2s
423
+ read_from_head true
424
+ #{encoding_config}
425
+ ]
426
+
427
+ d.run do
428
+ File.open("#{TMP_DIR}/tail.txt", "wb") { |f|
429
+ f.puts "s test"
430
+ }
431
+
432
+ sleep 4
433
+ emits = d.emits
434
+ assert_equal(1, emits.length)
435
+ assert_equal(encoding, emits[0][2]['message1'].encoding)
436
+ end
437
+ end
438
+
380
439
  def test_multiline_with_multiple_formats
381
440
  File.open("#{TMP_DIR}/tail.txt", "w") { |f| }
382
441
 
@@ -680,4 +739,119 @@ class TailInputTest < Test::Unit::TestCase
680
739
  d.emits
681
740
  end
682
741
  end
742
+
743
+ sub_test_case "tail_path" do
744
+ def test_tail_path_with_singleline
745
+ File.open("#{TMP_DIR}/tail.txt", "wb") {|f|
746
+ f.puts "test1"
747
+ f.puts "test2"
748
+ }
749
+
750
+ d = create_driver(%[path_key path] + SINGLE_LINE_CONFIG)
751
+
752
+ d.run do
753
+ sleep 1
754
+
755
+ File.open("#{TMP_DIR}/tail.txt", "ab") {|f|
756
+ f.puts "test3"
757
+ f.puts "test4"
758
+ }
759
+ sleep 1
760
+ end
761
+
762
+ emits = d.emits
763
+ assert_equal(true, emits.length > 0)
764
+ emits.each do |emit|
765
+ assert_equal("#{TMP_DIR}/tail.txt", emit[2]["path"])
766
+ end
767
+ end
768
+
769
+ def test_tail_path_with_multiline_with_firstline
770
+ File.open("#{TMP_DIR}/tail.txt", "wb") { |f| }
771
+
772
+ d = create_driver %[
773
+ path_key path
774
+ format multiline
775
+ format1 /^s (?<message1>[^\\n]+)(\\nf (?<message2>[^\\n]+))?(\\nf (?<message3>.*))?/
776
+ format_firstline /^[s]/
777
+ ]
778
+ d.run do
779
+ File.open("#{TMP_DIR}/tail.txt", "ab") { |f|
780
+ f.puts "f test1"
781
+ f.puts "s test2"
782
+ f.puts "f test3"
783
+ f.puts "f test4"
784
+ f.puts "s test5"
785
+ f.puts "s test6"
786
+ f.puts "f test7"
787
+ f.puts "s test8"
788
+ }
789
+ sleep 1
790
+ end
791
+
792
+ emits = d.emits
793
+ assert(emits.length == 4)
794
+ emits.each do |emit|
795
+ assert_equal("#{TMP_DIR}/tail.txt", emit[2]["path"])
796
+ end
797
+ end
798
+
799
+ def test_tail_path_with_multiline_without_firstline
800
+ File.open("#{TMP_DIR}/tail.txt", "wb") { |f| }
801
+
802
+ d = create_driver %[
803
+ path_key path
804
+ format multiline
805
+ format1 /(?<var1>foo \\d)\\n/
806
+ format2 /(?<var2>bar \\d)\\n/
807
+ format3 /(?<var3>baz \\d)/
808
+ ]
809
+ d.run do
810
+ File.open("#{TMP_DIR}/tail.txt", "ab") { |f|
811
+ f.puts "foo 1"
812
+ f.puts "bar 1"
813
+ f.puts "baz 1"
814
+ }
815
+ sleep 1
816
+ end
817
+
818
+ emits = d.emits
819
+ assert(emits.length > 0)
820
+ emits.each do |emit|
821
+ assert_equal("#{TMP_DIR}/tail.txt", emit[2]["path"])
822
+ end
823
+ end
824
+
825
+ def test_tail_path_with_multiline_with_multiple_paths
826
+ files = ["#{TMP_DIR}/tail1.txt", "#{TMP_DIR}/tail2.txt"]
827
+ files.each { |file| File.open(file, "wb") { |f| } }
828
+
829
+ d = create_driver(%[
830
+ path #{files[0]},#{files[1]}
831
+ path_key path
832
+ tag t1
833
+ format multiline
834
+ format1 /^[s|f] (?<message>.*)/
835
+ format_firstline /^[s]/
836
+ ], false)
837
+ d.run do
838
+ files.each do |file|
839
+ File.open(file, 'ab') { |f|
840
+ f.puts "f #{file} line should be ignored"
841
+ f.puts "s test1"
842
+ f.puts "f test2"
843
+ f.puts "f test3"
844
+ f.puts "s test4"
845
+ }
846
+ end
847
+ sleep 1
848
+ end
849
+
850
+ emits = d.emits
851
+ assert(emits.length == 4)
852
+ assert_equal(files, [emits[0][2]["path"], emits[1][2]["path"]].sort)
853
+ # "test4" events are here because these events are flushed at shutdown phase
854
+ assert_equal(files, [emits[2][2]["path"], emits[3][2]["path"]].sort)
855
+ end
856
+ end
683
857
  end
@@ -0,0 +1,26 @@
1
+ require_relative '../helper'
2
+ require 'fluent/plugin/string_util'
3
+
4
+ class StringUtilTest < Test::Unit::TestCase
5
+ def setup
6
+ @null_value_pattern = Regexp.new("^(-|null|NULL)$")
7
+ end
8
+
9
+ sub_test_case 'valid string' do
10
+ test 'null string' do
11
+ assert_equal Fluent::StringUtil.match_regexp(@null_value_pattern, "null").to_s, "null"
12
+ assert_equal Fluent::StringUtil.match_regexp(@null_value_pattern, "NULL").to_s, "NULL"
13
+ assert_equal Fluent::StringUtil.match_regexp(@null_value_pattern, "-").to_s, "-"
14
+ end
15
+
16
+ test 'normal string' do
17
+ assert_equal Fluent::StringUtil.match_regexp(@null_value_pattern, "fluentd"), nil
18
+ end
19
+ end
20
+
21
+ sub_test_case 'invalid string' do
22
+ test 'normal string' do
23
+ assert_equal Fluent::StringUtil.match_regexp(@null_value_pattern, "\xff"), nil
24
+ end
25
+ end
26
+ end
data/test/test_buffer.rb CHANGED
@@ -589,6 +589,24 @@ module FluentBufferTest
589
589
  assert db.emit('key', data, chain)
590
590
  end
591
591
 
592
+ def test_emit_with_drop_oldest_chunk
593
+ db = dummy_buffer(:drop_oldest_chunk)
594
+
595
+ assert !db.emit('key', data, chain)
596
+ assert db.emit('key', data, chain)
597
+
598
+ # oldest (and only) chunk in queue is dropped before newer data is enqueued
599
+ assert db.emit('key', data, chain)
600
+
601
+ assert db.queue.size == 1
602
+
603
+ assert !pop_chunk(db)
604
+ assert db.queue.size == 0
605
+
606
+ # queue is now empty so can emit data again
607
+ assert db.emit('key', data, chain)
608
+ end
609
+
592
610
  def pop_chunk(db)
593
611
  out = DummyOutput.new
594
612
  c1 = DummyChunk.new('k1', 1)
data/test/test_log.rb ADDED
@@ -0,0 +1,340 @@
1
+ require_relative 'helper'
2
+ require 'fluent/engine'
3
+ require 'fluent/log'
4
+
5
+ class LogTest < Test::Unit::TestCase
6
+ def setup
7
+ @log_device = Fluent::Test::DummyLogDevice.new
8
+ @timestamp = Time.parse("2016-04-21 11:58:41 +0900")
9
+ @timestamp_str = @timestamp.strftime("%Y-%m-%d %H:%M:%S %z")
10
+ stub(Time).now { @timestamp }
11
+ end
12
+
13
+ def teardown
14
+ Thread.current[:last_repeated_stacktrace] = nil
15
+ end
16
+
17
+ sub_test_case "log level" do
18
+ data(
19
+ trace: [Fluent::Log::LEVEL_TRACE, 0],
20
+ debug: [Fluent::Log::LEVEL_DEBUG, 1],
21
+ info: [Fluent::Log::LEVEL_INFO, 2],
22
+ warn: [Fluent::Log::LEVEL_WARN, 3],
23
+ error: [Fluent::Log::LEVEL_ERROR, 4],
24
+ fatal: [Fluent::Log::LEVEL_FATAL, 5],
25
+ )
26
+ def test_output(data)
27
+ log_level, start = data
28
+ log = Fluent::Log.new(@log_device, log_level)
29
+ log.trace "trace log"
30
+ log.debug "debug log"
31
+ log.info "info log"
32
+ log.warn "warn log"
33
+ log.error "error log"
34
+ log.fatal "fatal log"
35
+ expected = [
36
+ "#{@timestamp_str} [trace]: trace log\n",
37
+ "#{@timestamp_str} [debug]: debug log\n",
38
+ "#{@timestamp_str} [info]: info log\n",
39
+ "#{@timestamp_str} [warn]: warn log\n",
40
+ "#{@timestamp_str} [error]: error log\n",
41
+ "#{@timestamp_str} [fatal]: fatal log\n"
42
+ ][start..-1]
43
+ assert_equal(expected, log.out.logs)
44
+ end
45
+
46
+ data(
47
+ trace: [Fluent::Log::LEVEL_TRACE, 0],
48
+ debug: [Fluent::Log::LEVEL_DEBUG, 1],
49
+ info: [Fluent::Log::LEVEL_INFO, 2],
50
+ warn: [Fluent::Log::LEVEL_WARN, 3],
51
+ error: [Fluent::Log::LEVEL_ERROR, 4],
52
+ fatal: [Fluent::Log::LEVEL_FATAL, 5],
53
+ )
54
+ def test_output_with_block(data)
55
+ log_level, start = data
56
+ log = Fluent::Log.new(@log_device, log_level)
57
+ log.trace { "trace log" }
58
+ log.debug { "debug log" }
59
+ log.info { "info log" }
60
+ log.warn { "warn log" }
61
+ log.error { "error log" }
62
+ log.fatal { "fatal log" }
63
+ expected = [
64
+ "#{@timestamp_str} [trace]: trace log\n",
65
+ "#{@timestamp_str} [debug]: debug log\n",
66
+ "#{@timestamp_str} [info]: info log\n",
67
+ "#{@timestamp_str} [warn]: warn log\n",
68
+ "#{@timestamp_str} [error]: error log\n",
69
+ "#{@timestamp_str} [fatal]: fatal log\n"
70
+ ][start..-1]
71
+ assert_equal(expected, log.out.logs)
72
+ end
73
+
74
+ data(
75
+ trace: [Fluent::Log::LEVEL_TRACE, { trace: true, debug: true, info: true, warn: true, error: true, fatal: true }],
76
+ debug: [Fluent::Log::LEVEL_DEBUG, { trace: false, debug: true, info: true, warn: true, error: true, fatal: true }],
77
+ info: [Fluent::Log::LEVEL_INFO, { trace: false, debug: false, info: true, warn: true, error: true, fatal: true }],
78
+ warn: [Fluent::Log::LEVEL_WARN, { trace: false, debug: false, info: false, warn: true, error: true, fatal: true }],
79
+ error: [Fluent::Log::LEVEL_ERROR, { trace: false, debug: false, info: false, warn: false, error: true, fatal: true }],
80
+ fatal: [Fluent::Log::LEVEL_FATAL, { trace: false, debug: false, info: false, warn: false, error: false, fatal: true }],
81
+ )
82
+ def test_execute_block(data)
83
+ log_level, expected = data
84
+ log = Fluent::Log.new(@log_device, log_level)
85
+ block_called = {
86
+ trace: false,
87
+ debug: false,
88
+ info: false,
89
+ warn: false,
90
+ error: false,
91
+ fatal: false,
92
+ }
93
+ log.trace { block_called[:trace] = true }
94
+ log.debug { block_called[:debug] = true }
95
+ log.info { block_called[:info] = true }
96
+ log.warn { block_called[:warn] = true }
97
+ log.error { block_called[:error] = true }
98
+ log.fatal { block_called[:fatal] = true }
99
+ assert_equal(expected, block_called)
100
+ end
101
+
102
+ data(
103
+ trace: [Fluent::Log::LEVEL_TRACE, 0],
104
+ debug: [Fluent::Log::LEVEL_DEBUG, 3],
105
+ info: [Fluent::Log::LEVEL_INFO, 6],
106
+ warn: [Fluent::Log::LEVEL_WARN, 9],
107
+ error: [Fluent::Log::LEVEL_ERROR, 12],
108
+ fatal: [Fluent::Log::LEVEL_FATAL, 15],
109
+ )
110
+ def test_backtrace(data)
111
+ log_level, start = data
112
+ backtrace = ["line 1", "line 2", "line 3"]
113
+ log = Fluent::Log.new(@log_device, log_level)
114
+ log.trace_backtrace(backtrace)
115
+ log.debug_backtrace(backtrace)
116
+ log.info_backtrace(backtrace)
117
+ log.warn_backtrace(backtrace)
118
+ log.error_backtrace(backtrace)
119
+ log.fatal_backtrace(backtrace)
120
+ expected = [
121
+ " #{@timestamp_str} [trace]: line 1\n",
122
+ " #{@timestamp_str} [trace]: line 2\n",
123
+ " #{@timestamp_str} [trace]: line 3\n",
124
+ " #{@timestamp_str} [debug]: line 1\n",
125
+ " #{@timestamp_str} [debug]: line 2\n",
126
+ " #{@timestamp_str} [debug]: line 3\n",
127
+ " #{@timestamp_str} [info]: line 1\n",
128
+ " #{@timestamp_str} [info]: line 2\n",
129
+ " #{@timestamp_str} [info]: line 3\n",
130
+ " #{@timestamp_str} [warn]: line 1\n",
131
+ " #{@timestamp_str} [warn]: line 2\n",
132
+ " #{@timestamp_str} [warn]: line 3\n",
133
+ " #{@timestamp_str} [error]: line 1\n",
134
+ " #{@timestamp_str} [error]: line 2\n",
135
+ " #{@timestamp_str} [error]: line 3\n",
136
+ " #{@timestamp_str} [fatal]: line 1\n",
137
+ " #{@timestamp_str} [fatal]: line 2\n",
138
+ " #{@timestamp_str} [fatal]: line 3\n"
139
+ ][start..-1]
140
+ assert_equal(expected, log.out.logs)
141
+ end
142
+ end
143
+
144
+ sub_test_case "suppress repeated backtrace" do
145
+ def test_same_log_level
146
+ backtrace = ["line 1", "line 2", "line 3"]
147
+ log = Fluent::Log.new(@log_device, Fluent::Log::LEVEL_TRACE, suppress_repeated_stacktrace: true)
148
+ log.trace_backtrace(backtrace)
149
+ log.trace_backtrace(backtrace)
150
+ log.trace_backtrace(backtrace + ["line 4"])
151
+ log.trace_backtrace(backtrace)
152
+ log.trace_backtrace(backtrace)
153
+ expected = [
154
+ " #{@timestamp_str} [trace]: line 1\n",
155
+ " #{@timestamp_str} [trace]: line 2\n",
156
+ " #{@timestamp_str} [trace]: line 3\n",
157
+ " #{@timestamp_str} [trace]: suppressed same stacktrace\n",
158
+ " #{@timestamp_str} [trace]: line 1\n",
159
+ " #{@timestamp_str} [trace]: line 2\n",
160
+ " #{@timestamp_str} [trace]: line 3\n",
161
+ " #{@timestamp_str} [trace]: line 4\n",
162
+ " #{@timestamp_str} [trace]: line 1\n",
163
+ " #{@timestamp_str} [trace]: line 2\n",
164
+ " #{@timestamp_str} [trace]: line 3\n",
165
+ " #{@timestamp_str} [trace]: suppressed same stacktrace\n",
166
+ ]
167
+ assert_equal(expected, log.out.logs)
168
+ end
169
+
170
+ def test_different_log_level
171
+ backtrace = ["line 1", "line 2", "line 3"]
172
+ log = Fluent::Log.new(@log_device, Fluent::Log::LEVEL_TRACE, suppress_repeated_stacktrace: true)
173
+ log.trace_backtrace(backtrace)
174
+ log.debug_backtrace(backtrace)
175
+ log.info_backtrace(backtrace)
176
+ log.warn_backtrace(backtrace)
177
+ log.error_backtrace(backtrace)
178
+ log.fatal_backtrace(backtrace)
179
+ expected = [
180
+ " #{@timestamp_str} [trace]: line 1\n",
181
+ " #{@timestamp_str} [trace]: line 2\n",
182
+ " #{@timestamp_str} [trace]: line 3\n",
183
+ " #{@timestamp_str} [debug]: suppressed same stacktrace\n",
184
+ " #{@timestamp_str} [info]: suppressed same stacktrace\n",
185
+ " #{@timestamp_str} [warn]: suppressed same stacktrace\n",
186
+ " #{@timestamp_str} [error]: suppressed same stacktrace\n",
187
+ " #{@timestamp_str} [fatal]: suppressed same stacktrace\n",
188
+ ]
189
+ assert_equal(expected, log.out.logs)
190
+ end
191
+ end
192
+
193
+ def test_dup
194
+ log1 = Fluent::Log.new(@log_device, Fluent::Log::LEVEL_TRACE)
195
+ log2 = log1.dup
196
+ log1.level = Fluent::Log::LEVEL_DEBUG
197
+ original_tag = log1.tag
198
+ log1.tag = "changed"
199
+ assert_equal(Fluent::Log::LEVEL_DEBUG, log1.level)
200
+ assert_equal(Fluent::Log::LEVEL_TRACE, log2.level)
201
+ assert_equal("changed", log1.tag)
202
+ assert_equal(original_tag, log2.tag)
203
+ end
204
+
205
+ def test_disable_events
206
+ log = Fluent::Log.new(@log_device, Fluent::Log::LEVEL_TRACE)
207
+ engine = log.instance_variable_get("@engine")
208
+ mock(engine).push_log_event(anything, anything, anything).once
209
+ log.trace "trace log"
210
+ log.disable_events(Thread.current)
211
+ log.trace "trace log"
212
+ end
213
+ end
214
+
215
+ class PluginLoggerTest < Test::Unit::TestCase
216
+ def setup
217
+ @log_device = Fluent::Test::DummyLogDevice.new
218
+ @timestamp = Time.parse("2016-04-21 11:58:41 +0900")
219
+ @timestamp_str = @timestamp.strftime("%Y-%m-%d %H:%M:%S %z")
220
+ stub(Time).now { @timestamp }
221
+ @logger = Fluent::Log.new(@log_device, Fluent::Log::LEVEL_TRACE)
222
+ end
223
+
224
+ def teardown
225
+ Thread.current[:last_repeated_stacktrace] = nil
226
+ end
227
+
228
+ def test_initialize
229
+ log = Fluent::PluginLogger.new(@logger)
230
+ logger = log.instance_variable_get("@logger")
231
+ assert_equal(logger, @logger)
232
+ end
233
+
234
+ def test_level
235
+ log = Fluent::PluginLogger.new(@logger)
236
+ assert_equal(log.level, @logger.level)
237
+ log.level = "fatal"
238
+ assert_equal(Fluent::Log::LEVEL_FATAL, log.level)
239
+ assert_equal(Fluent::Log::LEVEL_TRACE, @logger.level)
240
+ end
241
+
242
+ def test_enable_color
243
+ log = Fluent::PluginLogger.new(@logger)
244
+ log.enable_color(true)
245
+ assert_equal(true, log.enable_color?)
246
+ assert_equal(true, @logger.enable_color?)
247
+ log.enable_color(false)
248
+ assert_equal(false, log.enable_color?)
249
+ assert_equal(false, @logger.enable_color?)
250
+ log.enable_color
251
+ assert_equal(true, log.enable_color?)
252
+ assert_equal(true, @logger.enable_color?)
253
+ end
254
+
255
+ sub_test_case "delegators" do
256
+ def setup
257
+ super
258
+ @log = Fluent::PluginLogger.new(@logger)
259
+ end
260
+
261
+ def test_enable_debug
262
+ mock(@logger).enable_debug
263
+ @log.enable_debug
264
+ end
265
+
266
+ def test_enable_event
267
+ mock(@logger).enable_event
268
+ @log.enable_event
269
+ end
270
+
271
+ def test_disable_events
272
+ mock(@logger).disable_events(Thread.current)
273
+ @log.disable_events(Thread.current)
274
+ end
275
+
276
+ def test_tag
277
+ assert_equal(@log.tag, @logger.tag)
278
+ @log.tag = "dummy"
279
+ assert_equal(@log.tag, @logger.tag)
280
+ end
281
+
282
+ def test_time_format
283
+ assert_equal(@log.time_format, @logger.time_format)
284
+ @log.time_format = "time_format"
285
+ assert_equal(@log.time_format, @logger.time_format)
286
+ end
287
+
288
+ def test_event
289
+ mock(@logger).event(Fluent::Log::LEVEL_TRACE, { key: "value" })
290
+ @log.event(Fluent::Log::LEVEL_TRACE, { key: "value" })
291
+ end
292
+
293
+ def test_caller_line
294
+ mock(@logger).caller_line(Time.now, 1, Fluent::Log::LEVEL_TRACE)
295
+ @log.caller_line(Time.now, 1, Fluent::Log::LEVEL_TRACE)
296
+ end
297
+
298
+ def test_puts
299
+ mock(@logger).puts("log")
300
+ @log.puts("log")
301
+ end
302
+
303
+ def test_write
304
+ mock(@logger).write("log")
305
+ @log.write("log")
306
+ end
307
+
308
+ def test_out
309
+ assert_equal(@log.out, @logger.out)
310
+ @log.out = Object.new
311
+ assert_equal(@log.out, @logger.out)
312
+ end
313
+ end
314
+ end
315
+
316
+ class PluginLoggerMixinTest < Test::Unit::TestCase
317
+ class DummyPlugin < Fluent::Input
318
+ end
319
+
320
+ def create_driver(conf)
321
+ Fluent::Test::TestDriver.new(DummyPlugin).configure(conf)
322
+ end
323
+
324
+ def setup
325
+ Fluent::Test.setup
326
+ end
327
+
328
+ def test_default_log
329
+ plugin = DummyPlugin.new
330
+ log = plugin.log
331
+ assert_equal($log, log)
332
+ end
333
+
334
+ def test_log_level
335
+ d = create_driver(%[log_level fatal])
336
+ log = d.instance.log
337
+ assert_not_equal($log.level, log.level)
338
+ assert_equal(Fluent::Log::LEVEL_FATAL, log.level)
339
+ end
340
+ 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.23
4
+ version: 0.12.24
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-05-04 00:00:00.000000000 Z
11
+ date: 2016-05-20 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: msgpack
@@ -140,26 +140,6 @@ dependencies:
140
140
  - - ">="
141
141
  - !ruby/object:Gem::Version
142
142
  version: 1.0.0
143
- - !ruby/object:Gem::Dependency
144
- name: string-scrub
145
- requirement: !ruby/object:Gem::Requirement
146
- requirements:
147
- - - ">="
148
- - !ruby/object:Gem::Version
149
- version: 0.0.3
150
- - - "<="
151
- - !ruby/object:Gem::Version
152
- version: 0.0.5
153
- type: :runtime
154
- prerelease: false
155
- version_requirements: !ruby/object:Gem::Requirement
156
- requirements:
157
- - - ">="
158
- - !ruby/object:Gem::Version
159
- version: 0.0.3
160
- - - "<="
161
- - !ruby/object:Gem::Version
162
- version: 0.0.5
163
143
  - !ruby/object:Gem::Dependency
164
144
  name: rake
165
145
  requirement: !ruby/object:Gem::Requirement
@@ -451,6 +431,7 @@ files:
451
431
  - test/plugin/test_out_roundrobin.rb
452
432
  - test/plugin/test_out_stdout.rb
453
433
  - test/plugin/test_out_stream.rb
434
+ - test/plugin/test_string_util.rb
454
435
  - test/scripts/exec_script.rb
455
436
  - test/scripts/fluent/plugin/formatter_known.rb
456
437
  - test/scripts/fluent/plugin/out_test.rb
@@ -463,6 +444,7 @@ files:
463
444
  - test/test_filter.rb
464
445
  - test/test_formatter.rb
465
446
  - test/test_input.rb
447
+ - test/test_log.rb
466
448
  - test/test_match.rb
467
449
  - test/test_mixin.rb
468
450
  - test/test_output.rb
@@ -538,6 +520,7 @@ test_files:
538
520
  - test/plugin/test_out_roundrobin.rb
539
521
  - test/plugin/test_out_stdout.rb
540
522
  - test/plugin/test_out_stream.rb
523
+ - test/plugin/test_string_util.rb
541
524
  - test/scripts/exec_script.rb
542
525
  - test/scripts/fluent/plugin/formatter_known.rb
543
526
  - test/scripts/fluent/plugin/out_test.rb
@@ -550,6 +533,7 @@ test_files:
550
533
  - test/test_filter.rb
551
534
  - test/test_formatter.rb
552
535
  - test/test_input.rb
536
+ - test/test_log.rb
553
537
  - test/test_match.rb
554
538
  - test/test_mixin.rb
555
539
  - test/test_output.rb