fluentd 1.9.3-x64-mingw32 → 1.10.0-x64-mingw32

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.

Files changed (38) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/issue-auto-closer.yml +12 -0
  3. data/CHANGELOG.md +48 -0
  4. data/lib/fluent/msgpack_factory.rb +13 -6
  5. data/lib/fluent/plugin/buffer.rb +2 -2
  6. data/lib/fluent/plugin/in_dummy.rb +1 -1
  7. data/lib/fluent/plugin/in_tail.rb +35 -26
  8. data/lib/fluent/plugin/out_forward.rb +4 -6
  9. data/lib/fluent/plugin/out_forward/handshake_protocol.rb +4 -0
  10. data/lib/fluent/plugin/parser_syslog.rb +114 -34
  11. data/lib/fluent/plugin/sd_file.rb +1 -0
  12. data/lib/fluent/plugin/sd_srv.rb +135 -0
  13. data/lib/fluent/plugin_helper/cert_option.rb +15 -2
  14. data/lib/fluent/plugin_helper/server.rb +3 -1
  15. data/lib/fluent/plugin_helper/socket_option.rb +19 -1
  16. data/lib/fluent/version.rb +1 -1
  17. data/test/command/test_fluentd.rb +14 -2
  18. data/test/helper.rb +2 -2
  19. data/test/plugin/in_tail/test_fifo.rb +121 -0
  20. data/test/plugin/in_tail/test_io_handler.rb +132 -0
  21. data/test/plugin/in_tail/test_position_file.rb +1 -1
  22. data/test/plugin/out_forward/test_handshake_protocol.rb +10 -1
  23. data/test/plugin/test_buf_file.rb +3 -1
  24. data/test/plugin/test_buffer.rb +20 -0
  25. data/test/plugin/test_compressable.rb +7 -4
  26. data/test/plugin/test_in_dummy.rb +12 -14
  27. data/test/plugin/test_in_forward.rb +2 -2
  28. data/test/plugin/test_in_tail.rb +6 -6
  29. data/test/plugin/test_out_forward.rb +28 -1
  30. data/test/plugin/test_output_as_buffered_secondary.rb +1 -1
  31. data/test/plugin/test_parser_syslog.rb +46 -34
  32. data/test/plugin/test_sd_file.rb +17 -0
  33. data/test/plugin/test_sd_srv.rb +230 -0
  34. data/test/plugin_helper/data/cert/cert-with-CRLF.pem +19 -0
  35. data/test/plugin_helper/test_cert_option.rb +2 -0
  36. data/test/plugin_helper/test_child_process.rb +5 -3
  37. data/test/test_msgpack_factory.rb +18 -0
  38. metadata +14 -2
@@ -85,6 +85,7 @@ module Fluent
85
85
  fetch_server_info
86
86
  rescue => e
87
87
  @log.error("sd_file: #{e}")
88
+ return
88
89
  end
89
90
 
90
91
  if s.nil?
