fluentd 1.2.6 → 1.3.0
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 +4 -4
- data/.travis.yml +4 -4
- data/CHANGELOG.md +27 -0
- data/CONTRIBUTING.md +2 -0
- data/lib/fluent/config/v1_parser.rb +1 -2
- data/lib/fluent/plugin/buffer.rb +1 -1
- data/lib/fluent/plugin/in_forward.rb +1 -1
- data/lib/fluent/plugin/in_http.rb +38 -0
- data/lib/fluent/plugin/in_syslog.rb +19 -6
- data/lib/fluent/plugin/out_forward.rb +1 -1
- data/lib/fluent/plugin/output.rb +61 -23
- data/lib/fluent/plugin_helper/server.rb +16 -0
- data/lib/fluent/version.rb +1 -1
- data/test/plugin/test_buffer.rb +1 -1
- data/test/plugin/test_in_http.rb +23 -0
- data/test/plugin/test_in_syslog.rb +46 -0
- data/test/plugin/test_output.rb +32 -23
- data/test/plugin/test_output_as_buffered.rb +19 -0
- data/test/plugin/test_output_as_buffered_retries.rb +8 -0
- data/test/plugin/test_output_as_buffered_secondary.rb +1 -1
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: c973680de36bad1981b1cc6bd3c23c391a04fc66
|
4
|
+
data.tar.gz: eb6b2c87bd06d19c2d90050355059e6030f47c14
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 390c4766aca79e9e3e235fad17b66e5ab0d979df6d77e91ac7f03b4be9a831b60214bef63a5cf0811548a8020f4954df4f27beda58dedeb5948a0f804b6a1c42
|
7
|
+
data.tar.gz: cef9e1fcf326748d4a72420c5ae4f6b50f3ed8af8178ba0f405ee667a675435121de90099a27170d96ac9592ed421e71655a40b2060a1967f58f62e1c311c047
|
data/.travis.yml
CHANGED
@@ -9,13 +9,13 @@ matrix:
|
|
9
9
|
include:
|
10
10
|
- rvm: 2.1.10
|
11
11
|
os: linux
|
12
|
-
- rvm: 2.2.
|
12
|
+
- rvm: 2.2.10
|
13
13
|
os: linux
|
14
|
-
- rvm: 2.3.
|
14
|
+
- rvm: 2.3.8
|
15
15
|
os: linux
|
16
|
-
- rvm: 2.4.
|
16
|
+
- rvm: 2.4.5
|
17
17
|
os: linux
|
18
|
-
- rvm: 2.5.
|
18
|
+
- rvm: 2.5.3
|
19
19
|
os: linux
|
20
20
|
- rvm: ruby-head
|
21
21
|
os: linux
|
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,30 @@
|
|
1
|
+
# v1.3
|
2
|
+
|
3
|
+
## Release v1.3.0 - 2018/11/10
|
4
|
+
|
5
|
+
### New features
|
6
|
+
|
7
|
+
* output: Change thread execution control
|
8
|
+
https://github.com/fluent/fluentd/pull/2170
|
9
|
+
* in_syslog: Support octet counting frame
|
10
|
+
https://github.com/fluent/fluentd/pull/2147
|
11
|
+
* Use `flush_thread_count` value for `queued_chunks_limit_size` when `queued_chunks_limit_size` is not specified
|
12
|
+
https://github.com/fluent/fluentd/pull/2173
|
13
|
+
|
14
|
+
### Enhancements
|
15
|
+
|
16
|
+
* output: Show backtrace for unrecoverable errors
|
17
|
+
https://github.com/fluent/fluentd/pull/2149
|
18
|
+
* in_http: Implement support for CORS preflight requests
|
19
|
+
https://github.com/fluent/fluentd/pull/2144
|
20
|
+
|
21
|
+
### Bug fixes
|
22
|
+
|
23
|
+
* server: Fix deadlock between on_writable and close in sockets
|
24
|
+
https://github.com/fluent/fluentd/pull/2165
|
25
|
+
* output: show correct error when wrong plugin is specified for secondary
|
26
|
+
https://github.com/fluent/fluentd/pull/2169
|
27
|
+
|
1
28
|
# v1.2
|
2
29
|
|
3
30
|
## Release v1.2.6 - 2018/10/03
|
data/CONTRIBUTING.md
CHANGED
@@ -29,6 +29,8 @@ submitting an issue to Fluentd. Even better you can submit a Pull Request with a
|
|
29
29
|
If you find a bug of 3rd party plugins, please submit an issue to each plugin repository.
|
30
30
|
And use [omnibus-td-agent](https://github.com/treasure-data/omnibus-td-agent) repository for td-agent releated issues.
|
31
31
|
|
32
|
+
Note: Before report the issue, check latest version first. Sometimes users report fixed bug with older version.
|
33
|
+
|
32
34
|
## Patch Guidelines
|
33
35
|
|
34
36
|
Here are some things that would increase a chance that your patch is accepted:
|
@@ -174,9 +174,8 @@ module Fluent
|
|
174
174
|
ss = StringScanner.new(data)
|
175
175
|
V1Parser.new(ss, basepath, fname, @eval_context).parse_element(true, nil, attrs, elems)
|
176
176
|
end
|
177
|
-
|
178
177
|
rescue SystemCallError => e
|
179
|
-
cpe = ConfigParseError.new("include error #{uri}")
|
178
|
+
cpe = ConfigParseError.new("include error #{uri} - #{e}")
|
180
179
|
cpe.set_backtrace(e.backtrace)
|
181
180
|
raise cpe
|
182
181
|
end
|
data/lib/fluent/plugin/buffer.rb
CHANGED
@@ -199,7 +199,7 @@ module Fluent::Plugin
|
|
199
199
|
when :pingpong
|
200
200
|
success, reason_or_salt, shared_key = check_ping(msg, conn.remote_addr, user_auth_salt, nonce)
|
201
201
|
unless success
|
202
|
-
conn.on(:write_complete) { |c| c.
|
202
|
+
conn.on(:write_complete) { |c| c.close_after_write_complete }
|
203
203
|
send_data.call(serializer, generate_pong(false, reason_or_salt, nonce, shared_key))
|
204
204
|
next
|
205
205
|
end
|
@@ -342,6 +342,10 @@ module Fluent::Plugin
|
|
342
342
|
# For multiple X-Forwarded-For headers. Use first header value.
|
343
343
|
v = v.first if v.is_a?(Array)
|
344
344
|
@remote_addr = v.split(",").first
|
345
|
+
when /Access-Control-Request-Method/i
|
346
|
+
@access_control_request_method = v
|
347
|
+
when /Access-Control-Request-Headers/i
|
348
|
+
@access_control_request_headers = v
|
345
349
|
end
|
346
350
|
}
|
347
351
|
if expect
|
@@ -367,9 +371,43 @@ module Fluent::Plugin
|
|
367
371
|
@body << chunk
|
368
372
|
end
|
369
373
|
|
374
|
+
# Web browsers can send an OPTIONS request before performing POST
|
375
|
+
# to check if cross-origin requests are supported.
|
376
|
+
def handle_options_request
|
377
|
+
# Is CORS enabled in the first place?
|
378
|
+
if @cors_allow_origins.nil?
|
379
|
+
return send_response_and_close("403 Forbidden", {}, "")
|
380
|
+
end
|
381
|
+
|
382
|
+
# in_http does not support HTTP methods except POST
|
383
|
+
if @access_control_request_method != 'POST'
|
384
|
+
return send_response_and_close("403 Forbidden", {}, "")
|
385
|
+
end
|
386
|
+
|
387
|
+
header = {
|
388
|
+
"Access-Control-Allow-Methods" => "POST",
|
389
|
+
"Access-Control-Allow-Headers" => @access_control_request_headers || "",
|
390
|
+
}
|
391
|
+
|
392
|
+
# Check the origin and send back a CORS response
|
393
|
+
if @cors_allow_origins.include?('*')
|
394
|
+
header["Access-Control-Allow-Origin"] = "*"
|
395
|
+
send_response_and_close("200 OK", header, "")
|
396
|
+
elsif @cors_allow_origins.include?(@origin)
|
397
|
+
header["Access-Control-Allow-Origin"] = @origin
|
398
|
+
send_response_and_close("200 OK", header, "")
|
399
|
+
else
|
400
|
+
send_response_and_close("403 Forbidden", {}, "")
|
401
|
+
end
|
402
|
+
end
|
403
|
+
|
370
404
|
def on_message_complete
|
371
405
|
return if closing?
|
372
406
|
|
407
|
+
if @parser.http_method == 'OPTIONS'
|
408
|
+
return handle_options_request()
|
409
|
+
end
|
410
|
+
|
373
411
|
# CORS check
|
374
412
|
# ==========
|
375
413
|
# For every incoming request, we check if we have some CORS
|
@@ -75,6 +75,8 @@ module Fluent::Plugin
|
|
75
75
|
config_param :tag, :string
|
76
76
|
desc 'The transport protocol used to receive logs.(udp, tcp)'
|
77
77
|
config_param :protocol_type, :enum, list: [:tcp, :udp], default: :udp
|
78
|
+
desc 'The message frame type.(traditional, octet_count)'
|
79
|
+
config_param :frame_type, :enum, list: [:traditional, :octet_count], default: :traditional
|
78
80
|
|
79
81
|
desc 'If true, add source host to event record.'
|
80
82
|
config_param :include_source_host, :bool, default: false, deprecated: 'use "source_hostname_key" or "source_address_key" instead.'
|
@@ -152,18 +154,29 @@ module Fluent::Plugin
|
|
152
154
|
end
|
153
155
|
|
154
156
|
def start_tcp_server
|
155
|
-
|
156
|
-
|
157
|
+
octet_count_frame = @frame_type == :octet_count
|
158
|
+
|
159
|
+
# syslog family adds "\n" to each message when transport is TCP and traditional frame
|
160
|
+
delimiter = octet_count_frame ? " " : "\n"
|
157
161
|
delimiter_size = delimiter.size
|
158
162
|
server_create_connection(:in_syslog_tcp_server, @port, bind: @bind, resolve_name: @resolve_hostname) do |conn|
|
159
163
|
conn.data do |data|
|
160
164
|
buffer = conn.buffer
|
161
165
|
buffer << data
|
162
166
|
pos = 0
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
+
if octet_count_frame
|
168
|
+
while idx = buffer.index(delimiter, pos)
|
169
|
+
num = Integer(buffer[pos..idx])
|
170
|
+
pos = idx + num
|
171
|
+
msg = buffer[idx + 1...pos]
|
172
|
+
message_handler(msg, conn)
|
173
|
+
end
|
174
|
+
else
|
175
|
+
while idx = buffer.index(delimiter, pos)
|
176
|
+
msg = buffer[pos...idx]
|
177
|
+
pos = idx + delimiter_size
|
178
|
+
message_handler(msg, conn)
|
179
|
+
end
|
167
180
|
end
|
168
181
|
buffer.slice!(0, pos) if pos > 0
|
169
182
|
end
|
@@ -828,7 +828,7 @@ module Fluent::Plugin
|
|
828
828
|
when :pingpong
|
829
829
|
succeeded, reason = check_pong(ri, data)
|
830
830
|
unless succeeded
|
831
|
-
@log.warn "connection refused to #{@name}: #{reason}"
|
831
|
+
@log.warn "connection refused to #{@name || @host}: #{reason}"
|
832
832
|
disable! # shutdown
|
833
833
|
return
|
834
834
|
end
|
data/lib/fluent/plugin/output.rb
CHANGED
@@ -154,7 +154,7 @@ module Fluent
|
|
154
154
|
end
|
155
155
|
|
156
156
|
# Internal states
|
157
|
-
FlushThreadState = Struct.new(:thread, :next_clock)
|
157
|
+
FlushThreadState = Struct.new(:thread, :next_clock, :mutex, :cond_var)
|
158
158
|
DequeuedChunkInfo = Struct.new(:chunk_id, :time, :timeout) do
|
159
159
|
def expired?
|
160
160
|
time + timeout < Time.now
|
@@ -354,6 +354,10 @@ module Fluent
|
|
354
354
|
log.warn "'flush_interval' is ignored because default 'flush_mode' is not 'interval': '#{@flush_mode}'"
|
355
355
|
end
|
356
356
|
end
|
357
|
+
|
358
|
+
if @buffer.queued_chunks_limit_size.nil?
|
359
|
+
@buffer.queued_chunks_limit_size = @buffer_config.flush_thread_count
|
360
|
+
end
|
357
361
|
end
|
358
362
|
|
359
363
|
if @secondary_config
|
@@ -368,6 +372,9 @@ module Fluent
|
|
368
372
|
end
|
369
373
|
secondary_conf = conf.elements(name: 'secondary').first
|
370
374
|
@secondary = Plugin.new_output(secondary_type)
|
375
|
+
unless @secondary.respond_to?(:acts_as_secondary)
|
376
|
+
raise Fluent::ConfigError, "Failed to setup secondary plugin in '#{conf['@type']}'. '#{secondary_type}' plugin in not allowed due to non buffered output"
|
377
|
+
end
|
371
378
|
@secondary.acts_as_secondary(self)
|
372
379
|
@secondary.configure(secondary_conf)
|
373
380
|
if (self.class != @secondary.class) && (@custom_format || @secondary.implement?(:custom_format))
|
@@ -440,7 +447,7 @@ module Fluent
|
|
440
447
|
|
441
448
|
@buffer_config.flush_thread_count.times do |i|
|
442
449
|
thread_title = "flush_thread_#{i}".to_sym
|
443
|
-
thread_state = FlushThreadState.new(nil, nil)
|
450
|
+
thread_state = FlushThreadState.new(nil, nil, Mutex.new, ConditionVariable.new)
|
444
451
|
thread = thread_create(thread_title) do
|
445
452
|
flush_thread_run(thread_state)
|
446
453
|
end
|
@@ -506,9 +513,14 @@ module Fluent
|
|
506
513
|
@output_flush_threads_running = false
|
507
514
|
if @output_flush_threads && !@output_flush_threads.empty?
|
508
515
|
@output_flush_threads.each do |state|
|
509
|
-
|
510
|
-
|
511
|
-
|
516
|
+
# to wakeup thread and make it to stop by itself
|
517
|
+
state.mutex.synchronize {
|
518
|
+
if state.thread && state.thread.status
|
519
|
+
state.next_clock = 0
|
520
|
+
state.cond_var.signal
|
521
|
+
end
|
522
|
+
}
|
523
|
+
Thread.pass
|
512
524
|
state.thread.join
|
513
525
|
end
|
514
526
|
end
|
@@ -1119,24 +1131,29 @@ module Fluent
|
|
1119
1131
|
if @secondary
|
1120
1132
|
if using_secondary
|
1121
1133
|
log.warn "got unrecoverable error in secondary.", error: e
|
1134
|
+
log.warn_backtrace
|
1122
1135
|
backup_chunk(chunk, using_secondary, output.delayed_commit)
|
1123
1136
|
else
|
1124
1137
|
if (self.class == @secondary.class)
|
1125
1138
|
log.warn "got unrecoverable error in primary and secondary type is same as primary. Skip secondary", error: e
|
1139
|
+
log.warn_backtrace
|
1126
1140
|
backup_chunk(chunk, using_secondary, output.delayed_commit)
|
1127
1141
|
else
|
1128
1142
|
# Call secondary output directly without retry update.
|
1129
1143
|
# In this case, delayed commit causes inconsistent state in dequeued chunks so async output in secondary is not allowed for now.
|
1130
1144
|
if @secondary.delayed_commit
|
1131
1145
|
log.warn "got unrecoverable error in primary and secondary is async output. Skip secondary for backup", error: e
|
1146
|
+
log.warn_backtrace
|
1132
1147
|
backup_chunk(chunk, using_secondary, output.delayed_commit)
|
1133
1148
|
else
|
1134
1149
|
log.warn "got unrecoverable error in primary. Skip retry and flush chunk to secondary", error: e
|
1150
|
+
log.warn_backtrace
|
1135
1151
|
begin
|
1136
1152
|
@secondary.write(chunk)
|
1137
1153
|
commit_write(chunk_id, delayed: output.delayed_commit, secondary: true)
|
1138
1154
|
rescue => e
|
1139
1155
|
log.warn "got an error in secondary for unrecoverable error", error: e
|
1156
|
+
log.warn_backtrace
|
1140
1157
|
backup_chunk(chunk, using_secondary, output.delayed_commit)
|
1141
1158
|
end
|
1142
1159
|
end
|
@@ -1144,6 +1161,7 @@ module Fluent
|
|
1144
1161
|
end
|
1145
1162
|
else
|
1146
1163
|
log.warn "got unrecoverable error in primary and no secondary", error: e
|
1164
|
+
log.warn_backtrace
|
1147
1165
|
backup_chunk(chunk, using_secondary, output.delayed_commit)
|
1148
1166
|
end
|
1149
1167
|
rescue => e
|
@@ -1254,12 +1272,15 @@ module Fluent
|
|
1254
1272
|
# Without locks: it is rough but enough to select "next" writer selection
|
1255
1273
|
@output_flush_thread_current_position = (@output_flush_thread_current_position + 1) % @buffer_config.flush_thread_count
|
1256
1274
|
state = @output_flush_threads[@output_flush_thread_current_position]
|
1257
|
-
state.
|
1258
|
-
|
1259
|
-
|
1260
|
-
|
1261
|
-
|
1262
|
-
|
1275
|
+
state.mutex.synchronize {
|
1276
|
+
if state.thread && state.thread.status # "run"/"sleep"/"aborting" or false(successfully stop) or nil(killed by exception)
|
1277
|
+
state.next_clock = 0
|
1278
|
+
state.cond_var.signal
|
1279
|
+
else
|
1280
|
+
log.warn "thread is already dead"
|
1281
|
+
end
|
1282
|
+
}
|
1283
|
+
Thread.pass
|
1263
1284
|
end
|
1264
1285
|
|
1265
1286
|
def force_flush
|
@@ -1296,8 +1317,13 @@ module Fluent
|
|
1296
1317
|
# only for tests of output plugin
|
1297
1318
|
def flush_thread_wakeup
|
1298
1319
|
@output_flush_threads.each do |state|
|
1299
|
-
state.
|
1300
|
-
|
1320
|
+
state.mutex.synchronize {
|
1321
|
+
if state.thread && state.thread.status
|
1322
|
+
state.next_clock = 0
|
1323
|
+
state.cond_var.signal
|
1324
|
+
end
|
1325
|
+
}
|
1326
|
+
Thread.pass
|
1301
1327
|
end
|
1302
1328
|
end
|
1303
1329
|
|
@@ -1376,6 +1402,7 @@ module Fluent
|
|
1376
1402
|
end
|
1377
1403
|
log.debug "flush_thread actually running"
|
1378
1404
|
|
1405
|
+
state.mutex.lock
|
1379
1406
|
begin
|
1380
1407
|
# This thread don't use `thread_current_running?` because this thread should run in `before_shutdown` phase
|
1381
1408
|
while @output_flush_threads_running
|
@@ -1391,23 +1418,32 @@ module Fluent
|
|
1391
1418
|
elsif next_retry_time && next_retry_time > Time.now
|
1392
1419
|
interval = next_retry_time.to_f - Time.now.to_f
|
1393
1420
|
else
|
1394
|
-
|
1395
|
-
|
1396
|
-
|
1397
|
-
|
1398
|
-
|
1399
|
-
|
1400
|
-
|
1401
|
-
|
1421
|
+
state.mutex.unlock
|
1422
|
+
begin
|
1423
|
+
try_flush
|
1424
|
+
# next_flush_time uses flush_thread_interval or flush_thread_burst_interval (or retrying)
|
1425
|
+
interval = next_flush_time.to_f - Time.now.to_f
|
1426
|
+
# TODO: if secondary && delayed-commit, next_flush_time will be much longer than expected
|
1427
|
+
# because @retry still exists (#commit_write is not called yet in #try_flush)
|
1428
|
+
# @retry should be cleared if delayed commit is enabled? Or any other solution?
|
1429
|
+
state.next_clock = Fluent::Clock.now + interval
|
1430
|
+
ensure
|
1431
|
+
state.mutex.lock
|
1432
|
+
end
|
1402
1433
|
end
|
1403
1434
|
|
1404
1435
|
if @dequeued_chunks_mutex.synchronize{ !@dequeued_chunks.empty? && @dequeued_chunks.first.expired? }
|
1405
1436
|
unless @output_flush_interrupted
|
1406
|
-
|
1437
|
+
state.mutex.unlock
|
1438
|
+
begin
|
1439
|
+
try_rollback_write
|
1440
|
+
ensure
|
1441
|
+
state.mutex.lock
|
1442
|
+
end
|
1407
1443
|
end
|
1408
1444
|
end
|
1409
1445
|
|
1410
|
-
|
1446
|
+
state.cond_var.wait(state.mutex, interval) if interval > 0
|
1411
1447
|
end
|
1412
1448
|
rescue => e
|
1413
1449
|
# normal errors are rescued by output plugins in #try_flush
|
@@ -1415,6 +1451,8 @@ module Fluent
|
|
1415
1451
|
log.error "error on output thread", error: e
|
1416
1452
|
log.error_backtrace
|
1417
1453
|
raise
|
1454
|
+
ensure
|
1455
|
+
state.mutex.unlock
|
1418
1456
|
end
|
1419
1457
|
end
|
1420
1458
|
end
|
@@ -408,6 +408,10 @@ module Fluent
|
|
408
408
|
raise "not implemented here"
|
409
409
|
end
|
410
410
|
|
411
|
+
def close_after_write_complete
|
412
|
+
@sock.close_after_write_complete = true
|
413
|
+
end
|
414
|
+
|
411
415
|
def close
|
412
416
|
@sock.close if @close_socket
|
413
417
|
end
|
@@ -492,6 +496,8 @@ module Fluent
|
|
492
496
|
|
493
497
|
module EventHandler
|
494
498
|
class UDPServer < Coolio::IO
|
499
|
+
attr_writer :close_after_write_complete # dummy for consistent method call in callbacks
|
500
|
+
|
495
501
|
def initialize(sock, max_bytes, flags, close_socket, log, under_plugin_development, &callback)
|
496
502
|
raise ArgumentError, "socket must be a UDPSocket: sock = #{sock}" unless sock.is_a?(UDPSocket)
|
497
503
|
|
@@ -543,6 +549,7 @@ module Fluent
|
|
543
549
|
|
544
550
|
class TCPServer < Coolio::TCPSocket
|
545
551
|
attr_reader :closing
|
552
|
+
attr_writer :close_after_write_complete
|
546
553
|
|
547
554
|
def initialize(sock, socket_option_setter, close_callback, log, under_plugin_development, connect_callback)
|
548
555
|
raise ArgumentError, "socket must be a TCPSocket: sock=#{sock}" unless sock.is_a?(TCPSocket)
|
@@ -560,6 +567,7 @@ module Fluent
|
|
560
567
|
@close_callback = close_callback
|
561
568
|
|
562
569
|
@callback_connection = nil
|
570
|
+
@close_after_write_complete = false
|
563
571
|
@closing = false
|
564
572
|
|
565
573
|
@mutex = Mutex.new # to serialize #write and #close
|
@@ -587,6 +595,11 @@ module Fluent
|
|
587
595
|
end
|
588
596
|
end
|
589
597
|
|
598
|
+
def on_writable
|
599
|
+
super
|
600
|
+
close if @close_after_write_complete
|
601
|
+
end
|
602
|
+
|
590
603
|
def on_connect
|
591
604
|
@callback_connection = TCPCallbackSocket.new(self)
|
592
605
|
@connect_callback.call(@callback_connection)
|
@@ -625,6 +638,7 @@ module Fluent
|
|
625
638
|
|
626
639
|
class TLSServer < Coolio::Socket
|
627
640
|
attr_reader :closing
|
641
|
+
attr_writer :close_after_write_complete
|
628
642
|
|
629
643
|
# It can't use Coolio::TCPSocket, because Coolio::TCPSocket checks that underlying socket (1st argument of super) is TCPSocket.
|
630
644
|
def initialize(sock, context, socket_option_setter, close_callback, log, under_plugin_development, connect_callback)
|
@@ -645,6 +659,7 @@ module Fluent
|
|
645
659
|
@close_callback = close_callback
|
646
660
|
|
647
661
|
@callback_connection = nil
|
662
|
+
@close_after_write_complete = false
|
648
663
|
@closing = false
|
649
664
|
|
650
665
|
@mutex = Mutex.new # to serialize #write and #close
|
@@ -738,6 +753,7 @@ module Fluent
|
|
738
753
|
@_handler_write_buffer.slice!(0, written_bytes)
|
739
754
|
super
|
740
755
|
end
|
756
|
+
close if @close_after_write_complete
|
741
757
|
rescue IO::WaitWritable, IO::WaitReadable
|
742
758
|
return
|
743
759
|
rescue Errno::EINTR
|
data/lib/fluent/version.rb
CHANGED
data/test/plugin/test_buffer.rb
CHANGED
@@ -170,7 +170,7 @@ class BufferTest < Test::Unit::TestCase
|
|
170
170
|
|
171
171
|
sub_test_case 'with default configuration and dummy implementation' do
|
172
172
|
setup do
|
173
|
-
@p = create_buffer({})
|
173
|
+
@p = create_buffer({'queued_chunks_limit_size' => 100})
|
174
174
|
@dm0 = create_metadata(Time.parse('2016-04-11 16:00:00 +0000').to_i, nil, nil)
|
175
175
|
@dm1 = create_metadata(Time.parse('2016-04-11 16:10:00 +0000').to_i, nil, nil)
|
176
176
|
@dm2 = create_metadata(Time.parse('2016-04-11 16:20:00 +0000').to_i, nil, nil)
|
data/test/plugin/test_in_http.rb
CHANGED
@@ -624,6 +624,23 @@ class HttpInputTest < Test::Unit::TestCase
|
|
624
624
|
end
|
625
625
|
end
|
626
626
|
|
627
|
+
def test_cors_preflight
|
628
|
+
d = create_driver(CONFIG + 'cors_allow_origins ["*"]')
|
629
|
+
|
630
|
+
d.run do
|
631
|
+
header = {
|
632
|
+
"Origin" => "http://foo.com",
|
633
|
+
"Access-Control-Request-Method" => "POST",
|
634
|
+
"Access-Control-Request-Headers" => "Content-Type",
|
635
|
+
}
|
636
|
+
res = options("/cors.test", {}, header)
|
637
|
+
|
638
|
+
assert_equal "200", res.code
|
639
|
+
assert_equal "*", res["Access-Control-Allow-Origin"]
|
640
|
+
assert_equal "POST", res["Access-Control-Allow-Methods"]
|
641
|
+
end
|
642
|
+
end
|
643
|
+
|
627
644
|
def test_content_encoding_gzip
|
628
645
|
d = create_driver
|
629
646
|
|
@@ -744,6 +761,12 @@ class HttpInputTest < Test::Unit::TestCase
|
|
744
761
|
assert_equal $test_in_http_connection_object_ids[0], $test_in_http_connection_object_ids[1]
|
745
762
|
end
|
746
763
|
|
764
|
+
def options(path, params, header = {})
|
765
|
+
http = Net::HTTP.new("127.0.0.1", PORT)
|
766
|
+
req = Net::HTTP::Options.new(path, header)
|
767
|
+
http.request(req)
|
768
|
+
end
|
769
|
+
|
747
770
|
def post(path, params, header = {}, &block)
|
748
771
|
http = Net::HTTP.new("127.0.0.1", PORT)
|
749
772
|
req = Net::HTTP::Post.new(path, header)
|
@@ -286,4 +286,50 @@ EOS
|
|
286
286
|
assert_equal(options[:facility], events[i][2]['facility']) if options[:facility]
|
287
287
|
}
|
288
288
|
end
|
289
|
+
|
290
|
+
sub_test_case 'octet counting frame' do
|
291
|
+
def test_msg_size_with_tcp
|
292
|
+
d = create_driver([CONFIG, 'protocol_type tcp', 'frame_type octet_count'].join("\n"))
|
293
|
+
tests = create_test_case
|
294
|
+
|
295
|
+
d.run(expect_emits: 2) do
|
296
|
+
tests.each {|test|
|
297
|
+
TCPSocket.open('127.0.0.1', PORT) do |s|
|
298
|
+
s.send(test['msg'], 0)
|
299
|
+
end
|
300
|
+
}
|
301
|
+
end
|
302
|
+
|
303
|
+
assert(d.events.size > 0)
|
304
|
+
compare_test_result(d.events, tests)
|
305
|
+
end
|
306
|
+
|
307
|
+
def test_msg_size_with_same_tcp_connection
|
308
|
+
d = create_driver([CONFIG, 'protocol_type tcp', 'frame_type octet_count'].join("\n"))
|
309
|
+
tests = create_test_case
|
310
|
+
|
311
|
+
d.run(expect_emits: 2) do
|
312
|
+
TCPSocket.open('127.0.0.1', PORT) do |s|
|
313
|
+
tests.each {|test|
|
314
|
+
s.send(test['msg'], 0)
|
315
|
+
}
|
316
|
+
end
|
317
|
+
end
|
318
|
+
|
319
|
+
assert(d.events.size > 0)
|
320
|
+
compare_test_result(d.events, tests)
|
321
|
+
end
|
322
|
+
|
323
|
+
def create_test_case(large_message: false)
|
324
|
+
msgs = [
|
325
|
+
{'msg' => '<6>Sep 10 00:00:00 localhost logger: ' + 'x' * 100, 'expected' => 'x' * 100},
|
326
|
+
{'msg' => '<6>Sep 10 00:00:00 localhost logger: ' + 'x' * 1024, 'expected' => 'x' * 1024},
|
327
|
+
]
|
328
|
+
msgs.each { |msg|
|
329
|
+
m = msg['msg']
|
330
|
+
msg['msg'] = "#{m.size + 1} #{m}"
|
331
|
+
}
|
332
|
+
msgs
|
333
|
+
end
|
334
|
+
end
|
289
335
|
end
|
data/test/plugin/test_output.rb
CHANGED
@@ -828,34 +828,43 @@ class OutputTest < Test::Unit::TestCase
|
|
828
828
|
assert_equal :interval, @i.instance_variable_get(:@flush_mode)
|
829
829
|
end
|
830
830
|
|
831
|
-
|
832
|
-
|
833
|
-
|
834
|
-
|
831
|
+
sub_test_case 'configure secondary' do
|
832
|
+
test "Warn if primary type is different from secondary type and either primary or secondary has custom_format" do
|
833
|
+
o = create_output(:buffered)
|
834
|
+
mock(o.log).warn("secondary type should be same with primary one",
|
835
|
+
{ primary: o.class.to_s, secondary: "Fluent::Plugin::TestOutput" })
|
836
|
+
|
837
|
+
o.configure(config_element('ROOT','',{},[config_element('secondary','',{'@type'=>'test', 'name' => "cool"})]))
|
838
|
+
assert_not_nil o.instance_variable_get(:@secondary)
|
839
|
+
end
|
835
840
|
|
836
|
-
|
837
|
-
|
838
|
-
|
841
|
+
test "don't warn if primary type is the same as secondary type" do
|
842
|
+
o = Fluent::Plugin::TestOutput.new
|
843
|
+
mock(o.log).warn("secondary type should be same with primary one",
|
844
|
+
{ primary: o.class.to_s, secondary: "Fluent::Plugin::TestOutput" }).never
|
839
845
|
|
840
|
-
|
841
|
-
|
842
|
-
|
843
|
-
|
846
|
+
o.configure(config_element('ROOT','',{'name' => "cool2"},
|
847
|
+
[config_element('secondary','',{'@type'=>'test', 'name' => "cool"}),
|
848
|
+
config_element('buffer','',{'@type'=>'memory'})]
|
849
|
+
))
|
850
|
+
assert_not_nil o.instance_variable_get(:@secondary)
|
851
|
+
end
|
844
852
|
|
845
|
-
|
846
|
-
|
847
|
-
|
848
|
-
|
849
|
-
assert_not_nil o.instance_variable_get(:@secondary)
|
850
|
-
end
|
853
|
+
test "don't warn if primary type is different from secondary type and both don't have custom_format" do
|
854
|
+
o = create_output(:standard)
|
855
|
+
mock(o.log).warn("secondary type should be same with primary one",
|
856
|
+
{ primary: o.class.to_s, secondary: "Fluent::Plugin::TestOutput" }).never
|
851
857
|
|
852
|
-
|
853
|
-
|
854
|
-
|
855
|
-
{ primary: o.class.to_s, secondary: "Fluent::Plugin::TestOutput" }).never
|
858
|
+
o.configure(config_element('ROOT','',{},[config_element('secondary','',{'@type'=>'test', 'name' => "cool"})]))
|
859
|
+
assert_not_nil o.instance_variable_get(:@secondary)
|
860
|
+
end
|
856
861
|
|
857
|
-
|
858
|
-
|
862
|
+
test "raise configuration error if secondary type specifies non buffered output" do
|
863
|
+
o = create_output(:standard)
|
864
|
+
assert_raise Fluent::ConfigError do
|
865
|
+
o.configure(config_element('ROOT','',{},[config_element('secondary','',{'@type'=>'copy'})]))
|
866
|
+
end
|
867
|
+
end
|
859
868
|
end
|
860
869
|
end
|
861
870
|
|
@@ -219,6 +219,24 @@ class BufferedOutputTest < Test::Unit::TestCase
|
|
219
219
|
Timecop.return
|
220
220
|
end
|
221
221
|
|
222
|
+
test 'queued_chunks_limit_size is same as flush_thread_count by default' do
|
223
|
+
hash = {'flush_thread_count' => 4}
|
224
|
+
i = create_output
|
225
|
+
i.register(:prefer_buffered_processing) { true }
|
226
|
+
i.configure(config_element('ROOT', '', {}, [config_element('buffer','tag',hash)]))
|
227
|
+
|
228
|
+
assert_equal 4, i.buffer.queued_chunks_limit_size
|
229
|
+
end
|
230
|
+
|
231
|
+
test 'prefer queued_chunks_limit_size parameter than flush_thread_count' do
|
232
|
+
hash = {'flush_thread_count' => 4, 'queued_chunks_limit_size' => 2}
|
233
|
+
i = create_output
|
234
|
+
i.register(:prefer_buffered_processing) { true }
|
235
|
+
i.configure(config_element('ROOT', '', {}, [config_element('buffer','tag',hash)]))
|
236
|
+
|
237
|
+
assert_equal 2, i.buffer.queued_chunks_limit_size
|
238
|
+
end
|
239
|
+
|
222
240
|
sub_test_case 'chunk feature in #write for output plugins' do
|
223
241
|
setup do
|
224
242
|
@stored_global_logger = $log
|
@@ -1064,6 +1082,7 @@ class BufferedOutputTest < Test::Unit::TestCase
|
|
1064
1082
|
'flush_thread_count' => 1,
|
1065
1083
|
'flush_thread_burst_interval' => 0.1,
|
1066
1084
|
'chunk_limit_size' => 1024,
|
1085
|
+
'queued_chunks_limit_size' => 100
|
1067
1086
|
}
|
1068
1087
|
@i = create_output(:buffered)
|
1069
1088
|
@i.configure(config_element('ROOT','',{},[config_element('buffer',chunk_key,hash)]))
|
@@ -123,6 +123,7 @@ class BufferedOutputRetryTest < Test::Unit::TestCase
|
|
123
123
|
'flush_interval' => 1,
|
124
124
|
'flush_thread_burst_interval' => 0.1,
|
125
125
|
'retry_randomize' => false,
|
126
|
+
'queued_chunks_limit_size' => 100
|
126
127
|
}
|
127
128
|
@i.configure(config_element('ROOT','',{},[config_element('buffer',chunk_key,hash)]))
|
128
129
|
@i.register(:prefer_buffered_processing){ true }
|
@@ -252,6 +253,7 @@ class BufferedOutputRetryTest < Test::Unit::TestCase
|
|
252
253
|
'flush_thread_burst_interval' => 0.1,
|
253
254
|
'retry_randomize' => false,
|
254
255
|
'retry_timeout' => 3600,
|
256
|
+
'queued_chunks_limit_size' => 100
|
255
257
|
}
|
256
258
|
@i.configure(config_element('ROOT','',{},[config_element('buffer',chunk_key,hash)]))
|
257
259
|
@i.register(:prefer_buffered_processing){ true }
|
@@ -342,6 +344,7 @@ class BufferedOutputRetryTest < Test::Unit::TestCase
|
|
342
344
|
'flush_thread_burst_interval' => 0.1,
|
343
345
|
'retry_randomize' => false,
|
344
346
|
'retry_max_times' => 10,
|
347
|
+
'queued_chunks_limit_size' => 100
|
345
348
|
}
|
346
349
|
@i.configure(config_element('ROOT','',{},[config_element('buffer',chunk_key,hash)]))
|
347
350
|
@i.register(:prefer_buffered_processing){ true }
|
@@ -485,6 +488,7 @@ class BufferedOutputRetryTest < Test::Unit::TestCase
|
|
485
488
|
'retry_type' => :periodic,
|
486
489
|
'retry_wait' => 3,
|
487
490
|
'retry_randomize' => false,
|
491
|
+
'queued_chunks_limit_size' => 100
|
488
492
|
}
|
489
493
|
@i.configure(config_element('ROOT','',{},[config_element('buffer',chunk_key,hash)]))
|
490
494
|
@i.register(:prefer_buffered_processing){ true }
|
@@ -531,6 +535,7 @@ class BufferedOutputRetryTest < Test::Unit::TestCase
|
|
531
535
|
'retry_wait' => 30,
|
532
536
|
'retry_randomize' => false,
|
533
537
|
'retry_timeout' => 120,
|
538
|
+
'queued_chunks_limit_size' => 100
|
534
539
|
}
|
535
540
|
@i.configure(config_element('ROOT','',{},[config_element('buffer',chunk_key,hash)]))
|
536
541
|
@i.register(:prefer_buffered_processing){ true }
|
@@ -626,6 +631,7 @@ class BufferedOutputRetryTest < Test::Unit::TestCase
|
|
626
631
|
'retry_wait' => 3,
|
627
632
|
'retry_randomize' => false,
|
628
633
|
'retry_max_times' => 10,
|
634
|
+
'queued_chunks_limit_size' => 100
|
629
635
|
}
|
630
636
|
@i.configure(config_element('ROOT','',{},[config_element('buffer',chunk_key,hash)]))
|
631
637
|
@i.register(:prefer_buffered_processing){ true }
|
@@ -724,6 +730,7 @@ class BufferedOutputRetryTest < Test::Unit::TestCase
|
|
724
730
|
'retry_randomize' => false,
|
725
731
|
'retry_timeout' => 3600,
|
726
732
|
'retry_max_times' => 10,
|
733
|
+
'queued_chunks_limit_size' => 100
|
727
734
|
}
|
728
735
|
@i.configure(config_element('ROOT','',{},[config_element('buffer',chunk_key,hash)]))
|
729
736
|
@i.register(:prefer_buffered_processing){ true }
|
@@ -795,6 +802,7 @@ class BufferedOutputRetryTest < Test::Unit::TestCase
|
|
795
802
|
'retry_wait' => 30,
|
796
803
|
'retry_timeout' => 360,
|
797
804
|
'retry_max_times' => 10,
|
805
|
+
'queued_chunks_limit_size' => 100
|
798
806
|
}
|
799
807
|
@i.configure(config_element('ROOT','',{},[config_element('buffer',chunk_key,hash)]))
|
800
808
|
@i.register(:prefer_buffered_processing){ true }
|
@@ -544,7 +544,7 @@ class BufferedOutputSecondaryTest < Test::Unit::TestCase
|
|
544
544
|
test 'secondary plugin can do delayed commit even if primary does not do it, and non-committed chunks will be rollbacked by primary' do
|
545
545
|
written = []
|
546
546
|
chunks = []
|
547
|
-
priconf = config_element('buffer','tag',{'flush_interval' => 1, 'retry_type' => :periodic, 'retry_wait' => 3, 'retry_timeout' => 60, 'delayed_commit_timeout' => 2, 'retry_randomize' => false})
|
547
|
+
priconf = config_element('buffer','tag',{'flush_interval' => 1, 'retry_type' => :periodic, 'retry_wait' => 3, 'retry_timeout' => 60, 'delayed_commit_timeout' => 2, 'retry_randomize' => false, 'queued_chunks_limit_size' => 10})
|
548
548
|
secconf = config_element('secondary','',{'@type' => 'output_secondary_test2'})
|
549
549
|
@i.configure(config_element('ROOT','',{},[priconf,secconf]))
|
550
550
|
@i.register(:prefer_buffered_processing){ true }
|
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: 1.
|
4
|
+
version: 1.3.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Sadayuki Furuhashi
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2018-10
|
11
|
+
date: 2018-11-10 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: msgpack
|