fluentd 0.14.2 → 0.14.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/ChangeLog +77 -1131
- data/Rakefile +15 -0
- data/appveyor.yml +2 -2
- data/example/in_forward.conf +3 -0
- data/example/in_forward_client.conf +37 -0
- data/example/in_forward_shared_key.conf +15 -0
- data/example/in_forward_users.conf +24 -0
- data/example/multi_filters.conf +61 -0
- data/example/out_forward.conf +13 -13
- data/example/out_forward_client.conf +109 -0
- data/example/out_forward_shared_key.conf +36 -0
- data/example/out_forward_users.conf +65 -0
- data/example/{out_buffered_null.conf → out_null.conf} +10 -6
- data/example/secondary_file.conf +41 -0
- data/fluentd.gemspec +19 -16
- data/lib/fluent/agent.rb +5 -0
- data/lib/fluent/command/debug.rb +4 -4
- data/lib/fluent/compat/handle_tag_and_time_mixin.rb +2 -0
- data/lib/fluent/compat/output.rb +5 -2
- data/lib/fluent/config/configure_proxy.rb +26 -5
- data/lib/fluent/config/error.rb +3 -0
- data/lib/fluent/config/section.rb +15 -0
- data/lib/fluent/event_router.rb +77 -4
- data/lib/fluent/plugin/base.rb +12 -3
- data/lib/fluent/plugin/buffer.rb +5 -1
- data/lib/fluent/plugin/filter.rb +48 -6
- data/lib/fluent/plugin/filter_record_transformer.rb +3 -1
- data/lib/fluent/plugin/filter_stdout.rb +0 -4
- data/lib/fluent/plugin/in_debug_agent.rb +5 -5
- data/lib/fluent/plugin/in_dummy.rb +9 -1
- data/lib/fluent/plugin/in_forward.rb +330 -62
- data/lib/fluent/plugin/in_monitor_agent.rb +31 -77
- data/lib/fluent/plugin/in_tail.rb +76 -92
- data/lib/fluent/plugin/multi_output.rb +4 -0
- data/lib/fluent/plugin/out_forward.rb +328 -222
- data/lib/fluent/plugin/out_null.rb +37 -0
- data/lib/fluent/plugin/out_secondary_file.rb +128 -0
- data/lib/fluent/plugin/out_stdout.rb +38 -2
- data/lib/fluent/plugin/output.rb +29 -6
- data/lib/fluent/plugin/storage_local.rb +16 -0
- data/lib/fluent/plugin_helper/timer.rb +6 -1
- data/lib/fluent/root_agent.rb +4 -1
- data/lib/fluent/supervisor.rb +62 -9
- data/lib/fluent/test/base.rb +3 -0
- data/lib/fluent/test/driver/base.rb +5 -0
- data/lib/fluent/test/formatter_test.rb +2 -0
- data/lib/fluent/test/parser_test.rb +2 -0
- data/lib/fluent/test/startup_shutdown.rb +33 -0
- data/lib/fluent/version.rb +1 -1
- data/lib/fluent/winsvc.rb +1 -2
- data/test/compat/test_calls_super.rb +2 -0
- data/test/config/test_configurable.rb +88 -0
- data/test/config/test_types.rb +7 -3
- data/test/plugin/test_filter.rb +121 -23
- data/test/plugin/test_filter_record_transformer.rb +22 -6
- data/test/plugin/test_in_debug_agent.rb +2 -2
- data/test/plugin/test_in_forward.rb +920 -407
- data/test/plugin/test_in_monitor_agent.rb +333 -0
- data/test/plugin/test_in_tail.rb +686 -595
- data/test/plugin/test_out_forward.rb +150 -207
- data/test/plugin/test_out_null.rb +85 -9
- data/test/plugin/test_out_secondary_file.rb +432 -0
- data/test/plugin/test_out_stdout.rb +143 -45
- data/test/plugin/test_output.rb +53 -0
- data/test/plugin/test_output_as_buffered.rb +13 -0
- data/test/plugin/test_output_as_buffered_overflow.rb +3 -0
- data/test/plugin/test_output_as_buffered_retries.rb +11 -0
- data/test/plugin/test_output_as_buffered_secondary.rb +12 -0
- data/test/plugin/test_output_as_standard.rb +12 -0
- data/test/plugin_helper/test_compat_parameters.rb +2 -0
- data/test/plugin_helper/test_timer.rb +31 -0
- data/test/test_event_router.rb +87 -0
- data/test/test_filter.rb +48 -6
- data/test/test_log.rb +5 -2
- data/test/test_output.rb +41 -1
- data/test/test_plugin_classes.rb +17 -9
- data/test/test_root_agent.rb +188 -0
- metadata +62 -68
- data/lib/fluent/plugin/out_buffered_null.rb +0 -59
- data/lib/fluent/plugin/out_buffered_stdout.rb +0 -70
- data/test/plugin/test_out_buffered_null.rb +0 -79
- data/test/plugin/test_out_buffered_stdout.rb +0 -122
|
@@ -18,10 +18,47 @@ require 'fluent/plugin/output'
|
|
|
18
18
|
|
|
19
19
|
module Fluent::Plugin
|
|
20
20
|
class NullOutput < Output
|
|
21
|
+
# This plugin is for tests of non-buffered/buffered plugins
|
|
21
22
|
Fluent::Plugin.register_output('null', self)
|
|
22
23
|
|
|
24
|
+
config_section :buffer do
|
|
25
|
+
config_set_default :chunk_keys, ['tag']
|
|
26
|
+
config_set_default :flush_at_shutdown, true
|
|
27
|
+
config_set_default :chunk_limit_size, 10 * 1024
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def prefer_buffered_processing
|
|
31
|
+
false
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def prefer_delayed_commit
|
|
35
|
+
@delayed
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
attr_accessor :feed_proc, :delayed
|
|
39
|
+
|
|
40
|
+
def initialize
|
|
41
|
+
super
|
|
42
|
+
@delayed = false
|
|
43
|
+
@feed_proc = nil
|
|
44
|
+
end
|
|
45
|
+
|
|
23
46
|
def process(tag, es)
|
|
24
47
|
# Do nothing
|
|
25
48
|
end
|
|
49
|
+
|
|
50
|
+
def write(chunk)
|
|
51
|
+
if @feed_proc
|
|
52
|
+
@feed_proc.call(chunk)
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
def try_write(chunk)
|
|
57
|
+
if @feed_proc
|
|
58
|
+
@feed_proc.call(chunk)
|
|
59
|
+
end
|
|
60
|
+
# not to commit chunks for testing
|
|
61
|
+
# commit_write(chunk.unique_id)
|
|
62
|
+
end
|
|
26
63
|
end
|
|
27
64
|
end
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
#
|
|
2
|
+
# Fluentd
|
|
3
|
+
#
|
|
4
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
5
|
+
# you may not use this file except in compliance with the License.
|
|
6
|
+
# You may obtain a copy of the License at
|
|
7
|
+
#
|
|
8
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
9
|
+
#
|
|
10
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
11
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
12
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
13
|
+
# See the License for the specific language governing permissions and
|
|
14
|
+
# limitations under the License.
|
|
15
|
+
#
|
|
16
|
+
|
|
17
|
+
require "fileutils"
|
|
18
|
+
require "fluent/plugin/file_util"
|
|
19
|
+
require "fluent/plugin/output"
|
|
20
|
+
require "fluent/config/error"
|
|
21
|
+
|
|
22
|
+
module Fluent::Plugin
|
|
23
|
+
class SecondaryFileOutput < Output
|
|
24
|
+
Fluent::Plugin.register_output("secondary_file", self)
|
|
25
|
+
|
|
26
|
+
FILE_PERMISSION = 0644
|
|
27
|
+
DIR_PERMISSION = 0755
|
|
28
|
+
PLACEHOLDER_REGEX = /\${(tag(\[\d+\])?|[\w.@-]+)}/
|
|
29
|
+
|
|
30
|
+
desc "The directory path of the output file."
|
|
31
|
+
config_param :directory, :string
|
|
32
|
+
desc "The basename of the output file."
|
|
33
|
+
config_param :basename, :string, default: "dump.bin"
|
|
34
|
+
desc "The flushed chunk is appended to existence file or not."
|
|
35
|
+
config_param :append, :bool, default: false
|
|
36
|
+
config_param :compress, :enum, list: [:text, :gzip], default: :text
|
|
37
|
+
|
|
38
|
+
def configure(conf)
|
|
39
|
+
super
|
|
40
|
+
|
|
41
|
+
unless @as_secondary
|
|
42
|
+
raise Fluent::ConfigError, "This plugin can only be used in the <secondary> section"
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
if @basename.include?("/")
|
|
46
|
+
raise Fluent::ConfigError, "basename should not include `/`"
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
@path_without_suffix = File.join(@directory, @basename)
|
|
50
|
+
validate_compatible_with_primary_buffer!(@path_without_suffix)
|
|
51
|
+
|
|
52
|
+
@suffix = case @compress
|
|
53
|
+
when :text
|
|
54
|
+
""
|
|
55
|
+
when :gzip
|
|
56
|
+
".gz"
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
test_path = @path_without_suffix
|
|
60
|
+
unless Fluent::FileUtil.writable_p?(test_path)
|
|
61
|
+
raise Fluent::ConfigError, "out_secondary_file: `#{@directory}` should be writable"
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
@dir_perm = system_config.dir_permission || DIR_PERMISSION
|
|
65
|
+
@file_perm = system_config.file_permission || FILE_PERMISSION
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
def write(chunk)
|
|
69
|
+
path_without_suffix = extract_placeholders(@path_without_suffix, chunk.metadata)
|
|
70
|
+
path = generate_path(path_without_suffix)
|
|
71
|
+
FileUtils.mkdir_p File.dirname(path), mode: @dir_perm
|
|
72
|
+
|
|
73
|
+
case @compress
|
|
74
|
+
when :text
|
|
75
|
+
File.open(path, "ab", @file_perm) {|f|
|
|
76
|
+
f.flock(File::LOCK_EX)
|
|
77
|
+
chunk.write_to(f)
|
|
78
|
+
}
|
|
79
|
+
when :gzip
|
|
80
|
+
File.open(path, "ab", @file_perm) {|f|
|
|
81
|
+
f.flock(File::LOCK_EX)
|
|
82
|
+
gz = Zlib::GzipWriter.new(f)
|
|
83
|
+
chunk.write_to(gz)
|
|
84
|
+
gz.close
|
|
85
|
+
}
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
path
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
private
|
|
92
|
+
|
|
93
|
+
def validate_compatible_with_primary_buffer!(path_without_suffix)
|
|
94
|
+
placeholders = path_without_suffix.scan(PLACEHOLDER_REGEX).flat_map(&:first) # to trim suffix [\d+]
|
|
95
|
+
|
|
96
|
+
if !@chunk_key_time && has_time_format?(path_without_suffix)
|
|
97
|
+
raise Fluent::ConfigError, "out_secondary_file: basename or directory has an incompatible placeholder, remove time formats, like `%Y%m%d`, from basename or directory"
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
if !@chunk_key_tag && (ph = placeholders.find { |placeholder| placeholder.match(/tag(\[\d+\])?/) })
|
|
101
|
+
raise Fluent::ConfigError, "out_secondary_file: basename or directory has an incompatible placeholder #{ph}, remove tag placeholder, like `${tag}`, from basename or directory"
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
vars = placeholders.reject { |placeholder| placeholder.match(/tag(\[\d+\])?/) }
|
|
105
|
+
|
|
106
|
+
if ph = vars.find { |v| !@chunk_keys.include?(v) }
|
|
107
|
+
raise Fluent::ConfigError, "out_secondary_file: basename or directory has an incompatible placeholder #{ph}, remove variable placeholder, like `${varname}`, from basename or directory"
|
|
108
|
+
end
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
def has_time_format?(str)
|
|
112
|
+
str != Time.now.strftime(str)
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
def generate_path(path_without_suffix)
|
|
116
|
+
if @append
|
|
117
|
+
"#{path_without_suffix}#{@suffix}"
|
|
118
|
+
else
|
|
119
|
+
i = 0
|
|
120
|
+
loop do
|
|
121
|
+
path = "#{path_without_suffix}.#{i}#{@suffix}"
|
|
122
|
+
return path unless File.exist?(path)
|
|
123
|
+
i += 1
|
|
124
|
+
end
|
|
125
|
+
end
|
|
126
|
+
end
|
|
127
|
+
end
|
|
128
|
+
end
|
|
@@ -24,25 +24,61 @@ module Fluent::Plugin
|
|
|
24
24
|
|
|
25
25
|
DEFAULT_FORMAT_TYPE = 'json'
|
|
26
26
|
|
|
27
|
+
config_section :buffer do
|
|
28
|
+
config_set_default :chunk_keys, ['tag']
|
|
29
|
+
config_set_default :flush_at_shutdown, true
|
|
30
|
+
config_set_default :chunk_limit_size, 10 * 1024
|
|
31
|
+
end
|
|
32
|
+
|
|
27
33
|
config_section :format do
|
|
28
34
|
config_set_default :@type, DEFAULT_FORMAT_TYPE
|
|
29
35
|
end
|
|
30
36
|
|
|
37
|
+
def prefer_buffered_processing
|
|
38
|
+
false
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def prefer_delayed_commit
|
|
42
|
+
@delayed
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
attr_accessor :delayed
|
|
46
|
+
|
|
47
|
+
def initialize
|
|
48
|
+
super
|
|
49
|
+
@delayed = false
|
|
50
|
+
end
|
|
51
|
+
|
|
31
52
|
def configure(conf)
|
|
32
53
|
if conf['output_type'] && !conf['format']
|
|
33
54
|
conf['format'] = conf['output_type']
|
|
34
55
|
end
|
|
35
56
|
compat_parameters_convert(conf, :inject, :formatter)
|
|
57
|
+
|
|
36
58
|
super
|
|
59
|
+
|
|
37
60
|
@formatter = formatter_create(conf: conf.elements('format').first, default_type: DEFAULT_FORMAT_TYPE)
|
|
38
61
|
end
|
|
39
62
|
|
|
40
63
|
def process(tag, es)
|
|
41
64
|
es.each {|time,record|
|
|
42
|
-
|
|
43
|
-
$log.write "#{Time.at(time).localtime} #{tag}: #{@formatter.format(tag, time, r).chomp}\n"
|
|
65
|
+
$log.write(format(tag, time, record))
|
|
44
66
|
}
|
|
45
67
|
$log.flush
|
|
46
68
|
end
|
|
69
|
+
|
|
70
|
+
def format(tag, time, record)
|
|
71
|
+
record = inject_values_to_record(tag, time, record)
|
|
72
|
+
"#{Time.at(time).localtime} #{tag}: #{@formatter.format(tag, time, record).chomp}\n"
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
def write(chunk)
|
|
76
|
+
chunk.write_to($log)
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
def try_write(chunk)
|
|
80
|
+
chunk.write_to($log)
|
|
81
|
+
commit_write(chunk.unique_id)
|
|
82
|
+
end
|
|
47
83
|
end
|
|
48
84
|
end
|
data/lib/fluent/plugin/output.rb
CHANGED
|
@@ -138,7 +138,7 @@ module Fluent
|
|
|
138
138
|
end
|
|
139
139
|
end
|
|
140
140
|
|
|
141
|
-
attr_reader :as_secondary, :delayed_commit, :delayed_commit_timeout
|
|
141
|
+
attr_reader :as_secondary, :delayed_commit, :delayed_commit_timeout, :timekey_zone
|
|
142
142
|
attr_reader :num_errors, :emit_count, :emit_records, :write_count, :rollback_count
|
|
143
143
|
|
|
144
144
|
# for tests
|
|
@@ -185,13 +185,21 @@ module Fluent
|
|
|
185
185
|
@simple_chunking = nil
|
|
186
186
|
@chunk_keys = @chunk_key_time = @chunk_key_tag = nil
|
|
187
187
|
@flush_mode = nil
|
|
188
|
+
@timekey_zone = nil
|
|
188
189
|
end
|
|
189
190
|
|
|
190
191
|
def acts_as_secondary(primary)
|
|
191
192
|
@as_secondary = true
|
|
192
193
|
@primary_instance = primary
|
|
194
|
+
@chunk_keys = @primary_instance.chunk_keys || []
|
|
195
|
+
@chunk_key_tag = @primary_instance.chunk_key_tag || false
|
|
196
|
+
if @primary_instance.chunk_key_time
|
|
197
|
+
@chunk_key_time = @primary_instance.chunk_key_time
|
|
198
|
+
@timekey_zone = @primary_instance.timekey_zone
|
|
199
|
+
@output_time_formatter_cache = {}
|
|
200
|
+
end
|
|
201
|
+
|
|
193
202
|
(class << self; self; end).module_eval do
|
|
194
|
-
define_method(:extract_placeholders){ |str, metadata| @primary_instance.extract_placeholders(str, metadata) }
|
|
195
203
|
define_method(:commit_write){ |chunk_id| @primary_instance.commit_write(chunk_id, delayed: delayed_commit, secondary: true) }
|
|
196
204
|
define_method(:rollback_write){ |chunk_id| @primary_instance.rollback_write(chunk_id) }
|
|
197
205
|
end
|
|
@@ -251,7 +259,7 @@ module Fluent
|
|
|
251
259
|
if @chunk_key_time
|
|
252
260
|
raise Fluent::ConfigError, "<buffer ...> argument includes 'time', but timekey is not configured" unless @buffer_config.timekey
|
|
253
261
|
Fluent::Timezone.validate!(@buffer_config.timekey_zone)
|
|
254
|
-
@
|
|
262
|
+
@timekey_zone = @buffer_config.timekey_use_utc ? '+0000' : @buffer_config.timekey_zone
|
|
255
263
|
@output_time_formatter_cache = {}
|
|
256
264
|
end
|
|
257
265
|
|
|
@@ -301,7 +309,7 @@ module Fluent
|
|
|
301
309
|
@secondary.acts_as_secondary(self)
|
|
302
310
|
@secondary.configure(secondary_conf)
|
|
303
311
|
@secondary.router = router if @secondary.has_router?
|
|
304
|
-
if self.class != @secondary.class
|
|
312
|
+
if (self.class != @secondary.class) && (@custom_format || @secondary.implement?(:custom_format))
|
|
305
313
|
log.warn "secondary type should be same with primary one", primary: self.class.to_s, secondary: @secondary.class.to_s
|
|
306
314
|
end
|
|
307
315
|
else
|
|
@@ -384,6 +392,11 @@ module Fluent
|
|
|
384
392
|
@secondary.start if @secondary
|
|
385
393
|
end
|
|
386
394
|
|
|
395
|
+
def after_start
|
|
396
|
+
super
|
|
397
|
+
@secondary.after_start if @secondary
|
|
398
|
+
end
|
|
399
|
+
|
|
387
400
|
def stop
|
|
388
401
|
@secondary.stop if @secondary
|
|
389
402
|
@buffer.stop if @buffering && @buffer
|
|
@@ -472,13 +485,13 @@ module Fluent
|
|
|
472
485
|
|
|
473
486
|
# TODO: optimize this code
|
|
474
487
|
def extract_placeholders(str, metadata)
|
|
475
|
-
if metadata.
|
|
488
|
+
if metadata.empty?
|
|
476
489
|
str
|
|
477
490
|
else
|
|
478
491
|
rvalue = str
|
|
479
492
|
# strftime formatting
|
|
480
493
|
if @chunk_key_time # this section MUST be earlier than rest to use raw 'str'
|
|
481
|
-
@output_time_formatter_cache[str] ||= Fluent::Timezone.formatter(@
|
|
494
|
+
@output_time_formatter_cache[str] ||= Fluent::Timezone.formatter(@timekey_zone, str)
|
|
482
495
|
rvalue = @output_time_formatter_cache[str].call(metadata.timekey)
|
|
483
496
|
end
|
|
484
497
|
# ${tag}, ${tag[0]}, ${tag[1]}, ...
|
|
@@ -922,6 +935,11 @@ module Fluent
|
|
|
922
935
|
interval = @buffer_config.flush_thread_interval
|
|
923
936
|
end
|
|
924
937
|
|
|
938
|
+
while !self.after_started? && !self.stopped?
|
|
939
|
+
sleep 0.5
|
|
940
|
+
end
|
|
941
|
+
log.debug "enqueue_thread actually running"
|
|
942
|
+
|
|
925
943
|
begin
|
|
926
944
|
while @output_flush_threads_running
|
|
927
945
|
now_int = Time.now.to_i
|
|
@@ -969,6 +987,11 @@ module Fluent
|
|
|
969
987
|
clock_id = Process::CLOCK_MONOTONIC rescue Process::CLOCK_MONOTONIC_RAW
|
|
970
988
|
state.next_time = Process.clock_gettime(clock_id) + flush_thread_interval
|
|
971
989
|
|
|
990
|
+
while !self.after_started? && !self.stopped?
|
|
991
|
+
sleep 0.5
|
|
992
|
+
end
|
|
993
|
+
log.debug "flush_thread actually running"
|
|
994
|
+
|
|
972
995
|
begin
|
|
973
996
|
# This thread don't use `thread_current_running?` because this thread should run in `before_shutdown` phase
|
|
974
997
|
while @output_flush_threads_running
|
|
@@ -1,3 +1,19 @@
|
|
|
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
|
+
|
|
1
17
|
require 'fluent/plugin'
|
|
2
18
|
require 'fluent/plugin/storage'
|
|
3
19
|
|
|
@@ -66,6 +66,7 @@ module Fluent
|
|
|
66
66
|
def initialize(title, interval, repeat, log, checker, &callback)
|
|
67
67
|
@title = title
|
|
68
68
|
@callback = callback
|
|
69
|
+
@repeat = repeat
|
|
69
70
|
@log = log
|
|
70
71
|
@checker = checker
|
|
71
72
|
super(interval, repeat)
|
|
@@ -76,8 +77,12 @@ module Fluent
|
|
|
76
77
|
rescue => e
|
|
77
78
|
@log.error "Unexpected error raised. Stopping the timer.", title: @title, error: e
|
|
78
79
|
@log.error_backtrace
|
|
79
|
-
|
|
80
|
+
detach
|
|
80
81
|
@log.error "Timer detached.", title: @title
|
|
82
|
+
ensure
|
|
83
|
+
if attached?
|
|
84
|
+
detach unless @repeat
|
|
85
|
+
end
|
|
81
86
|
end
|
|
82
87
|
end
|
|
83
88
|
end
|
data/lib/fluent/root_agent.rb
CHANGED
|
@@ -91,7 +91,7 @@ module Fluent
|
|
|
91
91
|
else
|
|
92
92
|
conf.elements(name: 'source').each { |e|
|
|
93
93
|
type = e['@type']
|
|
94
|
-
raise ConfigError, "Missing 'type' parameter on <source> directive" unless type
|
|
94
|
+
raise ConfigError, "Missing '@type' parameter on <source> directive" unless type
|
|
95
95
|
add_source(type, e)
|
|
96
96
|
}
|
|
97
97
|
end
|
|
@@ -137,6 +137,9 @@ module Fluent
|
|
|
137
137
|
lifecycle(desc: true) do |i| # instance
|
|
138
138
|
i.start unless i.started?
|
|
139
139
|
end
|
|
140
|
+
lifecycle(desc: true) do |i|
|
|
141
|
+
i.after_start unless i.after_started?
|
|
142
|
+
end
|
|
140
143
|
end
|
|
141
144
|
|
|
142
145
|
def flush!
|
data/lib/fluent/supervisor.rb
CHANGED
|
@@ -29,8 +29,10 @@ require 'shellwords'
|
|
|
29
29
|
|
|
30
30
|
if Fluent.windows?
|
|
31
31
|
require 'windows/library'
|
|
32
|
+
require 'windows/synchronize'
|
|
32
33
|
require 'windows/system_info'
|
|
33
34
|
include Windows::Library
|
|
35
|
+
include Windows::Synchronize
|
|
34
36
|
include Windows::SystemInfo
|
|
35
37
|
require 'win32/ipc'
|
|
36
38
|
require 'win32/event'
|
|
@@ -48,16 +50,17 @@ module Fluent
|
|
|
48
50
|
end
|
|
49
51
|
install_supervisor_signal_handlers
|
|
50
52
|
|
|
53
|
+
if config[:signame]
|
|
54
|
+
@signame = config[:signame]
|
|
55
|
+
install_windows_event_handler
|
|
56
|
+
end
|
|
57
|
+
|
|
51
58
|
socket_manager_path = ServerEngine::SocketManager::Server.generate_path
|
|
52
59
|
ServerEngine::SocketManager::Server.open(socket_manager_path)
|
|
53
60
|
ENV['SERVERENGINE_SOCKETMANAGER_PATH'] = socket_manager_path.to_s
|
|
54
61
|
end
|
|
55
62
|
|
|
56
63
|
def after_run
|
|
57
|
-
if Time.now - @start_time < 1
|
|
58
|
-
$log.warn "process died within 1 second. exit."
|
|
59
|
-
end
|
|
60
|
-
|
|
61
64
|
stop_rpc_server if @rpc_endpoint
|
|
62
65
|
end
|
|
63
66
|
|
|
@@ -135,6 +138,22 @@ module Fluent
|
|
|
135
138
|
end unless Fluent.windows?
|
|
136
139
|
end
|
|
137
140
|
|
|
141
|
+
def install_windows_event_handler
|
|
142
|
+
Thread.new do
|
|
143
|
+
ev = Win32::Event.new(@signame)
|
|
144
|
+
begin
|
|
145
|
+
ev.reset
|
|
146
|
+
until WaitForSingleObject(ev.handle, 0) == WAIT_OBJECT_0
|
|
147
|
+
sleep 1
|
|
148
|
+
end
|
|
149
|
+
kill_worker
|
|
150
|
+
stop(true)
|
|
151
|
+
ensure
|
|
152
|
+
ev.close
|
|
153
|
+
end
|
|
154
|
+
end
|
|
155
|
+
end
|
|
156
|
+
|
|
138
157
|
def supervisor_sighup_handler
|
|
139
158
|
kill_worker
|
|
140
159
|
end
|
|
@@ -220,10 +239,13 @@ module Fluent
|
|
|
220
239
|
logger_initializer.init
|
|
221
240
|
logger = $log
|
|
222
241
|
|
|
242
|
+
command_sender = Fluent.windows? ? "pipe" : "signal"
|
|
243
|
+
|
|
223
244
|
# ServerEngine's "daemonize" option is boolean, and path of pid file is brought by "pid_path"
|
|
224
245
|
pid_path = params['daemonize']
|
|
225
246
|
daemonize = !!params['daemonize']
|
|
226
247
|
main_cmd = params['main_cmd']
|
|
248
|
+
signame = params['signame']
|
|
227
249
|
|
|
228
250
|
se_config = {
|
|
229
251
|
worker_type: 'spawn',
|
|
@@ -233,6 +255,8 @@ module Fluent
|
|
|
233
255
|
log_stderr: false,
|
|
234
256
|
enable_heartbeat: true,
|
|
235
257
|
auto_heartbeat: false,
|
|
258
|
+
unrecoverable_exit_codes: [2],
|
|
259
|
+
stop_immediately_at_unrecoverable_exit: true,
|
|
236
260
|
logger: logger,
|
|
237
261
|
log: logger.out,
|
|
238
262
|
log_path: log_path,
|
|
@@ -240,6 +264,7 @@ module Fluent
|
|
|
240
264
|
logger_initializer: logger_initializer,
|
|
241
265
|
chuser: chuser,
|
|
242
266
|
chgroup: chgroup,
|
|
267
|
+
chumask: 0,
|
|
243
268
|
suppress_repeated_stacktrace: suppress_repeated_stacktrace,
|
|
244
269
|
daemonize: daemonize,
|
|
245
270
|
rpc_endpoint: rpc_endpoint,
|
|
@@ -250,8 +275,10 @@ module Fluent
|
|
|
250
275
|
WorkerModule.name,
|
|
251
276
|
path,
|
|
252
277
|
JSON.dump(params)],
|
|
278
|
+
command_sender: command_sender,
|
|
253
279
|
fluentd_conf: fluentd_conf,
|
|
254
280
|
main_cmd: main_cmd,
|
|
281
|
+
signame: signame,
|
|
255
282
|
}
|
|
256
283
|
if daemonize
|
|
257
284
|
se_config[:pid_path] = pid_path
|
|
@@ -280,8 +307,8 @@ module Fluent
|
|
|
280
307
|
if @path && @path != "-"
|
|
281
308
|
@io = File.open(@path, "a")
|
|
282
309
|
if @chuser || @chgroup
|
|
283
|
-
chuid = @chuser ? ServerEngine::
|
|
284
|
-
chgid = @chgroup ? ServerEngine::
|
|
310
|
+
chuid = @chuser ? ServerEngine::Privilege.get_etc_passwd(@chuser).uid : nil
|
|
311
|
+
chgid = @chgroup ? ServerEngine::Privilege.get_etc_group(@chgroup).gid : nil
|
|
285
312
|
File.chown(chuid, chgid, @path)
|
|
286
313
|
end
|
|
287
314
|
else
|
|
@@ -489,6 +516,7 @@ module Fluent
|
|
|
489
516
|
params['chgroup'] = @chgroup
|
|
490
517
|
params['use_v1_config'] = @use_v1_config
|
|
491
518
|
params['suppress_repeated_stacktrace'] = @suppress_repeated_stacktrace
|
|
519
|
+
params['signame'] = @signame
|
|
492
520
|
|
|
493
521
|
se = ServerEngine.create(ServerModule, WorkerModule){
|
|
494
522
|
Fluent::Supervisor.load_config(@config_path, params)
|
|
@@ -527,6 +555,29 @@ module Fluent
|
|
|
527
555
|
trap :USR1 do
|
|
528
556
|
flush_buffer
|
|
529
557
|
end unless Fluent.windows?
|
|
558
|
+
|
|
559
|
+
if Fluent.windows?
|
|
560
|
+
command_pipe = STDIN.dup
|
|
561
|
+
STDIN.reopen(File::NULL, "rb")
|
|
562
|
+
command_pipe.binmode
|
|
563
|
+
command_pipe.sync = true
|
|
564
|
+
|
|
565
|
+
Thread.new do
|
|
566
|
+
loop do
|
|
567
|
+
cmd = command_pipe.gets.chomp
|
|
568
|
+
case cmd
|
|
569
|
+
when "GRACEFUL_STOP", "IMMEDIATE_STOP"
|
|
570
|
+
$log.debug "fluentd main process get #{cmd} command"
|
|
571
|
+
@finished = true
|
|
572
|
+
$log.debug "getting start to shutdown main process"
|
|
573
|
+
Fluent::Engine.stop
|
|
574
|
+
break
|
|
575
|
+
else
|
|
576
|
+
$log.warn "fluentd main process get unknown command [#{cmd}]"
|
|
577
|
+
end
|
|
578
|
+
end
|
|
579
|
+
end
|
|
580
|
+
end
|
|
530
581
|
end
|
|
531
582
|
|
|
532
583
|
def flush_buffer
|
|
@@ -549,6 +600,8 @@ module Fluent
|
|
|
549
600
|
def main_process(&block)
|
|
550
601
|
Process.setproctitle("worker:#{@process_name}") if @process_name
|
|
551
602
|
|
|
603
|
+
configuration_error = false
|
|
604
|
+
|
|
552
605
|
begin
|
|
553
606
|
block.call
|
|
554
607
|
rescue Fluent::ConfigError
|
|
@@ -562,7 +615,7 @@ module Fluent
|
|
|
562
615
|
console.error "config error", file: @config_path, error: $!.to_s
|
|
563
616
|
console.debug_backtrace
|
|
564
617
|
end
|
|
565
|
-
|
|
618
|
+
configuration_error = true
|
|
566
619
|
rescue
|
|
567
620
|
$log.error "unexpected error", error: $!.to_s
|
|
568
621
|
$log.error_backtrace
|
|
@@ -576,7 +629,7 @@ module Fluent
|
|
|
576
629
|
end
|
|
577
630
|
end
|
|
578
631
|
|
|
579
|
-
exit! 1
|
|
632
|
+
exit!(configuration_error ? 2 : 1)
|
|
580
633
|
end
|
|
581
634
|
|
|
582
635
|
def read_config
|
|
@@ -598,7 +651,7 @@ module Fluent
|
|
|
598
651
|
end
|
|
599
652
|
|
|
600
653
|
def change_privilege
|
|
601
|
-
ServerEngine::
|
|
654
|
+
ServerEngine::Privilege.change(@chuser, @chgroup)
|
|
602
655
|
end
|
|
603
656
|
|
|
604
657
|
def init_engine
|
data/lib/fluent/test/base.rb
CHANGED
|
@@ -30,7 +30,9 @@ module Fluent
|
|
|
30
30
|
if block
|
|
31
31
|
# Create new class for test w/ overwritten methods
|
|
32
32
|
# klass.dup is worse because its ancestors does NOT include original class name
|
|
33
|
+
klass_name = klass.name
|
|
33
34
|
klass = Class.new(klass)
|
|
35
|
+
klass.define_singleton_method("name") { klass_name }
|
|
34
36
|
klass.module_eval(&block)
|
|
35
37
|
end
|
|
36
38
|
@instance = klass.new
|
|
@@ -62,6 +64,7 @@ module Fluent
|
|
|
62
64
|
# num_waits is for checking thread status. This will be removed after improved plugin API
|
|
63
65
|
def run(num_waits = 10, &block)
|
|
64
66
|
@instance.start
|
|
67
|
+
@instance.after_start
|
|
65
68
|
begin
|
|
66
69
|
# wait until thread starts
|
|
67
70
|
num_waits.times { sleep 0.05 }
|
|
@@ -31,7 +31,9 @@ module Fluent
|
|
|
31
31
|
if block
|
|
32
32
|
# Create new class for test w/ overwritten methods
|
|
33
33
|
# klass.dup is worse because its ancestors does NOT include original class name
|
|
34
|
+
klass_name = klass.name
|
|
34
35
|
klass = Class.new(klass)
|
|
36
|
+
klass.define_singleton_method("name") { klass_name }
|
|
35
37
|
klass.module_eval(&block)
|
|
36
38
|
end
|
|
37
39
|
@instance = klass.new
|
|
@@ -86,6 +88,9 @@ module Fluent
|
|
|
86
88
|
@instance.start
|
|
87
89
|
instance_hook_after_started
|
|
88
90
|
end
|
|
91
|
+
unless @instance.after_started?
|
|
92
|
+
@instance.after_start
|
|
93
|
+
end
|
|
89
94
|
end
|
|
90
95
|
|
|
91
96
|
def instance_hook_after_started
|
|
@@ -26,7 +26,9 @@ module Fluent
|
|
|
26
26
|
if block
|
|
27
27
|
# Create new class for test w/ overwritten methods
|
|
28
28
|
# klass.dup is worse because its ancestors does NOT include original class name
|
|
29
|
+
klass_name = klass_or_str.name
|
|
29
30
|
klass_or_str = Class.new(klass_or_str)
|
|
31
|
+
klass_or_str.define_singleton_method("name") { klass_name }
|
|
30
32
|
klass_or_str.module_eval(&block)
|
|
31
33
|
end
|
|
32
34
|
@instance = klass_or_str.new
|
|
@@ -25,7 +25,9 @@ module Fluent
|
|
|
25
25
|
if block
|
|
26
26
|
# Create new class for test w/ overwritten methods
|
|
27
27
|
# klass.dup is worse because its ancestors does NOT include original class name
|
|
28
|
+
klass_name = klass_or_str.name
|
|
28
29
|
klass_or_str = Class.new(klass_or_str)
|
|
30
|
+
klass_or_str.define_singleton_method("name") { klass_name }
|
|
29
31
|
klass_or_str.module_eval(&block)
|
|
30
32
|
end
|
|
31
33
|
case klass_or_str.instance_method(:initialize).arity
|
|
@@ -0,0 +1,33 @@
|
|
|
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 'serverengine'
|
|
18
|
+
|
|
19
|
+
module Fluent
|
|
20
|
+
module Test
|
|
21
|
+
module StartupShutdown
|
|
22
|
+
def startup
|
|
23
|
+
socket_manager_path = ServerEngine::SocketManager::Server.generate_path
|
|
24
|
+
@server = ServerEngine::SocketManager::Server.open(socket_manager_path)
|
|
25
|
+
ENV['SERVERENGINE_SOCKETMANAGER_PATH'] = socket_manager_path.to_s
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def shutdown
|
|
29
|
+
@server.close
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
end
|