fluentd 1.13.3 → 1.16.5
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.github/ISSUE_TEMPLATE/{bug_report.yaml → bug_report.yml} +2 -0
- data/.github/ISSUE_TEMPLATE/config.yml +2 -2
- data/.github/ISSUE_TEMPLATE/{feature_request.yaml → feature_request.yml} +1 -0
- data/.github/workflows/stale-actions.yml +11 -9
- data/.github/workflows/test.yml +32 -0
- data/CHANGELOG.md +490 -10
- data/CONTRIBUTING.md +2 -2
- data/MAINTAINERS.md +7 -5
- data/README.md +3 -23
- data/Rakefile +1 -1
- data/SECURITY.md +14 -0
- data/fluentd.gemspec +7 -8
- data/lib/fluent/command/cat.rb +13 -3
- data/lib/fluent/command/ctl.rb +6 -3
- data/lib/fluent/command/fluentd.rb +73 -65
- data/lib/fluent/command/plugin_config_formatter.rb +1 -1
- data/lib/fluent/compat/output.rb +9 -6
- data/lib/fluent/config/dsl.rb +1 -1
- data/lib/fluent/config/error.rb +12 -0
- data/lib/fluent/config/literal_parser.rb +2 -2
- data/lib/fluent/config/parser.rb +1 -1
- data/lib/fluent/config/v1_parser.rb +3 -3
- data/lib/fluent/config/yaml_parser/fluent_value.rb +47 -0
- data/lib/fluent/config/yaml_parser/loader.rb +108 -0
- data/lib/fluent/config/yaml_parser/parser.rb +166 -0
- data/lib/fluent/config/yaml_parser/section_builder.rb +107 -0
- data/lib/fluent/config/yaml_parser.rb +56 -0
- data/lib/fluent/config.rb +14 -1
- data/lib/fluent/counter/server.rb +1 -1
- data/lib/fluent/counter/validator.rb +3 -3
- data/lib/fluent/daemon.rb +2 -4
- data/lib/fluent/engine.rb +1 -1
- data/lib/fluent/env.rb +4 -0
- data/lib/fluent/error.rb +3 -0
- data/lib/fluent/event.rb +8 -4
- data/lib/fluent/event_router.rb +47 -2
- data/lib/fluent/file_wrapper.rb +137 -0
- data/lib/fluent/log/console_adapter.rb +66 -0
- data/lib/fluent/log.rb +44 -5
- data/lib/fluent/match.rb +1 -1
- data/lib/fluent/msgpack_factory.rb +6 -1
- data/lib/fluent/oj_options.rb +1 -2
- data/lib/fluent/plugin/bare_output.rb +49 -8
- data/lib/fluent/plugin/base.rb +26 -9
- data/lib/fluent/plugin/buf_file.rb +34 -5
- data/lib/fluent/plugin/buf_file_single.rb +32 -3
- data/lib/fluent/plugin/buffer/file_chunk.rb +1 -1
- data/lib/fluent/plugin/buffer.rb +216 -70
- data/lib/fluent/plugin/filter.rb +35 -1
- data/lib/fluent/plugin/filter_record_transformer.rb +1 -1
- data/lib/fluent/plugin/in_forward.rb +2 -2
- data/lib/fluent/plugin/in_http.rb +39 -10
- data/lib/fluent/plugin/in_monitor_agent.rb +4 -2
- data/lib/fluent/plugin/in_sample.rb +1 -1
- data/lib/fluent/plugin/in_syslog.rb +13 -1
- data/lib/fluent/plugin/in_tail/group_watch.rb +204 -0
- data/lib/fluent/plugin/in_tail/position_file.rb +33 -33
- data/lib/fluent/plugin/in_tail.rb +216 -84
- data/lib/fluent/plugin/in_tcp.rb +47 -2
- data/lib/fluent/plugin/input.rb +39 -1
- data/lib/fluent/plugin/metrics.rb +119 -0
- data/lib/fluent/plugin/metrics_local.rb +96 -0
- data/lib/fluent/plugin/multi_output.rb +43 -6
- data/lib/fluent/plugin/out_copy.rb +1 -1
- data/lib/fluent/plugin/out_exec_filter.rb +2 -2
- data/lib/fluent/plugin/out_file.rb +20 -2
- data/lib/fluent/plugin/out_forward/ack_handler.rb +19 -4
- data/lib/fluent/plugin/out_forward/socket_cache.rb +2 -0
- data/lib/fluent/plugin/out_forward.rb +17 -9
- data/lib/fluent/plugin/out_secondary_file.rb +39 -22
- data/lib/fluent/plugin/output.rb +167 -78
- data/lib/fluent/plugin/parser.rb +3 -4
- data/lib/fluent/plugin/parser_apache2.rb +1 -1
- data/lib/fluent/plugin/parser_json.rb +1 -1
- data/lib/fluent/plugin/parser_syslog.rb +1 -1
- data/lib/fluent/plugin/storage_local.rb +3 -5
- data/lib/fluent/plugin.rb +10 -1
- data/lib/fluent/plugin_helper/child_process.rb +3 -0
- data/lib/fluent/plugin_helper/event_emitter.rb +8 -1
- data/lib/fluent/plugin_helper/event_loop.rb +2 -2
- data/lib/fluent/plugin_helper/http_server/server.rb +2 -1
- data/lib/fluent/plugin_helper/metrics.rb +129 -0
- data/lib/fluent/plugin_helper/record_accessor.rb +1 -1
- data/lib/fluent/plugin_helper/retry_state.rb +14 -4
- data/lib/fluent/plugin_helper/server.rb +35 -6
- data/lib/fluent/plugin_helper/service_discovery.rb +2 -2
- data/lib/fluent/plugin_helper/socket.rb +13 -2
- data/lib/fluent/plugin_helper/thread.rb +3 -3
- data/lib/fluent/plugin_helper.rb +1 -0
- data/lib/fluent/plugin_id.rb +3 -2
- data/lib/fluent/registry.rb +2 -1
- data/lib/fluent/root_agent.rb +6 -0
- data/lib/fluent/rpc.rb +4 -3
- data/lib/fluent/supervisor.rb +283 -259
- data/lib/fluent/system_config.rb +13 -3
- data/lib/fluent/test/driver/base.rb +11 -5
- data/lib/fluent/test/driver/filter.rb +4 -0
- data/lib/fluent/test/startup_shutdown.rb +6 -8
- data/lib/fluent/time.rb +21 -20
- data/lib/fluent/version.rb +1 -1
- data/lib/fluent/win32api.rb +38 -0
- data/lib/fluent/winsvc.rb +5 -8
- data/templates/new_gem/test/helper.rb.erb +0 -1
- data/test/command/test_cat.rb +31 -2
- data/test/command/test_ctl.rb +1 -2
- data/test/command/test_fluentd.rb +209 -24
- data/test/command/test_plugin_config_formatter.rb +0 -1
- data/test/compat/test_parser.rb +6 -6
- data/test/config/test_system_config.rb +13 -11
- data/test/config/test_types.rb +1 -1
- data/test/log/test_console_adapter.rb +110 -0
- data/test/plugin/in_tail/test_io_handler.rb +26 -8
- data/test/plugin/in_tail/test_position_file.rb +48 -59
- data/test/plugin/out_forward/test_ack_handler.rb +39 -0
- data/test/plugin/out_forward/test_socket_cache.rb +26 -1
- data/test/plugin/test_bare_output.rb +14 -1
- data/test/plugin/test_base.rb +133 -1
- data/test/plugin/test_buf_file.rb +62 -23
- data/test/plugin/test_buf_file_single.rb +65 -0
- data/test/plugin/test_buffer.rb +267 -3
- data/test/plugin/test_buffer_chunk.rb +11 -0
- data/test/plugin/test_filter.rb +12 -1
- data/test/plugin/test_filter_parser.rb +1 -1
- data/test/plugin/test_filter_stdout.rb +2 -2
- data/test/plugin/test_in_forward.rb +9 -11
- data/test/plugin/test_in_http.rb +65 -3
- data/test/plugin/test_in_monitor_agent.rb +216 -11
- data/test/plugin/test_in_object_space.rb +9 -3
- data/test/plugin/test_in_syslog.rb +35 -0
- data/test/plugin/test_in_tail.rb +1393 -385
- data/test/plugin/test_in_tcp.rb +87 -2
- data/test/plugin/test_in_udp.rb +28 -0
- data/test/plugin/test_in_unix.rb +2 -2
- data/test/plugin/test_input.rb +12 -1
- data/test/plugin/test_metrics.rb +294 -0
- data/test/plugin/test_metrics_local.rb +96 -0
- data/test/plugin/test_multi_output.rb +25 -1
- data/test/plugin/test_out_exec.rb +6 -4
- data/test/plugin/test_out_exec_filter.rb +6 -2
- data/test/plugin/test_out_file.rb +34 -17
- data/test/plugin/test_out_forward.rb +78 -77
- data/test/plugin/test_out_http.rb +1 -0
- data/test/plugin/test_out_stdout.rb +2 -2
- data/test/plugin/test_output.rb +297 -12
- data/test/plugin/test_output_as_buffered.rb +44 -44
- data/test/plugin/test_output_as_buffered_compress.rb +32 -18
- data/test/plugin/test_output_as_buffered_retries.rb +54 -7
- data/test/plugin/test_output_as_buffered_secondary.rb +4 -4
- data/test/plugin/test_parser_regexp.rb +1 -6
- data/test/plugin/test_parser_syslog.rb +1 -1
- data/test/plugin_helper/test_cert_option.rb +1 -1
- data/test/plugin_helper/test_child_process.rb +38 -16
- data/test/plugin_helper/test_event_emitter.rb +29 -0
- data/test/plugin_helper/test_http_server_helper.rb +1 -1
- data/test/plugin_helper/test_metrics.rb +137 -0
- data/test/plugin_helper/test_retry_state.rb +602 -38
- data/test/plugin_helper/test_server.rb +78 -6
- data/test/plugin_helper/test_timer.rb +2 -2
- data/test/test_config.rb +191 -24
- data/test/test_event_router.rb +17 -0
- data/test/test_file_wrapper.rb +53 -0
- data/test/test_formatter.rb +24 -21
- data/test/test_log.rb +122 -40
- data/test/test_msgpack_factory.rb +32 -0
- data/test/test_plugin_classes.rb +102 -0
- data/test/test_root_agent.rb +30 -1
- data/test/test_supervisor.rb +477 -257
- data/test/test_time_parser.rb +22 -0
- metadata +55 -34
- data/.drone.yml +0 -35
- data/.github/workflows/issue-auto-closer.yml +0 -12
- data/.github/workflows/linux-test.yaml +0 -36
- data/.github/workflows/macos-test.yaml +0 -30
- data/.github/workflows/windows-test.yaml +0 -46
- data/.gitlab-ci.yml +0 -103
- data/lib/fluent/plugin/file_wrapper.rb +0 -187
- data/test/plugin/test_file_wrapper.rb +0 -126
- data/test/test_logger_initializer.rb +0 -46
data/lib/fluent/event_router.rb
CHANGED
@@ -47,6 +47,8 @@ module Fluent
|
|
47
47
|
@match_cache = MatchCache.new
|
48
48
|
@default_collector = default_collector
|
49
49
|
@emit_error_handler = emit_error_handler
|
50
|
+
@metric_callbacks = {}
|
51
|
+
@caller_plugin_id = nil
|
50
52
|
end
|
51
53
|
|
52
54
|
attr_accessor :default_collector
|
@@ -83,6 +85,22 @@ module Fluent
|
|
83
85
|
@match_rules << Rule.new(pattern, collector)
|
84
86
|
end
|
85
87
|
|
88
|
+
def add_metric_callbacks(caller_plugin_id, callback)
|
89
|
+
@metric_callbacks[caller_plugin_id] = callback
|
90
|
+
end
|
91
|
+
|
92
|
+
def caller_plugin_id=(caller_plugin_id)
|
93
|
+
@caller_plugin_id = caller_plugin_id
|
94
|
+
end
|
95
|
+
|
96
|
+
def find_callback
|
97
|
+
if @caller_plugin_id
|
98
|
+
@metric_callbacks[@caller_plugin_id]
|
99
|
+
else
|
100
|
+
nil
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
86
104
|
def emit(tag, time, record)
|
87
105
|
unless record.nil?
|
88
106
|
emit_stream(tag, OneEventStream.new(time, record))
|
@@ -95,6 +113,11 @@ module Fluent
|
|
95
113
|
|
96
114
|
def emit_stream(tag, es)
|
97
115
|
match(tag).emit_events(tag, es)
|
116
|
+
if callback = find_callback
|
117
|
+
callback.call(es)
|
118
|
+
end
|
119
|
+
rescue Pipeline::OutputError => e
|
120
|
+
@emit_error_handler.handle_emits_error(tag, e.processed_es, e.internal_error)
|
98
121
|
rescue => e
|
99
122
|
@emit_error_handler.handle_emits_error(tag, es, e)
|
100
123
|
end
|
@@ -140,6 +163,17 @@ module Fluent
|
|
140
163
|
private
|
141
164
|
|
142
165
|
class Pipeline
|
166
|
+
|
167
|
+
class OutputError < StandardError
|
168
|
+
attr_reader :internal_error
|
169
|
+
attr_reader :processed_es
|
170
|
+
|
171
|
+
def initialize(internal_error, processed_es)
|
172
|
+
@internal_error = internal_error
|
173
|
+
@processed_es = processed_es
|
174
|
+
end
|
175
|
+
end
|
176
|
+
|
143
177
|
def initialize
|
144
178
|
@filters = []
|
145
179
|
@output = nil
|
@@ -157,7 +191,12 @@ module Fluent
|
|
157
191
|
|
158
192
|
def emit_events(tag, es)
|
159
193
|
processed = @optimizer.filter_stream(tag, es)
|
160
|
-
|
194
|
+
|
195
|
+
begin
|
196
|
+
@output.emit_events(tag, processed)
|
197
|
+
rescue => e
|
198
|
+
raise OutputError.new(e, processed)
|
199
|
+
end
|
161
200
|
end
|
162
201
|
|
163
202
|
class FilterOptimizer
|
@@ -175,7 +214,11 @@ module Fluent
|
|
175
214
|
if optimizable?
|
176
215
|
optimized_filter_stream(tag, es)
|
177
216
|
else
|
178
|
-
@filters.reduce(es) { |acc, filter|
|
217
|
+
@filters.reduce(es) { |acc, filter|
|
218
|
+
filtered_es = filter.filter_stream(tag, acc)
|
219
|
+
filter.measure_metrics(filtered_es)
|
220
|
+
filtered_es
|
221
|
+
}
|
179
222
|
end
|
180
223
|
end
|
181
224
|
|
@@ -193,6 +236,7 @@ module Fluent
|
|
193
236
|
begin
|
194
237
|
filtered_time, filtered_record = filter.filter_with_time(tag, filtered_time, filtered_record)
|
195
238
|
throw :break_loop unless filtered_record && filtered_time
|
239
|
+
filter.measure_metrics(OneEventStream.new(time, record))
|
196
240
|
rescue => e
|
197
241
|
filter.router.emit_error_event(tag, filtered_time, filtered_record, e)
|
198
242
|
end
|
@@ -200,6 +244,7 @@ module Fluent
|
|
200
244
|
begin
|
201
245
|
filtered_record = filter.filter(tag, filtered_time, filtered_record)
|
202
246
|
throw :break_loop unless filtered_record
|
247
|
+
filter.measure_metrics(OneEventStream.new(time, record))
|
203
248
|
rescue => e
|
204
249
|
filter.router.emit_error_event(tag, filtered_time, filtered_record, e)
|
205
250
|
end
|
@@ -0,0 +1,137 @@
|
|
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
|
+
unless Fluent.windows?
|
18
|
+
Fluent::FileWrapper = File
|
19
|
+
else
|
20
|
+
require 'fluent/win32api'
|
21
|
+
|
22
|
+
module Fluent
|
23
|
+
module FileWrapper
|
24
|
+
def self.open(path, mode='r')
|
25
|
+
io = WindowsFile.new(path, mode).io
|
26
|
+
if block_given?
|
27
|
+
v = yield io
|
28
|
+
io.close
|
29
|
+
v
|
30
|
+
else
|
31
|
+
io
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
def self.stat(path)
|
36
|
+
f = WindowsFile.new(path)
|
37
|
+
s = f.stat
|
38
|
+
f.close
|
39
|
+
s
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
class WindowsFile
|
44
|
+
include File::Constants
|
45
|
+
|
46
|
+
attr_reader :io
|
47
|
+
|
48
|
+
INVALID_HANDLE_VALUE = -1
|
49
|
+
|
50
|
+
def initialize(path, mode_enc='r')
|
51
|
+
@path = path
|
52
|
+
mode, enc = mode_enc.split(":", 2)
|
53
|
+
@io = File.open(path, mode2flags(mode))
|
54
|
+
@io.set_encoding(enc) if enc
|
55
|
+
@file_handle = Win32API._get_osfhandle(@io.to_i)
|
56
|
+
@io.instance_variable_set(:@file_index, self.ino)
|
57
|
+
def @io.ino
|
58
|
+
@file_index
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
def close
|
63
|
+
@io.close
|
64
|
+
@file_handle = INVALID_HANDLE_VALUE
|
65
|
+
end
|
66
|
+
|
67
|
+
# To keep backward compatibility, we continue to use GetFileInformationByHandle()
|
68
|
+
# to get file id.
|
69
|
+
# Note that Ruby's File.stat uses GetFileInformationByHandleEx() with FileIdInfo
|
70
|
+
# and returned value is different with above one, former one is 64 bit while
|
71
|
+
# later one is 128bit.
|
72
|
+
def ino
|
73
|
+
by_handle_file_information = '\0'*(4+8+8+8+4+4+4+4+4+4) #72bytes
|
74
|
+
|
75
|
+
unless Win32API.GetFileInformationByHandle(@file_handle, by_handle_file_information)
|
76
|
+
return 0
|
77
|
+
end
|
78
|
+
|
79
|
+
by_handle_file_information.unpack("I11Q1")[11] # fileindex
|
80
|
+
end
|
81
|
+
|
82
|
+
def stat
|
83
|
+
raise Errno::ENOENT if delete_pending
|
84
|
+
s = File.stat(@path)
|
85
|
+
s.instance_variable_set :@ino, self.ino
|
86
|
+
def s.ino; @ino; end
|
87
|
+
s
|
88
|
+
end
|
89
|
+
|
90
|
+
private
|
91
|
+
|
92
|
+
def mode2flags(mode)
|
93
|
+
# Always inject File::Constants::SHARE_DELETE
|
94
|
+
# https://github.com/fluent/fluentd/pull/3585#issuecomment-1101502617
|
95
|
+
# To enable SHARE_DELETE, BINARY is also required.
|
96
|
+
# https://bugs.ruby-lang.org/issues/11218
|
97
|
+
# https://github.com/ruby/ruby/blob/d6684f063bc53e3cab025bd39526eca3b480b5e7/win32/win32.c#L6332-L6345
|
98
|
+
flags = BINARY | SHARE_DELETE
|
99
|
+
case mode.delete("b")
|
100
|
+
when "r"
|
101
|
+
flags |= RDONLY
|
102
|
+
when "r+"
|
103
|
+
flags |= RDWR
|
104
|
+
when "w"
|
105
|
+
flags |= WRONLY | CREAT | TRUNC
|
106
|
+
when "w+"
|
107
|
+
flags |= RDWR | CREAT | TRUNC
|
108
|
+
when "a"
|
109
|
+
flags |= WRONLY | CREAT | APPEND
|
110
|
+
when "a+"
|
111
|
+
flags |= RDWR | CREAT | APPEND
|
112
|
+
else
|
113
|
+
raise Errno::EINVAL.new("Unsupported mode by Fluent::FileWrapper: #{mode}")
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
117
|
+
# DeletePending is a Windows-specific file state that roughly means
|
118
|
+
# "this file is queued for deletion, so close any open handlers"
|
119
|
+
#
|
120
|
+
# This flag can be retrieved via GetFileInformationByHandleEx().
|
121
|
+
#
|
122
|
+
# https://docs.microsoft.com/en-us/windows/win32/api/winbase/nf-winbase-getfileinformationbyhandleex
|
123
|
+
#
|
124
|
+
def delete_pending
|
125
|
+
file_standard_info = 0x01
|
126
|
+
bufsize = 1024
|
127
|
+
buf = '\0' * bufsize
|
128
|
+
|
129
|
+
unless Win32API.GetFileInformationByHandleEx(@file_handle, file_standard_info, buf, bufsize)
|
130
|
+
return false
|
131
|
+
end
|
132
|
+
|
133
|
+
return buf.unpack("QQICC")[3] != 0
|
134
|
+
end
|
135
|
+
end
|
136
|
+
end
|
137
|
+
end
|
@@ -0,0 +1,66 @@
|
|
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 'console/terminal/logger'
|
18
|
+
|
19
|
+
module Fluent
|
20
|
+
class Log
|
21
|
+
# Async gem which is used by http_server helper switched logger mechanism to
|
22
|
+
# Console gem which isn't complatible with Ruby's standard Logger (since
|
23
|
+
# v1.17). This class adapts it to Fluentd's logger mechanism.
|
24
|
+
class ConsoleAdapter < Console::Terminal::Logger
|
25
|
+
def self.wrap(logger)
|
26
|
+
_, level = Console::Logger::LEVELS.find { |key, value|
|
27
|
+
if logger.level <= 0
|
28
|
+
key == :debug
|
29
|
+
else
|
30
|
+
value == logger.level - 1
|
31
|
+
end
|
32
|
+
}
|
33
|
+
Console::Logger.new(ConsoleAdapter.new(logger), level: level)
|
34
|
+
end
|
35
|
+
|
36
|
+
def initialize(logger)
|
37
|
+
@logger = logger
|
38
|
+
# When `verbose` is `true`, following items will be added as a prefix or
|
39
|
+
# suffix of the subject:
|
40
|
+
# * Severity
|
41
|
+
# * Object ID
|
42
|
+
# * PID
|
43
|
+
# * Time
|
44
|
+
# Severity and Time are added by Fluentd::Log too so they are redundant.
|
45
|
+
# PID is the worker's PID so it's also redundant.
|
46
|
+
# Object ID will be too verbose in usual cases.
|
47
|
+
# So set it as `false` here to suppress redundant items.
|
48
|
+
super(StringIO.new, verbose: false)
|
49
|
+
end
|
50
|
+
|
51
|
+
def call(subject = nil, *arguments, name: nil, severity: 'info', **options, &block)
|
52
|
+
if LEVEL_TEXT.include?(severity.to_s)
|
53
|
+
level = severity
|
54
|
+
else
|
55
|
+
@logger.warn("Unknown severity: #{severity}")
|
56
|
+
level = 'warn'
|
57
|
+
end
|
58
|
+
|
59
|
+
@io.seek(0)
|
60
|
+
@io.truncate(0)
|
61
|
+
super
|
62
|
+
@logger.send(level, @io.string.chomp)
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
data/lib/fluent/log.rb
CHANGED
@@ -51,6 +51,8 @@ module Fluent
|
|
51
51
|
LOG_TYPES = [LOG_TYPE_SUPERVISOR, LOG_TYPE_WORKER0, LOG_TYPE_DEFAULT].freeze
|
52
52
|
LOG_ROTATE_AGE = %w(daily weekly monthly)
|
53
53
|
|
54
|
+
IGNORE_SAME_LOG_MAX_CACHE_SIZE = 1000 # If need, make this an option of system config.
|
55
|
+
|
54
56
|
def self.str_to_level(log_level_str)
|
55
57
|
case log_level_str.downcase
|
56
58
|
when "trace" then LEVEL_TRACE
|
@@ -67,8 +69,29 @@ module Fluent
|
|
67
69
|
LEVEL_TEXT.map{|t| "#{LOG_EVENT_TAG_PREFIX}.#{t}" }
|
68
70
|
end
|
69
71
|
|
72
|
+
# Create a unique path for each process.
|
73
|
+
#
|
74
|
+
# >>> per_process_path("C:/tmp/test.log", :worker, 1)
|
75
|
+
# C:/tmp/test-1.log
|
76
|
+
# >>> per_process_path("C:/tmp/test.log", :supervisor, 0)
|
77
|
+
# C:/tmp/test-supervisor-0.log
|
78
|
+
def self.per_process_path(path, process_type, worker_id)
|
79
|
+
path = Pathname(path)
|
80
|
+
ext = path.extname
|
81
|
+
|
82
|
+
if process_type == :supervisor
|
83
|
+
suffix = "-#{process_type}-0#{ext}" # "-0" for backword compatibility.
|
84
|
+
else
|
85
|
+
suffix = "-#{worker_id}#{ext}"
|
86
|
+
end
|
87
|
+
return path.sub_ext(suffix).to_s
|
88
|
+
end
|
89
|
+
|
70
90
|
def initialize(logger, opts={})
|
71
|
-
#
|
91
|
+
# When ServerEngine changes the logger.level, the Fluentd logger level should also change.
|
92
|
+
# So overwrites logger.level= below.
|
93
|
+
# However, currently Fluentd doesn't use the ServerEngine's reloading feature,
|
94
|
+
# so maybe we don't need this overwriting anymore.
|
72
95
|
orig_logger_level_setter = logger.class.public_instance_method(:level=).bind(logger)
|
73
96
|
me = self
|
74
97
|
# The original ruby logger sets the number as each log level like below.
|
@@ -92,6 +115,7 @@ module Fluent
|
|
92
115
|
# So if serverengine's logger level is changed, fluentd's log level will be changed to that + 1.
|
93
116
|
logger.define_singleton_method(:level=) {|level| orig_logger_level_setter.call(level); me.level = self.level + 1 }
|
94
117
|
|
118
|
+
@path = opts[:path]
|
95
119
|
@logger = logger
|
96
120
|
@out = logger.instance_variable_get(:@logdev)
|
97
121
|
@level = logger.level + 1
|
@@ -102,7 +126,8 @@ module Fluent
|
|
102
126
|
@time_format = nil
|
103
127
|
@formatter = nil
|
104
128
|
|
105
|
-
self.format = :text
|
129
|
+
self.format = opts.fetch(:format, :text)
|
130
|
+
self.time_format = opts[:time_format] if opts.key?(:time_format)
|
106
131
|
enable_color out.tty?
|
107
132
|
# TODO: This variable name is unclear so we should change to better name.
|
108
133
|
@threads_exclude_events = []
|
@@ -154,8 +179,12 @@ module Fluent
|
|
154
179
|
|
155
180
|
attr_reader :format
|
156
181
|
attr_reader :time_format
|
157
|
-
attr_accessor :log_event_enabled, :ignore_repeated_log_interval, :ignore_same_log_interval
|
182
|
+
attr_accessor :log_event_enabled, :ignore_repeated_log_interval, :ignore_same_log_interval, :suppress_repeated_stacktrace
|
158
183
|
attr_accessor :out
|
184
|
+
# Strictly speaking, we should also change @logger.level when the setter of @level is called.
|
185
|
+
# Currently, we don't need to do it, since Fluentd::Log doesn't use ServerEngine::DaemonLogger.level.
|
186
|
+
# Since We overwrites logger.level= so that @logger.level is applied to @level,
|
187
|
+
# we need to find a good way to do this, otherwise we will end up in an endless loop.
|
159
188
|
attr_accessor :level
|
160
189
|
attr_accessor :optional_header, :optional_attrs
|
161
190
|
|
@@ -202,9 +231,12 @@ module Fluent
|
|
202
231
|
@time_formatter = Strftime.new(@time_format) rescue nil
|
203
232
|
end
|
204
233
|
|
234
|
+
def stdout?
|
235
|
+
@out == $stdout
|
236
|
+
end
|
237
|
+
|
205
238
|
def reopen!
|
206
|
-
|
207
|
-
@logger.reopen! if @logger
|
239
|
+
@out.reopen(@path, "a") if @path && @path != "-"
|
208
240
|
nil
|
209
241
|
end
|
210
242
|
|
@@ -447,6 +479,13 @@ module Fluent
|
|
447
479
|
false
|
448
480
|
end
|
449
481
|
else
|
482
|
+
if cached_log.size >= IGNORE_SAME_LOG_MAX_CACHE_SIZE
|
483
|
+
cached_log.reject! do |_, cached_time|
|
484
|
+
(time - cached_time) > @ignore_same_log_interval
|
485
|
+
end
|
486
|
+
end
|
487
|
+
# If the size is still over, we have no choice but to clear it.
|
488
|
+
cached_log.clear if cached_log.size >= IGNORE_SAME_LOG_MAX_CACHE_SIZE
|
450
489
|
cached_log[message] = time
|
451
490
|
false
|
452
491
|
end
|
data/lib/fluent/match.rb
CHANGED
@@ -100,7 +100,12 @@ module Fluent
|
|
100
100
|
end
|
101
101
|
|
102
102
|
def self.thread_local_msgpack_unpacker
|
103
|
-
Thread.current[:local_msgpack_unpacker]
|
103
|
+
unpacker = Thread.current[:local_msgpack_unpacker]
|
104
|
+
if unpacker.nil?
|
105
|
+
return Thread.current[:local_msgpack_unpacker] = MessagePackFactory.engine_factory.unpacker
|
106
|
+
end
|
107
|
+
unpacker.reset
|
108
|
+
unpacker
|
104
109
|
end
|
105
110
|
end
|
106
111
|
end
|
data/lib/fluent/oj_options.rb
CHANGED
@@ -4,14 +4,13 @@ module Fluent
|
|
4
4
|
class OjOptions
|
5
5
|
OPTIONS = {
|
6
6
|
'bigdecimal_load': :symbol,
|
7
|
-
'max_nesting': :integer,
|
8
7
|
'mode': :symbol,
|
9
8
|
'use_to_json': :bool
|
10
9
|
}
|
11
10
|
|
12
11
|
ALLOWED_VALUES = {
|
13
12
|
'bigdecimal_load': %i[bigdecimal float auto],
|
14
|
-
'mode': %i[strict null compat json rails
|
13
|
+
'mode': %i[strict null compat json rails custom]
|
15
14
|
}
|
16
15
|
|
17
16
|
DEFAULTS = {
|
@@ -23,37 +23,78 @@ require 'fluent/plugin_helper'
|
|
23
23
|
module Fluent
|
24
24
|
module Plugin
|
25
25
|
class BareOutput < Base
|
26
|
+
include PluginHelper::Mixin # for metrics
|
27
|
+
|
26
28
|
# DO NOT USE THIS plugin for normal output plugin. Use Output instead.
|
27
29
|
# This output plugin base class is only for meta-output plugins
|
28
30
|
# which cannot be implemented on MultiOutput.
|
29
31
|
# E.g,: forest, config-expander
|
30
32
|
|
33
|
+
helpers_internal :metrics
|
34
|
+
|
31
35
|
include PluginId
|
32
36
|
include PluginLoggerMixin
|
33
37
|
include PluginHelper::Mixin
|
34
38
|
|
35
|
-
attr_reader :num_errors, :emit_count, :emit_records
|
36
|
-
|
37
39
|
def process(tag, es)
|
38
40
|
raise NotImplementedError, "BUG: output plugins MUST implement this method"
|
39
41
|
end
|
40
42
|
|
43
|
+
def num_errors
|
44
|
+
@num_errors_metrics.get
|
45
|
+
end
|
46
|
+
|
47
|
+
def emit_count
|
48
|
+
@emit_count_metrics.get
|
49
|
+
end
|
50
|
+
|
51
|
+
def emit_size
|
52
|
+
@emit_size_metrics.get
|
53
|
+
end
|
54
|
+
|
55
|
+
def emit_records
|
56
|
+
@emit_records_metrics.get
|
57
|
+
end
|
58
|
+
|
41
59
|
def initialize
|
42
60
|
super
|
43
61
|
@counter_mutex = Mutex.new
|
44
62
|
# TODO: well organized counters
|
45
|
-
@
|
46
|
-
@
|
47
|
-
@
|
63
|
+
@num_errors_metrics = nil
|
64
|
+
@emit_count_metrics = nil
|
65
|
+
@emit_records_metrics = nil
|
66
|
+
@emit_size_metrics = nil
|
67
|
+
end
|
68
|
+
|
69
|
+
def configure(conf)
|
70
|
+
super
|
71
|
+
|
72
|
+
@num_errors_metrics = metrics_create(namespace: "fluentd", subsystem: "bare_output", name: "num_errors", help_text: "Number of count num errors")
|
73
|
+
@emit_count_metrics = metrics_create(namespace: "fluentd", subsystem: "bare_output", name: "emit_count", help_text: "Number of count emits")
|
74
|
+
@emit_records_metrics = metrics_create(namespace: "fluentd", subsystem: "bare_output", name: "emit_records", help_text: "Number of emit records")
|
75
|
+
@emit_size_metrics = metrics_create(namespace: "fluentd", subsystem: "bare_output", name: "emit_size", help_text: "Total size of emit events")
|
76
|
+
@enable_size_metrics = !!system_config.enable_size_metrics
|
77
|
+
end
|
78
|
+
|
79
|
+
def statistics
|
80
|
+
stats = {
|
81
|
+
'num_errors' => @num_errors_metrics.get,
|
82
|
+
'emit_records' => @emit_records_metrics.get,
|
83
|
+
'emit_count' => @emit_count_metrics.get,
|
84
|
+
'emit_size' => @emit_size_metrics.get,
|
85
|
+
}
|
86
|
+
|
87
|
+
{ 'bare_output' => stats }
|
48
88
|
end
|
49
89
|
|
50
90
|
def emit_sync(tag, es)
|
51
|
-
@
|
91
|
+
@emit_count_metrics.inc
|
52
92
|
begin
|
53
93
|
process(tag, es)
|
54
|
-
@
|
94
|
+
@emit_records_metrics.add(es.size)
|
95
|
+
@emit_size_metrics.add(es.to_msgpack_stream.bytesize) if @enable_size_metrics
|
55
96
|
rescue
|
56
|
-
@
|
97
|
+
@num_errors_metrics.inc
|
57
98
|
raise
|
58
99
|
end
|
59
100
|
end
|
data/lib/fluent/plugin/base.rb
CHANGED
@@ -31,6 +31,7 @@ module Fluent
|
|
31
31
|
def initialize
|
32
32
|
@log = nil
|
33
33
|
super
|
34
|
+
@fluentd_lock_dir = ENV['FLUENTD_LOCK_DIR']
|
34
35
|
@_state = State.new(false, false, false, false, false, false, false, false, false)
|
35
36
|
@_context_router = nil
|
36
37
|
@_fluentd_worker_id = nil
|
@@ -52,14 +53,12 @@ module Fluent
|
|
52
53
|
end
|
53
54
|
|
54
55
|
def configure(conf)
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
1
|
60
|
-
end
|
61
|
-
system_config_override(workers: workers)
|
56
|
+
raise ArgumentError, "BUG: type of conf must be Fluent::Config::Element, but #{conf.class} is passed." unless conf.is_a?(Fluent::Config::Element)
|
57
|
+
|
58
|
+
if conf.for_this_worker? || (Fluent::Engine.supervisor_mode && !conf.for_every_workers?)
|
59
|
+
system_config_override(workers: conf.target_worker_ids.size)
|
62
60
|
end
|
61
|
+
|
63
62
|
super(conf, system_config.strict_config_value)
|
64
63
|
@_state ||= State.new(false, false, false, false, false, false, false, false, false)
|
65
64
|
@_state.configure = true
|
@@ -70,10 +69,28 @@ module Fluent
|
|
70
69
|
true
|
71
70
|
end
|
72
71
|
|
72
|
+
def get_lock_path(name)
|
73
|
+
name = name.gsub(/[^a-zA-Z0-9]/, "_")
|
74
|
+
File.join(@fluentd_lock_dir, "fluentd-#{name}.lock")
|
75
|
+
end
|
76
|
+
|
77
|
+
def acquire_worker_lock(name)
|
78
|
+
if @fluentd_lock_dir.nil?
|
79
|
+
raise InvalidLockDirectory, "can't acquire lock because FLUENTD_LOCK_DIR isn't set"
|
80
|
+
end
|
81
|
+
lock_path = get_lock_path(name)
|
82
|
+
File.open(lock_path, "w") do |f|
|
83
|
+
f.flock(File::LOCK_EX)
|
84
|
+
yield
|
85
|
+
end
|
86
|
+
# Update access time to prevent tmpwatch from deleting a lock file.
|
87
|
+
FileUtils.touch(lock_path);
|
88
|
+
end
|
89
|
+
|
73
90
|
def string_safe_encoding(str)
|
74
91
|
unless str.valid_encoding?
|
75
|
-
log.info "invalid byte sequence is replaced in `#{str}`" if self.respond_to?(:log)
|
76
92
|
str = str.scrub('?')
|
93
|
+
log.info "invalid byte sequence is replaced in `#{str}`" if self.respond_to?(:log)
|
77
94
|
end
|
78
95
|
yield str
|
79
96
|
end
|
@@ -173,7 +190,7 @@ module Fluent
|
|
173
190
|
# Thread::Backtrace::Location#path returns base filename or absolute path.
|
174
191
|
# #absolute_path returns absolute_path always.
|
175
192
|
# https://bugs.ruby-lang.org/issues/12159
|
176
|
-
if
|
193
|
+
if /\/test_[^\/]+\.rb$/.match?(location.absolute_path) # location.path =~ /test_.+\.rb$/
|
177
194
|
return true
|
178
195
|
end
|
179
196
|
end
|
@@ -39,8 +39,8 @@ module Fluent
|
|
39
39
|
config_set_default :chunk_limit_size, DEFAULT_CHUNK_LIMIT_SIZE
|
40
40
|
config_set_default :total_limit_size, DEFAULT_TOTAL_LIMIT_SIZE
|
41
41
|
|
42
|
-
config_param :file_permission, :string, default: nil # '0644'
|
43
|
-
config_param :dir_permission, :string, default: nil # '0755'
|
42
|
+
config_param :file_permission, :string, default: nil # '0644' (Fluent::DEFAULT_FILE_PERMISSION)
|
43
|
+
config_param :dir_permission, :string, default: nil # '0755' (Fluent::DEFAULT_DIR_PERMISSION)
|
44
44
|
|
45
45
|
def initialize
|
46
46
|
super
|
@@ -139,13 +139,20 @@ module Fluent
|
|
139
139
|
def resume
|
140
140
|
stage = {}
|
141
141
|
queue = []
|
142
|
+
exist_broken_file = false
|
142
143
|
|
143
144
|
patterns = [@path]
|
144
145
|
patterns.unshift @additional_resume_path if @additional_resume_path
|
145
146
|
Dir.glob(escaped_patterns(patterns)) do |path|
|
146
147
|
next unless File.file?(path)
|
147
148
|
|
148
|
-
|
149
|
+
if owner.respond_to?(:buffer_config) && owner.buffer_config&.flush_at_shutdown
|
150
|
+
# When `flush_at_shutdown` is `true`, the remaining chunk files during resuming are possibly broken
|
151
|
+
# since there may be a power failure or similar failure.
|
152
|
+
log.warn { "restoring buffer file: path = #{path}" }
|
153
|
+
else
|
154
|
+
log.debug { "restoring buffer file: path = #{path}" }
|
155
|
+
end
|
149
156
|
|
150
157
|
m = new_metadata() # this metadata will be overwritten by resuming .meta file content
|
151
158
|
# so it should not added into @metadata_list for now
|
@@ -158,6 +165,7 @@ module Fluent
|
|
158
165
|
begin
|
159
166
|
chunk = Fluent::Plugin::Buffer::FileChunk.new(m, path, mode, compress: @compress) # file chunk resumes contents of metadata
|
160
167
|
rescue Fluent::Plugin::Buffer::FileChunk::FileChunkError => e
|
168
|
+
exist_broken_file = true
|
161
169
|
handle_broken_files(path, mode, e)
|
162
170
|
next
|
163
171
|
end
|
@@ -182,6 +190,15 @@ module Fluent
|
|
182
190
|
|
183
191
|
queue.sort_by!{ |chunk| chunk.modified_at }
|
184
192
|
|
193
|
+
# If one of the files is corrupted, other files may also be corrupted and be undetected.
|
194
|
+
# The time priods of each chunk are helpful to check the data.
|
195
|
+
if exist_broken_file
|
196
|
+
log.info "Since a broken chunk file was found, it is possible that other files remaining at the time of resuming were also broken. Here is the list of the files."
|
197
|
+
(stage.values + queue).each { |chunk|
|
198
|
+
log.info " #{chunk.path}:", :created_at => chunk.created_at, :modified_at => chunk.modified_at
|
199
|
+
}
|
200
|
+
end
|
201
|
+
|
185
202
|
return stage, queue
|
186
203
|
end
|
187
204
|
|
@@ -195,8 +212,20 @@ module Fluent
|
|
195
212
|
end
|
196
213
|
|
197
214
|
def handle_broken_files(path, mode, e)
|
198
|
-
log.error "found broken chunk file during resume.
|
199
|
-
|
215
|
+
log.error "found broken chunk file during resume.", :path => path, :mode => mode, :err_msg => e.message
|
216
|
+
unique_id = Fluent::Plugin::Buffer::FileChunk.unique_id_from_path(path)
|
217
|
+
backup(unique_id) { |f|
|
218
|
+
File.open(path, 'rb') { |chunk|
|
219
|
+
chunk.set_encoding(Encoding::ASCII_8BIT)
|
220
|
+
chunk.sync = true
|
221
|
+
chunk.binmode
|
222
|
+
IO.copy_stream(chunk, f)
|
223
|
+
}
|
224
|
+
}
|
225
|
+
rescue => error
|
226
|
+
log.error "backup failed. Delete corresponding files.", :err_msg => error.message
|
227
|
+
ensure
|
228
|
+
log.warn "disable_chunk_backup is true. #{dump_unique_id_hex(unique_id)} chunk is thrown away." if @disable_chunk_backup
|
200
229
|
File.unlink(path, path + '.meta') rescue nil
|
201
230
|
end
|
202
231
|
|