fluentd 1.13.3 → 1.16.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/.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
|
|