fluentd 1.6.3 → 1.7.0.rc1
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/.drone.yml +35 -0
- data/.github/ISSUE_TEMPLATE/bug_report.md +2 -0
- data/README.md +5 -1
- data/fluentd.gemspec +1 -1
- data/lib/fluent/clock.rb +4 -0
- data/lib/fluent/compat/output.rb +3 -3
- data/lib/fluent/compat/socket_util.rb +1 -1
- data/lib/fluent/config/element.rb +3 -3
- data/lib/fluent/config/literal_parser.rb +1 -1
- data/lib/fluent/config/section.rb +4 -1
- data/lib/fluent/error.rb +4 -0
- data/lib/fluent/event.rb +28 -24
- data/lib/fluent/event_router.rb +2 -1
- data/lib/fluent/log.rb +1 -1
- data/lib/fluent/msgpack_factory.rb +8 -0
- data/lib/fluent/plugin/bare_output.rb +4 -4
- data/lib/fluent/plugin/buf_file_single.rb +211 -0
- data/lib/fluent/plugin/buffer.rb +62 -63
- data/lib/fluent/plugin/buffer/chunk.rb +21 -3
- data/lib/fluent/plugin/buffer/file_chunk.rb +37 -12
- data/lib/fluent/plugin/buffer/file_single_chunk.rb +314 -0
- data/lib/fluent/plugin/buffer/memory_chunk.rb +2 -1
- data/lib/fluent/plugin/compressable.rb +10 -6
- data/lib/fluent/plugin/filter_grep.rb +2 -2
- data/lib/fluent/plugin/formatter_csv.rb +10 -6
- data/lib/fluent/plugin/in_syslog.rb +10 -3
- data/lib/fluent/plugin/in_tail.rb +7 -2
- data/lib/fluent/plugin/in_tcp.rb +34 -7
- data/lib/fluent/plugin/multi_output.rb +4 -4
- data/lib/fluent/plugin/out_exec_filter.rb +1 -0
- data/lib/fluent/plugin/out_file.rb +13 -3
- data/lib/fluent/plugin/out_forward.rb +126 -588
- data/lib/fluent/plugin/out_forward/ack_handler.rb +161 -0
- data/lib/fluent/plugin/out_forward/connection_manager.rb +113 -0
- data/lib/fluent/plugin/out_forward/error.rb +28 -0
- data/lib/fluent/plugin/out_forward/failure_detector.rb +84 -0
- data/lib/fluent/plugin/out_forward/handshake_protocol.rb +121 -0
- data/lib/fluent/plugin/out_forward/load_balancer.rb +111 -0
- data/lib/fluent/plugin/out_forward/socket_cache.rb +138 -0
- data/lib/fluent/plugin/out_http.rb +231 -0
- data/lib/fluent/plugin/output.rb +29 -35
- data/lib/fluent/plugin/parser.rb +77 -0
- data/lib/fluent/plugin/parser_csv.rb +75 -0
- data/lib/fluent/plugin_helper/server.rb +1 -1
- data/lib/fluent/plugin_helper/thread.rb +1 -0
- data/lib/fluent/root_agent.rb +1 -1
- data/lib/fluent/time.rb +4 -2
- data/lib/fluent/timezone.rb +21 -7
- data/lib/fluent/version.rb +1 -1
- data/test/command/test_fluentd.rb +1 -1
- data/test/command/test_plugin_generator.rb +18 -2
- data/test/config/test_configurable.rb +78 -40
- data/test/counter/test_store.rb +1 -1
- data/test/helper.rb +1 -0
- data/test/helpers/process_extenstion.rb +33 -0
- data/test/plugin/out_forward/test_ack_handler.rb +101 -0
- data/test/plugin/out_forward/test_connection_manager.rb +145 -0
- data/test/plugin/out_forward/test_handshake_protocol.rb +103 -0
- data/test/plugin/out_forward/test_load_balancer.rb +60 -0
- data/test/plugin/out_forward/test_socket_cache.rb +139 -0
- data/test/plugin/test_buf_file.rb +118 -2
- data/test/plugin/test_buf_file_single.rb +734 -0
- data/test/plugin/test_buffer.rb +4 -48
- data/test/plugin/test_buffer_file_chunk.rb +19 -1
- data/test/plugin/test_buffer_file_single_chunk.rb +620 -0
- data/test/plugin/test_formatter_csv.rb +16 -0
- data/test/plugin/test_in_syslog.rb +56 -6
- data/test/plugin/test_in_tail.rb +1 -1
- data/test/plugin/test_in_tcp.rb +25 -0
- data/test/plugin/test_out_forward.rb +75 -201
- data/test/plugin/test_out_http.rb +352 -0
- data/test/plugin/test_output_as_buffered.rb +27 -24
- data/test/plugin/test_parser.rb +40 -0
- data/test/plugin/test_parser_csv.rb +83 -0
- data/test/plugin_helper/test_record_accessor.rb +1 -1
- data/test/test_time_formatter.rb +140 -121
- metadata +35 -6
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: f5ce0f9135cf13b32692d41e65dbb869738702a0d328ee1decec0b8848000feb
|
4
|
+
data.tar.gz: c803e8f6ece1fa166c1a988867f04c660f0f7dd821c5d7f09dbd1a96d478b9c7
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 18909799465547cfe11c00f701e5aad6845a346be63a29eddf872d56d3ae680ad967e852af5f468a451d1decb051cffce5d8d5e68d8ab3c97307137ccf22a9fa
|
7
|
+
data.tar.gz: 6fbed649b9f164f3f699667e57dd2706e98f11c69b2c31210767f520a4f62310e34a14ac12c5ddde5244f5232c83f6b7ad83dc925a096054ec71abd493ceeecd
|
data/.drone.yml
ADDED
@@ -0,0 +1,35 @@
|
|
1
|
+
kind: pipeline
|
2
|
+
name: fluentd-test-arm64-2-6-3
|
3
|
+
|
4
|
+
platform:
|
5
|
+
os: linux
|
6
|
+
arch: arm64
|
7
|
+
|
8
|
+
steps:
|
9
|
+
- name: fluentd-test-arm64-2-6-3
|
10
|
+
image: arm64v8/ruby:2.6.3
|
11
|
+
commands:
|
12
|
+
- apt update
|
13
|
+
- apt -y install libgmp3-dev
|
14
|
+
- export BUNDLE_GEMFILE=$PWD/Gemfile
|
15
|
+
- gem update --system
|
16
|
+
- bundle install --jobs=3 --retry=3 --path=${BUNDLE_PATH:-vendor/bundle}
|
17
|
+
- bundle exec rake test
|
18
|
+
---
|
19
|
+
kind: pipeline
|
20
|
+
name: fluentd-test-arm64-latest
|
21
|
+
|
22
|
+
platform:
|
23
|
+
os: linux
|
24
|
+
arch: arm64
|
25
|
+
|
26
|
+
steps:
|
27
|
+
- name: fluentd-test-arm64-latest
|
28
|
+
image: arm64v8/ruby:latest
|
29
|
+
commands:
|
30
|
+
- apt update
|
31
|
+
- apt -y install libgmp3-dev
|
32
|
+
- export BUNDLE_GEMFILE=$PWD/Gemfile
|
33
|
+
- gem update --system
|
34
|
+
- bundle install --jobs=3 --retry=3 --path=${BUNDLE_PATH:-vendor/bundle}
|
35
|
+
- bundle exec rake test
|
@@ -22,6 +22,8 @@ Check [CONTRIBUTING guideline](https://github.com/fluent/fluentd/blob/master/CON
|
|
22
22
|
- Operating system: `cat /etc/os-release`
|
23
23
|
- Kernel version: `uname -r`
|
24
24
|
|
25
|
+
If you hit the problem with older fluentd version, try latest version first.
|
26
|
+
|
25
27
|
**Your Configuration**
|
26
28
|
|
27
29
|
```
|
data/README.md
CHANGED
@@ -10,6 +10,10 @@ GitLab CI:
|
|
10
10
|
|
11
11
|
[![pipeline status](https://gitlab.com/fluent/fluentd/badges/master/pipeline.svg)](https://gitlab.com/fluent/fluentd/commits/master)
|
12
12
|
|
13
|
+
Drone CI for Arm64:
|
14
|
+
|
15
|
+
[![pipeline status](https://cloud.drone.io/api/badges/fluent/fluentd/status.svg?branch=master)](https://cloud.drone.io/fluent/fluentd)
|
16
|
+
|
13
17
|
[Fluentd](https://www.fluentd.org/) collects events from various data sources and writes them to files, RDBMS, NoSQL, IaaS, SaaS, Hadoop and so on. Fluentd helps you unify your logging infrastructure (Learn more about the [Unified Logging Layer](https://www.fluentd.org/blog/unified-logging-layer)).
|
14
18
|
|
15
19
|
<p align="center">
|
@@ -78,7 +82,7 @@ You can run specified test via `TEST` environment variable:
|
|
78
82
|
- Slack / Community: https://slack.fluentd.org
|
79
83
|
- Newsletters: https://www.fluentd.org/newsletter
|
80
84
|
- Author: [Sadayuki Furuhashi](https://github.com/frsyuki)
|
81
|
-
- Copyright: 2011-
|
85
|
+
- Copyright: 2011-2019 Fluentd Authors
|
82
86
|
- License: Apache License, Version 2.0
|
83
87
|
|
84
88
|
## Contributors:
|
data/fluentd.gemspec
CHANGED
@@ -24,7 +24,7 @@ Gem::Specification.new do |gem|
|
|
24
24
|
gem.add_runtime_dependency("serverengine", [">= 2.0.4", "< 3.0.0"])
|
25
25
|
gem.add_runtime_dependency("http_parser.rb", [">= 0.5.1", "< 0.7.0"])
|
26
26
|
gem.add_runtime_dependency("sigdump", ["~> 0.2.2"])
|
27
|
-
gem.add_runtime_dependency("tzinfo", ["~>
|
27
|
+
gem.add_runtime_dependency("tzinfo", ["~> 2.0"])
|
28
28
|
gem.add_runtime_dependency("tzinfo-data", ["~> 1.0"])
|
29
29
|
gem.add_runtime_dependency("strptime", [">= 0.2.2", "< 1.0.0"])
|
30
30
|
gem.add_runtime_dependency("dig_rb", ["~> 1.0.0"])
|
data/lib/fluent/clock.rb
CHANGED
data/lib/fluent/compat/output.rb
CHANGED
@@ -352,7 +352,7 @@ module Fluent
|
|
352
352
|
write_guard do
|
353
353
|
@buffer.write({meta => data}, format: ->(_data){ _data }, size: ->(){ size }, enqueue: enqueue)
|
354
354
|
end
|
355
|
-
@
|
355
|
+
@counter_mutex.synchronize{ @emit_records += size }
|
356
356
|
return [meta]
|
357
357
|
end
|
358
358
|
|
@@ -363,7 +363,7 @@ module Fluent
|
|
363
363
|
write_guard do
|
364
364
|
@buffer.write({meta => bulk}, format: ->(_data){ _data }, size: ->(){ size }, enqueue: enqueue)
|
365
365
|
end
|
366
|
-
@
|
366
|
+
@counter_mutex.synchronize{ @emit_records += size }
|
367
367
|
return [meta]
|
368
368
|
end
|
369
369
|
|
@@ -373,7 +373,7 @@ module Fluent
|
|
373
373
|
write_guard do
|
374
374
|
@buffer.write({meta => data}, enqueue: enqueue)
|
375
375
|
end
|
376
|
-
@
|
376
|
+
@counter_mutex.synchronize{ @emit_records += size }
|
377
377
|
[meta]
|
378
378
|
end
|
379
379
|
|
@@ -31,7 +31,7 @@ module Fluent
|
|
31
31
|
@unused = unused || attrs.keys
|
32
32
|
@v1_config = false
|
33
33
|
@corresponding_proxies = [] # some plugins use flat parameters, e.g. in_http doesn't provide <format> section for parser.
|
34
|
-
@unused_in =
|
34
|
+
@unused_in = nil # if this element is not used in plugins, correspoing plugin name and parent element name is set, e.g. [source, plugin class].
|
35
35
|
|
36
36
|
# it's global logger, not plugin logger: deprecated message should be global warning, not plugin level.
|
37
37
|
@logger = defined?($log) ? $log : nil
|
@@ -110,13 +110,13 @@ module Fluent
|
|
110
110
|
end
|
111
111
|
|
112
112
|
def has_key?(key)
|
113
|
-
@unused_in =
|
113
|
+
@unused_in = [] # some sections, e.g. <store> in copy, is not defined by config_section so clear unused flag for better warning message in check_not_fetched.
|
114
114
|
@unused.delete(key)
|
115
115
|
super
|
116
116
|
end
|
117
117
|
|
118
118
|
def [](key)
|
119
|
-
@unused_in =
|
119
|
+
@unused_in = [] # ditto
|
120
120
|
@unused.delete(key)
|
121
121
|
|
122
122
|
if RESERVED_PARAMETERS.include?(key) && !has_key?(key) && has_key?(RESERVED_PARAMETERS_COMPAT[key])
|
@@ -207,8 +207,11 @@ module Fluent
|
|
207
207
|
elems = conf.respond_to?(:elements) ? conf.elements : []
|
208
208
|
elems.each { |e|
|
209
209
|
next if plugin_class.nil? && Fluent::Config::V1Parser::ELEM_SYMBOLS.include?(e.name) # skip pre-defined non-plugin elements because it doens't have proxy section
|
210
|
+
next if e.unused_in && e.unused_in.empty? # the section is used at least once
|
210
211
|
|
211
|
-
|
212
|
+
if proxy.sections.any? { |name, subproxy| e.name == subproxy.name.to_s || e.name == subproxy.alias.to_s }
|
213
|
+
e.unused_in = []
|
214
|
+
else
|
212
215
|
parent_name = if conf.arg.empty?
|
213
216
|
conf.name
|
214
217
|
else
|
data/lib/fluent/error.rb
CHANGED
data/lib/fluent/event.rb
CHANGED
@@ -50,30 +50,30 @@ module Fluent
|
|
50
50
|
raise NotImplementedError, "DO NOT USE THIS CLASS directly."
|
51
51
|
end
|
52
52
|
|
53
|
-
def each(&block)
|
53
|
+
def each(unapcker: nil, &block)
|
54
54
|
raise NotImplementedError, "DO NOT USE THIS CLASS directly."
|
55
55
|
end
|
56
56
|
|
57
|
-
def to_msgpack_stream(time_int: false)
|
58
|
-
return to_msgpack_stream_forced_integer if time_int
|
59
|
-
out = msgpack_packer
|
57
|
+
def to_msgpack_stream(time_int: false, packer: nil)
|
58
|
+
return to_msgpack_stream_forced_integer(packer: packer) if time_int
|
59
|
+
out = packer || msgpack_packer
|
60
60
|
each {|time,record|
|
61
61
|
out.write([time,record])
|
62
62
|
}
|
63
|
-
out.
|
63
|
+
out.full_pack
|
64
64
|
end
|
65
65
|
|
66
|
-
def to_compressed_msgpack_stream(time_int: false)
|
67
|
-
packed = to_msgpack_stream(time_int: time_int)
|
66
|
+
def to_compressed_msgpack_stream(time_int: false, packer: nil)
|
67
|
+
packed = to_msgpack_stream(time_int: time_int, packer: packer)
|
68
68
|
compress(packed)
|
69
69
|
end
|
70
70
|
|
71
|
-
def to_msgpack_stream_forced_integer
|
72
|
-
out = msgpack_packer
|
71
|
+
def to_msgpack_stream_forced_integer(packer: nil)
|
72
|
+
out = packer || msgpack_packer
|
73
73
|
each {|time,record|
|
74
74
|
out.write([time.to_i,record])
|
75
75
|
}
|
76
|
-
out.
|
76
|
+
out.full_pack
|
77
77
|
end
|
78
78
|
end
|
79
79
|
|
@@ -87,6 +87,10 @@ module Fluent
|
|
87
87
|
OneEventStream.new(@time, @record.dup)
|
88
88
|
end
|
89
89
|
|
90
|
+
def empty?
|
91
|
+
false
|
92
|
+
end
|
93
|
+
|
90
94
|
def size
|
91
95
|
1
|
92
96
|
end
|
@@ -103,7 +107,7 @@ module Fluent
|
|
103
107
|
end
|
104
108
|
end
|
105
109
|
|
106
|
-
def each(&block)
|
110
|
+
def each(unpacker: nil, &block)
|
107
111
|
block.call(@time, @record)
|
108
112
|
nil
|
109
113
|
end
|
@@ -139,7 +143,7 @@ module Fluent
|
|
139
143
|
ArrayEventStream.new(@entries.slice(index, num))
|
140
144
|
end
|
141
145
|
|
142
|
-
def each(&block)
|
146
|
+
def each(unpacker: nil, &block)
|
143
147
|
@entries.each(&block)
|
144
148
|
nil
|
145
149
|
end
|
@@ -186,7 +190,7 @@ module Fluent
|
|
186
190
|
MultiEventStream.new(@time_array.slice(index, num), @record_array.slice(index, num))
|
187
191
|
end
|
188
192
|
|
189
|
-
def each(&block)
|
193
|
+
def each(unpacker: nil, &block)
|
190
194
|
time_array = @time_array
|
191
195
|
record_array = @record_array
|
192
196
|
for i in 0..time_array.length-1
|
@@ -230,11 +234,11 @@ module Fluent
|
|
230
234
|
true
|
231
235
|
end
|
232
236
|
|
233
|
-
def ensure_unpacked!
|
237
|
+
def ensure_unpacked!(unpacker: nil)
|
234
238
|
return if @unpacked_times && @unpacked_records
|
235
239
|
@unpacked_times = []
|
236
240
|
@unpacked_records = []
|
237
|
-
msgpack_unpacker.feed_each(@data) do |time, record|
|
241
|
+
(unpacker || msgpack_unpacker).feed_each(@data) do |time, record|
|
238
242
|
@unpacked_times << time
|
239
243
|
@unpacked_records << record
|
240
244
|
end
|
@@ -250,7 +254,7 @@ module Fluent
|
|
250
254
|
MultiEventStream.new(@unpacked_times.slice(index, num), @unpacked_records.slice(index, num))
|
251
255
|
end
|
252
256
|
|
253
|
-
def each(&block)
|
257
|
+
def each(unpacker: nil, &block)
|
254
258
|
if @unpacked_times
|
255
259
|
@unpacked_times.each_with_index do |time, i|
|
256
260
|
block.call(time, @unpacked_records[i])
|
@@ -258,7 +262,7 @@ module Fluent
|
|
258
262
|
else
|
259
263
|
@unpacked_times = []
|
260
264
|
@unpacked_records = []
|
261
|
-
msgpack_unpacker.feed_each(@data) do |time, record|
|
265
|
+
(unpacker || msgpack_unpacker).feed_each(@data) do |time, record|
|
262
266
|
@unpacked_times << time
|
263
267
|
@unpacked_records << record
|
264
268
|
block.call(time, record)
|
@@ -268,7 +272,7 @@ module Fluent
|
|
268
272
|
nil
|
269
273
|
end
|
270
274
|
|
271
|
-
def to_msgpack_stream(time_int: false)
|
275
|
+
def to_msgpack_stream(time_int: false, packer: nil)
|
272
276
|
# time_int is always ignored because @data is always packed binary in this class
|
273
277
|
@data
|
274
278
|
end
|
@@ -286,17 +290,17 @@ module Fluent
|
|
286
290
|
super
|
287
291
|
end
|
288
292
|
|
289
|
-
def ensure_unpacked!
|
293
|
+
def ensure_unpacked!(unpacker: nil)
|
290
294
|
ensure_decompressed!
|
291
295
|
super
|
292
296
|
end
|
293
297
|
|
294
|
-
def each(&block)
|
298
|
+
def each(unpacker: nil, &block)
|
295
299
|
ensure_decompressed!
|
296
300
|
super
|
297
301
|
end
|
298
302
|
|
299
|
-
def to_msgpack_stream(time_int: false)
|
303
|
+
def to_msgpack_stream(time_int: false, packer: nil)
|
300
304
|
ensure_decompressed!
|
301
305
|
super
|
302
306
|
end
|
@@ -318,15 +322,15 @@ module Fluent
|
|
318
322
|
include MessagePackFactory::Mixin
|
319
323
|
# chunk.extend(ChunkEventStreamer)
|
320
324
|
# => chunk.each{|time, record| ... }
|
321
|
-
def each(&block)
|
325
|
+
def each(unpacker: nil, &block)
|
322
326
|
open do |io|
|
323
|
-
msgpack_unpacker(io).each(&block)
|
327
|
+
(unpacker || msgpack_unpacker(io)).each(&block)
|
324
328
|
end
|
325
329
|
nil
|
326
330
|
end
|
327
331
|
alias :msgpack_each :each
|
328
332
|
|
329
|
-
def to_msgpack_stream(time_int: false)
|
333
|
+
def to_msgpack_stream(time_int: false, packer: nil)
|
330
334
|
# time_int is always ignored because data is already packed and written in chunk
|
331
335
|
read
|
332
336
|
end
|
data/lib/fluent/event_router.rb
CHANGED
@@ -17,6 +17,7 @@
|
|
17
17
|
require 'fluent/match'
|
18
18
|
require 'fluent/event'
|
19
19
|
require 'fluent/filter'
|
20
|
+
require 'fluent/msgpack_factory'
|
20
21
|
|
21
22
|
module Fluent
|
22
23
|
#
|
@@ -182,7 +183,7 @@ module Fluent
|
|
182
183
|
|
183
184
|
def optimized_filter_stream(tag, es)
|
184
185
|
new_es = MultiEventStream.new
|
185
|
-
es.each do |time, record|
|
186
|
+
es.each(unpacker: Fluent::MessagePackFactory.thread_local_msgpack_unpacker) do |time, record|
|
186
187
|
filtered_record = record
|
187
188
|
filtered_time = time
|
188
189
|
|
data/lib/fluent/log.rb
CHANGED
@@ -606,7 +606,7 @@ module Fluent
|
|
606
606
|
end
|
607
607
|
end
|
608
608
|
|
609
|
-
# This class
|
609
|
+
# This class delegates some methods which are used in `Fluent::Logger` to a instance variable(`dev`) in `Logger::LogDevice` class
|
610
610
|
# https://github.com/ruby/ruby/blob/7b2d47132ff8ee950b0f978ab772dee868d9f1b0/lib/logger.rb#L661
|
611
611
|
class LogDeviceIO < ::Logger::LogDevice
|
612
612
|
def flush
|
@@ -58,5 +58,13 @@ module Fluent
|
|
58
58
|
factory.register_type(Fluent::EventTime::TYPE, Fluent::EventTime)
|
59
59
|
@@engine_factory = factory
|
60
60
|
end
|
61
|
+
|
62
|
+
def self.thread_local_msgpack_packer
|
63
|
+
Thread.current[:local_msgpack_packer] ||= MessagePackFactory.engine_factory.packer
|
64
|
+
end
|
65
|
+
|
66
|
+
def self.thread_local_msgpack_unpacker
|
67
|
+
Thread.current[:local_msgpack_unpacker] ||= MessagePackFactory.engine_factory.unpacker
|
68
|
+
end
|
61
69
|
end
|
62
70
|
end
|
@@ -40,7 +40,7 @@ module Fluent
|
|
40
40
|
|
41
41
|
def initialize
|
42
42
|
super
|
43
|
-
@
|
43
|
+
@counter_mutex = Mutex.new
|
44
44
|
# TODO: well organized counters
|
45
45
|
@num_errors = 0
|
46
46
|
@emit_count = 0
|
@@ -48,12 +48,12 @@ module Fluent
|
|
48
48
|
end
|
49
49
|
|
50
50
|
def emit_sync(tag, es)
|
51
|
-
@
|
51
|
+
@counter_mutex.synchronize{ @emit_count += 1 }
|
52
52
|
begin
|
53
53
|
process(tag, es)
|
54
|
-
@
|
54
|
+
@counter_mutex.synchronize{ @emit_records += es.size }
|
55
55
|
rescue
|
56
|
-
@
|
56
|
+
@counter_mutex.synchronize{ @num_errors += 1 }
|
57
57
|
raise
|
58
58
|
end
|
59
59
|
end
|
@@ -0,0 +1,211 @@
|
|
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 'fileutils'
|
18
|
+
|
19
|
+
require 'fluent/plugin/buffer'
|
20
|
+
require 'fluent/plugin/buffer/file_single_chunk'
|
21
|
+
require 'fluent/system_config'
|
22
|
+
|
23
|
+
module Fluent
|
24
|
+
module Plugin
|
25
|
+
class FileSingleBuffer < Fluent::Plugin::Buffer
|
26
|
+
Plugin.register_buffer('file_single', self)
|
27
|
+
|
28
|
+
include SystemConfig::Mixin
|
29
|
+
|
30
|
+
DEFAULT_CHUNK_LIMIT_SIZE = 256 * 1024 * 1024 # 256MB
|
31
|
+
DEFAULT_TOTAL_LIMIT_SIZE = 64 * 1024 * 1024 * 1024 # 64GB
|
32
|
+
|
33
|
+
PATH_SUFFIX = ".#{Fluent::Plugin::Buffer::FileSingleChunk::PATH_EXT}"
|
34
|
+
DIR_PERMISSION = 0755
|
35
|
+
|
36
|
+
desc 'The path where buffer chunks are stored.'
|
37
|
+
config_param :path, :string, default: nil
|
38
|
+
desc 'Calculate the number of record in chunk during resume'
|
39
|
+
config_param :calc_num_records, :bool, default: true
|
40
|
+
desc 'The format of chunk. This is used to calculate the number of record'
|
41
|
+
config_param :chunk_format, :enum, list: [:msgpack, :text, :auto], default: :auto
|
42
|
+
|
43
|
+
config_set_default :chunk_limit_size, DEFAULT_CHUNK_LIMIT_SIZE
|
44
|
+
config_set_default :total_limit_size, DEFAULT_TOTAL_LIMIT_SIZE
|
45
|
+
|
46
|
+
desc 'The permission of chunk file. If no specified, <system> setting or 0644 is used'
|
47
|
+
config_param :file_permission, :string, default: nil
|
48
|
+
desc 'The permission of chunk directory. If no specified, <system> setting or 0755 is used'
|
49
|
+
config_param :dir_permission, :string, default: nil
|
50
|
+
|
51
|
+
@@buffer_paths = {}
|
52
|
+
|
53
|
+
def initialize
|
54
|
+
super
|
55
|
+
|
56
|
+
@multi_workers_available = false
|
57
|
+
@additional_resume_path = nil
|
58
|
+
end
|
59
|
+
|
60
|
+
def configure(conf)
|
61
|
+
super
|
62
|
+
|
63
|
+
if @chunk_format == :auto
|
64
|
+
@chunk_format = owner.formatted_to_msgpack_binary? ? :msgpack : :text
|
65
|
+
end
|
66
|
+
|
67
|
+
@key_in_path = nil
|
68
|
+
if owner.chunk_keys.empty?
|
69
|
+
log.debug "use event tag for buffer key"
|
70
|
+
else
|
71
|
+
if owner.chunk_key_tag
|
72
|
+
raise Fluent::ConfigError, "chunk keys must be tag or one field"
|
73
|
+
elsif owner.chunk_keys.size > 1
|
74
|
+
raise Fluent::ConfigError, "2 or more chunk keys is not allowed"
|
75
|
+
else
|
76
|
+
@key_in_path = owner.chunk_keys.first.to_sym
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
multi_workers_configured = owner.system_config.workers > 1
|
81
|
+
using_plugin_root_dir = false
|
82
|
+
unless @path
|
83
|
+
if root_dir = owner.plugin_root_dir
|
84
|
+
@path = File.join(root_dir, 'buffer')
|
85
|
+
using_plugin_root_dir = true # plugin_root_dir path contains worker id
|
86
|
+
else
|
87
|
+
raise Fluent::ConfigError, "buffer path is not configured. specify 'path' in <buffer>"
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
type_of_owner = Plugin.lookup_type_from_class(@_owner.class)
|
92
|
+
if @@buffer_paths.has_key?(@path) && !called_in_test?
|
93
|
+
type_using_this_path = @@buffer_paths[@path]
|
94
|
+
raise Fluent::ConfigError, "Other '#{type_using_this_path}' plugin already uses same buffer path: type = #{type_of_owner}, buffer path = #{@path}"
|
95
|
+
end
|
96
|
+
|
97
|
+
@@buffer_paths[@path] = type_of_owner
|
98
|
+
|
99
|
+
specified_directory_exists = File.exist?(@path) && File.directory?(@path)
|
100
|
+
unexisting_path_for_directory = !File.exist?(@path) && !@path.include?('.*')
|
101
|
+
|
102
|
+
if specified_directory_exists || unexisting_path_for_directory # directory
|
103
|
+
if using_plugin_root_dir || !multi_workers_configured
|
104
|
+
@path = File.join(@path, "fsb.*#{PATH_SUFFIX}")
|
105
|
+
else
|
106
|
+
@path = File.join(@path, "worker#{fluentd_worker_id}", "fsb.*#{PATH_SUFFIX}")
|
107
|
+
if fluentd_worker_id == 0
|
108
|
+
# worker 0 always checks unflushed buffer chunks to be resumed (might be created while non-multi-worker configuration)
|
109
|
+
@additional_resume_path = File.join(File.expand_path("../../", @path), "fsb.*#{PATH_SUFFIX}")
|
110
|
+
end
|
111
|
+
end
|
112
|
+
@multi_workers_available = true
|
113
|
+
else # specified path is file path
|
114
|
+
if File.basename(@path).include?('.*.')
|
115
|
+
new_path = File.join(File.dirname(@path), "fsb.*#{PATH_SUFFIX}")
|
116
|
+
log.warn "file_single doesn't allow user specified 'prefix.*.suffix' style path. Use '#{new_path}' for file instead: #{@path}"
|
117
|
+
@path = new_path
|
118
|
+
elsif File.basename(@path).end_with?('.*')
|
119
|
+
@path = @path + PATH_SUFFIX
|
120
|
+
else
|
121
|
+
# existing file will be ignored
|
122
|
+
@path = @path + ".*#{PATH_SUFFIX}"
|
123
|
+
end
|
124
|
+
@multi_workers_available = false
|
125
|
+
end
|
126
|
+
|
127
|
+
@dir_permission = if @dir_permission
|
128
|
+
@dir_permission.to_i(8)
|
129
|
+
else
|
130
|
+
system_config.dir_permission || DIR_PERMISSION
|
131
|
+
end
|
132
|
+
end
|
133
|
+
|
134
|
+
# This method is called only when multi worker is configured
|
135
|
+
def multi_workers_ready?
|
136
|
+
unless @multi_workers_available
|
137
|
+
log.error "file_single buffer with multi workers should be configured to use directory 'path', or system root_dir and plugin id"
|
138
|
+
end
|
139
|
+
@multi_workers_available
|
140
|
+
end
|
141
|
+
|
142
|
+
def start
|
143
|
+
FileUtils.mkdir_p(File.dirname(@path), mode: @dir_permission)
|
144
|
+
|
145
|
+
super
|
146
|
+
end
|
147
|
+
|
148
|
+
def persistent?
|
149
|
+
true
|
150
|
+
end
|
151
|
+
|
152
|
+
def resume
|
153
|
+
stage = {}
|
154
|
+
queue = []
|
155
|
+
|
156
|
+
patterns = [@path]
|
157
|
+
patterns.unshift @additional_resume_path if @additional_resume_path
|
158
|
+
Dir.glob(patterns) do |path|
|
159
|
+
next unless File.file?(path)
|
160
|
+
|
161
|
+
log.debug { "restoring buffer file: path = #{path}" }
|
162
|
+
|
163
|
+
m = new_metadata() # this metadata will be updated in FileSingleChunk.new
|
164
|
+
mode = Fluent::Plugin::Buffer::FileSingleChunk.assume_chunk_state(path)
|
165
|
+
if mode == :unknown
|
166
|
+
log.debug "unknown state chunk found", path: path
|
167
|
+
next
|
168
|
+
end
|
169
|
+
|
170
|
+
begin
|
171
|
+
chunk = Fluent::Plugin::Buffer::FileSingleChunk.new(m, path, mode, @key_in_path)
|
172
|
+
chunk.restore_size(@chunk_format) if @calc_num_records
|
173
|
+
rescue Fluent::Plugin::Buffer::FileSingleChunk::FileChunkError => e
|
174
|
+
handle_broken_files(path, mode, e)
|
175
|
+
next
|
176
|
+
end
|
177
|
+
|
178
|
+
case chunk.state
|
179
|
+
when :staged
|
180
|
+
stage[chunk.metadata] = chunk
|
181
|
+
when :queued
|
182
|
+
queue << chunk
|
183
|
+
end
|
184
|
+
end
|
185
|
+
|
186
|
+
queue.sort_by!(&:modified_at)
|
187
|
+
|
188
|
+
return stage, queue
|
189
|
+
end
|
190
|
+
|
191
|
+
def generate_chunk(metadata)
|
192
|
+
# FileChunk generates real path with unique_id
|
193
|
+
if @file_permission
|
194
|
+
chunk = Fluent::Plugin::Buffer::FileSingleChunk.new(metadata, @path, :create, @key_in_path, perm: @file_permission, compress: @compress)
|
195
|
+
else
|
196
|
+
chunk = Fluent::Plugin::Buffer::FileSingleChunk.new(metadata, @path, :create, @key_in_path, compress: @compress)
|
197
|
+
end
|
198
|
+
|
199
|
+
log.debug "Created new chunk", chunk_id: dump_unique_id_hex(chunk.unique_id), metadata: metadata
|
200
|
+
|
201
|
+
chunk
|
202
|
+
end
|
203
|
+
|
204
|
+
def handle_broken_files(path, mode, e)
|
205
|
+
log.error "found broken chunk file during resume. Delete corresponding files:", path: path, mode: mode, err_msg: e.message
|
206
|
+
# After support 'backup_dir' feature, these files are moved to backup_dir instead of unlink.
|
207
|
+
File.unlink(path) rescue nil
|
208
|
+
end
|
209
|
+
end
|
210
|
+
end
|
211
|
+
end
|