@@ -0,0 +1,135 @@
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
+ require 'resolv'
18
+
19
+ require 'fluent/plugin_helper'
20
+ require 'fluent/plugin/service_discovery'
21
+
22
+ module Fluent
23
+ module Plugin
24
+ class SrvServiceDiscovery < ServiceDiscovery
25
+ include PluginHelper::Mixin
26
+
27
+ Plugin.register_sd('srv', self)
28
+
29
+ helpers :timer
30
+
31
+ desc 'Service without underscore in RFC2782'
32
+ config_param :service, :string
33
+ desc 'Proto without underscore in RFC2782'
34
+ config_param :proto, :string, default: 'tcp'
35
+ desc 'Name in RFC2782'
36
+ config_param :hostname, :string
37
+ desc 'hostname of DNS server to request the SRV record'
38
+ config_param :dns_server_host, :string, default: nil
39
+ desc 'interval of requesting to DNS server'
40
+ config_param :interval, :integer, default: 60
41
+ desc "resolve hostname to IP addr of SRV's Target"
42
+ config_param :dns_lookup, :bool, default: true
43
+ desc 'The shared key per server'
44
+ config_param :shared_key, :string, default: nil, secret: true
45
+ desc 'The username for authentication'
46
+ config_param :username, :string, default: ''
47
+ desc 'The password for authentication'
48
+ config_param :password, :string, default: '', secret: true
49
+
50
+ def initialize
51
+ super
52
+ @target = nil
53
+ end
54
+
55
+ def configure(conf)
56
+ super
57
+
58
+ @target = "_#{@service}._#{@proto}.#{@hostname}"
59
+ @dns_resolve =
60
+ if @dns_server_host.nil?
61
+ Resolv::DNS.new
62
+ elsif @dns_server_host.include?(':') # e.g. 127.0.0.1:8600
63
+ host, port = @dns_server_host.split(':', 2)
64
+ Resolv::DNS.new(nameserver_port: [[host, port.to_i]])
65
+ else
66
+ Resolv::DNS.new(nameserver: @dns_server_host)
67
+ end
68
+
69
+ @services = fetch_srv_record
70
+ end
71
+
72
+ def start(queue)
73
+ timer_execute(:"sd_srv_record_#{@target}", @interval) do
74
+ refresh_srv_records(queue)
75
+ end
76
+
77
+ super()
78
+ end
79
+
80
+ private
81
+
82
+ def refresh_srv_records(queue)
83
+ s = begin
84
+ fetch_srv_record
85
+ rescue => e
86
+ @log.error("sd_srv: #{e}")
87
+ return
88
+ end
89
+
90
+ if s.nil? || s.empty?
91
+ return
92
+ end
93
+
94
+ diff = []
95
+ join = s - @services
96
+ # Need service_in first to guarantee that server exist at least one all time.
97
+ join.each do |j|
98
+ diff << ServiceDiscovery.service_in_msg(j)
99
+ end
100
+
101
+ drain = @services - s
102
+ drain.each do |d|
103
+ diff << ServiceDiscovery.service_out_msg(d)
104
+ end
105
+
106
+ @services = s
107
+
108
+ diff.each do |a|
109
+ queue.push(a)
110
+ end
111
+ end
112
+
113
+ def fetch_srv_record
114
+ adders = @dns_resolve.getresources(@target, Resolv::DNS::Resource::IN::SRV)
115
+
116
+ services = []
117
+
118
+ adders.each do |addr|
119
+ host = @dns_lookup ? dns_lookup!(addr.target) : addr.target
120
+ services << [
121
+ addr.priority,
122
+ Service.new(:srv, host.to_s, addr.port.to_i, addr.target.to_s, addr.weight, false, @username, @password, @shared_key)
123
+ ]
124
+ end
125
+
126
+ services.sort_by(&:first).flat_map { |s| s[1] }
127
+ end
128
+
129
+ def dns_lookup!(host)
130
+ # may need to cache the result
131
+ @dns_resolve.getaddress(host) # get first result for now
132
+ end
133
+ end
134
+ end
135
+ end
@@ -36,7 +36,7 @@ module Fluent
36
36
  end
37
37
 
38
38
  if conf.client_cert_auth
39
- ctx.verify_mode = OpenSSL::SSL::VERIFY_PEER | OpenSSL::SSL::VERIFY_FAIL_IF_NO_PEER_CERT
39
+ ctx.verify_mode = OpenSSL::SSL::VERIFY_PEER | OpenSSL::SSL::VERIFY_FAIL_IF_NO_PEER_CERT
40
40
  end
41
41
 
42
42
  ctx.ca_file = conf.ca_path
@@ -45,6 +45,16 @@ module Fluent
45
45
  if extra && !extra.empty?
46
46
  ctx.extra_chain_cert = extra
47
47
  end
48
+ if conf.cert_verifier
49
+ sandbox = Class.new
50
+ ctx.verify_callback = if File.exist?(conf.cert_verifier)
51
+ verifier = File.read(conf.cert_verifier)
52
+ sandbox.instance_eval(verifier, File.basename(conf.cert_verifier))
53
+ else
54
+ sandbox.instance_eval(conf.cert_verifier)
55
+ end
56
+ end
57
+
48
58
  Fluent::TLS.set_version_to_context(ctx, version, conf.min_version, conf.max_version)
49
59
 
50
60
  ctx
@@ -171,9 +181,12 @@ module Fluent
171
181
 
172
182
  def cert_option_certificates_from_file(path)
173
183
  data = File.read(path)
