fluent-plugin-prometheus 1.7.0 → 1.8.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/ChangeLog +11 -0
- data/README.md +27 -4
- data/fluent-plugin-prometheus.gemspec +2 -2
- data/lib/fluent/plugin/in_prometheus.rb +147 -81
- data/lib/fluent/plugin/in_prometheus/async_wrapper.rb +46 -0
- data/lib/fluent/plugin/in_prometheus_monitor.rb +3 -3
- data/lib/fluent/plugin/in_prometheus_output_monitor.rb +3 -3
- data/lib/fluent/plugin/in_prometheus_tail_monitor.rb +3 -3
- data/lib/fluent/plugin/prometheus.rb +17 -18
- data/lib/fluent/plugin/prometheus/placeholder_expander.rb +132 -0
- data/spec/fluent/plugin/filter_prometheus_spec.rb +20 -10
- data/spec/fluent/plugin/in_prometheus_monitor_spec.rb +0 -1
- data/spec/fluent/plugin/in_prometheus_spec.rb +225 -0
- data/spec/fluent/plugin/in_prometheus_tail_monitor_spec.rb +42 -0
- data/spec/fluent/plugin/out_prometheus_spec.rb +18 -9
- data/spec/fluent/plugin/prometheus/placeholder_expander_spec.rb +110 -0
- data/spec/fluent/plugin/shared.rb +51 -102
- metadata +13 -6
- data/spec/fluent/plugin/prometheus_spec.rb +0 -101
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: f48e673585638a68fa128172db768d7f35035a04f4d55bb98737e7cfd5d7cdf2
|
4
|
+
data.tar.gz: 1030ddd92d55c5e6603e49933760eadac266f5081cbb54169b0ebc237bcfe933
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: adb129f3a83c6be98247e2180d6cb26037a1e8e32e10c555232cf9d267867b4b5b6d5168adebbb9b8ed16b1193c9ff7e8879b379ccb92406b66cb2abc67075e1
|
7
|
+
data.tar.gz: 2e7a8eecf131d31d08266507620939f7f9b73613d39009d6c2349bbb166b778dae82baa6ea84021793b5374c1372b5cd0673b126ec5b6981c9dfd368c8da0922
|
data/ChangeLog
ADDED
data/README.md
CHANGED
@@ -8,9 +8,13 @@ A fluent plugin that instruments metrics from records and exposes them via web i
|
|
8
8
|
|
9
9
|
| fluent-plugin-prometheus | fluentd | ruby |
|
10
10
|
|--------------------------|------------|--------|
|
11
|
-
| 1.x.y | >=
|
11
|
+
| 1.x.y | >= v1.9.1 | >= 2.4 |
|
12
|
+
| 1.[0-7].y | >= v0.14.8 | >= 2.1 |
|
12
13
|
| 0.x.y | >= v0.12.0 | >= 1.9 |
|
13
14
|
|
15
|
+
Since v1.8.0, fluent-plugin-prometheus uses [http_server helper](https://docs.fluentd.org/plugin-helper-overview/api-plugin-helper-http_server) to launch HTTP server.
|
16
|
+
If you want to handle lots of connections, install `async-http` gem.
|
17
|
+
|
14
18
|
## Installation
|
15
19
|
|
16
20
|
Add this line to your application's Gemfile:
|
@@ -63,6 +67,19 @@ More configuration parameters:
|
|
63
67
|
When using multiple workers, each worker binds to port + `fluent_worker_id`.
|
64
68
|
To scrape metrics from all workers at once, you can access http://localhost:24231/aggregated_metrics.
|
65
69
|
|
70
|
+
#### TLS setting
|
71
|
+
|
72
|
+
Use `<trasnport tls>`. See [transport config article](https://docs.fluentd.org/configuration/transport-section) for more details.
|
73
|
+
|
74
|
+
```
|
75
|
+
<source>
|
76
|
+
@type prometheus
|
77
|
+
<transport tls>
|
78
|
+
# TLS parameters...
|
79
|
+
</transport
|
80
|
+
</source>
|
81
|
+
```
|
82
|
+
|
66
83
|
### prometheus_monitor input plugin
|
67
84
|
|
68
85
|
This plugin collects internal metrics in Fluentd. The metrics are similar to/part of [monitor_agent](https://docs.fluentd.org/input/monitor_agent).
|
@@ -71,7 +88,7 @@ This plugin collects internal metrics in Fluentd. The metrics are similar to/par
|
|
71
88
|
#### Exposed metrics
|
72
89
|
|
73
90
|
- `fluentd_status_buffer_queue_length`
|
74
|
-
- `
|
91
|
+
- `fluentd_status_buffer_total_bytes`
|
75
92
|
- `fluentd_status_retry_count`
|
76
93
|
- `fluentd_status_buffer_newest_timekey` from fluentd v1.4.2
|
77
94
|
- `fluentd_status_buffer_oldest_timekey` from fluentd v1.4.2
|
@@ -107,7 +124,7 @@ Metrics for output
|
|
107
124
|
- `fluentd_output_status_emit_records`
|
108
125
|
- `fluentd_output_status_write_count`
|
109
126
|
- `fluentd_output_status_rollback_count`
|
110
|
-
- `fluentd_output_status_flush_time_count` from fluentd v1.6.0
|
127
|
+
- `fluentd_output_status_flush_time_count` in milliseconds from fluentd v1.6.0
|
111
128
|
- `fluentd_output_status_slow_flush_count` from fluentd v1.6.0
|
112
129
|
|
113
130
|
Metrics for buffer
|
@@ -369,7 +386,13 @@ Reserved placeholders are:
|
|
369
386
|
- `${worker_id}`: fluent worker id
|
370
387
|
- `${tag}`: tag name
|
371
388
|
- only available in Prometheus output/filter plugin
|
372
|
-
|
389
|
+
- `${tag_parts[N]}` refers to the Nth part of the tag.
|
390
|
+
- only available in Prometheus output/filter plugin
|
391
|
+
- `${tag_prefix[N]}` refers to the [0..N] part of the tag.
|
392
|
+
- only available in Prometheus output/filter plugin
|
393
|
+
- `${tag_suffix[N]}` refers to the [`tagsize`-1-N..] part of the tag.
|
394
|
+
- where `tagsize` is the size of tag which is splitted with `.` (when tag is `1.2.3`, then `tagsize` is 3)
|
395
|
+
- only available in Prometheus output/filter plugin
|
373
396
|
|
374
397
|
### top-level labels and labels inside metric
|
375
398
|
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Gem::Specification.new do |spec|
|
2
2
|
spec.name = "fluent-plugin-prometheus"
|
3
|
-
spec.version = "1.
|
3
|
+
spec.version = "1.8.1"
|
4
4
|
spec.authors = ["Masahiro Sano"]
|
5
5
|
spec.email = ["sabottenda@gmail.com"]
|
6
6
|
spec.summary = %q{A fluent plugin that collects metrics and exposes for Prometheus.}
|
@@ -13,7 +13,7 @@ Gem::Specification.new do |spec|
|
|
13
13
|
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
14
14
|
spec.require_paths = ["lib"]
|
15
15
|
|
16
|
-
spec.add_dependency "fluentd", ">=
|
16
|
+
spec.add_dependency "fluentd", ">= 1.9.1", "< 2"
|
17
17
|
spec.add_dependency "prometheus-client", "< 0.10"
|
18
18
|
spec.add_development_dependency "bundler"
|
19
19
|
spec.add_development_dependency "rake"
|
@@ -2,13 +2,13 @@ require 'fluent/plugin/input'
|
|
2
2
|
require 'fluent/plugin/prometheus'
|
3
3
|
require 'fluent/plugin/prometheus_metrics'
|
4
4
|
require 'net/http'
|
5
|
-
require '
|
5
|
+
require 'openssl'
|
6
6
|
|
7
7
|
module Fluent::Plugin
|
8
8
|
class PrometheusInput < Fluent::Plugin::Input
|
9
9
|
Fluent::Plugin.register_input('prometheus', self)
|
10
10
|
|
11
|
-
helpers :thread
|
11
|
+
helpers :thread, :http_server
|
12
12
|
|
13
13
|
config_param :bind, :string, default: '0.0.0.0'
|
14
14
|
config_param :port, :integer, default: 24231
|
@@ -17,30 +17,25 @@ module Fluent::Plugin
|
|
17
17
|
|
18
18
|
desc 'Enable ssl configuration for the server'
|
19
19
|
config_section :ssl, required: false, multi: false do
|
20
|
-
config_param :enable, :bool, default: false
|
20
|
+
config_param :enable, :bool, default: false, deprecated: 'Use <transport tls> section'
|
21
21
|
|
22
22
|
desc 'Path to the ssl certificate in PEM format. Read from file and added to conf as "SSLCertificate"'
|
23
|
-
config_param :certificate_path, :string, default: nil
|
23
|
+
config_param :certificate_path, :string, default: nil, deprecated: 'Use cert_path in <transport tls> section'
|
24
24
|
|
25
25
|
desc 'Path to the ssl private key in PEM format. Read from file and added to conf as "SSLPrivateKey"'
|
26
|
-
config_param :private_key_path, :string, default: nil
|
26
|
+
config_param :private_key_path, :string, default: nil, deprecated: 'Use private_key_path in <transport tls> section'
|
27
27
|
|
28
28
|
desc 'Path to CA in PEM format. Read from file and added to conf as "SSLCACertificateFile"'
|
29
|
-
config_param :ca_path, :string, default: nil
|
29
|
+
config_param :ca_path, :string, default: nil, deprecated: 'Use ca_path in <transport tls> section'
|
30
30
|
|
31
31
|
desc 'Additional ssl conf for the server. Ref: https://github.com/ruby/webrick/blob/master/lib/webrick/ssl.rb'
|
32
|
-
config_param :extra_conf, :hash, default:
|
32
|
+
config_param :extra_conf, :hash, default: nil, symbolize_keys: true, deprecated: 'See http helper config'
|
33
33
|
end
|
34
34
|
|
35
|
-
attr_reader :registry
|
36
|
-
|
37
|
-
attr_reader :num_workers
|
38
|
-
attr_reader :base_port
|
39
|
-
attr_reader :metrics_path
|
40
|
-
|
41
35
|
def initialize
|
42
36
|
super
|
43
37
|
@registry = ::Prometheus::Client.registry
|
38
|
+
@secure = nil
|
44
39
|
end
|
45
40
|
|
46
41
|
def configure(conf)
|
@@ -55,6 +50,7 @@ module Fluent::Plugin
|
|
55
50
|
nil
|
56
51
|
end
|
57
52
|
@num_workers = sysconf && sysconf.workers ? sysconf.workers : 1
|
53
|
+
@secure = @transport_config.protocol == :tls || (@ssl && @ssl['enable'])
|
58
54
|
|
59
55
|
@base_port = @port
|
60
56
|
@port += fluentd_worker_id
|
@@ -66,7 +62,71 @@ module Fluent::Plugin
|
|
66
62
|
|
67
63
|
def start
|
68
64
|
super
|
69
|
-
|
65
|
+
|
66
|
+
scheme = @secure ? 'https' : 'http'
|
67
|
+
log.debug "listening prometheus http server on #{scheme}:://#{@bind}:#{@port}/#{@metrics_path} for worker#{fluentd_worker_id}"
|
68
|
+
|
69
|
+
proto = @secure ? :tls : :tcp
|
70
|
+
|
71
|
+
if @ssl && @ssl['enable'] && @ssl['extra_conf']
|
72
|
+
start_webrick
|
73
|
+
return
|
74
|
+
end
|
75
|
+
|
76
|
+
begin
|
77
|
+
require 'async'
|
78
|
+
require 'fluent/plugin/in_prometheus/async_wrapper'
|
79
|
+
extend AsyncWrapper
|
80
|
+
rescue LoadError => _
|
81
|
+
# ignore
|
82
|
+
end
|
83
|
+
|
84
|
+
tls_opt = if @ssl && @ssl['enable']
|
85
|
+
ssl_config = {}
|
86
|
+
|
87
|
+
if (@ssl['certificate_path'] && @ssl['private_key_path'].nil?) || (@ssl['certificate_path'].nil? && @ssl['private_key_path'])
|
88
|
+
raise Fluent::ConfigError.new('both certificate_path and private_key_path must be defined')
|
89
|
+
end
|
90
|
+
|
91
|
+
if @ssl['certificate_path']
|
92
|
+
ssl_config['cert_path'] = @ssl['certificate_path']
|
93
|
+
end
|
94
|
+
|
95
|
+
if @ssl['private_key_path']
|
96
|
+
ssl_config['private_key_path'] = @ssl['private_key_path']
|
97
|
+
end
|
98
|
+
|
99
|
+
if @ssl['ca_path']
|
100
|
+
ssl_config['ca_path'] = @ssl['ca_path']
|
101
|
+
# Only ca_path is insecure in fluentd
|
102
|
+
# https://github.com/fluent/fluentd/blob/2236ad45197ba336fd9faf56f442252c8b226f25/lib/fluent/plugin_helper/cert_option.rb#L68
|
103
|
+
ssl_config['insecure'] = true
|
104
|
+
end
|
105
|
+
|
106
|
+
ssl_config
|
107
|
+
end
|
108
|
+
|
109
|
+
http_server_create_http_server(:in_prometheus_server, addr: @bind, port: @port, logger: log, proto: proto, tls_opts: tls_opt) do |server|
|
110
|
+
server.get(@metrics_path) { |_req| all_metrics }
|
111
|
+
server.get(@aggregated_metrics_path) { |_req| all_workers_metrics }
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
115
|
+
def shutdown
|
116
|
+
if @webrick_server
|
117
|
+
@webrick_server.shutdown
|
118
|
+
@webrick_server = nil
|
119
|
+
end
|
120
|
+
super
|
121
|
+
end
|
122
|
+
|
123
|
+
private
|
124
|
+
|
125
|
+
# For compatiblity because http helper can't support extra_conf option
|
126
|
+
def start_webrick
|
127
|
+
require 'webrick/https'
|
128
|
+
require 'webrick'
|
129
|
+
|
70
130
|
config = {
|
71
131
|
BindAddress: @bind,
|
72
132
|
Port: @port,
|
@@ -74,90 +134,96 @@ module Fluent::Plugin
|
|
74
134
|
Logger: WEBrick::Log.new(STDERR, WEBrick::Log::FATAL),
|
75
135
|
AccessLog: [],
|
76
136
|
}
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
end
|
90
|
-
if @ssl['private_key_path']
|
91
|
-
key = OpenSSL::PKey::RSA.new(File.read(@ssl['private_key_path']))
|
92
|
-
ssl_config[:SSLPrivateKey] = key
|
93
|
-
end
|
94
|
-
ssl_config[:SSLCACertificateFile] = @ssl['ca_path'] if @ssl['ca_path']
|
95
|
-
ssl_config = ssl_config.merge(@ssl['extra_conf'])
|
96
|
-
config = ssl_config.merge(config)
|
137
|
+
if (@ssl['certificate_path'] && @ssl['private_key_path'].nil?) || (@ssl['certificate_path'].nil? && @ssl['private_key_path'])
|
138
|
+
raise RuntimeError.new("certificate_path and private_key_path most both be defined")
|
139
|
+
end
|
140
|
+
|
141
|
+
ssl_config = {
|
142
|
+
SSLEnable: true,
|
143
|
+
SSLCertName: [['CN', 'nobody'], ['DC', 'example']]
|
144
|
+
}
|
145
|
+
|
146
|
+
if @ssl['certificate_path']
|
147
|
+
cert = OpenSSL::X509::Certificate.new(File.read(@ssl['certificate_path']))
|
148
|
+
ssl_config[:SSLCertificate] = cert
|
97
149
|
end
|
150
|
+
|
151
|
+
if @ssl['private_key_path']
|
152
|
+
key = OpenSSL::PKey.read(@ssl['private_key_path'])
|
153
|
+
ssl_config[:SSLPrivateKey] = key
|
154
|
+
end
|
155
|
+
|
156
|
+
ssl_config[:SSLCACertificateFile] = @ssl['ca_path'] if @ssl['ca_path']
|
157
|
+
ssl_config = ssl_config.merge(@ssl['extra_conf']) if @ssl['extra_conf']
|
158
|
+
config = ssl_config.merge(config)
|
159
|
+
|
98
160
|
@log.on_debug do
|
99
161
|
@log.debug("WEBrick conf: #{config}")
|
100
162
|
end
|
101
163
|
|
102
|
-
@
|
103
|
-
@
|
104
|
-
|
105
|
-
|
106
|
-
|
164
|
+
@webrick_server = WEBrick::HTTPServer.new(config)
|
165
|
+
@webrick_server.mount_proc(@metrics_path) do |_req, res|
|
166
|
+
status, header, body = all_metrics
|
167
|
+
res.status = status
|
168
|
+
res['Content-Type'] = header['Content-Type']
|
169
|
+
res.body = body
|
170
|
+
res
|
107
171
|
end
|
108
|
-
end
|
109
172
|
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
173
|
+
@webrick_server.mount_proc(@aggregated_metrics_path) do |_req, res|
|
174
|
+
status, header, body = all_workers_metrics
|
175
|
+
res.status = status
|
176
|
+
res['Content-Type'] = header['Content-Type']
|
177
|
+
res.body = body
|
178
|
+
res
|
179
|
+
end
|
180
|
+
|
181
|
+
thread_create(:in_prometheus_webrick) do
|
182
|
+
@webrick_server.start
|
114
183
|
end
|
115
|
-
super
|
116
184
|
end
|
117
185
|
|
118
|
-
|
119
|
-
|
120
|
-
|
186
|
+
def all_metrics
|
187
|
+
[200, { 'Content-Type' => ::Prometheus::Client::Formats::Text::CONTENT_TYPE }, ::Prometheus::Client::Formats::Text.marshal(@registry)]
|
188
|
+
rescue => e
|
189
|
+
[500, { 'Content-Type' => 'text/plain' }, e.to_s]
|
190
|
+
end
|
191
|
+
|
192
|
+
def all_workers_metrics
|
193
|
+
full_result = PromMetricsAggregator.new
|
194
|
+
|
195
|
+
send_request_to_each_worker do |resp|
|
196
|
+
if resp.code.to_s == '200'
|
197
|
+
full_result.add_metrics(resp.body)
|
198
|
+
end
|
121
199
|
end
|
122
200
|
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
201
|
+
[200, { 'Content-Type' => ::Prometheus::Client::Formats::Text::CONTENT_TYPE }, full_result.get_metrics]
|
202
|
+
rescue => e
|
203
|
+
[500, { 'Content-Type' => 'text/plain' }, e.to_s]
|
204
|
+
end
|
205
|
+
|
206
|
+
def send_request_to_each_worker
|
207
|
+
bind = (@bind == '0.0.0.0') ? '127.0.0.1' : @bind
|
208
|
+
[*(@base_port...(@base_port + @num_workers))].each do |worker_port|
|
209
|
+
do_request(host: bind, port: worker_port, secure: @secure) do |http|
|
210
|
+
yield(http.get(@metrics_path))
|
211
|
+
end
|
131
212
|
end
|
132
213
|
end
|
133
214
|
|
134
|
-
|
135
|
-
|
136
|
-
|
215
|
+
# might be replaced by AsyncWrapper if async gem is installed
|
216
|
+
def do_request(host:, port:, secure:)
|
217
|
+
http = Net::HTTP.new(host, port)
|
218
|
+
|
219
|
+
if secure
|
220
|
+
http.use_ssl = true
|
221
|
+
# target is our child process. so it's secure.
|
222
|
+
http.verify_mode = OpenSSL::SSL::VERIFY_NONE
|
137
223
|
end
|
138
224
|
|
139
|
-
|
140
|
-
|
141
|
-
res['Content-Type'] = ::Prometheus::Client::Formats::Text::CONTENT_TYPE
|
142
|
-
|
143
|
-
full_result = PromMetricsAggregator.new
|
144
|
-
fluent_server_ip = @prometheus.bind == '0.0.0.0' ? '127.0.0.1' : @prometheus.bind
|
145
|
-
current_worker = 0
|
146
|
-
while current_worker < @prometheus.num_workers
|
147
|
-
Net::HTTP.start(fluent_server_ip, @prometheus.base_port + current_worker) do |http|
|
148
|
-
req = Net::HTTP::Get.new(@prometheus.metrics_path)
|
149
|
-
result = http.request(req)
|
150
|
-
if result.is_a?(Net::HTTPSuccess)
|
151
|
-
full_result.add_metrics(result.body)
|
152
|
-
end
|
153
|
-
end
|
154
|
-
current_worker += 1
|
155
|
-
end
|
156
|
-
res.body = full_result.get_metrics
|
157
|
-
rescue
|
158
|
-
res.status = 500
|
159
|
-
res['Content-Type'] = 'text/plain'
|
160
|
-
res.body = $!.to_s
|
225
|
+
http.start do
|
226
|
+
yield(http)
|
161
227
|
end
|
162
228
|
end
|
163
229
|
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
require 'async'
|
2
|
+
|
3
|
+
module Fluent::Plugin
|
4
|
+
class PrometheusInput
|
5
|
+
module AsyncWrapper
|
6
|
+
def do_request(host:, port:, secure:)
|
7
|
+
endpoint =
|
8
|
+
if secure
|
9
|
+
context = OpenSSL::SSL::SSLContext.new
|
10
|
+
context.verify_mode = OpenSSL::SSL::VERIFY_NONE
|
11
|
+
Async::HTTP::Endpoint.parse("https://#{host}:#{port}", ssl_context: context)
|
12
|
+
else
|
13
|
+
Async::HTTP::Endpoint.parse("http://#{host}:#{port}")
|
14
|
+
end
|
15
|
+
|
16
|
+
client = Async::HTTP::Client.new(endpoint)
|
17
|
+
yield(AsyncHttpWrapper.new(client))
|
18
|
+
end
|
19
|
+
|
20
|
+
Response = Struct.new(:code, :body, :headers)
|
21
|
+
|
22
|
+
class AsyncHttpWrapper
|
23
|
+
def initialize(http)
|
24
|
+
@http = http
|
25
|
+
end
|
26
|
+
|
27
|
+
def get(path)
|
28
|
+
error = nil
|
29
|
+
response = Async::Task.current.async {
|
30
|
+
begin
|
31
|
+
@http.get(path)
|
32
|
+
rescue => e # Async::Reactor rescue all error. handle it by itself
|
33
|
+
error = e
|
34
|
+
end
|
35
|
+
}.wait
|
36
|
+
|
37
|
+
if error
|
38
|
+
raise error
|
39
|
+
end
|
40
|
+
|
41
|
+
Response.new(response.status.to_s, response.body.read, response.headers)
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|