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/plugin/in_tcp.rb
CHANGED
|
@@ -36,10 +36,16 @@ module Fluent::Plugin
|
|
|
36
36
|
desc "The field name of the client's address."
|
|
37
37
|
config_param :source_address_key, :string, default: nil
|
|
38
38
|
|
|
39
|
+
# Setting default to nil for backward compatibility
|
|
40
|
+
desc "The max bytes of message."
|
|
41
|
+
config_param :message_length_limit, :size, default: nil
|
|
42
|
+
|
|
39
43
|
config_param :blocking_timeout, :time, default: 0.5
|
|
40
44
|
|
|
41
45
|
desc 'The payload is read up to this character.'
|
|
42
46
|
config_param :delimiter, :string, default: "\n" # syslog family add "\n" to each message and this seems only way to split messages in tcp stream
|
|
47
|
+
desc 'Check the remote connection is still available by sending a keepalive packet if this value is true.'
|
|
48
|
+
config_param :send_keepalive_packet, :bool, default: false
|
|
43
49
|
|
|
44
50
|
# in_forward like host/network restriction
|
|
45
51
|
config_section :security, required: false, multi: false do
|
|
@@ -100,8 +106,9 @@ module Fluent::Plugin
|
|
|
100
106
|
|
|
101
107
|
log.info "listening tcp socket", bind: @bind, port: @port
|
|
102
108
|
del_size = @delimiter.length
|
|
109
|
+
discard_till_next_delimiter = false
|
|
103
110
|
if @_extract_enabled && @_extract_tag_key
|
|
104
|
-
server_create(:in_tcp_server_single_emit, @port, bind: @bind, resolve_name: !!@source_hostname_key) do |data, conn|
|
|
111
|
+
server_create(:in_tcp_server_single_emit, @port, bind: @bind, resolve_name: !!@source_hostname_key, send_keepalive_packet: @send_keepalive_packet) do |data, conn|
|
|
105
112
|
unless check_client(conn)
|
|
106
113
|
conn.close
|
|
107
114
|
next
|
|
@@ -114,6 +121,16 @@ module Fluent::Plugin
|
|
|
114
121
|
msg = buf[pos...i]
|
|
115
122
|
pos = i + del_size
|
|
116
123
|
|
|
124
|
+
if discard_till_next_delimiter
|
|
125
|
+
discard_till_next_delimiter = false
|
|
126
|
+
next
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
if !@message_length_limit.nil? && @message_length_limit < msg.bytesize
|
|
130
|
+
log.info "The received data is larger than 'message_length_limit', dropped:", limit: @message_length_limit, size: msg.bytesize, head: msg[...32]
|
|
131
|
+
next
|
|
132
|
+
end
|
|
133
|
+
|
|
117
134
|
@parser.parse(msg) do |time, record|
|
|
118
135
|
unless time && record
|
|
119
136
|
log.warn "pattern not matched", message: msg
|
|
@@ -129,9 +146,18 @@ module Fluent::Plugin
|
|
|
129
146
|
end
|
|
130
147
|
end
|
|
131
148
|
buf.slice!(0, pos) if pos > 0
|
|
149
|
+
# If the buffer size exceeds the limit here, it means that the next message will definitely exceed the limit.
|
|
150
|
+
# So we should clear the buffer here. Otherwise, it will keep storing useless data until the next delimiter comes.
|
|
151
|
+
if !@message_length_limit.nil? && @message_length_limit < buf.bytesize
|
|
152
|
+
log.info "The buffer size exceeds 'message_length_limit', cleared:", limit: @message_length_limit, size: buf.bytesize, head: buf[...32]
|
|
153
|
+
buf.clear
|
|
154
|
+
# We should discard the subsequent data until the next delimiter comes.
|
|
155
|
+
discard_till_next_delimiter = true
|
|
156
|
+
next
|
|
157
|
+
end
|
|
132
158
|
end
|
|
133
159
|
else
|
|
134
|
-
server_create(:in_tcp_server_batch_emit, @port, bind: @bind, resolve_name: !!@source_hostname_key) do |data, conn|
|
|
160
|
+
server_create(:in_tcp_server_batch_emit, @port, bind: @bind, resolve_name: !!@source_hostname_key, send_keepalive_packet: @send_keepalive_packet) do |data, conn|
|
|
135
161
|
unless check_client(conn)
|
|
136
162
|
conn.close
|
|
137
163
|
next
|
|
@@ -145,6 +171,16 @@ module Fluent::Plugin
|
|
|
145
171
|
msg = buf[pos...i]
|
|
146
172
|
pos = i + del_size
|
|
147
173
|
|
|
174
|
+
if discard_till_next_delimiter
|
|
175
|
+
discard_till_next_delimiter = false
|
|
176
|
+
next
|
|
177
|
+
end
|
|
178
|
+
|
|
179
|
+
if !@message_length_limit.nil? && @message_length_limit < msg.bytesize
|
|
180
|
+
log.info "The received data is larger than 'message_length_limit', dropped:", limit: @message_length_limit, size: msg.bytesize, head: msg[...32]
|
|
181
|
+
next
|
|
182
|
+
end
|
|
183
|
+
|
|
148
184
|
@parser.parse(msg) do |time, record|
|
|
149
185
|
unless time && record
|
|
150
186
|
log.warn "pattern not matched", message: msg
|
|
@@ -159,6 +195,15 @@ module Fluent::Plugin
|
|
|
159
195
|
end
|
|
160
196
|
router.emit_stream(@tag, es)
|
|
161
197
|
buf.slice!(0, pos) if pos > 0
|
|
198
|
+
# If the buffer size exceeds the limit here, it means that the next message will definitely exceed the limit.
|
|
199
|
+
# So we should clear the buffer here. Otherwise, it will keep storing useless data until the next delimiter comes.
|
|
200
|
+
if !@message_length_limit.nil? && @message_length_limit < buf.bytesize
|
|
201
|
+
log.info "The buffer size exceeds 'message_length_limit', cleared:", limit: @message_length_limit, size: buf.bytesize, head: buf[...32]
|
|
202
|
+
buf.clear
|
|
203
|
+
# We should discard the subsequent data until the next delimiter comes.
|
|
204
|
+
discard_till_next_delimiter = true
|
|
205
|
+
next
|
|
206
|
+
end
|
|
162
207
|
end
|
|
163
208
|
end
|
|
164
209
|
end
|
data/lib/fluent/plugin/input.rb
CHANGED
|
@@ -27,7 +27,45 @@ module Fluent
|
|
|
27
27
|
include PluginLoggerMixin
|
|
28
28
|
include PluginHelper::Mixin
|
|
29
29
|
|
|
30
|
-
helpers_internal :event_emitter
|
|
30
|
+
helpers_internal :event_emitter, :metrics
|
|
31
|
+
|
|
32
|
+
def initialize
|
|
33
|
+
super
|
|
34
|
+
@emit_records_metrics = nil
|
|
35
|
+
@emit_size_metrics = nil
|
|
36
|
+
@counter_mutex = Mutex.new
|
|
37
|
+
@enable_size_metrics = false
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def emit_records
|
|
41
|
+
@emit_records_metrics.get
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def emit_size
|
|
45
|
+
@emit_size_metrics.get
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def configure(conf)
|
|
49
|
+
super
|
|
50
|
+
|
|
51
|
+
@emit_records_metrics = metrics_create(namespace: "fluentd", subsystem: "input", name: "emit_records", help_text: "Number of count emit records")
|
|
52
|
+
@emit_size_metrics = metrics_create(namespace: "fluentd", subsystem: "input", name: "emit_size", help_text: "Total size of emit events")
|
|
53
|
+
@enable_size_metrics = !!system_config.enable_size_metrics
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
def statistics
|
|
57
|
+
stats = {
|
|
58
|
+
'emit_records' => @emit_records_metrics.get,
|
|
59
|
+
'emit_size' => @emit_size_metrics.get,
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
{ 'input' => stats }
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
def metric_callback(es)
|
|
66
|
+
@emit_records_metrics.add(es.size)
|
|
67
|
+
@emit_size_metrics.add(es.to_msgpack_stream.bytesize) if @enable_size_metrics
|
|
68
|
+
end
|
|
31
69
|
|
|
32
70
|
def multi_workers_ready?
|
|
33
71
|
false
|
|
@@ -0,0 +1,119 @@
|
|
|
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 'socket'
|
|
18
|
+
|
|
19
|
+
require 'fluent/plugin/base'
|
|
20
|
+
|
|
21
|
+
require 'fluent/log'
|
|
22
|
+
require 'fluent/unique_id'
|
|
23
|
+
require 'fluent/plugin_id'
|
|
24
|
+
|
|
25
|
+
module Fluent
|
|
26
|
+
module Plugin
|
|
27
|
+
class Metrics < Base
|
|
28
|
+
include PluginId
|
|
29
|
+
include PluginLoggerMixin
|
|
30
|
+
include UniqueId::Mixin
|
|
31
|
+
|
|
32
|
+
DEFAULT_TYPE = 'local'
|
|
33
|
+
|
|
34
|
+
configured_in :metrics
|
|
35
|
+
|
|
36
|
+
config_param :default_labels, :hash, default: {agent: "Fluentd", hostname: "#{Socket.gethostname}"}
|
|
37
|
+
config_param :labels, :hash, default: {}
|
|
38
|
+
|
|
39
|
+
attr_reader :use_gauge_metric
|
|
40
|
+
attr_reader :has_methods_for_gauge, :has_methods_for_counter
|
|
41
|
+
|
|
42
|
+
def initialize
|
|
43
|
+
super
|
|
44
|
+
|
|
45
|
+
@has_methods_for_counter = false
|
|
46
|
+
@has_methods_for_gauge = false
|
|
47
|
+
@use_gauge_metric = false
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def configure(conf)
|
|
51
|
+
super
|
|
52
|
+
|
|
53
|
+
if use_gauge_metric
|
|
54
|
+
@has_methods_for_gauge = has_methods_for_gauge?
|
|
55
|
+
else
|
|
56
|
+
@has_methods_for_counter = has_methods_for_counter?
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
# Some metrics should be counted by gauge.
|
|
61
|
+
# ref: https://prometheus.io/docs/concepts/metric_types/#gauge
|
|
62
|
+
def use_gauge_metric=(use_gauge_metric=false)
|
|
63
|
+
@use_gauge_metric = use_gauge_metric
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
def create(namespace:, subsystem:,name:,help_text:,labels: {})
|
|
67
|
+
# This API is for cmetrics type.
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
def get
|
|
71
|
+
raise NotImplementedError, "Implement this method in child class"
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
def inc
|
|
75
|
+
raise NotImplementedError, "Implement this method in child class"
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
def dec
|
|
79
|
+
raise NotImplementedError, "Implement this method in child class"
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
def add(value)
|
|
83
|
+
raise NotImplementedError, "Implement this method in child class"
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
def sub(value)
|
|
87
|
+
raise NotImplementedError, "Implement this method in child class"
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
def set(value)
|
|
91
|
+
raise NotImplementedError, "Implement this method in child class"
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
private
|
|
95
|
+
|
|
96
|
+
def has_methods_for_counter?
|
|
97
|
+
implemented_methods = self.class.instance_methods(false)
|
|
98
|
+
|
|
99
|
+
if [:get, :inc, :add].all? {|e| implemented_methods.include?(e)} &&
|
|
100
|
+
[:set].all?{|e| self.class.method_defined?(e)}
|
|
101
|
+
true
|
|
102
|
+
else
|
|
103
|
+
raise "BUG: metrics plugin on counter mode MUST implement `get`, `inc`, `add` methods. And aliased `set` methods should be aliased from another method"
|
|
104
|
+
end
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
def has_methods_for_gauge?
|
|
108
|
+
implemented_methods = self.class.instance_methods(false)
|
|
109
|
+
|
|
110
|
+
if [:get, :inc, :add].all? {|e| implemented_methods.include?(e)} &&
|
|
111
|
+
[:set, :dec, :sub].all?{|e| self.class.method_defined?(e)}
|
|
112
|
+
true
|
|
113
|
+
else
|
|
114
|
+
raise "BUG: metrics plugin on gauge mode MUST implement `get`, `inc`, and `add` methods. And `dec`, `sub`, and `set` methods should be aliased from other methods"
|
|
115
|
+
end
|
|
116
|
+
end
|
|
117
|
+
end
|
|
118
|
+
end
|
|
119
|
+
end
|
|
@@ -0,0 +1,96 @@
|
|
|
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 'fluent/plugin'
|
|
18
|
+
require 'fluent/plugin/metrics'
|
|
19
|
+
|
|
20
|
+
module Fluent
|
|
21
|
+
module Plugin
|
|
22
|
+
class LocalMetrics < Metrics
|
|
23
|
+
Fluent::Plugin.register_metrics('local', self)
|
|
24
|
+
|
|
25
|
+
def initialize
|
|
26
|
+
super
|
|
27
|
+
@store = 0
|
|
28
|
+
@monitor = Monitor.new
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def configure(conf)
|
|
32
|
+
super
|
|
33
|
+
|
|
34
|
+
if use_gauge_metric
|
|
35
|
+
class << self
|
|
36
|
+
alias_method :dec, :dec_gauge
|
|
37
|
+
alias_method :set, :set_gauge
|
|
38
|
+
alias_method :sub, :sub_gauge
|
|
39
|
+
end
|
|
40
|
+
else
|
|
41
|
+
class << self
|
|
42
|
+
alias_method :set, :set_counter
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def multi_workers_ready?
|
|
48
|
+
true
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
def get
|
|
52
|
+
@monitor.synchronize do
|
|
53
|
+
@store
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
def inc
|
|
58
|
+
@monitor.synchronize do
|
|
59
|
+
@store += 1
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
def dec_gauge
|
|
64
|
+
@monitor.synchronize do
|
|
65
|
+
@store -= 1
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
def add(value)
|
|
70
|
+
@monitor.synchronize do
|
|
71
|
+
@store += value
|
|
72
|
+
end
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
def sub_gauge(value)
|
|
76
|
+
@monitor.synchronize do
|
|
77
|
+
@store -= value
|
|
78
|
+
end
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
def set_counter(value)
|
|
82
|
+
return if @store > value
|
|
83
|
+
|
|
84
|
+
@monitor.synchronize do
|
|
85
|
+
@store = value
|
|
86
|
+
end
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
def set_gauge(value)
|
|
90
|
+
@monitor.synchronize do
|
|
91
|
+
@store = value
|
|
92
|
+
end
|
|
93
|
+
end
|
|
94
|
+
end
|
|
95
|
+
end
|
|
96
|
+
end
|
|
@@ -27,6 +27,7 @@ module Fluent
|
|
|
27
27
|
include PluginHelper::Mixin # for event_emitter
|
|
28
28
|
|
|
29
29
|
helpers :event_emitter # to get router from agent, which will be supplied to child plugins
|
|
30
|
+
helpers_internal :metrics
|
|
30
31
|
|
|
31
32
|
config_section :store, param_name: :stores, multi: true, required: true do
|
|
32
33
|
config_argument :arg, :string, default: ''
|
|
@@ -46,11 +47,40 @@ module Fluent
|
|
|
46
47
|
|
|
47
48
|
@counter_mutex = Mutex.new
|
|
48
49
|
# TODO: well organized counters
|
|
49
|
-
@
|
|
50
|
-
@
|
|
51
|
-
@
|
|
50
|
+
@num_errors_metrics = nil
|
|
51
|
+
@emit_count_metrics = nil
|
|
52
|
+
@emit_records_metrics = nil
|
|
53
|
+
@emit_size_metrics = nil
|
|
52
54
|
# @write_count = 0
|
|
53
55
|
# @rollback_count = 0
|
|
56
|
+
@enable_size_metrics = false
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
def num_errors
|
|
60
|
+
@num_errors_metrics.get
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
def emit_count
|
|
64
|
+
@emit_count_metrics.get
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
def emit_size
|
|
68
|
+
@emit_size_metrics.get
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
def emit_records
|
|
72
|
+
@emit_records_metrics.get
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
def statistics
|
|
76
|
+
stats = {
|
|
77
|
+
'num_errors' => @num_errors_metrics.get,
|
|
78
|
+
'emit_records' => @emit_records_metrics.get,
|
|
79
|
+
'emit_count' => @emit_count_metrics.get,
|
|
80
|
+
'emit_size' => @emit_size_metrics.get,
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
{ 'multi_output' => stats }
|
|
54
84
|
end
|
|
55
85
|
|
|
56
86
|
def multi_output?
|
|
@@ -60,6 +90,12 @@ module Fluent
|
|
|
60
90
|
def configure(conf)
|
|
61
91
|
super
|
|
62
92
|
|
|
93
|
+
@num_errors_metrics = metrics_create(namespace: "fluentd", subsystem: "multi_output", name: "num_errors", help_text: "Number of count num errors")
|
|
94
|
+
@emit_count_metrics = metrics_create(namespace: "fluentd", subsystem: "multi_output", name: "emit_records", help_text: "Number of count emits")
|
|
95
|
+
@emit_records_metrics = metrics_create(namespace: "fluentd", subsystem: "multi_output", name: "emit_records", help_text: "Number of emit records")
|
|
96
|
+
@emit_size_metrics = metrics_create(namespace: "fluentd", subsystem: "multi_output", name: "emit_size", help_text: "Total size of emit events")
|
|
97
|
+
@enable_size_metrics = !!system_config.enable_size_metrics
|
|
98
|
+
|
|
63
99
|
@stores.each do |store|
|
|
64
100
|
store_conf = store.corresponding_config_element
|
|
65
101
|
type = store_conf['@type']
|
|
@@ -143,12 +179,13 @@ module Fluent
|
|
|
143
179
|
end
|
|
144
180
|
|
|
145
181
|
def emit_sync(tag, es)
|
|
146
|
-
@
|
|
182
|
+
@emit_count_metrics.inc
|
|
147
183
|
begin
|
|
148
184
|
process(tag, es)
|
|
149
|
-
@
|
|
185
|
+
@emit_records_metrics.add(es.size)
|
|
186
|
+
@emit_size_metrics.add(es.to_msgpack_stream.bytesize) if @enable_size_metrics
|
|
150
187
|
rescue
|
|
151
|
-
@
|
|
188
|
+
@num_errors_metrics.inc
|
|
152
189
|
raise
|
|
153
190
|
end
|
|
154
191
|
end
|
|
@@ -46,7 +46,7 @@ module Fluent::Plugin
|
|
|
46
46
|
@ignore_errors << (store.arg.include?('ignore_error'))
|
|
47
47
|
@ignore_if_prev_successes << (store.arg.include?('ignore_if_prev_success'))
|
|
48
48
|
}
|
|
49
|
-
if @ignore_errors.uniq.size == 1 && @ignore_errors.include?(true) &&
|
|
49
|
+
if @ignore_errors.uniq.size == 1 && @ignore_errors.include?(true) && !@ignore_if_prev_successes.include?(true)
|
|
50
50
|
log.warn "ignore_errors are specified in all <store>, but ignore_if_prev_success is not specified. Is this intended?"
|
|
51
51
|
end
|
|
52
52
|
end
|
|
@@ -163,7 +163,7 @@ module Fluent::Plugin
|
|
|
163
163
|
0
|
|
164
164
|
elsif (@child_respawn == 'inf') || (@child_respawn == '-1')
|
|
165
165
|
-1
|
|
166
|
-
elsif @child_respawn
|
|
166
|
+
elsif /^\d+$/.match?(@child_respawn)
|
|
167
167
|
@child_respawn.to_i
|
|
168
168
|
else
|
|
169
169
|
raise ConfigError, "child_respawn option argument invalid: none(or 0), inf(or -1) or positive number"
|
|
@@ -187,7 +187,7 @@ module Fluent::Plugin
|
|
|
187
187
|
@rr = 0
|
|
188
188
|
|
|
189
189
|
exit_callback = ->(status){
|
|
190
|
-
c = @children.
|
|
190
|
+
c = @children.find{|child| child.pid == status.pid }
|
|
191
191
|
if c
|
|
192
192
|
unless self.stopped?
|
|
193
193
|
log.warn "child process exits with error code", code: status.to_i, status: status.exitstatus, signal: status.termsig
|
|
@@ -181,6 +181,13 @@ module Fluent::Plugin
|
|
|
181
181
|
@dir_perm = system_config.dir_permission || Fluent::DEFAULT_DIR_PERMISSION
|
|
182
182
|
@file_perm = system_config.file_permission || Fluent::DEFAULT_FILE_PERMISSION
|
|
183
183
|
@need_lock = system_config.workers > 1
|
|
184
|
+
|
|
185
|
+
# https://github.com/fluent/fluentd/issues/3569
|
|
186
|
+
@need_ruby_on_macos_workaround = false
|
|
187
|
+
if @append && Fluent.macos?
|
|
188
|
+
condition = Gem::Dependency.new('', [">= 2.7.0", "< 3.1.0"])
|
|
189
|
+
@need_ruby_on_macos_workaround = true if condition.match?('', RUBY_VERSION)
|
|
190
|
+
end
|
|
184
191
|
end
|
|
185
192
|
|
|
186
193
|
def multi_workers_ready?
|
|
@@ -210,7 +217,13 @@ module Fluent::Plugin
|
|
|
210
217
|
end
|
|
211
218
|
|
|
212
219
|
if @append
|
|
213
|
-
|
|
220
|
+
if @need_lock
|
|
221
|
+
acquire_worker_lock(path) do
|
|
222
|
+
writer.call(path, chunk)
|
|
223
|
+
end
|
|
224
|
+
else
|
|
225
|
+
writer.call(path, chunk)
|
|
226
|
+
end
|
|
214
227
|
else
|
|
215
228
|
find_filepath_available(path, with_lock: @need_lock) do |actual_path|
|
|
216
229
|
writer.call(actual_path, chunk)
|
|
@@ -223,7 +236,12 @@ module Fluent::Plugin
|
|
|
223
236
|
|
|
224
237
|
def write_without_compression(path, chunk)
|
|
225
238
|
File.open(path, "ab", @file_perm) do |f|
|
|
226
|
-
|
|
239
|
+
if @need_ruby_on_macos_workaround
|
|
240
|
+
content = chunk.read()
|
|
241
|
+
f.puts content
|
|
242
|
+
else
|
|
243
|
+
chunk.write_to(f)
|
|
244
|
+
end
|
|
227
245
|
end
|
|
228
246
|
end
|
|
229
247
|
|
|
@@ -59,7 +59,13 @@ module Fluent::Plugin
|
|
|
59
59
|
@ack_waitings = new_list
|
|
60
60
|
end
|
|
61
61
|
|
|
62
|
-
|
|
62
|
+
begin
|
|
63
|
+
readable_sockets, _, _ = IO.select(sockets, nil, nil, select_interval)
|
|
64
|
+
rescue IOError
|
|
65
|
+
@log.info "connection closed while waiting for readable sockets"
|
|
66
|
+
readable_sockets = nil
|
|
67
|
+
end
|
|
68
|
+
|
|
63
69
|
if readable_sockets
|
|
64
70
|
readable_sockets.each do |sock|
|
|
65
71
|
results << read_ack_from_sock(sock)
|
|
@@ -109,13 +115,22 @@ module Fluent::Plugin
|
|
|
109
115
|
raw_data = sock.instance_of?(Fluent::PluginHelper::Socket::WrappedSocket::TLS) ? sock.readpartial(@read_length) : sock.recv(@read_length)
|
|
110
116
|
rescue Errno::ECONNRESET, EOFError # ECONNRESET for #recv, #EOFError for #readpartial
|
|
111
117
|
raw_data = ''
|
|
118
|
+
rescue IOError
|
|
119
|
+
@log.info "socket closed while receiving ack response"
|
|
120
|
+
return nil, Result::FAILED
|
|
112
121
|
end
|
|
113
122
|
|
|
114
123
|
info = find(sock)
|
|
115
124
|
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
125
|
+
if info.nil?
|
|
126
|
+
# The info can be deleted by another thread during `sock.recv()` and `find()`.
|
|
127
|
+
# This is OK since another thread has completed to process the ack, so we can skip this.
|
|
128
|
+
# Note: exclusion mechanism about `collect_response()` may need to be considered.
|
|
129
|
+
@log.debug "could not find the ack info. this ack may be processed by another thread."
|
|
130
|
+
return nil, Result::FAILED
|
|
131
|
+
elsif raw_data.empty?
|
|
132
|
+
# When connection is closed by remote host, socket is ready to read and #recv returns an empty string that means EOF.
|
|
133
|
+
# If this happens we assume the data wasn't delivered and retry it.
|
|
119
134
|
@log.warn 'destination node closed the connection. regard it as unavailable.', host: info.node.host, port: info.node.port
|
|
120
135
|
# info.node.disable!
|
|
121
136
|
return info, Result::FAILED
|
|
@@ -50,6 +50,7 @@ module Fluent::Plugin
|
|
|
50
50
|
def checkin(sock)
|
|
51
51
|
@mutex.synchronize do
|
|
52
52
|
if (s = @inflight_sockets.delete(sock))
|
|
53
|
+
s.timeout = timeout
|
|
53
54
|
@available_sockets[s.key] << s
|
|
54
55
|
else
|
|
55
56
|
@log.debug("there is no socket #{sock}")
|
|
@@ -122,6 +123,7 @@ module Fluent::Plugin
|
|
|
122
123
|
t = Time.now
|
|
123
124
|
if (s = @available_sockets[key].find { |sock| !expired_socket?(sock, time: t) })
|
|
124
125
|
@inflight_sockets[s.sock] = @available_sockets[key].delete(s)
|
|
126
|
+
s.timeout = timeout
|
|
125
127
|
s
|
|
126
128
|
else
|
|
127
129
|
nil
|
|
@@ -167,6 +167,8 @@ module Fluent::Plugin
|
|
|
167
167
|
@usock = nil
|
|
168
168
|
@keep_alive_watcher_interval = 5 # TODO
|
|
169
169
|
@suspend_flush = false
|
|
170
|
+
@healthy_nodes_count_metrics = nil
|
|
171
|
+
@registered_nodes_count_metrics = nil
|
|
170
172
|
end
|
|
171
173
|
|
|
172
174
|
def configure(conf)
|
|
@@ -265,6 +267,9 @@ module Fluent::Plugin
|
|
|
265
267
|
end
|
|
266
268
|
|
|
267
269
|
raise Fluent::ConfigError, "ack_response_timeout must be a positive integer" if @ack_response_timeout < 1
|
|
270
|
+
@healthy_nodes_count_metrics = metrics_create(namespace: "fluentd", subsystem: "output", name: "healthy_nodes_count", help_text: "Number of count healthy nodes", prefer_gauge: true)
|
|
271
|
+
@registered_nodes_count_metrics = metrics_create(namespace: "fluentd", subsystem: "output", name: "registered_nodes_count", help_text: "Number of count registered nodes", prefer_gauge: true)
|
|
272
|
+
|
|
268
273
|
end
|
|
269
274
|
|
|
270
275
|
def multi_workers_ready?
|
|
@@ -418,18 +423,21 @@ module Fluent::Plugin
|
|
|
418
423
|
def statistics
|
|
419
424
|
stats = super
|
|
420
425
|
services = service_discovery_services
|
|
421
|
-
|
|
422
|
-
|
|
426
|
+
@healthy_nodes_count_metrics.set(0)
|
|
427
|
+
@registered_nodes_count_metrics.set(services.size)
|
|
423
428
|
services.each do |s|
|
|
424
429
|
if s.available?
|
|
425
|
-
|
|
430
|
+
@healthy_nodes_count_metrics.inc
|
|
426
431
|
end
|
|
427
432
|
end
|
|
428
433
|
|
|
429
|
-
stats
|
|
430
|
-
'
|
|
431
|
-
|
|
432
|
-
|
|
434
|
+
stats = {
|
|
435
|
+
'output' => stats["output"].merge({
|
|
436
|
+
'healthy_nodes_count' => @healthy_nodes_count_metrics.get,
|
|
437
|
+
'registered_nodes_count' => @registered_nodes_count_metrics.get,
|
|
438
|
+
})
|
|
439
|
+
}
|
|
440
|
+
stats
|
|
433
441
|
end
|
|
434
442
|
|
|
435
443
|
# MessagePack FixArray length is 3
|
|
@@ -513,8 +521,8 @@ module Fluent::Plugin
|
|
|
513
521
|
when AckHandler::Result::SUCCESS
|
|
514
522
|
commit_write(chunk_id)
|
|
515
523
|
when AckHandler::Result::FAILED
|
|
516
|
-
node
|
|
517
|
-
rollback_write(chunk_id, update_retry: false)
|
|
524
|
+
node&.disable!
|
|
525
|
+
rollback_write(chunk_id, update_retry: false) if chunk_id
|
|
518
526
|
when AckHandler::Result::CHUNKID_UNMATCHED
|
|
519
527
|
rollback_write(chunk_id, update_retry: false)
|
|
520
528
|
else
|