174
- pattern = Regexp.compile('-+BEGIN CERTIFICATE-+\n(?:[^-]*\n)+-+END CERTIFICATE-+\n?', Regexp::MULTILINE)
184
+ pattern = Regexp.compile('-+BEGIN CERTIFICATE-+\r?\n(?:[^-]*\r?\n)+-+END CERTIFICATE-+\r?\n?', Regexp::MULTILINE)
175
185
  list = []
176
186
  data.scan(pattern){|match| list << OpenSSL::X509::Certificate.new(match) }
187
+ if list.length == 0
188
+ log.warn "cert_path does not contain a valid certificate"
189
+ end
177
190
  list
178
191
  end
179
192
  end
@@ -243,7 +243,7 @@ module Fluent
243
243
  :protocol, :version, :min_version, :max_version, :ciphers, :insecure,
244
244
  :ca_path, :cert_path, :private_key_path, :private_key_passphrase, :client_cert_auth,
245
245
  :ca_cert_path, :ca_private_key_path, :ca_private_key_passphrase,
246
- :generate_private_key_length,
246
+ :cert_verifier, :generate_private_key_length,
247
247
  :generate_cert_country, :generate_cert_state, :generate_cert_state,
248
248
  :generate_cert_locality, :generate_cert_common_name,
249
249
  :generate_cert_expiration, :generate_cert_digest,
@@ -281,6 +281,8 @@ module Fluent
281
281
  config_param :ca_private_key_path, :string, default: nil
282
282
  config_param :ca_private_key_passphrase, :string, default: nil, secret: true
283
283
 
284
+ config_param :cert_verifier, :string, default: nil
285
+
284
286
  # Options for generating certs by private CA certs or self-signed
285
287
  config_param :generate_private_key_length, :integer, default: 2048
286
288
  config_param :generate_cert_country, :string, default: 'US'
@@ -21,6 +21,8 @@ require 'fcntl'
21
21
  module Fluent
22
22
  module PluginHelper
23
23
  module SocketOption
24
+ # ref: https://docs.microsoft.com/en-us/windows/win32/api/winsock/ns-winsock-linger
25
+ FORMAT_STRUCT_LINGER_WINDOWS = 'S!S!' # { u_short l_onoff; u_short l_linger; }
24
26
  FORMAT_STRUCT_LINGER = 'I!I!' # { int l_onoff; int l_linger; }
25
27
  FORMAT_STRUCT_TIMEVAL = 'L!L!' # { time_t tv_sec; suseconds_t tv_usec; }
26
28
 
@@ -49,9 +51,25 @@ module Fluent
49
51
  if nonblock
50
52
  sock.fcntl(Fcntl::F_SETFL, Fcntl::O_NONBLOCK)
51
53
  end
52
- if linger_timeout
54
+ if Fluent.windows?
55
+ # To prevent closing socket forcibly on Windows,
56
+ # this options shouldn't be set up when linger_timeout equals to 0 (including nil).
57
+ # This unintended behavior always ocurrs on Windows when linger_timeout.to_i == 0.
58
+ # This unintented behavior causes "Errno::ECONNRESET: An existing connection was forcibly
59
+ # closed by the remote host." on Windows.
60
+ if linger_timeout.to_i > 0
61
+ if linger_timeout >= 2**16
62
+ log.warn "maximum linger_timeout is 65535(2^16 - 1). Set to 65535 forcibly."
63
+ linger_timeout = 2**16 - 1
64
+ end
65
+ optval = [1, linger_timeout.to_i].pack(FORMAT_STRUCT_LINGER_WINDOWS)
66
+ socket_option_set_one(sock, :SO_LINGER, optval)
67
+ end
68
+ else
69
+ if linger_timeout
53
70
  optval = [1, linger_timeout.to_i].pack(FORMAT_STRUCT_LINGER)
54
71
  socket_option_set_one(sock, :SO_LINGER, optval)
72
+ end
55
73
  end
56
74
  if recv_timeout
57
75
  optval = [recv_timeout.to_i, 0].pack(FORMAT_STRUCT_TIMEVAL)
@@ -16,6 +16,6 @@
16
16
 
17
17
  module Fluent
18
18
 
19
- VERSION = '1.9.3'
19
+ VERSION = '1.10.0'
20
20
 
21
21
  end
@@ -84,6 +84,18 @@ class TestFluentdCommand < ::Test::Unit::TestCase
84
84
  null_stream.close rescue nil
85
85
  end
86
86
 
