fluent-plugin-prometheus 1.7.0 → 1.8.1

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 95008f0031db355e5a5bbf4ed21f8f9f0a07839480e40bf44e974046b04f562f
4
- data.tar.gz: 994a30da7b1ff0ad77f12e5b47d9218a2deb97effc31a36fa83175077cc6ed69
3
+ metadata.gz: f48e673585638a68fa128172db768d7f35035a04f4d55bb98737e7cfd5d7cdf2
4
+ data.tar.gz: 1030ddd92d55c5e6603e49933760eadac266f5081cbb54169b0ebc237bcfe933
5
5
  SHA512:
6
- metadata.gz: e0dc26e78e242e51e0fa87257b85c68af2ae32c11f7dabd1b4245e3ad7e1b1a7182b1b8432578f98af8a5a22d14c14e664285a238f74ebc40df0c0461026f323
7
- data.tar.gz: fb31a79f327ce31e8203fa1fa9a5b9d9166410d71f9b62d4fd5411d246c768dfde5c9c076ff6e72f0d9f5bd4c7657c9180eb91ba4ee383729856da0704930404
6
+ metadata.gz: adb129f3a83c6be98247e2180d6cb26037a1e8e32e10c555232cf9d267867b4b5b6d5168adebbb9b8ed16b1193c9ff7e8879b379ccb92406b66cb2abc67075e1
7
+ data.tar.gz: 2e7a8eecf131d31d08266507620939f7f9b73613d39009d6c2349bbb166b778dae82baa6ea84021793b5374c1372b5cd0673b126ec5b6981c9dfd368c8da0922
@@ -0,0 +1,11 @@
1
+ Release 1.8.1 - 2020/07/06
2
+
3
+ * Fix aggregate bug with async-http
4
+
5
+ Release 1.8.0 - 2020/04/17
6
+
7
+ * Use http_server helper
8
+ * Require fluentd v1.9.1 or later
9
+
10
+
11
+ For older releases, see commits on github repository.
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 | >= v0.14.8 | >= 2.1 |
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
- - `fluentd_status_buffer_total_queued_size`
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.7.0"
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", ">= 0.14.20", "< 2"
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 'webrick'
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: {:SSLCertName => [['CN','nobody'],['DC','example']]}, symbolize_keys: true
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
- log.debug "listening prometheus http server on http://#{@bind}:#{@port}/#{@metrics_path} for worker#{fluentd_worker_id}"
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
- unless @ssl.nil? || !@ssl['enable']
78
- require 'webrick/https'
79
- require 'openssl'
80
- if (@ssl['certificate_path'] && @ssl['private_key_path'].nil?) || (@ssl['certificate_path'].nil? && @ssl['private_key_path'])
81
- raise RuntimeError.new("certificate_path and private_key_path most both be defined")
82
- end
83
- ssl_config = {
84
- SSLEnable: true
85
- }
86
- if @ssl['certificate_path']
87
- cert = OpenSSL::X509::Certificate.new(File.read(@ssl['certificate_path']))
88
- ssl_config[:SSLCertificate] = cert
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
- @server = WEBrick::HTTPServer.new(config)
103
- @server.mount(@metrics_path, MonitorServlet, self)
104
- @server.mount(@aggregated_metrics_path, MonitorServletAll, self)
105
- thread_create(:in_prometheus) do
106
- @server.start
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
- def shutdown
111
- if @server
112
- @server.shutdown
113
- @server = nil
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
- class MonitorServlet < WEBrick::HTTPServlet::AbstractServlet
119
- def initialize(server, prometheus)
120
- @prometheus = prometheus
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
- def do_GET(req, res)
124
- res.status = 200
125
- res['Content-Type'] = ::Prometheus::Client::Formats::Text::CONTENT_TYPE
126
- res.body = ::Prometheus::Client::Formats::Text.marshal(@prometheus.registry)
127
- rescue
128
- res.status = 500
129
- res['Content-Type'] = 'text/plain'
130
- res.body = $!.to_s
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
- class MonitorServletAll < WEBrick::HTTPServlet::AbstractServlet
135
- def initialize(server, prometheus)
136
- @prometheus = prometheus
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
- def do_GET(req, res)
140
- res.status = 200
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