fluentd 0.10.35 → 0.10.36
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.
- data/.travis.yml +13 -0
- data/ChangeLog +9 -0
- data/fluentd.gemspec +1 -1
- data/lib/fluent/buffer.rb +210 -214
- data/lib/fluent/command/fluentd.rb +4 -0
- data/lib/fluent/config.rb +1 -0
- data/lib/fluent/engine.rb +10 -10
- data/lib/fluent/output.rb +404 -406
- data/lib/fluent/plugin/buf_file.rb +146 -151
- data/lib/fluent/plugin/buf_memory.rb +62 -67
- data/lib/fluent/plugin/in_debug_agent.rb +27 -31
- data/lib/fluent/plugin/in_exec.rb +86 -90
- data/lib/fluent/plugin/in_forward.rb +171 -171
- data/lib/fluent/plugin/in_gc_stat.rb +43 -47
- data/lib/fluent/plugin/in_http.rb +214 -216
- data/lib/fluent/plugin/in_monitor_agent.rb +212 -214
- data/lib/fluent/plugin/in_object_space.rb +75 -79
- data/lib/fluent/plugin/in_status.rb +44 -50
- data/lib/fluent/plugin/in_stream.rb +159 -160
- data/lib/fluent/plugin/in_syslog.rb +149 -153
- data/lib/fluent/plugin/in_tail.rb +382 -387
- data/lib/fluent/plugin/out_copy.rb +40 -45
- data/lib/fluent/plugin/out_exec.rb +52 -57
- data/lib/fluent/plugin/out_exec_filter.rb +327 -331
- data/lib/fluent/plugin/out_file.rb +78 -74
- data/lib/fluent/plugin/out_forward.rb +410 -414
- data/lib/fluent/plugin/out_null.rb +15 -19
- data/lib/fluent/plugin/out_roundrobin.rb +63 -68
- data/lib/fluent/plugin/out_stdout.rb +9 -14
- data/lib/fluent/plugin/out_stream.rb +83 -90
- data/lib/fluent/plugin/out_test.rb +42 -46
- data/lib/fluent/supervisor.rb +15 -0
- data/lib/fluent/version.rb +1 -1
- data/test/plugin/in_stream.rb +2 -0
- data/test/plugin/out_file.rb +19 -1
- metadata +6 -5
@@ -16,58 +16,53 @@
|
|
16
16
|
# limitations under the License.
|
17
17
|
#
|
18
18
|
module Fluent
|
19
|
+
class CopyOutput < MultiOutput
|
20
|
+
Plugin.register_output('copy', self)
|
19
21
|
|
22
|
+
def initialize
|
23
|
+
@outputs = []
|
24
|
+
end
|
20
25
|
|
21
|
-
|
22
|
-
Plugin.register_output('copy', self)
|
23
|
-
|
24
|
-
def initialize
|
25
|
-
@outputs = []
|
26
|
-
end
|
27
|
-
|
28
|
-
attr_reader :outputs
|
29
|
-
|
30
|
-
def configure(conf)
|
31
|
-
conf.elements.select {|e|
|
32
|
-
e.name == 'store'
|
33
|
-
}.each {|e|
|
34
|
-
type = e['type']
|
35
|
-
unless type
|
36
|
-
raise ConfigError, "Missing 'type' parameter on <store> directive"
|
37
|
-
end
|
38
|
-
$log.debug "adding store type=#{type.dump}"
|
39
|
-
|
40
|
-
output = Plugin.new_output(type)
|
41
|
-
output.configure(e)
|
42
|
-
@outputs << output
|
43
|
-
}
|
44
|
-
end
|
26
|
+
attr_reader :outputs
|
45
27
|
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
28
|
+
def configure(conf)
|
29
|
+
conf.elements.select {|e|
|
30
|
+
e.name == 'store'
|
31
|
+
}.each {|e|
|
32
|
+
type = e['type']
|
33
|
+
unless type
|
34
|
+
raise ConfigError, "Missing 'type' parameter on <store> directive"
|
35
|
+
end
|
36
|
+
$log.debug "adding store type=#{type.dump}"
|
51
37
|
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
38
|
+
output = Plugin.new_output(type)
|
39
|
+
output.configure(e)
|
40
|
+
@outputs << output
|
41
|
+
}
|
42
|
+
end
|
57
43
|
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
es.each {|time,record|
|
62
|
-
m.add(time, record)
|
44
|
+
def start
|
45
|
+
@outputs.each {|o|
|
46
|
+
o.start
|
63
47
|
}
|
64
|
-
es = m
|
65
48
|
end
|
66
|
-
chain = OutputChain.new(@outputs, tag, es, chain)
|
67
|
-
chain.next
|
68
|
-
end
|
69
|
-
end
|
70
49
|
|
50
|
+
def shutdown
|
51
|
+
@outputs.each {|o|
|
52
|
+
o.shutdown
|
53
|
+
}
|
54
|
+
end
|
71
55
|
|
56
|
+
def emit(tag, es, chain)
|
57
|
+
unless es.repeatable?
|
58
|
+
m = MultiEventStream.new
|
59
|
+
es.each {|time,record|
|
60
|
+
m.add(time, record)
|
61
|
+
}
|
62
|
+
es = m
|
63
|
+
end
|
64
|
+
chain = OutputChain.new(@outputs, tag, es, chain)
|
65
|
+
chain.next
|
66
|
+
end
|
67
|
+
end
|
72
68
|
end
|
73
|
-
|
@@ -16,76 +16,71 @@
|
|
16
16
|
# limitations under the License.
|
17
17
|
#
|
18
18
|
module Fluent
|
19
|
+
class ExecOutput < TimeSlicedOutput
|
20
|
+
Plugin.register_output('exec', self)
|
19
21
|
|
22
|
+
def initialize
|
23
|
+
super
|
24
|
+
require 'tempfile'
|
25
|
+
@localtime = false
|
26
|
+
end
|
20
27
|
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
require 'tempfile'
|
27
|
-
@localtime = false
|
28
|
-
end
|
29
|
-
|
30
|
-
config_param :command, :string
|
31
|
-
config_param :keys, :string
|
32
|
-
config_param :tag_key, :string, :default => nil
|
33
|
-
config_param :time_key, :string, :default => nil
|
34
|
-
config_param :time_format, :string, :default => nil
|
28
|
+
config_param :command, :string
|
29
|
+
config_param :keys, :string
|
30
|
+
config_param :tag_key, :string, :default => nil
|
31
|
+
config_param :time_key, :string, :default => nil
|
32
|
+
config_param :time_format, :string, :default => nil
|
35
33
|
|
36
|
-
|
37
|
-
|
34
|
+
def configure(conf)
|
35
|
+
super
|
38
36
|
|
39
|
-
|
37
|
+
@keys = @keys.split(',')
|
40
38
|
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
39
|
+
if @time_key
|
40
|
+
if @time_format
|
41
|
+
tf = TimeFormatter.new(@time_format, @localtime)
|
42
|
+
@time_format_proc = tf.method(:format)
|
43
|
+
else
|
44
|
+
@time_format_proc = Proc.new {|time| time.to_s }
|
45
|
+
end
|
47
46
|
end
|
48
47
|
end
|
49
|
-
end
|
50
48
|
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
49
|
+
def format(tag, time, record)
|
50
|
+
out = ''
|
51
|
+
last = @keys.length-1
|
52
|
+
for i in 0..last
|
53
|
+
key = @keys[i]
|
54
|
+
if key == @time_key
|
55
|
+
out << @time_format_proc.call(time)
|
56
|
+
elsif key == @tag_key
|
57
|
+
out << tag
|
58
|
+
else
|
59
|
+
out << record[key].to_s
|
60
|
+
end
|
61
|
+
out << "\t" if i != last
|
62
62
|
end
|
63
|
-
out << "\
|
63
|
+
out << "\n"
|
64
|
+
out
|
64
65
|
end
|
65
|
-
out << "\n"
|
66
|
-
out
|
67
|
-
end
|
68
66
|
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
67
|
+
def write(chunk)
|
68
|
+
if chunk.respond_to?(:path)
|
69
|
+
prog = "#{@command} #{chunk.path}"
|
70
|
+
else
|
71
|
+
tmpfile = Tempfile.new("fluent-plugin-exec-")
|
72
|
+
chunk.write_to(tmpfile)
|
73
|
+
tmpfile.close
|
74
|
+
prog = "#{@command} #{tmpfile.path}"
|
75
|
+
end
|
78
76
|
|
79
|
-
|
80
|
-
|
81
|
-
|
77
|
+
system(prog)
|
78
|
+
ecode = $?.to_i
|
79
|
+
tmpfile.delete if tmpfile
|
82
80
|
|
83
|
-
|
84
|
-
|
81
|
+
if ecode != 0
|
82
|
+
raise "command returns #{ecode}: #{prog}"
|
83
|
+
end
|
85
84
|
end
|
86
85
|
end
|
87
86
|
end
|
88
|
-
|
89
|
-
|
90
|
-
end
|
91
|
-
|
@@ -16,427 +16,423 @@
|
|
16
16
|
# limitations under the License.
|
17
17
|
#
|
18
18
|
module Fluent
|
19
|
+
class ExecFilterOutput < BufferedOutput
|
20
|
+
Plugin.register_output('exec_filter', self)
|
19
21
|
|
22
|
+
def initialize
|
23
|
+
super
|
24
|
+
end
|
20
25
|
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
end
|
26
|
+
SUPPORTED_FORMAT = {
|
27
|
+
'tsv' => :tsv,
|
28
|
+
'json' => :json,
|
29
|
+
'msgpack' => :msgpack,
|
30
|
+
}
|
27
31
|
|
28
|
-
|
29
|
-
'tsv' => :tsv,
|
30
|
-
'json' => :json,
|
31
|
-
'msgpack' => :msgpack,
|
32
|
-
}
|
32
|
+
config_param :command, :string
|
33
33
|
|
34
|
-
|
34
|
+
config_param :remove_prefix, :string, :default => nil
|
35
|
+
config_param :add_prefix, :string, :default => nil
|
35
36
|
|
36
|
-
|
37
|
-
|
37
|
+
config_param :in_format, :default => :tsv do |val|
|
38
|
+
f = SUPPORTED_FORMAT[val]
|
39
|
+
raise ConfigError, "Unsupported in_format '#{val}'" unless f
|
40
|
+
f
|
41
|
+
end
|
42
|
+
config_param :in_keys, :default => [] do |val|
|
43
|
+
val.split(',')
|
44
|
+
end
|
45
|
+
config_param :in_tag_key, :default => nil
|
46
|
+
config_param :in_time_key, :default => nil
|
47
|
+
config_param :in_time_format, :default => nil
|
38
48
|
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
config_param :out_format, :default => :tsv do |val|
|
52
|
-
f = SUPPORTED_FORMAT[val]
|
53
|
-
raise ConfigError, "Unsupported out_format '#{val}'" unless f
|
54
|
-
f
|
55
|
-
end
|
56
|
-
config_param :out_keys, :default => [] do |val| # for tsv format
|
57
|
-
val.split(',')
|
58
|
-
end
|
59
|
-
config_param :out_tag_key, :default => nil
|
60
|
-
config_param :out_time_key, :default => nil
|
61
|
-
config_param :out_time_format, :default => nil
|
49
|
+
config_param :out_format, :default => :tsv do |val|
|
50
|
+
f = SUPPORTED_FORMAT[val]
|
51
|
+
raise ConfigError, "Unsupported out_format '#{val}'" unless f
|
52
|
+
f
|
53
|
+
end
|
54
|
+
config_param :out_keys, :default => [] do |val| # for tsv format
|
55
|
+
val.split(',')
|
56
|
+
end
|
57
|
+
config_param :out_tag_key, :default => nil
|
58
|
+
config_param :out_time_key, :default => nil
|
59
|
+
config_param :out_time_format, :default => nil
|
62
60
|
|
63
|
-
|
61
|
+
config_param :tag, :string, :default => nil
|
64
62
|
|
65
|
-
|
66
|
-
|
63
|
+
config_param :time_key, :string, :default => nil
|
64
|
+
config_param :time_format, :string, :default => nil
|
67
65
|
|
68
|
-
|
69
|
-
|
66
|
+
config_param :localtime, :bool, :default => true
|
67
|
+
config_param :num_children, :integer, :default => 1
|
70
68
|
|
71
|
-
|
72
|
-
|
69
|
+
# nil, 'none' or 0: no respawn, 'inf' or -1: infinite times, positive integer: try to respawn specified times only
|
70
|
+
config_param :child_respawn, :string, :default => nil
|
73
71
|
|
74
|
-
|
75
|
-
|
72
|
+
# 0: output logs for all of messages to emit
|
73
|
+
config_param :suppress_error_log_interval, :time, :default => 0
|
76
74
|
|
77
|
-
|
75
|
+
config_set_default :flush_interval, 1
|
78
76
|
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
77
|
+
def configure(conf)
|
78
|
+
if tag_key = conf['tag_key']
|
79
|
+
# TODO obsoleted?
|
80
|
+
@in_tag_key = tag_key
|
81
|
+
@out_tag_key = tag_key
|
82
|
+
end
|
85
83
|
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
84
|
+
if time_key = conf['time_key']
|
85
|
+
# TODO obsoleted?
|
86
|
+
@in_time_key = time_key
|
87
|
+
@out_time_key = time_key
|
88
|
+
end
|
91
89
|
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
90
|
+
if time_format = conf['time_format']
|
91
|
+
# TODO obsoleted?
|
92
|
+
@in_time_format = time_format
|
93
|
+
@out_time_format = time_format
|
94
|
+
end
|
97
95
|
|
98
|
-
|
96
|
+
super
|
99
97
|
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
98
|
+
if localtime = conf['localtime']
|
99
|
+
@localtime = true
|
100
|
+
elsif utc = conf['utc']
|
101
|
+
@localtime = false
|
102
|
+
end
|
105
103
|
|
106
|
-
|
107
|
-
|
108
|
-
|
104
|
+
if !@tag && !@out_tag_key
|
105
|
+
raise ConfigError, "'tag' or 'out_tag_key' option is required on exec_filter output"
|
106
|
+
end
|
109
107
|
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
108
|
+
if @in_time_key
|
109
|
+
if f = @in_time_format
|
110
|
+
tf = TimeFormatter.new(f, @localtime)
|
111
|
+
@time_format_proc = tf.method(:format)
|
112
|
+
else
|
113
|
+
@time_format_proc = Proc.new {|time| time.to_s }
|
114
|
+
end
|
115
|
+
elsif @in_time_format
|
116
|
+
$log.warn "in_time_format effects nothing when in_time_key is not specified: #{conf}"
|
116
117
|
end
|
117
|
-
elsif @in_time_format
|
118
|
-
$log.warn "in_time_format effects nothing when in_time_key is not specified: #{conf}"
|
119
|
-
end
|
120
118
|
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
119
|
+
if @out_time_key
|
120
|
+
if f = @out_time_format
|
121
|
+
@time_parse_proc = Proc.new {|str| Time.strptime(str, f).to_i }
|
122
|
+
else
|
123
|
+
@time_parse_proc = Proc.new {|str| str.to_i }
|
124
|
+
end
|
125
|
+
elsif @out_time_format
|
126
|
+
$log.warn "out_time_format effects nothing when out_time_key is not specified: #{conf}"
|
126
127
|
end
|
127
|
-
elsif @out_time_format
|
128
|
-
$log.warn "out_time_format effects nothing when out_time_key is not specified: #{conf}"
|
129
|
-
end
|
130
128
|
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
129
|
+
if @remove_prefix
|
130
|
+
@removed_prefix_string = @remove_prefix + '.'
|
131
|
+
@removed_length = @removed_prefix_string.length
|
132
|
+
end
|
133
|
+
if @add_prefix
|
134
|
+
@added_prefix_string = @add_prefix + '.'
|
135
|
+
end
|
138
136
|
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
137
|
+
case @in_format
|
138
|
+
when :tsv
|
139
|
+
if @in_keys.empty?
|
140
|
+
raise ConfigError, "in_keys option is required on exec_filter output for tsv in_format"
|
141
|
+
end
|
142
|
+
@formatter = TSVFormatter.new(@in_keys)
|
143
|
+
when :json
|
144
|
+
@formatter = JSONFormatter.new
|
145
|
+
when :msgpack
|
146
|
+
@formatter = MessagePackFormatter.new
|
143
147
|
end
|
144
|
-
@formatter = TSVFormatter.new(@in_keys)
|
145
|
-
when :json
|
146
|
-
@formatter = JSONFormatter.new
|
147
|
-
when :msgpack
|
148
|
-
@formatter = MessagePackFormatter.new
|
149
|
-
end
|
150
148
|
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
149
|
+
case @out_format
|
150
|
+
when :tsv
|
151
|
+
if @out_keys.empty?
|
152
|
+
raise ConfigError, "out_keys option is required on exec_filter output for tsv in_format"
|
153
|
+
end
|
154
|
+
@parser = TSVParser.new(@out_keys, method(:on_message))
|
155
|
+
when :json
|
156
|
+
@parser = JSONParser.new(method(:on_message))
|
157
|
+
when :msgpack
|
158
|
+
@parser = MessagePackParser.new(method(:on_message))
|
155
159
|
end
|
156
|
-
@parser = TSVParser.new(@out_keys, method(:on_message))
|
157
|
-
when :json
|
158
|
-
@parser = JSONParser.new(method(:on_message))
|
159
|
-
when :msgpack
|
160
|
-
@parser = MessagePackParser.new(method(:on_message))
|
161
|
-
end
|
162
160
|
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
161
|
+
@respawns = if @child_respawn.nil? or @child_respawn == 'none' or @child_respawn == '0'
|
162
|
+
0
|
163
|
+
elsif @child_respawn == 'inf' or @child_respawn == '-1'
|
164
|
+
-1
|
165
|
+
elsif @child_respawn =~ /^\d+$/
|
166
|
+
@child_respawn.to_i
|
167
|
+
else
|
168
|
+
raise ConfigError, "child_respawn option argument invalid: none(or 0), inf(or -1) or positive number"
|
169
|
+
end
|
170
|
+
|
171
|
+
@suppress_error_log_interval ||= 0
|
172
|
+
@next_log_time = Time.now.to_i
|
173
|
+
end
|
176
174
|
|
177
|
-
|
178
|
-
|
175
|
+
def start
|
176
|
+
super
|
179
177
|
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
|
178
|
+
@children = []
|
179
|
+
@rr = 0
|
180
|
+
begin
|
181
|
+
@num_children.times do
|
182
|
+
c = ChildProcess.new(@parser, @respawns)
|
183
|
+
c.start(@command)
|
184
|
+
@children << c
|
185
|
+
end
|
186
|
+
rescue
|
187
|
+
shutdown
|
188
|
+
raise
|
187
189
|
end
|
188
|
-
rescue
|
189
|
-
shutdown
|
190
|
-
raise
|
191
190
|
end
|
192
|
-
end
|
193
191
|
|
194
|
-
|
195
|
-
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
|
202
|
-
|
203
|
-
def shutdown
|
204
|
-
super
|
192
|
+
def before_shutdown
|
193
|
+
super
|
194
|
+
$log.debug "out_exec_filter#before_shutdown called"
|
195
|
+
@children.each {|c|
|
196
|
+
c.finished = true
|
197
|
+
}
|
198
|
+
sleep 0.5 # TODO wait time before killing child process
|
199
|
+
end
|
205
200
|
|
206
|
-
|
207
|
-
|
208
|
-
true
|
209
|
-
}
|
210
|
-
end
|
201
|
+
def shutdown
|
202
|
+
super
|
211
203
|
|
212
|
-
|
213
|
-
|
214
|
-
|
215
|
-
|
216
|
-
end
|
204
|
+
@children.reject! {|c|
|
205
|
+
c.shutdown
|
206
|
+
true
|
207
|
+
}
|
217
208
|
end
|
218
209
|
|
219
|
-
|
220
|
-
|
221
|
-
|
222
|
-
|
223
|
-
|
224
|
-
end
|
225
|
-
if @in_tag_key
|
226
|
-
record[@in_tag_key] = tag
|
210
|
+
def format_stream(tag, es)
|
211
|
+
if @remove_prefix
|
212
|
+
if (tag[0, @removed_length] == @removed_prefix_string and tag.length > @removed_length) or tag == @removed_prefix
|
213
|
+
tag = tag[@removed_length..-1] || ''
|
214
|
+
end
|
227
215
|
end
|
228
|
-
@formatter.call(record, out)
|
229
|
-
}
|
230
216
|
|
231
|
-
|
232
|
-
end
|
217
|
+
out = ''
|
233
218
|
|
234
|
-
|
235
|
-
|
236
|
-
|
237
|
-
|
238
|
-
|
239
|
-
|
240
|
-
|
219
|
+
es.each {|time,record|
|
220
|
+
if @in_time_key
|
221
|
+
record[@in_time_key] = @time_format_proc.call(time)
|
222
|
+
end
|
223
|
+
if @in_tag_key
|
224
|
+
record[@in_tag_key] = tag
|
225
|
+
end
|
226
|
+
@formatter.call(record, out)
|
227
|
+
}
|
241
228
|
|
242
|
-
|
243
|
-
@pid = nil
|
244
|
-
@thread = nil
|
245
|
-
@parser = parser
|
246
|
-
@respawns = respawns
|
247
|
-
@mutex = Mutex.new
|
248
|
-
@finished = nil
|
229
|
+
out
|
249
230
|
end
|
250
231
|
|
251
|
-
def
|
252
|
-
@
|
253
|
-
@
|
254
|
-
@io = IO.popen(command, "r+")
|
255
|
-
@pid = @io.pid
|
256
|
-
@io.sync = true
|
257
|
-
@thread = Thread.new(&method(:run))
|
258
|
-
end
|
259
|
-
@finished = false
|
232
|
+
def write(chunk)
|
233
|
+
r = @rr = (@rr + 1) % @children.length
|
234
|
+
@children[r].write chunk
|
260
235
|
end
|
261
236
|
|
262
|
-
|
263
|
-
|
264
|
-
|
265
|
-
|
266
|
-
|
267
|
-
|
237
|
+
class ChildProcess
|
238
|
+
attr_accessor :finished
|
239
|
+
|
240
|
+
def initialize(parser,respawns=0)
|
241
|
+
@pid = nil
|
242
|
+
@thread = nil
|
243
|
+
@parser = parser
|
244
|
+
@respawns = respawns
|
245
|
+
@mutex = Mutex.new
|
246
|
+
@finished = nil
|
268
247
|
end
|
269
|
-
|
270
|
-
|
271
|
-
|
248
|
+
|
249
|
+
def start(command)
|
250
|
+
@command = command
|
251
|
+
@mutex.synchronize do
|
252
|
+
@io = IO.popen(command, "r+")
|
253
|
+
@pid = @io.pid
|
254
|
+
@io.sync = true
|
255
|
+
@thread = Thread.new(&method(:run))
|
256
|
+
end
|
257
|
+
@finished = false
|
272
258
|
end
|
273
|
-
|
274
|
-
|
275
|
-
|
276
|
-
|
259
|
+
|
260
|
+
def kill_child(join_wait)
|
261
|
+
begin
|
262
|
+
Process.kill(:TERM, @pid)
|
263
|
+
rescue Errno::ESRCH
|
264
|
+
# Errno::ESRCH 'No such process', ignore
|
265
|
+
# child process killed by signal chained from fluentd process
|
266
|
+
end
|
267
|
+
if @thread.join(join_wait)
|
268
|
+
# @thread successfully shutdown
|
269
|
+
return
|
270
|
+
end
|
271
|
+
begin
|
272
|
+
Process.kill(:KILL, @pid)
|
273
|
+
rescue Errno::ESRCH
|
274
|
+
# ignore if successfully killed by :TERM
|
275
|
+
end
|
276
|
+
@thread.join
|
277
277
|
end
|
278
|
-
@thread.join
|
279
|
-
end
|
280
278
|
|
281
|
-
|
282
|
-
|
283
|
-
|
284
|
-
|
279
|
+
def shutdown
|
280
|
+
@finished = true
|
281
|
+
@mutex.synchronize do
|
282
|
+
kill_child(60) # TODO wait time
|
283
|
+
end
|
285
284
|
end
|
286
|
-
end
|
287
285
|
|
288
|
-
|
289
|
-
|
290
|
-
|
291
|
-
|
292
|
-
|
293
|
-
|
294
|
-
|
295
|
-
|
296
|
-
|
297
|
-
|
286
|
+
def write(chunk)
|
287
|
+
begin
|
288
|
+
chunk.write_to(@io)
|
289
|
+
rescue Errno::EPIPE => e
|
290
|
+
# Broken pipe (child process unexpectedly exited)
|
291
|
+
$log.warn "exec_filter Broken pipe, child process maybe exited.", :command => @command
|
292
|
+
if try_respawn
|
293
|
+
retry # retry chunk#write_to with child respawned
|
294
|
+
else
|
295
|
+
raise e # to retry #write with other ChildProcess instance (when num_children > 1)
|
296
|
+
end
|
298
297
|
end
|
299
298
|
end
|
300
|
-
end
|
301
299
|
|
302
|
-
|
303
|
-
return false if @respawns == 0
|
304
|
-
@mutex.synchronize do
|
300
|
+
def try_respawn
|
305
301
|
return false if @respawns == 0
|
302
|
+
@mutex.synchronize do
|
303
|
+
return false if @respawns == 0
|
306
304
|
|
307
|
-
|
305
|
+
kill_child(5) # TODO wait time
|
308
306
|
|
309
|
-
|
310
|
-
|
311
|
-
|
312
|
-
|
307
|
+
@io = IO.popen(@command, "r+")
|
308
|
+
@pid = @io.pid
|
309
|
+
@io.sync = true
|
310
|
+
@thread = Thread.new(&method(:run))
|
313
311
|
|
314
|
-
|
312
|
+
@respawns -= 1 if @respawns > 0
|
313
|
+
end
|
314
|
+
$log.warn "exec_filter child process successfully respawned.", :command => @command, :respawns => @respawns
|
315
|
+
true
|
315
316
|
end
|
316
|
-
$log.warn "exec_filter child process successfully respawned.", :command => @command, :respawns => @respawns
|
317
|
-
true
|
318
|
-
end
|
319
317
|
|
320
|
-
|
321
|
-
|
322
|
-
|
323
|
-
|
324
|
-
|
325
|
-
|
326
|
-
|
327
|
-
|
328
|
-
|
329
|
-
|
330
|
-
|
318
|
+
def run
|
319
|
+
@parser.call(@io)
|
320
|
+
rescue
|
321
|
+
$log.error "exec_filter thread unexpectedly failed with an error.", :command=>@command, :error=>$!.to_s
|
322
|
+
$log.warn_backtrace $!.backtrace
|
323
|
+
ensure
|
324
|
+
pid, stat = Process.waitpid2(@pid)
|
325
|
+
unless @finished
|
326
|
+
$log.error "exec_filter process unexpectedly exited.", :command=>@command, :ecode=>stat.to_i
|
327
|
+
unless @respawns == 0
|
328
|
+
$log.warn "exec_filter child process will respawn for next input data (respawns #{@respawns})."
|
329
|
+
end
|
331
330
|
end
|
332
331
|
end
|
333
332
|
end
|
334
|
-
end
|
335
333
|
|
336
|
-
|
337
|
-
end
|
338
|
-
|
339
|
-
class TSVFormatter < Formatter
|
340
|
-
def initialize(in_keys)
|
341
|
-
@in_keys = in_keys
|
342
|
-
super()
|
334
|
+
class Formatter
|
343
335
|
end
|
344
336
|
|
345
|
-
|
346
|
-
|
347
|
-
|
348
|
-
|
349
|
-
out << record[key].to_s
|
350
|
-
out << "\t" if i != last
|
337
|
+
class TSVFormatter < Formatter
|
338
|
+
def initialize(in_keys)
|
339
|
+
@in_keys = in_keys
|
340
|
+
super()
|
351
341
|
end
|
352
|
-
out << "\n"
|
353
|
-
end
|
354
|
-
end
|
355
342
|
|
356
|
-
|
357
|
-
|
358
|
-
|
343
|
+
def call(record, out)
|
344
|
+
last = @in_keys.length-1
|
345
|
+
for i in 0..last
|
346
|
+
key = @in_keys[i]
|
347
|
+
out << record[key].to_s
|
348
|
+
out << "\t" if i != last
|
349
|
+
end
|
350
|
+
out << "\n"
|
351
|
+
end
|
359
352
|
end
|
360
|
-
end
|
361
353
|
|
362
|
-
|
363
|
-
|
364
|
-
|
354
|
+
class JSONFormatter < Formatter
|
355
|
+
def call(record, out)
|
356
|
+
out << Yajl.dump(record) << "\n"
|
357
|
+
end
|
365
358
|
end
|
366
|
-
end
|
367
359
|
|
368
|
-
|
369
|
-
|
370
|
-
|
371
|
-
|
372
|
-
time = Engine.now
|
360
|
+
class MessagePackFormatter < Formatter
|
361
|
+
def call(record, out)
|
362
|
+
record.to_msgpack(out)
|
363
|
+
end
|
373
364
|
end
|
374
365
|
|
375
|
-
|
376
|
-
|
377
|
-
|
378
|
-
|
379
|
-
|
380
|
-
|
381
|
-
else
|
382
|
-
tag = @tag
|
383
|
-
end
|
366
|
+
def on_message(record)
|
367
|
+
if val = record.delete(@out_time_key)
|
368
|
+
time = @time_parse_proc.call(val)
|
369
|
+
else
|
370
|
+
time = Engine.now
|
371
|
+
end
|
384
372
|
|
385
|
-
|
373
|
+
if val = record.delete(@out_tag_key)
|
374
|
+
tag = if @add_prefix
|
375
|
+
@added_prefix_string + val
|
376
|
+
else
|
377
|
+
val
|
378
|
+
end
|
379
|
+
else
|
380
|
+
tag = @tag
|
381
|
+
end
|
386
382
|
|
387
|
-
|
388
|
-
if @suppress_error_log_interval == 0 || Time.now.to_i > @next_log_time
|
389
|
-
$log.error "exec_filter failed to emit", :error=>$!.to_s, :error_class=>$!.class.to_s, :record=>Yajl.dump(record)
|
390
|
-
$log.warn_backtrace $!.backtrace
|
391
|
-
@next_log_time = Time.now.to_i + @suppress_error_log_interval
|
392
|
-
end
|
393
|
-
end
|
383
|
+
Engine.emit(tag, time, record)
|
394
384
|
|
395
|
-
|
396
|
-
|
397
|
-
|
385
|
+
rescue
|
386
|
+
if @suppress_error_log_interval == 0 || Time.now.to_i > @next_log_time
|
387
|
+
$log.error "exec_filter failed to emit", :error=>$!.to_s, :error_class=>$!.class.to_s, :record=>Yajl.dump(record)
|
388
|
+
$log.warn_backtrace $!.backtrace
|
389
|
+
@next_log_time = Time.now.to_i + @suppress_error_log_interval
|
390
|
+
end
|
398
391
|
end
|
399
|
-
end
|
400
392
|
|
401
|
-
|
402
|
-
|
403
|
-
|
404
|
-
|
393
|
+
class Parser
|
394
|
+
def initialize(on_message)
|
395
|
+
@on_message = on_message
|
396
|
+
end
|
405
397
|
end
|
406
398
|
|
407
|
-
|
408
|
-
|
409
|
-
|
399
|
+
class TSVParser < Parser
|
400
|
+
def initialize(out_keys, on_message)
|
401
|
+
@out_keys = out_keys
|
402
|
+
super(on_message)
|
403
|
+
end
|
410
404
|
|
411
|
-
|
412
|
-
|
413
|
-
|
405
|
+
def call(io)
|
406
|
+
io.each_line(&method(:each_line))
|
407
|
+
end
|
408
|
+
|
409
|
+
def each_line(line)
|
410
|
+
line.chomp!
|
411
|
+
vals = line.split("\t")
|
414
412
|
|
415
|
-
|
413
|
+
record = Hash[@out_keys.zip(vals)]
|
416
414
|
|
417
|
-
|
415
|
+
@on_message.call(record)
|
416
|
+
end
|
418
417
|
end
|
419
|
-
end
|
420
418
|
|
421
|
-
|
422
|
-
|
423
|
-
|
424
|
-
|
425
|
-
|
419
|
+
class JSONParser < Parser
|
420
|
+
def call(io)
|
421
|
+
y = Yajl::Parser.new
|
422
|
+
y.on_parse_complete = @on_message
|
423
|
+
y.parse(io)
|
424
|
+
end
|
426
425
|
end
|
427
|
-
end
|
428
426
|
|
429
|
-
|
430
|
-
|
431
|
-
|
432
|
-
|
433
|
-
|
434
|
-
|
427
|
+
class MessagePackParser < Parser
|
428
|
+
def call(io)
|
429
|
+
@u = MessagePack::Unpacker.new(io)
|
430
|
+
begin
|
431
|
+
@u.each(&@on_message)
|
432
|
+
rescue EOFError
|
433
|
+
end
|
435
434
|
end
|
436
435
|
end
|
437
436
|
end
|
438
437
|
end
|
439
438
|
|
440
|
-
|
441
|
-
end
|
442
|
-
|