87
+ def eager_read(io)
88
+ buf = +''
89
+
90
+ loop do
91
+ b = io.read_nonblock(1024, nil, exception: false)
92
+ if b == :wait_readable || b.nil?
93
+ return buf
94
+ end
95
+ buf << b
96
+ end
97
+ end
98
+
87
99
  def assert_log_matches(cmdline, *pattern_list, patterns_not_match: [], timeout: 10, env: {})
88
100
  matched = false
89
101
  assert_error_msg = "matched correctly"
@@ -97,7 +109,7 @@ class TestFluentdCommand < ::Test::Unit::TestCase
97
109
  next unless readables
98
110
  break if readables.first.eof?
99
111
 
100
- buf = readables.first.readpartial(1024)
112
+ buf = eager_read(readables.first)
101
113
  # puts buf
102
114
  stdio_buf << buf
103
115
  lines = stdio_buf.split("\n")
@@ -150,7 +162,7 @@ class TestFluentdCommand < ::Test::Unit::TestCase
150
162
  next unless readables
151
163
  next if readables.first.eof?
152
164
 
153
- stdio_buf << readables.first.readpartial(1024)
165
+ stdio_buf << eager_read(readables.first)
154
166
  lines = stdio_buf.split("\n")
155
167
  if lines.any?{|line| line.include?("fluentd worker is now running") }
156
168
  running = true
@@ -49,8 +49,8 @@ require 'fluent/plugin_helper'
49
49
  require 'fluent/msgpack_factory'
50
50
  require 'fluent/time'
51
51
  require 'serverengine'
52
- require 'helpers/fuzzy_assert'
53
- require 'helpers/process_extenstion'
52
+ require_relative 'helpers/fuzzy_assert'
53
+ require_relative 'helpers/process_extenstion'
54
54
 
55
55
  module Fluent
56
56
  module Plugin
