fluentd 0.14.7-x64-mingw32 → 0.14.10-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.
- checksums.yaml +4 -4
- data/.gitignore +2 -0
- data/.travis.yml +2 -0
- data/CONTRIBUTING.md +6 -1
- data/ChangeLog +95 -0
- data/Rakefile +21 -0
- data/appveyor.yml +1 -0
- data/code-of-conduct.md +3 -0
- data/example/out_exec_filter.conf +42 -0
- data/fluentd.gemspec +1 -1
- data/lib/fluent/agent.rb +2 -2
- data/lib/fluent/command/binlog_reader.rb +1 -1
- data/lib/fluent/command/cat.rb +15 -4
- data/lib/fluent/compat/output.rb +14 -9
- data/lib/fluent/compat/parser.rb +141 -11
- data/lib/fluent/config/configure_proxy.rb +2 -11
- data/lib/fluent/config/section.rb +8 -1
- data/lib/fluent/configurable.rb +1 -3
- data/lib/fluent/env.rb +1 -1
- data/lib/fluent/log.rb +1 -1
- data/lib/fluent/plugin/base.rb +17 -0
- data/lib/fluent/plugin/filter_parser.rb +108 -0
- data/lib/fluent/plugin/filter_record_transformer.rb +14 -35
- data/lib/fluent/plugin/filter_stdout.rb +1 -1
- data/lib/fluent/plugin/formatter.rb +5 -0
- data/lib/fluent/plugin/formatter_msgpack.rb +4 -0
- data/lib/fluent/plugin/formatter_stdout.rb +3 -2
- data/lib/fluent/plugin/formatter_tsv.rb +34 -0
- data/lib/fluent/plugin/in_exec.rb +48 -93
- data/lib/fluent/plugin/in_forward.rb +66 -265
- data/lib/fluent/plugin/in_http.rb +68 -65
- data/lib/fluent/plugin/in_monitor_agent.rb +8 -4
- data/lib/fluent/plugin/in_syslog.rb +42 -58
- data/lib/fluent/plugin/in_tail.rb +29 -14
- data/lib/fluent/plugin/in_tcp.rb +54 -14
- data/lib/fluent/plugin/in_udp.rb +49 -13
- data/lib/fluent/plugin/multi_output.rb +1 -3
- data/lib/fluent/plugin/out_exec.rb +58 -71
- data/lib/fluent/plugin/out_exec_filter.rb +199 -279
- data/lib/fluent/plugin/out_file.rb +172 -81
- data/lib/fluent/plugin/out_forward.rb +229 -206
- data/lib/fluent/plugin/out_stdout.rb +6 -21
- data/lib/fluent/plugin/output.rb +90 -59
- data/lib/fluent/plugin/parser.rb +121 -61
- data/lib/fluent/plugin/parser_csv.rb +9 -3
- data/lib/fluent/plugin/parser_json.rb +37 -35
- data/lib/fluent/plugin/parser_ltsv.rb +11 -19
- data/lib/fluent/plugin/parser_msgpack.rb +50 -0
- data/lib/fluent/plugin/parser_regexp.rb +15 -42
- data/lib/fluent/plugin/parser_tsv.rb +8 -3
- data/lib/fluent/plugin_helper.rb +10 -1
- data/lib/fluent/plugin_helper/child_process.rb +139 -73
- data/lib/fluent/plugin_helper/compat_parameters.rb +93 -4
- data/lib/fluent/plugin_helper/event_emitter.rb +14 -1
- data/lib/fluent/plugin_helper/event_loop.rb +24 -6
- data/lib/fluent/plugin_helper/extract.rb +16 -4
- data/lib/fluent/plugin_helper/formatter.rb +9 -11
- data/lib/fluent/plugin_helper/inject.rb +16 -1
- data/lib/fluent/plugin_helper/parser.rb +3 -3
- data/lib/fluent/plugin_helper/server.rb +494 -0
- data/lib/fluent/plugin_helper/socket.rb +101 -0
- data/lib/fluent/plugin_helper/socket_option.rb +84 -0
- data/lib/fluent/plugin_helper/timer.rb +1 -0
- data/lib/fluent/root_agent.rb +1 -1
- data/lib/fluent/test/driver/base.rb +95 -49
- data/lib/fluent/test/driver/base_owner.rb +18 -8
- data/lib/fluent/test/driver/multi_output.rb +2 -1
- data/lib/fluent/test/driver/output.rb +29 -6
- data/lib/fluent/test/helpers.rb +3 -1
- data/lib/fluent/test/log.rb +4 -0
- data/lib/fluent/test/startup_shutdown.rb +13 -0
- data/lib/fluent/time.rb +14 -8
- data/lib/fluent/version.rb +1 -1
- data/lib/fluent/winsvc.rb +1 -1
- data/test/command/test_binlog_reader.rb +5 -1
- data/test/compat/test_parser.rb +10 -0
- data/test/config/test_configurable.rb +193 -0
- data/test/config/test_configure_proxy.rb +0 -43
- data/test/helper.rb +36 -1
- data/test/plugin/test_base.rb +16 -0
- data/test/plugin/test_filter_parser.rb +665 -0
- data/test/plugin/test_filter_record_transformer.rb +36 -100
- data/test/plugin/test_filter_stdout.rb +18 -27
- data/test/plugin/test_in_dummy.rb +1 -1
- data/test/plugin/test_in_exec.rb +206 -94
- data/test/plugin/test_in_forward.rb +268 -347
- data/test/plugin/test_in_http.rb +310 -186
- data/test/plugin/test_in_monitor_agent.rb +65 -35
- data/test/plugin/test_in_syslog.rb +39 -3
- data/test/plugin/test_in_tcp.rb +78 -62
- data/test/plugin/test_in_udp.rb +101 -80
- data/test/plugin/test_out_exec.rb +223 -68
- data/test/plugin/test_out_exec_filter.rb +520 -169
- data/test/plugin/test_out_file.rb +637 -177
- data/test/plugin/test_out_forward.rb +242 -234
- data/test/plugin/test_out_null.rb +1 -1
- data/test/plugin/test_out_secondary_file.rb +4 -2
- data/test/plugin/test_out_stdout.rb +14 -35
- data/test/plugin/test_output_as_buffered.rb +60 -2
- data/test/plugin/test_parser.rb +359 -0
- data/test/plugin/test_parser_csv.rb +1 -2
- data/test/plugin/test_parser_json.rb +3 -4
- data/test/plugin/test_parser_labeled_tsv.rb +1 -2
- data/test/plugin/test_parser_none.rb +1 -2
- data/test/plugin/test_parser_regexp.rb +8 -4
- data/test/plugin/test_parser_tsv.rb +4 -3
- data/test/plugin_helper/test_child_process.rb +184 -0
- data/test/plugin_helper/test_compat_parameters.rb +88 -1
- data/test/plugin_helper/test_extract.rb +0 -1
- data/test/plugin_helper/test_formatter.rb +5 -2
- data/test/plugin_helper/test_inject.rb +21 -0
- data/test/plugin_helper/test_parser.rb +6 -5
- data/test/plugin_helper/test_server.rb +905 -0
- data/test/test_event_time.rb +3 -1
- data/test/test_output.rb +53 -2
- data/test/test_plugin_classes.rb +20 -0
- data/test/test_root_agent.rb +139 -0
- data/test/test_test_drivers.rb +135 -0
- metadata +28 -8
- data/test/plugin/test_parser_base.rb +0 -32
data/lib/fluent/plugin/in_tcp.rb
CHANGED
@@ -14,28 +14,68 @@
|
|
14
14
|
# limitations under the License.
|
15
15
|
#
|
16
16
|
|
17
|
-
require '
|
17
|
+
require 'fluent/plugin/input'
|
18
18
|
|
19
|
-
|
19
|
+
module Fluent::Plugin
|
20
|
+
class TcpInput < Input
|
21
|
+
Fluent::Plugin.register_input('tcp', self)
|
20
22
|
|
21
|
-
|
22
|
-
|
23
|
-
|
23
|
+
helpers :server, :parser, :extract, :compat_parameters
|
24
|
+
|
25
|
+
desc 'Tag of output events.'
|
26
|
+
config_param :tag, :string
|
27
|
+
desc 'The port to listen to.'
|
28
|
+
config_param :port, :integer, default: 5170
|
29
|
+
desc 'The bind address to listen to.'
|
30
|
+
config_param :bind, :string, default: '0.0.0.0'
|
31
|
+
|
32
|
+
desc "The field name of the client's hostname."
|
33
|
+
config_param :source_host_key, :string, default: nil, deprecated: "use source_hostname_key instead."
|
34
|
+
desc "The field name of the client's hostname."
|
35
|
+
config_param :source_hostname_key, :string, default: nil
|
36
|
+
|
37
|
+
config_param :blocking_timeout, :time, default: 0.5
|
24
38
|
|
25
|
-
config_set_default :port, 5170
|
26
39
|
desc 'The payload is read up to this character.'
|
27
40
|
config_param :delimiter, :string, default: "\n" # syslog family add "\n" to each message and this seems only way to split messages in tcp stream
|
28
41
|
|
29
|
-
def
|
30
|
-
|
42
|
+
def configure(conf)
|
43
|
+
compat_parameters_convert(conf, :parser)
|
44
|
+
super
|
45
|
+
@_event_loop_blocking_timeout = @blocking_timeout
|
46
|
+
@source_hostname_key ||= @source_host_key if @source_host_key
|
47
|
+
|
48
|
+
@parser = parser_create
|
49
|
+
end
|
50
|
+
|
51
|
+
def start
|
52
|
+
super
|
53
|
+
|
54
|
+
@buffer = ''
|
55
|
+
server_create(:in_tcp_server, @port, proto: :tcp, bind: @bind) do |data, conn|
|
56
|
+
@buffer << data
|
57
|
+
begin
|
58
|
+
pos = 0
|
59
|
+
while i = @buffer.index(@delimiter, pos)
|
60
|
+
msg = @buffer[pos...i]
|
61
|
+
pos = i + @delimiter.length
|
62
|
+
|
63
|
+
@parser.parse(msg) do |time, record|
|
64
|
+
unless time && record
|
65
|
+
log.warn "pattern not match", message: msg
|
66
|
+
next
|
67
|
+
end
|
31
68
|
|
32
|
-
|
33
|
-
|
34
|
-
|
69
|
+
tag = extract_tag_from_record(record)
|
70
|
+
tag ||= @tag
|
71
|
+
time ||= extract_time_from_record(record) || Fluent::EventTime.now
|
72
|
+
record[@source_hostname_key] = conn.remote_host if @source_hostname_key
|
73
|
+
router.emit(tag, time, record)
|
74
|
+
end
|
75
|
+
end
|
76
|
+
@buffer.slice!(0, pos) if pos > 0
|
77
|
+
end
|
35
78
|
end
|
36
|
-
client = ServerEngine::SocketManager::Client.new(socket_manager_path)
|
37
|
-
lsock = client.listen_tcp(@bind, @port)
|
38
|
-
Coolio::TCPServer.new(lsock, nil, SocketUtil::TcpHandler, log, @delimiter, callback)
|
39
79
|
end
|
40
80
|
end
|
41
81
|
end
|
data/lib/fluent/plugin/in_udp.rb
CHANGED
@@ -14,24 +14,60 @@
|
|
14
14
|
# limitations under the License.
|
15
15
|
#
|
16
16
|
|
17
|
-
require 'fluent/plugin/
|
17
|
+
require 'fluent/plugin/input'
|
18
18
|
|
19
|
-
module Fluent
|
20
|
-
class UdpInput <
|
21
|
-
Plugin.register_input('udp', self)
|
19
|
+
module Fluent::Plugin
|
20
|
+
class UdpInput < Input
|
21
|
+
Fluent::Plugin.register_input('udp', self)
|
22
|
+
|
23
|
+
helpers :server, :parser, :extract, :compat_parameters
|
24
|
+
|
25
|
+
desc 'Tag of output events.'
|
26
|
+
config_param :tag, :string
|
27
|
+
desc 'The port to listen to.'
|
28
|
+
config_param :port, :integer, default: 5160
|
29
|
+
desc 'The bind address to listen to.'
|
30
|
+
config_param :bind, :string, default: '0.0.0.0'
|
31
|
+
|
32
|
+
desc "The field name of the client's hostname."
|
33
|
+
config_param :source_host_key, :string, default: nil, deprecated: "use source_hostname_key instead."
|
34
|
+
desc "The field name of the client's hostname."
|
35
|
+
config_param :source_hostname_key, :string, default: nil
|
22
36
|
|
23
|
-
config_set_default :port, 5160
|
24
37
|
config_param :body_size_limit, :size, default: 4096
|
25
38
|
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
39
|
+
config_param :blocking_timeout, :time, default: 0.5
|
40
|
+
|
41
|
+
def configure(conf)
|
42
|
+
compat_parameters_convert(conf, :parser)
|
43
|
+
super
|
44
|
+
@_event_loop_blocking_timeout = @blocking_timeout
|
45
|
+
@source_hostname_key ||= @source_host_key if @source_host_key
|
46
|
+
|
47
|
+
@parser = parser_create
|
48
|
+
end
|
49
|
+
|
50
|
+
def start
|
51
|
+
super
|
52
|
+
|
53
|
+
log.info "listening udp socket", bind: @bind, port: @port
|
54
|
+
server_create(:in_udp_server, @port, proto: :udp, bind: @bind, max_bytes: @body_size_limit) do |data, sock|
|
55
|
+
data.chomp!
|
56
|
+
begin
|
57
|
+
@parser.parse(data) do |time, record|
|
58
|
+
unless time && record
|
59
|
+
log.warn "pattern not match", data: data
|
60
|
+
next
|
61
|
+
end
|
62
|
+
|
63
|
+
tag = extract_tag_from_record(record)
|
64
|
+
tag ||= @tag
|
65
|
+
time ||= extract_time_from_record(record) || Fluent::EventTime.now
|
66
|
+
record[@source_hostname_key] = sock.remote_host if @source_hostname_key
|
67
|
+
router.emit(tag, time, record)
|
68
|
+
end
|
69
|
+
end
|
31
70
|
end
|
32
|
-
client = ServerEngine::SocketManager::Client.new(socket_manager_path)
|
33
|
-
@usock = client.listen_udp(@bind, @port)
|
34
|
-
SocketUtil::UdpHandler.new(@usock, log, @body_size_limit, callback)
|
35
71
|
end
|
36
72
|
end
|
37
73
|
end
|
@@ -69,9 +69,7 @@ module Fluent
|
|
69
69
|
log.debug "adding store", type: type
|
70
70
|
|
71
71
|
output = Fluent::Plugin.new_output(type)
|
72
|
-
|
73
|
-
output.router = router
|
74
|
-
end
|
72
|
+
output.context_router = self.context_router
|
75
73
|
output.configure(store_conf)
|
76
74
|
@outputs << output
|
77
75
|
end
|
@@ -18,97 +18,84 @@ require 'tempfile'
|
|
18
18
|
|
19
19
|
require 'fluent/plugin/output'
|
20
20
|
require 'fluent/config/error'
|
21
|
-
require 'fluent/plugin/exec_util'
|
22
|
-
require 'fluent/mixin' # for TimeFormatter
|
23
21
|
|
24
22
|
module Fluent::Plugin
|
25
23
|
class ExecOutput < Output
|
26
24
|
Fluent::Plugin.register_output('exec', self)
|
27
25
|
|
28
|
-
helpers :compat_parameters
|
26
|
+
helpers :inject, :formatter, :compat_parameters, :child_process
|
29
27
|
|
30
|
-
desc 'The command (program) to execute. The exec plugin passes the path of a TSV file as the last
|
28
|
+
desc 'The command (program) to execute. The exec plugin passes the path of a TSV file as the last argument'
|
31
29
|
config_param :command, :string
|
32
|
-
|
33
|
-
config_param :
|
34
|
-
|
30
|
+
|
31
|
+
config_param :command_timeout, :time, default: 270 # 4min 30sec
|
32
|
+
|
33
|
+
config_section :format do
|
34
|
+
config_set_default :@type, 'tsv'
|
35
35
|
end
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
desc 'The format for event time used when the time_key parameter is specified. The default is UNIX time (integer).'
|
41
|
-
config_param :time_format, :string, default: nil
|
42
|
-
desc "The format used to map the incoming events to the program input. (#{Fluent::ExecUtil::SUPPORTED_FORMAT.keys.join(',')})"
|
43
|
-
config_param :format, default: :tsv, skip_accessor: true do |val|
|
44
|
-
f = Fluent::ExecUtil::SUPPORTED_FORMAT[val]
|
45
|
-
raise Fluent::ConfigError, "Unsupported format '#{val}'" unless f
|
46
|
-
f
|
36
|
+
|
37
|
+
config_section :inject do
|
38
|
+
config_set_default :time_type, :string
|
39
|
+
config_set_default :localtime, false
|
47
40
|
end
|
48
|
-
config_param :localtime, :bool, default: false
|
49
|
-
config_param :timezone, :string, default: nil
|
50
41
|
|
51
|
-
|
52
|
-
|
42
|
+
config_section :buffer do
|
43
|
+
config_set_default :delayed_commit_timeout, 300 # 5 min
|
53
44
|
end
|
54
45
|
|
55
|
-
|
56
|
-
compat_parameters_convert(conf, :buffer, default_chunk_key: 'time')
|
46
|
+
attr_reader :formatter # for tests
|
57
47
|
|
48
|
+
def configure(conf)
|
49
|
+
compat_parameters_convert(conf, :inject, :formatter, :buffer, default_chunk_key: 'time')
|
58
50
|
super
|
59
|
-
|
60
|
-
@formatter = case @format
|
61
|
-
when :tsv
|
62
|
-
if @keys.empty?
|
63
|
-
raise Fluent::ConfigError, "keys option is required on exec output for tsv format"
|
64
|
-
end
|
65
|
-
Fluent::ExecUtil::TSVFormatter.new(@keys)
|
66
|
-
when :json
|
67
|
-
Fluent::ExecUtil::JSONFormatter.new
|
68
|
-
when :msgpack
|
69
|
-
Fluent::ExecUtil::MessagePackFormatter.new
|
70
|
-
end
|
71
|
-
|
72
|
-
if @time_key
|
73
|
-
if @time_format
|
74
|
-
tf = Fluent::TimeFormatter.new(@time_format, @localtime, @timezone)
|
75
|
-
@time_format_proc = tf.method(:format)
|
76
|
-
else
|
77
|
-
@time_format_proc = Proc.new { |time| time.to_s }
|
78
|
-
end
|
79
|
-
end
|
51
|
+
@formatter = formatter_create
|
80
52
|
end
|
81
53
|
|
82
|
-
|
83
|
-
out = ''
|
84
|
-
if @time_key
|
85
|
-
record[@time_key] = @time_format_proc.call(time)
|
86
|
-
end
|
87
|
-
if @tag_key
|
88
|
-
record[@tag_key] = tag
|
89
|
-
end
|
90
|
-
@formatter.call(record, out)
|
91
|
-
out
|
92
|
-
end
|
54
|
+
NEWLINE = "\n"
|
93
55
|
|
94
|
-
def
|
95
|
-
|
96
|
-
|
56
|
+
def format(tag, time, record)
|
57
|
+
record = inject_values_to_record(tag, time, record)
|
58
|
+
if @formatter.formatter_type == :text_per_line
|
59
|
+
@formatter.format(tag, time, record).chomp + NEWLINE
|
97
60
|
else
|
98
|
-
|
99
|
-
tmpfile.binmode
|
100
|
-
chunk.write_to(tmpfile)
|
101
|
-
tmpfile.close
|
102
|
-
prog = "#{@command} #{tmpfile.path}"
|
61
|
+
@formatter.format(tag, time, record)
|
103
62
|
end
|
63
|
+
end
|
104
64
|
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
65
|
+
def try_write(chunk)
|
66
|
+
tmpfile = nil
|
67
|
+
prog = if chunk.respond_to?(:path)
|
68
|
+
"#{@command} #{chunk.path}"
|
69
|
+
else
|
70
|
+
tmpfile = Tempfile.new("fluent-plugin-out-exec-")
|
71
|
+
tmpfile.binmode
|
72
|
+
chunk.write_to(tmpfile)
|
73
|
+
tmpfile.close
|
74
|
+
"#{@command} #{tmpfile.path}"
|
75
|
+
end
|
76
|
+
chunk_id = chunk.unique_id
|
77
|
+
callback = ->(status){
|
78
|
+
begin
|
79
|
+
if tmpfile
|
80
|
+
tmpfile.delete rescue nil
|
81
|
+
end
|
82
|
+
if status && status.success?
|
83
|
+
commit_write(chunk_id)
|
84
|
+
elsif status
|
85
|
+
# #rollback_write will be done automatically if it isn't called at here.
|
86
|
+
# But it's after command_timeout, and this timeout should be longer than users expectation.
|
87
|
+
# So here, this plugin calls it explicitly.
|
88
|
+
rollback_write(chunk_id)
|
89
|
+
log.warn "command exits with error code", prog: prog, status: status.exitstatus, signal: status.termsig
|
90
|
+
else
|
91
|
+
rollback_write(chunk_id)
|
92
|
+
log.warn "command unexpectedly exits without exit status", prog: prog
|
93
|
+
end
|
94
|
+
rescue => e
|
95
|
+
log.error "unexpected error in child process callback", error: e
|
96
|
+
end
|
97
|
+
}
|
98
|
+
child_process_execute(:out_exec_process, prog, stderr: :connect, immediate: true, parallel: true, mode: [], wait_timeout: @command_timeout, on_exit_callback: callback)
|
112
99
|
end
|
113
100
|
end
|
114
101
|
end
|
@@ -13,148 +13,142 @@
|
|
13
13
|
# See the License for the specific language governing permissions and
|
14
14
|
# limitations under the License.
|
15
15
|
#
|
16
|
-
|
17
|
-
require 'yajl'
|
18
|
-
|
19
|
-
require 'fluent/output'
|
16
|
+
require 'fluent/plugin/output'
|
20
17
|
require 'fluent/env'
|
21
|
-
require 'fluent/time'
|
22
|
-
require 'fluent/timezone'
|
23
|
-
require 'fluent/plugin/exec_util'
|
24
18
|
require 'fluent/config/error'
|
25
19
|
|
26
|
-
|
27
|
-
class ExecFilterOutput < BufferedOutput
|
28
|
-
Plugin.register_output('exec_filter', self)
|
20
|
+
require 'yajl'
|
29
21
|
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
22
|
+
module Fluent::Plugin
|
23
|
+
class ExecFilterOutput < Output
|
24
|
+
Fluent::Plugin.register_output('exec_filter', self)
|
25
|
+
|
26
|
+
helpers :compat_parameters, :inject, :formatter, :parser, :extract, :child_process, :event_emitter
|
34
27
|
|
35
28
|
desc 'The command (program) to execute.'
|
36
29
|
config_param :command, :string
|
37
30
|
|
38
|
-
config_param :remove_prefix, :string, default: nil
|
39
|
-
config_param :add_prefix, :string, default: nil
|
31
|
+
config_param :remove_prefix, :string, default: nil, deprecated: "use @label instead for event routing"
|
32
|
+
config_param :add_prefix, :string, default: nil, deprecated: "use @label instead for event routing"
|
33
|
+
|
34
|
+
config_section :inject do
|
35
|
+
config_set_default :time_type, :unixtime
|
36
|
+
end
|
40
37
|
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
raise ConfigError, "Unsupported in_format '#{val}'" unless f
|
45
|
-
f
|
38
|
+
config_section :format do
|
39
|
+
config_set_default :@type, 'tsv'
|
40
|
+
config_set_default :localtime, true
|
46
41
|
end
|
47
|
-
|
48
|
-
|
49
|
-
|
42
|
+
|
43
|
+
config_section :parse do
|
44
|
+
config_set_default :@type, 'tsv'
|
45
|
+
config_set_default :time_key, nil
|
46
|
+
config_set_default :time_format, nil
|
47
|
+
config_set_default :localtime, true
|
48
|
+
config_set_default :estimate_current_event, false
|
50
49
|
end
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
config_param :in_time_key, default: nil
|
55
|
-
desc 'The format for event time used when the in_time_key parameter is specified.(Defauls is UNIX time)'
|
56
|
-
config_param :in_time_format, default: nil
|
57
|
-
|
58
|
-
desc "The format used to process the program output.(#{Fluent::ExecUtil::SUPPORTED_FORMAT.keys.join(',')})"
|
59
|
-
config_param :out_format, default: :tsv do |val|
|
60
|
-
f = Fluent::ExecUtil::SUPPORTED_FORMAT[val]
|
61
|
-
raise ConfigError, "Unsupported out_format '#{val}'" unless f
|
62
|
-
f
|
50
|
+
|
51
|
+
config_section :extract do
|
52
|
+
config_set_default :time_type, :float
|
63
53
|
end
|
64
|
-
|
65
|
-
|
66
|
-
|
54
|
+
|
55
|
+
config_section :buffer do
|
56
|
+
config_set_default :flush_mode, :interval
|
57
|
+
config_set_default :flush_interval, 1
|
67
58
|
end
|
68
|
-
desc 'The name of the key to use as the event tag.'
|
69
|
-
config_param :out_tag_key, default: nil
|
70
|
-
desc 'The name of the key to use as the event time.'
|
71
|
-
config_param :out_time_key, default: nil
|
72
|
-
desc 'The format for event time used when the in_time_key parameter is specified.(Defauls is UNIX time)'
|
73
|
-
config_param :out_time_format, default: nil
|
74
59
|
|
75
60
|
config_param :tag, :string, default: nil
|
76
61
|
|
77
|
-
config_param :
|
78
|
-
config_param :
|
62
|
+
config_param :tag_key, :string, default: nil, deprecated: "use 'tag_key' in <inject>/<extract> instead"
|
63
|
+
config_param :time_key, :string, default: nil, deprecated: "use 'time_key' in <inject>/<extract> instead"
|
64
|
+
config_param :time_format, :string, default: nil, deprecated: "use 'time_format' in <inject>/<extract> instead"
|
65
|
+
|
66
|
+
desc 'The default block size to read if parser requires partial read.'
|
67
|
+
config_param :read_block_size, :size, default: 10240 # 10k
|
79
68
|
|
80
|
-
desc 'If true, use localtime with in_time_format.'
|
81
|
-
config_param :localtime, :bool, default: true
|
82
|
-
desc 'If true, use timezone with in_time_format.'
|
83
|
-
config_param :timezone, :string, default: nil
|
84
69
|
desc 'The number of spawned process for command.'
|
85
70
|
config_param :num_children, :integer, default: 1
|
86
71
|
|
87
|
-
desc 'Respawn command when command exit.'
|
72
|
+
desc 'Respawn command when command exit. ["none", "inf" or positive integer for times to respawn (defaut: none)]'
|
88
73
|
# nil, 'none' or 0: no respawn, 'inf' or -1: infinite times, positive integer: try to respawn specified times only
|
89
74
|
config_param :child_respawn, :string, default: nil
|
90
75
|
|
91
76
|
# 0: output logs for all of messages to emit
|
92
77
|
config_param :suppress_error_log_interval, :time, default: 0
|
93
78
|
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
79
|
+
attr_reader :formatter, :parser # for tests
|
80
|
+
|
81
|
+
KEYS_FOR_IN_AND_OUT = {
|
82
|
+
'tag_key' => ['in_tag_key', 'out_tag_key'],
|
83
|
+
'time_key' => ['in_time_key', 'out_time_key'],
|
84
|
+
'time_format' => ['in_time_format', 'out_time_format'],
|
85
|
+
}
|
86
|
+
COMPAT_INJECT_PARAMS = {
|
87
|
+
'in_tag_key' => 'tag_key',
|
88
|
+
'in_time_key' => 'time_key',
|
89
|
+
'in_time_format' => 'time_format',
|
90
|
+
}
|
91
|
+
COMPAT_FORMAT_PARAMS = {
|
92
|
+
'in_format' => '@type',
|
93
|
+
'in_keys' => 'keys',
|
94
|
+
}
|
95
|
+
COMPAT_PARSE_PARAMS = {
|
96
|
+
'out_format' => '@type',
|
97
|
+
'out_keys' => 'keys',
|
98
|
+
}
|
99
|
+
COMPAT_EXTRACT_PARAMS = {
|
100
|
+
'out_tag_key' => 'tag_key',
|
101
|
+
'out_time_key' => 'time_key',
|
102
|
+
'out_time_format' => 'time_format',
|
103
|
+
}
|
104
|
+
|
105
|
+
def exec_filter_compat_parameters_copy_to_subsection!(conf, subsection_name, params)
|
106
|
+
return unless conf.elements(subsection_name).empty?
|
107
|
+
return unless params.keys.any?{|k| conf.has_key?(k) }
|
108
|
+
hash = {}
|
109
|
+
params.each_pair do |compat, current|
|
110
|
+
hash[current] = conf[compat] if conf.has_key?(compat)
|
107
111
|
end
|
112
|
+
conf.elements << Fluent::Config::Element.new(subsection_name, '', hash, [])
|
113
|
+
end
|
108
114
|
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
115
|
+
def exec_filter_compat_parameters_convert!(conf)
|
116
|
+
KEYS_FOR_IN_AND_OUT.each_pair do |inout, keys|
|
117
|
+
if conf.has_key?(inout)
|
118
|
+
keys.each do |k|
|
119
|
+
conf[k] = conf[inout]
|
120
|
+
end
|
121
|
+
end
|
113
122
|
end
|
123
|
+
exec_filter_compat_parameters_copy_to_subsection!(conf, 'inject', COMPAT_INJECT_PARAMS)
|
124
|
+
exec_filter_compat_parameters_copy_to_subsection!(conf, 'format', COMPAT_FORMAT_PARAMS)
|
125
|
+
exec_filter_compat_parameters_copy_to_subsection!(conf, 'parse', COMPAT_PARSE_PARAMS)
|
126
|
+
exec_filter_compat_parameters_copy_to_subsection!(conf, 'extract', COMPAT_EXTRACT_PARAMS)
|
127
|
+
end
|
114
128
|
|
115
|
-
|
129
|
+
def configure(conf)
|
130
|
+
exec_filter_compat_parameters_convert!(conf)
|
131
|
+
compat_parameters_convert(conf, :buffer)
|
116
132
|
|
117
|
-
if conf
|
118
|
-
|
119
|
-
|
120
|
-
|
133
|
+
if inject_section = conf.elements('inject').first
|
134
|
+
if inject_section.has_key?('time_format')
|
135
|
+
inject_section['time_type'] ||= 'string'
|
136
|
+
end
|
121
137
|
end
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
138
|
+
if extract_section = conf.elements('extract').first
|
139
|
+
if extract_section.has_key?('time_format')
|
140
|
+
extract_section['time_type'] ||= 'string'
|
141
|
+
end
|
126
142
|
end
|
127
143
|
|
128
|
-
|
129
|
-
raise ConfigError, "'tag' or 'out_tag_key' option is required on exec_filter output"
|
130
|
-
end
|
144
|
+
super
|
131
145
|
|
132
|
-
if
|
133
|
-
|
134
|
-
tf = TimeFormatter.new(f, @localtime, @timezone)
|
135
|
-
@time_format_proc = tf.method(:format)
|
136
|
-
else
|
137
|
-
@time_format_proc = Proc.new {|time| time.to_s }
|
138
|
-
end
|
139
|
-
elsif @in_time_format
|
140
|
-
log.warn "in_time_format effects nothing when in_time_key is not specified: #{conf}"
|
146
|
+
if !@tag && (!@extract_config || !@extract_config.tag_key)
|
147
|
+
raise Fluent::ConfigError, "'tag' or '<extract> tag_key </extract>' option is required on exec_filter output"
|
141
148
|
end
|
142
149
|
|
143
|
-
|
144
|
-
|
145
|
-
@time_parse_proc =
|
146
|
-
begin
|
147
|
-
strptime = Strptime.new(f)
|
148
|
-
Proc.new { |str| Fluent::EventTime.from_time(strptime.exec(str)) }
|
149
|
-
rescue
|
150
|
-
Proc.new {|str| Fluent::EventTime.from_time(Time.strptime(str, f)) }
|
151
|
-
end
|
152
|
-
else
|
153
|
-
@time_parse_proc = Proc.new {|str| Fluent::EventTime.from_time(Time.at(str.to_f)) }
|
154
|
-
end
|
155
|
-
elsif @out_time_format
|
156
|
-
log.warn "out_time_format effects nothing when out_time_key is not specified: #{conf}"
|
157
|
-
end
|
150
|
+
@formatter = formatter_create
|
151
|
+
@parser = parser_create
|
158
152
|
|
159
153
|
if @remove_prefix
|
160
154
|
@removed_prefix_string = @remove_prefix + '.'
|
@@ -164,30 +158,6 @@ module Fluent
|
|
164
158
|
@added_prefix_string = @add_prefix + '.'
|
165
159
|
end
|
166
160
|
|
167
|
-
case @in_format
|
168
|
-
when :tsv
|
169
|
-
if @in_keys.empty?
|
170
|
-
raise ConfigError, "in_keys option is required on exec_filter output for tsv in_format"
|
171
|
-
end
|
172
|
-
@formatter = Fluent::ExecUtil::TSVFormatter.new(@in_keys)
|
173
|
-
when :json
|
174
|
-
@formatter = Fluent::ExecUtil::JSONFormatter.new
|
175
|
-
when :msgpack
|
176
|
-
@formatter = Fluent::ExecUtil::MessagePackFormatter.new
|
177
|
-
end
|
178
|
-
|
179
|
-
case @out_format
|
180
|
-
when :tsv
|
181
|
-
if @out_keys.empty?
|
182
|
-
raise ConfigError, "out_keys option is required on exec_filter output for tsv in_format"
|
183
|
-
end
|
184
|
-
@parser = Fluent::ExecUtil::TSVParser.new(@out_keys, method(:on_message))
|
185
|
-
when :json
|
186
|
-
@parser = Fluent::ExecUtil::JSONParser.new(method(:on_message))
|
187
|
-
when :msgpack
|
188
|
-
@parser = Fluent::ExecUtil::MessagePackParser.new(method(:on_message))
|
189
|
-
end
|
190
|
-
|
191
161
|
@respawns = if @child_respawn.nil? or @child_respawn == 'none' or @child_respawn == '0'
|
192
162
|
0
|
193
163
|
elsif @child_respawn == 'inf' or @child_respawn == '-1'
|
@@ -202,192 +172,142 @@ module Fluent
|
|
202
172
|
@next_log_time = Time.now.to_i
|
203
173
|
end
|
204
174
|
|
175
|
+
ExecutedProcess = Struct.new(:mutex, :pid, :respawns, :readio, :writeio)
|
176
|
+
|
205
177
|
def start
|
206
178
|
super
|
207
179
|
|
180
|
+
@children_mutex = Mutex.new
|
208
181
|
@children = []
|
209
182
|
@rr = 0
|
210
|
-
begin
|
211
|
-
@num_children.times do
|
212
|
-
c = ChildProcess.new(@parser, @respawns, log)
|
213
|
-
c.start(@command)
|
214
|
-
@children << c
|
215
|
-
end
|
216
|
-
rescue
|
217
|
-
shutdown
|
218
|
-
raise
|
219
|
-
end
|
220
|
-
end
|
221
183
|
|
222
|
-
|
223
|
-
|
224
|
-
|
225
|
-
|
226
|
-
|
227
|
-
|
228
|
-
|
229
|
-
|
230
|
-
|
231
|
-
|
232
|
-
|
233
|
-
|
234
|
-
c.shutdown
|
235
|
-
true
|
184
|
+
exit_callback = ->(status){
|
185
|
+
c = @children.select{|child| child.pid == status.pid }.first
|
186
|
+
if c
|
187
|
+
unless self.stopped?
|
188
|
+
log.warn "child process exits with error code", code: status.to_i, status: status.exitstatus, signal: status.termsig
|
189
|
+
end
|
190
|
+
c.mutex.synchronize do
|
191
|
+
(c.writeio && c.writeio.close) rescue nil
|
192
|
+
(c.readio && c.readio.close) rescue nil
|
193
|
+
c.pid = c.readio = c.writeio = nil
|
194
|
+
end
|
195
|
+
end
|
236
196
|
}
|
237
|
-
|
238
|
-
|
239
|
-
|
240
|
-
|
241
|
-
|
242
|
-
|
243
|
-
|
244
|
-
|
197
|
+
child_process_callback = ->(index, readio, writeio){
|
198
|
+
pid = child_process_id
|
199
|
+
c = @children[index]
|
200
|
+
writeio.sync = true
|
201
|
+
c.mutex.synchronize do
|
202
|
+
c.pid = pid
|
203
|
+
c.respawns = @respawns
|
204
|
+
c.readio = readio
|
205
|
+
c.writeio = writeio
|
245
206
|
end
|
246
|
-
end
|
247
207
|
|
248
|
-
|
249
|
-
|
250
|
-
|
251
|
-
|
252
|
-
|
253
|
-
end
|
254
|
-
if @in_tag_key
|
255
|
-
record[@in_tag_key] = tag
|
208
|
+
run(readio)
|
209
|
+
}
|
210
|
+
execute_child_process = ->(index){
|
211
|
+
child_process_execute("out_exec_filter_child#{index}".to_sym, @command, on_exit_callback: exit_callback) do |readio, writeio|
|
212
|
+
child_process_callback.call(index, readio, writeio)
|
256
213
|
end
|
257
|
-
@formatter.call(record, out)
|
258
214
|
}
|
259
215
|
|
260
|
-
|
261
|
-
|
262
|
-
|
263
|
-
|
264
|
-
r = @rr = (@rr + 1) % @children.length
|
265
|
-
@children[r].write chunk
|
266
|
-
end
|
267
|
-
|
268
|
-
class ChildProcess
|
269
|
-
attr_accessor :finished
|
270
|
-
|
271
|
-
def initialize(parser, respawns=0, log = $log)
|
272
|
-
@pid = nil
|
273
|
-
@thread = nil
|
274
|
-
@parser = parser
|
275
|
-
@respawns = respawns
|
276
|
-
@mutex = Mutex.new
|
277
|
-
@finished = nil
|
278
|
-
@log = log
|
279
|
-
end
|
280
|
-
|
281
|
-
def start(command)
|
282
|
-
@command = command
|
283
|
-
@mutex.synchronize do
|
284
|
-
@io = IO.popen(command, "r+")
|
285
|
-
@pid = @io.pid
|
286
|
-
@io.sync = true
|
287
|
-
@thread = Thread.new(&method(:run))
|
216
|
+
@children_mutex.synchronize do
|
217
|
+
@num_children.times do |i|
|
218
|
+
@children << ExecutedProcess.new(Mutex.new, nil, 0, nil, nil)
|
219
|
+
execute_child_process.call(i)
|
288
220
|
end
|
289
|
-
@finished = false
|
290
221
|
end
|
291
222
|
|
292
|
-
|
293
|
-
|
294
|
-
|
295
|
-
|
296
|
-
|
297
|
-
|
298
|
-
|
299
|
-
|
300
|
-
|
301
|
-
|
302
|
-
|
303
|
-
|
304
|
-
|
305
|
-
|
306
|
-
|
307
|
-
# ignore if successfully killed by :TERM
|
223
|
+
if @respawns != 0
|
224
|
+
thread_create(:out_exec_filter_respawn_monitor) do
|
225
|
+
while thread_current_running?
|
226
|
+
@children.each_with_index do |c, i|
|
227
|
+
if c.mutex && c.mutex.synchronize{ c.pid.nil? && c.respawns != 0 }
|
228
|
+
respawns = c.mutex.synchronize do
|
229
|
+
c.respawns -= 1 if c.respawns > 0
|
230
|
+
c.respawns
|
231
|
+
end
|
232
|
+
log.info "respawning child process", num: i, respawn_counter: respawns
|
233
|
+
execute_child_process.call(i)
|
234
|
+
end
|
235
|
+
end
|
236
|
+
sleep 0.2
|
237
|
+
end
|
308
238
|
end
|
309
|
-
@thread.join
|
310
239
|
end
|
240
|
+
end
|
311
241
|
|
312
|
-
|
313
|
-
|
314
|
-
|
315
|
-
|
316
|
-
end
|
317
|
-
end
|
242
|
+
def terminate
|
243
|
+
@children = []
|
244
|
+
super
|
245
|
+
end
|
318
246
|
|
319
|
-
|
320
|
-
|
321
|
-
|
322
|
-
|
323
|
-
# Broken pipe (child process unexpectedly exited)
|
324
|
-
@log.warn "exec_filter Broken pipe, child process maybe exited.", command: @command
|
325
|
-
if try_respawn
|
326
|
-
retry # retry chunk#write_to with child respawned
|
327
|
-
else
|
328
|
-
raise e # to retry #write with other ChildProcess instance (when num_children > 1)
|
329
|
-
end
|
247
|
+
def tag_remove_prefix(tag)
|
248
|
+
if @remove_prefix
|
249
|
+
if (tag[0, @removed_length] == @removed_prefix_string and tag.length > @removed_length) or tag == @removed_prefix_string
|
250
|
+
tag = tag[@removed_length..-1] || ''
|
330
251
|
end
|
331
252
|
end
|
253
|
+
tag
|
254
|
+
end
|
332
255
|
|
333
|
-
|
334
|
-
return false if @respawns == 0
|
335
|
-
@mutex.synchronize do
|
336
|
-
return false if @respawns == 0
|
337
|
-
|
338
|
-
kill_child(5) # TODO wait time
|
339
|
-
|
340
|
-
@io = IO.popen(@command, "r+")
|
341
|
-
@pid = @io.pid
|
342
|
-
@io.sync = true
|
343
|
-
@thread = Thread.new(&method(:run))
|
256
|
+
NEWLINE = "\n"
|
344
257
|
|
345
|
-
|
346
|
-
|
347
|
-
|
348
|
-
|
258
|
+
def format(tag, time, record)
|
259
|
+
tag = tag_remove_prefix(tag)
|
260
|
+
record = inject_values_to_record(tag, time, record)
|
261
|
+
if @formatter.formatter_type == :text_per_line
|
262
|
+
@formatter.format(tag, time, record).chomp + NEWLINE
|
263
|
+
else
|
264
|
+
@formatter.format(tag, time, record)
|
349
265
|
end
|
266
|
+
end
|
350
267
|
|
351
|
-
|
352
|
-
|
353
|
-
|
354
|
-
|
355
|
-
@
|
356
|
-
|
357
|
-
|
358
|
-
unless @finished
|
359
|
-
@log.error "exec_filter process unexpectedly exited.", command: @command, ecode: stat.to_i
|
360
|
-
unless @respawns == 0
|
361
|
-
@log.warn "exec_filter child process will respawn for next input data (respawns #{@respawns})."
|
362
|
-
end
|
268
|
+
def write(chunk)
|
269
|
+
try_times = 0
|
270
|
+
while true
|
271
|
+
r = @rr = (@rr + 1) % @children.length
|
272
|
+
if @children[r].pid && writeio = @children[r].writeio
|
273
|
+
chunk.write_to(writeio)
|
274
|
+
break
|
363
275
|
end
|
276
|
+
try_times += 1
|
277
|
+
raise "no healthy child processes exist" if try_times >= @children.length
|
364
278
|
end
|
365
279
|
end
|
366
280
|
|
367
|
-
def
|
368
|
-
|
369
|
-
|
370
|
-
|
371
|
-
|
372
|
-
|
373
|
-
|
374
|
-
|
375
|
-
|
376
|
-
|
377
|
-
|
378
|
-
|
379
|
-
end
|
281
|
+
def run(io)
|
282
|
+
case
|
283
|
+
when @parser.implement?(:parse_io)
|
284
|
+
@parser.parse_io(io, &method(:on_record))
|
285
|
+
when @parser.implement?(:parse_partial_data)
|
286
|
+
until io.eof?
|
287
|
+
@parser.parse_partial_data(io.readpartial(@read_block_size), &method(:on_record))
|
288
|
+
end
|
289
|
+
when @parser.parser_type == :text_per_line
|
290
|
+
io.each_line do |line|
|
291
|
+
@parser.parse(line.chomp, &method(:on_record))
|
292
|
+
end
|
380
293
|
else
|
381
|
-
|
294
|
+
@parser.parse(io.read, &method(:on_record))
|
382
295
|
end
|
296
|
+
end
|
383
297
|
|
298
|
+
def on_record(time, record)
|
299
|
+
tag = extract_tag_from_record(record)
|
300
|
+
tag = @added_prefix_string + tag if tag && @add_prefix
|
301
|
+
tag ||= @tag
|
302
|
+
time ||= extract_time_from_record(record) || Fluent::EventTime.now
|
384
303
|
router.emit(tag, time, record)
|
385
|
-
rescue
|
304
|
+
rescue => e
|
386
305
|
if @suppress_error_log_interval == 0 || Time.now.to_i > @next_log_time
|
387
|
-
log.error "exec_filter failed to emit",
|
388
|
-
log.
|
306
|
+
log.error "exec_filter failed to emit", record: Yajl.dump(record), error: e
|
307
|
+
log.error_backtrace e.backtrace
|
389
308
|
@next_log_time = Time.now.to_i + @suppress_error_log_interval
|
390
309
|
end
|
310
|
+
router.emit_error_event(tag, time, record, e) if tag && time && record
|
391
311
|
end
|
392
312
|
end
|
393
313
|
end
|