fluentd 0.12.40 → 0.14.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/.github/ISSUE_TEMPLATE.md +6 -0
- data/.gitignore +2 -0
- data/.travis.yml +33 -21
- data/CONTRIBUTING.md +1 -0
- data/ChangeLog +810 -237
- data/README.md +0 -25
- data/Rakefile +2 -1
- data/Vagrantfile +17 -0
- data/appveyor.yml +35 -0
- data/example/filter_stdout.conf +5 -5
- data/example/in_forward.conf +2 -2
- data/example/in_http.conf +2 -2
- data/example/in_out_forward.conf +17 -0
- data/example/in_syslog.conf +2 -2
- data/example/in_tail.conf +2 -2
- data/example/in_tcp.conf +2 -2
- data/example/in_udp.conf +2 -2
- data/example/out_copy.conf +4 -4
- data/example/out_file.conf +2 -2
- data/example/out_forward.conf +2 -2
- data/example/out_forward_buf_file.conf +23 -0
- data/example/v0_12_filter.conf +8 -8
- data/fluent.conf +29 -0
- data/fluentd.gemspec +18 -11
- data/lib/fluent/agent.rb +60 -58
- data/lib/fluent/command/cat.rb +1 -1
- data/lib/fluent/command/debug.rb +7 -5
- data/lib/fluent/command/fluentd.rb +97 -2
- data/lib/fluent/compat/call_super_mixin.rb +67 -0
- data/lib/fluent/compat/filter.rb +50 -0
- data/lib/fluent/compat/formatter.rb +109 -0
- data/lib/fluent/compat/input.rb +50 -0
- data/lib/fluent/compat/output.rb +617 -0
- data/lib/fluent/compat/output_chain.rb +60 -0
- data/lib/fluent/compat/parser.rb +163 -0
- data/lib/fluent/compat/propagate_default.rb +62 -0
- data/lib/fluent/config.rb +23 -20
- data/lib/fluent/config/configure_proxy.rb +119 -70
- data/lib/fluent/config/dsl.rb +5 -18
- data/lib/fluent/config/element.rb +72 -8
- data/lib/fluent/config/error.rb +0 -3
- data/lib/fluent/config/literal_parser.rb +0 -2
- data/lib/fluent/config/parser.rb +4 -4
- data/lib/fluent/config/section.rb +39 -28
- data/lib/fluent/config/types.rb +2 -13
- data/lib/fluent/config/v1_parser.rb +1 -3
- data/lib/fluent/configurable.rb +48 -16
- data/lib/fluent/daemon.rb +15 -0
- data/lib/fluent/engine.rb +26 -52
- data/lib/fluent/env.rb +6 -4
- data/lib/fluent/event.rb +58 -11
- data/lib/fluent/event_router.rb +5 -5
- data/lib/fluent/filter.rb +2 -50
- data/lib/fluent/formatter.rb +4 -293
- data/lib/fluent/input.rb +2 -32
- data/lib/fluent/label.rb +2 -2
- data/lib/fluent/load.rb +3 -2
- data/lib/fluent/log.rb +107 -38
- data/lib/fluent/match.rb +0 -36
- data/lib/fluent/mixin.rb +117 -7
- data/lib/fluent/msgpack_factory.rb +62 -0
- data/lib/fluent/output.rb +7 -612
- data/lib/fluent/output_chain.rb +23 -0
- data/lib/fluent/parser.rb +4 -800
- data/lib/fluent/plugin.rb +100 -121
- data/lib/fluent/plugin/bare_output.rb +63 -0
- data/lib/fluent/plugin/base.rb +121 -0
- data/lib/fluent/plugin/buf_file.rb +101 -182
- data/lib/fluent/plugin/buf_memory.rb +9 -92
- data/lib/fluent/plugin/buffer.rb +473 -0
- data/lib/fluent/plugin/buffer/chunk.rb +135 -0
- data/lib/fluent/plugin/buffer/file_chunk.rb +339 -0
- data/lib/fluent/plugin/buffer/memory_chunk.rb +100 -0
- data/lib/fluent/plugin/exec_util.rb +80 -75
- data/lib/fluent/plugin/file_util.rb +33 -28
- data/lib/fluent/plugin/file_wrapper.rb +120 -0
- data/lib/fluent/plugin/filter.rb +51 -0
- data/lib/fluent/plugin/filter_grep.rb +13 -40
- data/lib/fluent/plugin/filter_record_transformer.rb +22 -18
- data/lib/fluent/plugin/formatter.rb +93 -0
- data/lib/fluent/plugin/formatter_csv.rb +48 -0
- data/lib/fluent/plugin/formatter_hash.rb +32 -0
- data/lib/fluent/plugin/formatter_json.rb +47 -0
- data/lib/fluent/plugin/formatter_ltsv.rb +42 -0
- data/lib/fluent/plugin/formatter_msgpack.rb +32 -0
- data/lib/fluent/plugin/formatter_out_file.rb +45 -0
- data/lib/fluent/plugin/formatter_single_value.rb +34 -0
- data/lib/fluent/plugin/formatter_stdout.rb +39 -0
- data/lib/fluent/plugin/in_debug_agent.rb +4 -0
- data/lib/fluent/plugin/in_dummy.rb +22 -18
- data/lib/fluent/plugin/in_exec.rb +18 -8
- data/lib/fluent/plugin/in_forward.rb +36 -79
- data/lib/fluent/plugin/in_gc_stat.rb +4 -0
- data/lib/fluent/plugin/in_http.rb +21 -18
- data/lib/fluent/plugin/in_monitor_agent.rb +15 -48
- data/lib/fluent/plugin/in_object_space.rb +6 -1
- data/lib/fluent/plugin/in_stream.rb +7 -3
- data/lib/fluent/plugin/in_syslog.rb +46 -95
- data/lib/fluent/plugin/in_tail.rb +51 -595
- data/lib/fluent/plugin/in_tcp.rb +8 -1
- data/lib/fluent/plugin/in_udp.rb +8 -14
- data/lib/fluent/plugin/input.rb +33 -0
- data/lib/fluent/plugin/multi_output.rb +95 -0
- data/lib/fluent/plugin/out_buffered_null.rb +59 -0
- data/lib/fluent/plugin/out_copy.rb +11 -7
- data/lib/fluent/plugin/out_exec.rb +15 -11
- data/lib/fluent/plugin/out_exec_filter.rb +18 -10
- data/lib/fluent/plugin/out_file.rb +34 -5
- data/lib/fluent/plugin/out_forward.rb +19 -9
- data/lib/fluent/plugin/out_null.rb +0 -14
- data/lib/fluent/plugin/out_roundrobin.rb +11 -7
- data/lib/fluent/plugin/out_stdout.rb +5 -7
- data/lib/fluent/plugin/out_stream.rb +3 -1
- data/lib/fluent/plugin/output.rb +979 -0
- data/lib/fluent/plugin/owned_by_mixin.rb +42 -0
- data/lib/fluent/plugin/parser.rb +244 -0
- data/lib/fluent/plugin/parser_apache.rb +24 -0
- data/lib/fluent/plugin/parser_apache2.rb +84 -0
- data/lib/fluent/plugin/parser_apache_error.rb +21 -0
- data/lib/fluent/plugin/parser_csv.rb +31 -0
- data/lib/fluent/plugin/parser_json.rb +79 -0
- data/lib/fluent/plugin/parser_ltsv.rb +50 -0
- data/lib/fluent/plugin/parser_multiline.rb +102 -0
- data/lib/fluent/plugin/parser_nginx.rb +24 -0
- data/lib/fluent/plugin/parser_none.rb +36 -0
- data/lib/fluent/plugin/parser_syslog.rb +82 -0
- data/lib/fluent/plugin/parser_tsv.rb +37 -0
- data/lib/fluent/plugin/socket_util.rb +120 -114
- data/lib/fluent/plugin/storage.rb +84 -0
- data/lib/fluent/plugin/storage_local.rb +116 -0
- data/lib/fluent/plugin/string_util.rb +16 -13
- data/lib/fluent/plugin_helper.rb +39 -0
- data/lib/fluent/plugin_helper/child_process.rb +298 -0
- data/lib/fluent/plugin_helper/compat_parameters.rb +99 -0
- data/lib/fluent/plugin_helper/event_emitter.rb +80 -0
- data/lib/fluent/plugin_helper/event_loop.rb +118 -0
- data/lib/fluent/plugin_helper/retry_state.rb +177 -0
- data/lib/fluent/plugin_helper/storage.rb +308 -0
- data/lib/fluent/plugin_helper/thread.rb +147 -0
- data/lib/fluent/plugin_helper/timer.rb +85 -0
- data/lib/fluent/plugin_id.rb +63 -0
- data/lib/fluent/process.rb +21 -30
- data/lib/fluent/registry.rb +21 -9
- data/lib/fluent/root_agent.rb +115 -40
- data/lib/fluent/supervisor.rb +330 -320
- data/lib/fluent/system_config.rb +42 -18
- data/lib/fluent/test.rb +6 -1
- data/lib/fluent/test/base.rb +23 -3
- data/lib/fluent/test/driver/base.rb +247 -0
- data/lib/fluent/test/driver/event_feeder.rb +98 -0
- data/lib/fluent/test/driver/filter.rb +35 -0
- data/lib/fluent/test/driver/input.rb +31 -0
- data/lib/fluent/test/driver/output.rb +78 -0
- data/lib/fluent/test/driver/test_event_router.rb +45 -0
- data/lib/fluent/test/filter_test.rb +0 -1
- data/lib/fluent/test/formatter_test.rb +2 -1
- data/lib/fluent/test/input_test.rb +23 -17
- data/lib/fluent/test/output_test.rb +28 -39
- data/lib/fluent/test/parser_test.rb +1 -1
- data/lib/fluent/time.rb +104 -1
- data/lib/fluent/{status.rb → unique_id.rb} +15 -24
- data/lib/fluent/version.rb +1 -1
- data/lib/fluent/winsvc.rb +72 -0
- data/test/compat/test_calls_super.rb +164 -0
- data/test/config/test_config_parser.rb +83 -0
- data/test/config/test_configurable.rb +547 -274
- data/test/config/test_configure_proxy.rb +146 -29
- data/test/config/test_dsl.rb +3 -181
- data/test/config/test_element.rb +274 -0
- data/test/config/test_literal_parser.rb +1 -1
- data/test/config/test_section.rb +79 -7
- data/test/config/test_system_config.rb +21 -0
- data/test/config/test_types.rb +3 -26
- data/test/helper.rb +78 -8
- data/test/plugin/test_bare_output.rb +118 -0
- data/test/plugin/test_base.rb +75 -0
- data/test/plugin/test_buf_file.rb +420 -521
- data/test/plugin/test_buf_memory.rb +32 -194
- data/test/plugin/test_buffer.rb +981 -0
- data/test/plugin/test_buffer_chunk.rb +110 -0
- data/test/plugin/test_buffer_file_chunk.rb +770 -0
- data/test/plugin/test_buffer_memory_chunk.rb +265 -0
- data/test/plugin/test_filter.rb +255 -0
- data/test/plugin/test_filter_grep.rb +2 -73
- data/test/plugin/test_filter_record_transformer.rb +24 -68
- data/test/plugin/test_filter_stdout.rb +6 -6
- data/test/plugin/test_in_debug_agent.rb +2 -0
- data/test/plugin/test_in_dummy.rb +11 -17
- data/test/plugin/test_in_exec.rb +6 -25
- data/test/plugin/test_in_forward.rb +112 -151
- data/test/plugin/test_in_gc_stat.rb +2 -0
- data/test/plugin/test_in_http.rb +106 -157
- data/test/plugin/test_in_object_space.rb +21 -5
- data/test/plugin/test_in_stream.rb +14 -13
- data/test/plugin/test_in_syslog.rb +30 -275
- data/test/plugin/test_in_tail.rb +95 -234
- data/test/plugin/test_in_tcp.rb +14 -0
- data/test/plugin/test_in_udp.rb +21 -13
- data/test/plugin/test_input.rb +122 -0
- data/test/plugin/test_multi_output.rb +180 -0
- data/test/plugin/test_out_buffered_null.rb +79 -0
- data/test/plugin/test_out_copy.rb +15 -2
- data/test/plugin/test_out_exec.rb +75 -25
- data/test/plugin/test_out_exec_filter.rb +74 -8
- data/test/plugin/test_out_file.rb +61 -7
- data/test/plugin/test_out_forward.rb +92 -15
- data/test/plugin/test_out_roundrobin.rb +1 -0
- data/test/plugin/test_out_stdout.rb +22 -13
- data/test/plugin/test_out_stream.rb +18 -0
- data/test/plugin/test_output.rb +515 -0
- data/test/plugin/test_output_as_buffered.rb +1540 -0
- data/test/plugin/test_output_as_buffered_overflow.rb +247 -0
- data/test/plugin/test_output_as_buffered_retries.rb +808 -0
- data/test/plugin/test_output_as_buffered_secondary.rb +776 -0
- data/test/plugin/test_output_as_standard.rb +362 -0
- data/test/plugin/test_owned_by.rb +35 -0
- data/test/plugin/test_storage.rb +167 -0
- data/test/plugin/test_storage_local.rb +8 -0
- data/test/plugin_helper/test_child_process.rb +599 -0
- data/test/plugin_helper/test_compat_parameters.rb +175 -0
- data/test/plugin_helper/test_event_emitter.rb +51 -0
- data/test/plugin_helper/test_event_loop.rb +52 -0
- data/test/plugin_helper/test_retry_state.rb +399 -0
- data/test/plugin_helper/test_storage.rb +411 -0
- data/test/plugin_helper/test_thread.rb +164 -0
- data/test/plugin_helper/test_timer.rb +100 -0
- data/test/scripts/exec_script.rb +0 -6
- data/test/scripts/fluent/plugin/out_test.rb +3 -0
- data/test/test_config.rb +13 -4
- data/test/test_event.rb +24 -13
- data/test/test_event_router.rb +8 -7
- data/test/test_event_time.rb +187 -0
- data/test/test_formatter.rb +13 -51
- data/test/test_input.rb +1 -1
- data/test/test_log.rb +239 -16
- data/test/test_mixin.rb +1 -1
- data/test/test_output.rb +53 -66
- data/test/test_parser.rb +105 -323
- data/test/test_plugin_helper.rb +81 -0
- data/test/test_root_agent.rb +4 -52
- data/test/test_supervisor.rb +272 -0
- data/test/test_unique_id.rb +47 -0
- metadata +180 -54
- data/lib/fluent/buffer.rb +0 -365
- data/lib/fluent/plugin/filter_parser.rb +0 -107
- data/lib/fluent/plugin/in_status.rb +0 -76
- data/lib/fluent/test/helpers.rb +0 -86
- data/test/plugin/data/log/foo/bar2 +0 -0
- data/test/plugin/test_filter_parser.rb +0 -744
- data/test/plugin/test_in_status.rb +0 -38
- data/test/test_buffer.rb +0 -624
@@ -0,0 +1,84 @@
|
|
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 'fluent/plugin/base'
|
18
|
+
require 'fluent/plugin/owned_by_mixin'
|
19
|
+
|
20
|
+
module Fluent
|
21
|
+
module Plugin
|
22
|
+
class Storage < Base
|
23
|
+
include OwnedByMixin
|
24
|
+
|
25
|
+
DEFAULT_TYPE = 'local'
|
26
|
+
|
27
|
+
configured_in :storage
|
28
|
+
|
29
|
+
config_param :persistent, :bool, default: false # load/save with all operations
|
30
|
+
config_param :autosave, :bool, default: true
|
31
|
+
config_param :autosave_interval, :time, default: 10
|
32
|
+
config_param :save_at_shutdown, :bool, default: true
|
33
|
+
|
34
|
+
def self.validate_key(key)
|
35
|
+
raise ArgumentError, "key must be a string (or symbol for to_s)" unless key.is_a?(String) || key.is_a?(Symbol)
|
36
|
+
key.to_s
|
37
|
+
end
|
38
|
+
|
39
|
+
attr_accessor :log
|
40
|
+
|
41
|
+
def persistent_always?
|
42
|
+
false
|
43
|
+
end
|
44
|
+
|
45
|
+
def synchronized?
|
46
|
+
false
|
47
|
+
end
|
48
|
+
|
49
|
+
def implementation
|
50
|
+
self
|
51
|
+
end
|
52
|
+
|
53
|
+
def load
|
54
|
+
# load storage data from any data source, or initialize storage internally
|
55
|
+
end
|
56
|
+
|
57
|
+
def save
|
58
|
+
# save internal data store into data source (to be loaded)
|
59
|
+
end
|
60
|
+
|
61
|
+
def get(key)
|
62
|
+
raise NotImplementedError, "Implement this method in child class"
|
63
|
+
end
|
64
|
+
|
65
|
+
def fetch(key, defval)
|
66
|
+
raise NotImplementedError, "Implement this method in child class"
|
67
|
+
end
|
68
|
+
|
69
|
+
def put(key, value)
|
70
|
+
# return value
|
71
|
+
raise NotImplementedError, "Implement this method in child class"
|
72
|
+
end
|
73
|
+
|
74
|
+
def delete(key)
|
75
|
+
# return deleted value
|
76
|
+
raise NotImplementedError, "Implement this method in child class"
|
77
|
+
end
|
78
|
+
|
79
|
+
def update(key, &block) # transactional get-and-update
|
80
|
+
raise NotImplementedError, "Implement this method in child class"
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
@@ -0,0 +1,116 @@
|
|
1
|
+
require 'fluent/plugin'
|
2
|
+
require 'fluent/plugin/storage'
|
3
|
+
|
4
|
+
require 'fileutils'
|
5
|
+
require 'yajl'
|
6
|
+
|
7
|
+
module Fluent
|
8
|
+
module Plugin
|
9
|
+
class LocalStorage < Storage
|
10
|
+
Fluent::Plugin.register_storage('local', self)
|
11
|
+
|
12
|
+
DEFAULT_DIR_MODE = 0755
|
13
|
+
DEFAULT_FILE_MODE = 0644
|
14
|
+
|
15
|
+
config_param :path, :string, default: nil
|
16
|
+
config_param :mode, :integer, default: DEFAULT_FILE_MODE
|
17
|
+
config_param :dir_mode, :integer, default: DEFAULT_DIR_MODE
|
18
|
+
config_param :pretty_print, :bool, default: false
|
19
|
+
|
20
|
+
def initialize
|
21
|
+
super
|
22
|
+
@store = {}
|
23
|
+
end
|
24
|
+
|
25
|
+
def configure(conf)
|
26
|
+
super
|
27
|
+
|
28
|
+
@on_memory = false
|
29
|
+
if !@path && !@_plugin_id_configured
|
30
|
+
if @persistent
|
31
|
+
raise Fluent::ConfigError, "Plugin @id or path for <storage> required to save data"
|
32
|
+
else
|
33
|
+
if @autosave
|
34
|
+
log.warn "both of Plugin @id and path for <storage> are not specified. Using on-memory store."
|
35
|
+
else
|
36
|
+
log.info "both of Plugin @id and path for <storage> are not specified. Using on-memory store."
|
37
|
+
end
|
38
|
+
@on_memory = true
|
39
|
+
end
|
40
|
+
elsif @path
|
41
|
+
# ok
|
42
|
+
else # @_plugin_id_configured is true
|
43
|
+
raise NotImplementedError, "implement this feature later with system_config"
|
44
|
+
## TODO: get process-wide directory for plugin storage, and generate path for this plugin storage instance
|
45
|
+
# path =
|
46
|
+
end
|
47
|
+
|
48
|
+
if !@on_memory
|
49
|
+
dir = File.dirname(@path)
|
50
|
+
FileUtils.mkdir_p(dir, mode: @dir_mode) unless File.exist?(dir)
|
51
|
+
if File.exist?(@path)
|
52
|
+
raise Fluent::ConfigError, "Plugin storage path '#{@path}' is not readable/writable" unless File.readable?(@path) && File.writable?(@path)
|
53
|
+
begin
|
54
|
+
data = Yajl::Parser.parse(open(@path, 'r:utf-8'){ |io| io.read })
|
55
|
+
raise Fluent::ConfigError, "Invalid contents (not object) in plugin storage file: '#{@path}'" unless data.is_a?(Hash)
|
56
|
+
rescue => e
|
57
|
+
log.error "failed to read data from plugin storage file", path: @path, error: e
|
58
|
+
raise Fluent::ConfigError, "Unexpected error: failed to read data from plugin storage file: '#{@path}'"
|
59
|
+
end
|
60
|
+
else
|
61
|
+
raise Fluent::ConfigError, "Directory is not writable for plugin storage file '#{@path}'" unless File.writable?(@path)
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
def load
|
67
|
+
return if @on_memory
|
68
|
+
return unless File.exist?(@path)
|
69
|
+
begin
|
70
|
+
json_string = open(@path, 'r:utf-8'){ |io| io.read }
|
71
|
+
json = Yajl::Parser.parse(json_string)
|
72
|
+
unless json.is_a?(Hash)
|
73
|
+
log.error "broken content for plugin storage (Hash required: ignored)", type: json.class
|
74
|
+
log.debug "broken content", content: json_string
|
75
|
+
return
|
76
|
+
end
|
77
|
+
@store = json
|
78
|
+
rescue => e
|
79
|
+
log.error "failed to load data for plugin storage from file", path: @path, error: e
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
def save
|
84
|
+
return if @on_memory
|
85
|
+
tmp_path = @path + '.tmp'
|
86
|
+
begin
|
87
|
+
json_string = Yajl::Encoder.encode(@store, pretty: @pretty_print)
|
88
|
+
open(tmp_path, 'w:utf-8', @mode){ |io| io.write json_string }
|
89
|
+
File.rename(tmp_path, @path)
|
90
|
+
rescue => e
|
91
|
+
log.error "failed to save data for plugin storage to file", path: @path, tmp: tmp_path, error: e
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
def get(key)
|
96
|
+
@store[key.to_s]
|
97
|
+
end
|
98
|
+
|
99
|
+
def fetch(key, defval)
|
100
|
+
@store.fetch(key.to_s, defval)
|
101
|
+
end
|
102
|
+
|
103
|
+
def put(key, value)
|
104
|
+
@store[key.to_s] = value
|
105
|
+
end
|
106
|
+
|
107
|
+
def delete(key)
|
108
|
+
@store.delete(key.to_s)
|
109
|
+
end
|
110
|
+
|
111
|
+
def update(key, &block)
|
112
|
+
@store[key.to_s] = block.call(@store[key.to_s])
|
113
|
+
end
|
114
|
+
end
|
115
|
+
end
|
116
|
+
end
|
@@ -14,21 +14,24 @@
|
|
14
14
|
# limitations under the License.
|
15
15
|
#
|
16
16
|
|
17
|
-
require "string-scrub" unless "".respond_to?(:scrub)
|
18
|
-
|
19
17
|
module Fluent
|
20
|
-
module
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
18
|
+
module Plugin
|
19
|
+
module StringUtil
|
20
|
+
def match_regexp(regexp, string)
|
21
|
+
begin
|
22
|
+
return regexp.match(string)
|
23
|
+
rescue ArgumentError => e
|
24
|
+
raise e unless e.message.index("invalid byte sequence in".freeze).zero?
|
25
|
+
$log.info "invalid byte sequence is replaced in `#{string}`"
|
26
|
+
string = string.scrub('?')
|
27
|
+
retry
|
28
|
+
end
|
29
|
+
return true
|
29
30
|
end
|
30
|
-
|
31
|
+
module_function :match_regexp
|
31
32
|
end
|
32
|
-
module_function :match_regexp
|
33
33
|
end
|
34
|
+
|
35
|
+
# obsolete
|
36
|
+
StringUtil = Fluent::Plugin::StringUtil
|
34
37
|
end
|
@@ -0,0 +1,39 @@
|
|
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 'fluent/plugin_helper/event_emitter'
|
18
|
+
require 'fluent/plugin_helper/thread'
|
19
|
+
require 'fluent/plugin_helper/event_loop'
|
20
|
+
require 'fluent/plugin_helper/timer'
|
21
|
+
require 'fluent/plugin_helper/child_process'
|
22
|
+
require 'fluent/plugin_helper/storage'
|
23
|
+
require 'fluent/plugin_helper/retry_state'
|
24
|
+
require 'fluent/plugin_helper/compat_parameters'
|
25
|
+
|
26
|
+
module Fluent
|
27
|
+
module PluginHelper
|
28
|
+
module Mixin
|
29
|
+
def self.included(mod)
|
30
|
+
mod.extend(Fluent::PluginHelper)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
def helpers(*snake_case_symbols)
|
35
|
+
helper_modules = snake_case_symbols.map{|name| Fluent::PluginHelper.const_get(name.to_s.split('_').map(&:capitalize).join) }
|
36
|
+
include(*helper_modules)
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,298 @@
|
|
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 'fluent/plugin_helper/thread'
|
18
|
+
require 'fluent/plugin_helper/timer'
|
19
|
+
|
20
|
+
require 'open3'
|
21
|
+
require 'timeout'
|
22
|
+
|
23
|
+
module Fluent
|
24
|
+
module PluginHelper
|
25
|
+
module ChildProcess
|
26
|
+
include Fluent::PluginHelper::Thread
|
27
|
+
include Fluent::PluginHelper::Timer
|
28
|
+
|
29
|
+
CHILD_PROCESS_LOOP_CHECK_INTERVAL = 0.2 # sec
|
30
|
+
CHILD_PROCESS_DEFAULT_EXIT_TIMEOUT = 10 # sec
|
31
|
+
CHILD_PROCESS_DEFAULT_KILL_TIMEOUT = 60 # sec
|
32
|
+
|
33
|
+
MODE_PARAMS = [:read, :write, :stderr, :read_with_stderr]
|
34
|
+
STDERR_OPTIONS = [:discard, :connect]
|
35
|
+
|
36
|
+
# stop : mark callback thread as stopped
|
37
|
+
# shutdown : close write IO to child processes (STDIN of child processes), send TERM (KILL for Windows) to all child processes
|
38
|
+
# close : send KILL to all child processes
|
39
|
+
# terminate: [-]
|
40
|
+
|
41
|
+
attr_reader :_child_process_processes # for tests
|
42
|
+
|
43
|
+
def child_process_running?
|
44
|
+
# checker for code in callback of child_process_execute
|
45
|
+
::Thread.current[:_fluentd_plugin_helper_child_process_running] || false
|
46
|
+
end
|
47
|
+
|
48
|
+
def child_process_id
|
49
|
+
::Thread.current[:_fluentd_plugin_helper_child_process_pid]
|
50
|
+
end
|
51
|
+
|
52
|
+
def child_process_exit_status
|
53
|
+
::Thread.current[:_fluentd_plugin_helper_child_process_exit_status]
|
54
|
+
end
|
55
|
+
|
56
|
+
def child_process_execute(
|
57
|
+
title, command,
|
58
|
+
arguments: nil, subprocess_name: nil, interval: nil, immediate: false, parallel: false,
|
59
|
+
mode: [:read, :write], stderr: :discard, env: {}, unsetenv: false, chdir: nil,
|
60
|
+
internal_encoding: 'utf-8', external_encoding: 'ascii-8bit', scrub: true, replace_string: nil,
|
61
|
+
&block
|
62
|
+
)
|
63
|
+
raise ArgumentError, "BUG: title must be a symbol" unless title.is_a? Symbol
|
64
|
+
raise ArgumentError, "BUG: arguments required if subprocess name is replaced" if subprocess_name && !arguments
|
65
|
+
|
66
|
+
raise ArgumentError, "BUG: invalid mode specification" unless mode.all?{|m| MODE_PARAMS.include?(m) }
|
67
|
+
raise ArgumentError, "BUG: read_with_stderr is exclusive with :read and :stderr" if mode.include?(:read_with_stderr) && (mode.include?(:read) || mode.include?(:stderr))
|
68
|
+
raise ArgumentError, "BUG: invalid stderr handling specification" unless STDERR_OPTIONS.include?(stderr)
|
69
|
+
|
70
|
+
raise ArgumentError, "BUG: block not specified which receive i/o object" unless block_given?
|
71
|
+
raise ArgumentError, "BUG: number of block arguments are different from size of mode" unless block.arity == mode.size
|
72
|
+
|
73
|
+
running = false
|
74
|
+
callback = ->(*args) {
|
75
|
+
running = true
|
76
|
+
begin
|
77
|
+
block.call(*args)
|
78
|
+
ensure
|
79
|
+
running = false
|
80
|
+
end
|
81
|
+
}
|
82
|
+
|
83
|
+
if immediate || !interval
|
84
|
+
child_process_execute_once(title, command, arguments, subprocess_name, mode, stderr, env, unsetenv, chdir, internal_encoding, external_encoding, scrub, replace_string, &callback)
|
85
|
+
end
|
86
|
+
|
87
|
+
if interval
|
88
|
+
timer_execute(:child_process_execute, interval, repeat: true) do
|
89
|
+
if !parallel && running
|
90
|
+
log.warn "previous child process is still running. skipped.", title: title, command: command, arguments: arguments, interval: interval, parallel: parallel
|
91
|
+
else
|
92
|
+
child_process_execute_once(title, command, arguments, subprocess_name, mode, stderr, env, unsetenv, chdir, internal_encoding, external_encoding, scrub, replace_string, &callback)
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
def initialize
|
99
|
+
super
|
100
|
+
# plugins MAY configure this parameter
|
101
|
+
@_child_process_exit_timeout = CHILD_PROCESS_DEFAULT_EXIT_TIMEOUT
|
102
|
+
@_child_process_kill_timeout = CHILD_PROCESS_DEFAULT_KILL_TIMEOUT
|
103
|
+
@_child_process_mutex = Mutex.new
|
104
|
+
end
|
105
|
+
|
106
|
+
def start
|
107
|
+
super
|
108
|
+
@_child_process_processes = {} # pid => ProcessInfo
|
109
|
+
end
|
110
|
+
|
111
|
+
def stop
|
112
|
+
@_child_process_mutex.synchronize{ @_child_process_processes.keys }.each do |pid|
|
113
|
+
process_info = @_child_process_processes[pid]
|
114
|
+
if process_info
|
115
|
+
process_info.thread[:_fluentd_plugin_helper_child_process_running] = false
|
116
|
+
end
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
def shutdown
|
121
|
+
@_child_process_mutex.synchronize{ @_child_process_processes.keys }.each do |pid|
|
122
|
+
process_info = @_child_process_processes[pid]
|
123
|
+
next if !process_info || !process_info.writeio_in_use
|
124
|
+
begin
|
125
|
+
Timeout.timeout(@_child_process_exit_timeout) do
|
126
|
+
process_info.writeio.close
|
127
|
+
end
|
128
|
+
rescue Timeout::Error
|
129
|
+
log.debug "External process #{process_info.title} doesn't exist after STDIN close in timeout #{@_child_process_exit_timeout}sec"
|
130
|
+
end
|
131
|
+
|
132
|
+
child_process_kill(process_info)
|
133
|
+
end
|
134
|
+
|
135
|
+
super
|
136
|
+
end
|
137
|
+
|
138
|
+
def close
|
139
|
+
while (pids = @_child_process_mutex.synchronize{ @_child_process_processes.keys }).size > 0
|
140
|
+
pids.each do |pid|
|
141
|
+
process_info = @_child_process_processes[pid]
|
142
|
+
if !process_info || !process_info.alive
|
143
|
+
@_child_process_mutex.synchronize{ @_child_process_processes.delete(pid) }
|
144
|
+
next
|
145
|
+
end
|
146
|
+
|
147
|
+
process_info.killed_at ||= Time.now # for illegular case (e.g., created after shutdown)
|
148
|
+
next if Time.now < process_info.killed_at + @_child_process_kill_timeout
|
149
|
+
|
150
|
+
child_process_kill(process_info, force: true)
|
151
|
+
@_child_process_mutex.synchronize{ @_child_process_processes.delete(pid) }
|
152
|
+
end
|
153
|
+
|
154
|
+
sleep CHILD_PROCESS_LOOP_CHECK_INTERVAL
|
155
|
+
end
|
156
|
+
|
157
|
+
super
|
158
|
+
end
|
159
|
+
|
160
|
+
def child_process_kill(process_info, force: false)
|
161
|
+
if !process_info || !process_info.alive
|
162
|
+
return
|
163
|
+
end
|
164
|
+
|
165
|
+
process_info.killed_at = Time.now unless force
|
166
|
+
|
167
|
+
begin
|
168
|
+
pid, status = Process.waitpid2(process_info.pid, Process::WNOHANG)
|
169
|
+
if pid && status
|
170
|
+
process_info.thread[:_fluentd_plugin_helper_child_process_exit_status] = status
|
171
|
+
process_info.alive = false
|
172
|
+
end
|
173
|
+
rescue Errno::ECHILD, Errno::ESRCH, Errno::EPERM
|
174
|
+
process_info.alive = false
|
175
|
+
rescue
|
176
|
+
# ignore
|
177
|
+
end
|
178
|
+
if !process_info.alive
|
179
|
+
return
|
180
|
+
end
|
181
|
+
|
182
|
+
begin
|
183
|
+
signal = (Fluent.windows? || force) ? :KILL : :TERM
|
184
|
+
Process.kill(signal, process_info.pid)
|
185
|
+
if force
|
186
|
+
process_info.alive = false
|
187
|
+
end
|
188
|
+
rescue Errno::ECHILD, Errno::ESRCH
|
189
|
+
process_info.alive = false
|
190
|
+
end
|
191
|
+
end
|
192
|
+
|
193
|
+
ProcessInfo = Struct.new(:title, :thread, :pid, :readio, :readio_in_use, :writeio, :writeio_in_use, :stderrio, :stderrio_in_use, :wait_thread, :alive, :killed_at)
|
194
|
+
|
195
|
+
def child_process_execute_once(
|
196
|
+
title, command, arguments, subprocess_name, mode, stderr, env, unsetenv, chdir,
|
197
|
+
internal_encoding, external_encoding, scrub, replace_string, &block
|
198
|
+
)
|
199
|
+
spawn_args = if arguments || subprocess_name
|
200
|
+
[ env, (subprocess_name ? [command, subprocess_name] : command), *(arguments || []) ]
|
201
|
+
else
|
202
|
+
[ env, command ]
|
203
|
+
end
|
204
|
+
spawn_opts = {
|
205
|
+
unsetenv_others: unsetenv,
|
206
|
+
}
|
207
|
+
if chdir
|
208
|
+
spawn_opts[:chdir] = chdir
|
209
|
+
end
|
210
|
+
|
211
|
+
encoding_options = {}
|
212
|
+
if scrub
|
213
|
+
encoding_options[:invalid] = encoding_options[:undef] = :replace
|
214
|
+
if replace_string
|
215
|
+
encoding_options[:replace] = replace_string
|
216
|
+
end
|
217
|
+
end
|
218
|
+
|
219
|
+
log.debug "Executing command", title: title, spawn: spawn_args, mode: mode, stderr: stderr
|
220
|
+
|
221
|
+
readio = writeio = stderrio = wait_thread = nil
|
222
|
+
readio_in_use = writeio_in_use = stderrio_in_use = false
|
223
|
+
|
224
|
+
if !mode.include?(:stderr) && !mode.include?(:read_with_stderr) && stderr != :discard # connect
|
225
|
+
writeio, readio, wait_thread = *Open3.popen2(*spawn_args, spawn_opts)
|
226
|
+
elsif mode.include?(:read_with_stderr)
|
227
|
+
writeio, readio, wait_thread = *Open3.popen2e(*spawn_args, spawn_opts)
|
228
|
+
else
|
229
|
+
writeio, readio, stderrio, wait_thread = *Open3.popen3(*spawn_args, spawn_opts)
|
230
|
+
if !mode.include?(:stderr) # stderr == :discard
|
231
|
+
stderrio.reopen(IO::NULL)
|
232
|
+
end
|
233
|
+
end
|
234
|
+
|
235
|
+
if mode.include?(:write)
|
236
|
+
writeio.set_encoding(external_encoding, internal_encoding, encoding_options)
|
237
|
+
writeio_in_use = true
|
238
|
+
end
|
239
|
+
if mode.include?(:read) || mode.include?(:read_with_stderr)
|
240
|
+
readio.set_encoding(external_encoding, internal_encoding, encoding_options)
|
241
|
+
readio_in_use = true
|
242
|
+
end
|
243
|
+
if mode.include?(:stderr)
|
244
|
+
stderrio.set_encoding(external_encoding, internal_encoding, encoding_options)
|
245
|
+
stderrio_in_use = true
|
246
|
+
end
|
247
|
+
|
248
|
+
pid = wait_thread.pid # wait_thread => Process::Waiter
|
249
|
+
|
250
|
+
io_objects = []
|
251
|
+
mode.each do |m|
|
252
|
+
io_objects << case m
|
253
|
+
when :read then readio
|
254
|
+
when :write then writeio
|
255
|
+
when :read_with_stderr then readio
|
256
|
+
when :stderr then stderrio
|
257
|
+
else
|
258
|
+
raise "BUG: invalid mode must be checked before here: '#{m}'"
|
259
|
+
end
|
260
|
+
end
|
261
|
+
|
262
|
+
m = Mutex.new
|
263
|
+
m.lock
|
264
|
+
thread = thread_create :child_process_callback do
|
265
|
+
m.lock # run after plugin thread get pid, thread instance and i/o
|
266
|
+
m.unlock
|
267
|
+
begin
|
268
|
+
block.call(*io_objects)
|
269
|
+
rescue EOFError => e
|
270
|
+
log.debug "Process exit and I/O closed", title: title, pid: pid, command: command, arguments: arguments
|
271
|
+
rescue IOError => e
|
272
|
+
if e.message == 'stream closed'
|
273
|
+
log.debug "Process I/O stream closed", title: title, pid: pid, command: command, arguments: arguments
|
274
|
+
else
|
275
|
+
log.error "Unexpected I/O error for child process", title: title, pid: pid, command: command, arguments: arguments, error: e
|
276
|
+
end
|
277
|
+
rescue => e
|
278
|
+
log.warn "Unexpected error while processing I/O for child process", title: title, pid: pid, command: command, error: e
|
279
|
+
end
|
280
|
+
process_info = @_child_process_mutex.synchronize do
|
281
|
+
process_info = @_child_process_processes[pid]
|
282
|
+
@_child_process_processes.delete(pid)
|
283
|
+
process_info
|
284
|
+
end
|
285
|
+
child_process_kill(process_info, force: true) if process_info && process_info.alive && ::Thread.current[:_fluentd_plugin_helper_child_process_running]
|
286
|
+
end
|
287
|
+
thread[:_fluentd_plugin_helper_child_process_running] = true
|
288
|
+
thread[:_fluentd_plugin_helper_child_process_pid] = pid
|
289
|
+
pinfo = ProcessInfo.new(title, thread, pid, readio, readio_in_use, writeio, writeio_in_use, stderrio, stderrio_in_use, wait_thread, true, nil)
|
290
|
+
@_child_process_mutex.synchronize do
|
291
|
+
@_child_process_processes[pid] = pinfo
|
292
|
+
end
|
293
|
+
m.unlock
|
294
|
+
pid
|
295
|
+
end
|
296
|
+
end
|
297
|
+
end
|
298
|
+
end
|