@@ -0,0 +1,121 @@
1
+ require_relative '../../helper'
2
+
3
+ require 'fluent/plugin/in_tail'
4
+
5
+ class IntailFIFO < Test::Unit::TestCase
6
+ sub_test_case '#read_line' do
7
+ test 'returns lines spliting per `\n`' do
8
+ fifo = Fluent::Plugin::TailInput::TailWatcher::FIFO.new(Encoding::ASCII_8BIT, Encoding::ASCII_8BIT)
9
+ text = ("test\n" * 3).force_encoding(Encoding::ASCII_8BIT)
10
+ fifo << text
11
+ lines = []
12
+ fifo.read_lines(lines)
13
+ assert_equal Encoding::ASCII_8BIT, lines[0].encoding
14
+ assert_equal ["test\n", "test\n", "test\n"], lines
15
+ end
16
+
17
+ test 'concant line when line is separated' do
18
+ fifo = Fluent::Plugin::TailInput::TailWatcher::FIFO.new(Encoding::ASCII_8BIT, Encoding::ASCII_8BIT)
19
+ text = ("test\n" * 3 + 'test').force_encoding(Encoding::ASCII_8BIT)
20
+ fifo << text
21
+ lines = []
22
+ fifo.read_lines(lines)
23
+ assert_equal Encoding::ASCII_8BIT, lines[0].encoding
24
+ assert_equal ["test\n", "test\n", "test\n"], lines
25
+
26
+ fifo << "2\n"
27
+ fifo.read_lines(lines)
28
+ assert_equal Encoding::ASCII_8BIT, lines[0].encoding
29
+ assert_equal ["test\n", "test\n", "test\n", "test2\n"], lines
30
+ end
31
+
32
+ test 'returns lines which convert encoding' do
33
+ fifo = Fluent::Plugin::TailInput::TailWatcher::FIFO.new(Encoding::ASCII_8BIT, Encoding::UTF_8)
34
+ text = ("test\n" * 3).force_encoding(Encoding::ASCII_8BIT)
35
+ fifo << text
36
+ lines = []
37
+ fifo.read_lines(lines)
38
+ assert_equal Encoding::UTF_8, lines[0].encoding
39
+ assert_equal ["test\n", "test\n", "test\n"], lines
40
+ end
41
+
42
+ test 'reads lines as from_encoding' do
43
+ fifo = Fluent::Plugin::TailInput::TailWatcher::FIFO.new(Encoding::UTF_8, Encoding::ASCII_8BIT)
44
+ text = ("test\n" * 3).force_encoding(Encoding::UTF_8)
45
+ fifo << text
46
+ lines = []
47
+ fifo.read_lines(lines)
48
+ assert_equal Encoding::ASCII_8BIT, lines[0].encoding
49
+ assert_equal ["test\n", "test\n", "test\n"], lines
50
+ end
51
+
52
+ sub_test_case 'when it includes multi byte chars' do
53
+ test 'handles it as ascii_8bit' do
54
+ fifo = Fluent::Plugin::TailInput::TailWatcher::FIFO.new(Encoding::ASCII_8BIT, Encoding::ASCII_8BIT)
55
+ text = ("てすと\n" * 3).force_encoding(Encoding::ASCII_8BIT)
56
+ fifo << text
57
+ lines = []
58
+ fifo.read_lines(lines)
59
+ assert_equal Encoding::ASCII_8BIT, lines[0].encoding
60
+ assert_equal ["てすと\n", "てすと\n", "てすと\n"].map { |e| e.force_encoding(Encoding::ASCII_8BIT) }, lines
61
+ end
62
+
63
+ test 'replaces character with ? when convert error happens' do
64
+ fifo = Fluent::Plugin::TailInput::TailWatcher::FIFO.new(Encoding::UTF_8, Encoding::ASCII_8BIT)
65
+ text = ("てすと\n" * 3).force_encoding(Encoding::UTF_8)
66
+ fifo << text
67
+ lines = []
68
+ fifo.read_lines(lines)
69
+ assert_equal Encoding::ASCII_8BIT, lines[0].encoding
70
+ assert_equal ["???\n", "???\n", "???\n"].map { |e| e.force_encoding(Encoding::ASCII_8BIT) }, lines
71
+ end
72
+ end
73
+
74
+ test 'reutrns nothing when buffer is empty' do
75
+ fifo = Fluent::Plugin::TailInput::TailWatcher::FIFO.new(Encoding::ASCII_8BIT, Encoding::ASCII_8BIT)
76
+ lines = []
77
+ fifo.read_lines(lines)
78
+ assert_equal [], lines
79
+
80
+ text = "test\n" * 3
81
+ fifo << text
82
+ fifo.read_lines(lines)
83
+ assert_equal ["test\n", "test\n", "test\n"], lines
84
+
85
+ lines = []
86
+ fifo.read_lines(lines)
87
+ assert_equal [], lines
88
+ end
89
+ end
90
+
91
+ sub_test_case '#<<' do
92
+ test 'does not make any change about encoding to an argument' do
93
+ fifo = Fluent::Plugin::TailInput::TailWatcher::FIFO.new(Encoding::ASCII_8BIT, Encoding::ASCII_8BIT)
94
+ text = ("test\n" * 3).force_encoding(Encoding::UTF_8)
95
+
96
+ assert_equal Encoding::UTF_8, text.encoding
97
+ fifo << text
98
+ assert_equal Encoding::UTF_8, text.encoding
99
+ end
100
+ end
101
+
102
+ sub_test_case '#bytesize' do
103
+ test 'reutrns buffer size' do
104
+ fifo = Fluent::Plugin::TailInput::TailWatcher::FIFO.new(Encoding::ASCII_8BIT, Encoding::ASCII_8BIT)
105
+ text = "test\n" * 3 + 'test'
106
+ fifo << text
107
+
108
+ assert_equal text.bytesize, fifo.bytesize
109
+ lines = []
110
+ fifo.read_lines(lines)
111
+ assert_equal ["test\n", "test\n", "test\n"], lines
112
+
113
+ assert_equal 'test'.bytesize, fifo.bytesize
114
+ fifo << "2\n"
115
+ fifo.read_lines(lines)
116
+ assert_equal ["test\n", "test\n", "test\n", "test2\n"], lines
117
+
118
+ assert_equal 0, fifo.bytesize
119
+ end
120
+ end
121
+ end
@@ -0,0 +1,132 @@
1
+ require_relative '../../helper'
2
+
3
+ require 'fluent/plugin/in_tail'
4
+ require 'tempfile'
5
+
6
+ class IntailIOHandlerTest < Test::Unit::TestCase
7
+ setup do
8
+ @file = Tempfile.new('intail_io_handler').binmode
9
+ end
10
+
11
+ teardown do
12
+ @file.close rescue nil
13
+ @file.unlink rescue nil
14
+ end
15
+
16
+ test '#on_notify load file content and passed it to receive_lines method' do
17
+ text = "this line is test\ntest line is test\n"
18
+ @file.write(text)
19
+ @file.close
20
+
21
+ watcher = 'watcher'
22
+
23
+ update_pos = 0
24
+
25
+ stub(watcher).pe do
26
+ pe = 'position_file'
27
+ stub(pe).read_pos { 0 }
28
+ stub(pe).update_pos { |val| update_pos = val }
29
+ pe
30
+ end
31
+
32
+ returned_lines = ''
33
+ r = Fluent::Plugin::TailInput::TailWatcher::IOHandler.new(watcher, path: @file.path, read_lines_limit: 100, log: $log, open_on_every_update: false) do |lines, _watcher|
34
+ returned_lines << lines.join
35
+ true
36
+ end
37
+
38
+ r.on_notify
39
+ assert_equal text.bytesize, update_pos
40
+ assert_equal text, returned_lines
41
+
42
+ r.on_notify
43
+
44
+ assert_equal text.bytesize, update_pos
45
+ assert_equal text, returned_lines
46
+ end
47
+
48
+ sub_test_case 'when open_on_every_update is true and read_pos returns always 0' do
49
+ test 'open new IO and change pos to 0 and read it' do
50
+ text = "this line is test\ntest line is test\n"
51
+ @file.write(text)
52
+ @file.close
53
+
54
+ update_pos = 0
55
+
56
+ watcher = 'watcher'
57
+ stub(watcher).pe do
58
+ pe = 'position_file'
59
+ stub(pe).read_pos { 0 }
60
+ stub(pe).update_pos { |val| update_pos = val }
61
+ pe
62
+ end
63
+
64
+ returned_lines = ''
65
+ r = Fluent::Plugin::TailInput::TailWatcher::IOHandler.new(watcher, path: @file.path, read_lines_limit: 100, log: $log, open_on_every_update: true) do |lines, _watcher|
66
+ returned_lines << lines.join
67
+ true
68
+ end
69
+
70
+ r.on_notify
71
+ assert_equal text.bytesize, update_pos
72
+ assert_equal text, returned_lines
73
+
74
+ r.on_notify
75
+ assert_equal text * 2, returned_lines
76
+ end
77
+ end
78
+
79
+ sub_test_case 'when limit is 5' do
80
+ test 'call receive_lines once when short line(less than 8192)' do
81
+ text = "line\n" * 8
82
+ @file.write(text)
83
+ @file.close
84
+
85
+ update_pos = 0
86
+
87
+ watcher = 'watcher'
88
+ stub(watcher).pe do
89
+ pe = 'position_file'
90
+ stub(pe).read_pos { 0 }
91
+ stub(pe).update_pos { |val| update_pos = val }
92
+ pe
93
+ end
94
+
95
+ returned_lines = []
96
+ r = Fluent::Plugin::TailInput::TailWatcher::IOHandler.new(watcher, path: @file.path, read_lines_limit: 5, log: $log, open_on_every_update: false) do |lines, _watcher|
97
+ returned_lines << lines.dup
98
+ true
99
+ end
100
+
101
+ r.on_notify
102
+ assert_equal 8, returned_lines[0].size
103
+ end
104
+
105
+ test 'call receive_lines some times when long line(more than 8192)' do
106
+ t = 'line' * (8192 / 8)
107
+ text = "#{t}\n" * 8
108
+ @file.write(text)
109
+ @file.close
110
+
111
+ update_pos = 0
112
+
113
+ watcher = 'watcher'
114
+ stub(watcher).pe do
115
+ pe = 'position_file'
116
+ stub(pe).read_pos { 0 }
117
+ stub(pe).update_pos { |val| update_pos = val }
118
+ pe
119
+ end
120
+
121
+ returned_lines = []
122
+ r = Fluent::Plugin::TailInput::TailWatcher::IOHandler.new(watcher, path: @file.path, read_lines_limit: 5, log: $log, open_on_every_update: false) do |lines, _watcher|
123
+ returned_lines << lines.dup
124
+ true
125
+ end
126
+
127
+ r.on_notify
128
+ assert_equal 5, returned_lines[0].size
129
+ assert_equal 3, returned_lines[1].size
130
+ end
131
+ end
132
+ end