yabeda-puma-plugin 0.1.0 → 0.5.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.github/workflows/build-release.yml +54 -0
- data/.github/workflows/test.yml +44 -0
- data/README.md +52 -4
- data/docs/diagram.png +0 -0
- data/lib/puma/plugin/yabeda.rb +6 -6
- data/lib/puma/plugin/yabeda_prometheus.rb +49 -0
- data/lib/yabeda/puma/plugin/statistics.rb +2 -2
- data/lib/yabeda/puma/plugin/statistics/fetcher.rb +19 -3
- data/lib/yabeda/puma/plugin/statistics/parser.rb +18 -6
- data/lib/yabeda/puma/plugin/version.rb +1 -1
- data/yabeda-puma-plugin.gemspec +2 -2
- metadata +12 -11
- data/.travis.yml +0 -9
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: ea655ec09c4edd3459d19356df62760dbb70049b24a0e5ccfb69eed7d501ec7d
|
4
|
+
data.tar.gz: 49cab44b0913f02a372473b3e8951a6c880020f3a22970c6f9cdadee7d3935ae
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: d68acb875bb1d793d0bf6c11f1df91313ec730bcf9984f362829586bdb7810e21b809c60980cf606feec93378957b218d046bb30ced55858f8cc704c3b073380
|
7
|
+
data.tar.gz: e8e0c33afd4c639ef3abb25485bf2fb9a3598a288d07033ad64ebffd354335f17558aef856afc18d439fb03cf0c767026c9e348b31de6d1ff1cc91a653ca1c7e
|
@@ -0,0 +1,54 @@
|
|
1
|
+
name: Build and release gem to RubyGems
|
2
|
+
|
3
|
+
on:
|
4
|
+
push:
|
5
|
+
tags:
|
6
|
+
- v*
|
7
|
+
|
8
|
+
jobs:
|
9
|
+
release:
|
10
|
+
runs-on: ubuntu-latest
|
11
|
+
steps:
|
12
|
+
- uses: actions/checkout@v2
|
13
|
+
with:
|
14
|
+
fetch-depth: 0 # Fetch current tag as annotated. See https://github.com/actions/checkout/issues/290
|
15
|
+
- uses: ruby/setup-ruby@v1
|
16
|
+
with:
|
17
|
+
ruby-version: 2.7
|
18
|
+
- name: "Extract data from tag: version, message, body"
|
19
|
+
id: tag
|
20
|
+
run: |
|
21
|
+
git fetch --tags --force # Really fetch annotated tag. See https://github.com/actions/checkout/issues/290#issuecomment-680260080
|
22
|
+
echo ::set-output name=version::${GITHUB_REF#refs/tags/v}
|
23
|
+
echo ::set-output name=subject::$(git for-each-ref $GITHUB_REF --format='%(contents:subject)')
|
24
|
+
# Multiline body for release. See https://github.community/t/set-output-truncates-multiline-strings/16852/5
|
25
|
+
BODY="$(git for-each-ref $GITHUB_REF --format='%(contents:body)')"
|
26
|
+
BODY="${BODY//'%'/'%25'}"
|
27
|
+
BODY="${BODY//$'\n'/'%0A'}"
|
28
|
+
BODY="${BODY//$'\r'/'%0D'}"
|
29
|
+
echo "::set-output name=body::$BODY"
|
30
|
+
- name: Build gem
|
31
|
+
run: gem build
|
32
|
+
- name: Check version
|
33
|
+
run: ls yabeda-puma-plugin-${{ steps.tag.outputs.version }}.gem
|
34
|
+
- name: Create Release
|
35
|
+
id: create_release
|
36
|
+
uses: actions/create-release@v1
|
37
|
+
env:
|
38
|
+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
39
|
+
with:
|
40
|
+
tag_name: ${{ github.ref }}
|
41
|
+
release_name: ${{ steps.tag.outputs.subject }}
|
42
|
+
body: ${{ steps.tag.outputs.body }}
|
43
|
+
draft: false
|
44
|
+
prerelease: false
|
45
|
+
- name: Upload Release Asset
|
46
|
+
id: upload-release-asset
|
47
|
+
uses: actions/upload-release-asset@v1
|
48
|
+
env:
|
49
|
+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
50
|
+
with:
|
51
|
+
upload_url: ${{ steps.create_release.outputs.upload_url }}
|
52
|
+
asset_path: yabeda-puma-plugin-${{ steps.tag.outputs.version }}.gem
|
53
|
+
asset_name: yabeda-puma-plugin-${{ steps.tag.outputs.version }}.gem
|
54
|
+
asset_content_type: application/x-tar
|
@@ -0,0 +1,44 @@
|
|
1
|
+
name: Run tests
|
2
|
+
|
3
|
+
on:
|
4
|
+
pull_request:
|
5
|
+
push:
|
6
|
+
branches:
|
7
|
+
- '**'
|
8
|
+
tags-ignore:
|
9
|
+
- 'v*'
|
10
|
+
|
11
|
+
jobs:
|
12
|
+
test:
|
13
|
+
name: "Run tests"
|
14
|
+
if: "! contains(toJSON(github.event.commits.latest.message), '[ci skip]')"
|
15
|
+
runs-on: ubuntu-latest
|
16
|
+
strategy:
|
17
|
+
fail-fast: false
|
18
|
+
matrix:
|
19
|
+
include:
|
20
|
+
- ruby: 2.7
|
21
|
+
- ruby: 2.6
|
22
|
+
- ruby: 2.5
|
23
|
+
container:
|
24
|
+
image: ruby:${{ matrix.ruby }}
|
25
|
+
env:
|
26
|
+
CI: true
|
27
|
+
steps:
|
28
|
+
- uses: actions/checkout@v2
|
29
|
+
- uses: actions/cache@v2
|
30
|
+
with:
|
31
|
+
path: vendor/bundle
|
32
|
+
key: bundle-${{ matrix.ruby }}-${{ hashFiles('**/*.gemspec') }}-${{ hashFiles('**/Gemfile') }}
|
33
|
+
restore-keys: |
|
34
|
+
bundle-${{ matrix.ruby }}-${{ hashFiles('**/*.gemspec') }}-${{ hashFiles('**/Gemfile') }}
|
35
|
+
bundle-${{ matrix.ruby }}-
|
36
|
+
- name: Upgrade Bundler to 2.0 (for older Rubies)
|
37
|
+
run: gem install bundler -v '~> 2.0'
|
38
|
+
- name: Bundle install
|
39
|
+
run: |
|
40
|
+
bundle config path vendor/bundle
|
41
|
+
bundle install
|
42
|
+
bundle update
|
43
|
+
- name: Run RSpec
|
44
|
+
run: bundle exec rspec
|
data/README.md
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
<a href="https://amplifr.com/?utm_source=
|
1
|
+
<a href="https://amplifr.com/?utm_source=yabeda-puma-plugin">
|
2
2
|
<img width="100" height="140" align="right"
|
3
3
|
alt="Sponsored by Amplifr" src="https://amplifr-direct.s3-eu-west-1.amazonaws.com/social_images/image/37b580d9-3668-4005-8d5a-137de3a3e77c.png" />
|
4
4
|
</a>
|
@@ -14,7 +14,7 @@ Works as the Puma plugin and provides following metrics:
|
|
14
14
|
- `puma_booted_workers` - the number of booted puma workers
|
15
15
|
- `puma_old_workers` - the number of old puma worker
|
16
16
|
|
17
|
-
Segmented by the worker:
|
17
|
+
Segmented by the worker (index of the worker):
|
18
18
|
- `puma_pool_capacity` - the capacity of each worker: the number of requests that the server is capable of taking right now. More details are [here](https://github.com/puma/puma/blob/0f8b10737e36fc24cdd572f76a739659b5fad9cb/lib/puma/server.rb#L167).
|
19
19
|
- `puma_running` - the number of running threads (spawned threads) for any puma worker
|
20
20
|
- `puma_max_threads` - preconfigured maximum number of worker threads
|
@@ -25,7 +25,7 @@ Segmented by the worker:
|
|
25
25
|
Add this line to your application's Gemfile:
|
26
26
|
|
27
27
|
```ruby
|
28
|
-
gem 'yabeda-puma'
|
28
|
+
gem 'yabeda-puma-plugin'
|
29
29
|
```
|
30
30
|
|
31
31
|
And then execute:
|
@@ -34,6 +34,8 @@ And then execute:
|
|
34
34
|
|
35
35
|
## Usage
|
36
36
|
|
37
|
+
### Collecting metrics
|
38
|
+
|
37
39
|
Add those 2 lines of code to your `config/puma.rb` file:
|
38
40
|
```ruby
|
39
41
|
activate_control_app
|
@@ -41,6 +43,52 @@ plugin :yabeda
|
|
41
43
|
```
|
42
44
|
It will activate default puma control application working over the unix socket, and runs the `yabeda` puma plugin, for registering and collecting the metrics.
|
43
45
|
|
46
|
+
### Exposing metrics
|
47
|
+
|
48
|
+
Some monitoring system agents (like NewRelic or DataDog) will send metrics automatically in the background. But for some of monitoring systems (like Prometheus) you have to explicitly set up metrics export.
|
49
|
+
|
50
|
+
#### Prometheus
|
51
|
+
|
52
|
+
##### On the same endpoint with your application
|
53
|
+
|
54
|
+
For non-Rails applications place following line in your `config.ru` _before_ running your application:
|
55
|
+
|
56
|
+
```ruby
|
57
|
+
use Yabeda::Prometheus::Exporter, path: "/metrics"
|
58
|
+
```
|
59
|
+
|
60
|
+
In Ruby on Rails applications you can add following line in `config/routes.rb` instead:
|
61
|
+
|
62
|
+
```ruby
|
63
|
+
mount Yabeda::Prometheus::Exporter => "/metrics"
|
64
|
+
```
|
65
|
+
|
66
|
+
In both cases your Puma instance metrics (along with your application metrics) will be available at `/metrics` endpoint.
|
67
|
+
|
68
|
+
##### On different port
|
69
|
+
|
70
|
+
Sometimes you don't want to expose metrics publicly for security reasons. For that case prometheus exporter plugin is bundled with this gem.
|
71
|
+
|
72
|
+
Don't forget to add either `yabeda-prometheus` or `yabeda-prometheus-mmap` gem into your `Gemfile`!
|
73
|
+
|
74
|
+
Add this plugin into your `config/puma.rb`:
|
75
|
+
|
76
|
+
```ruby
|
77
|
+
plugin :yabeda_prometheus
|
78
|
+
```
|
79
|
+
|
80
|
+
By default metrics will be available at `http://0.0.0.0:9394/metrics`.
|
81
|
+
|
82
|
+
Bind host, port, and path can be controlled either by config file option `prometheus_exporter_url`:
|
83
|
+
|
84
|
+
```ruby
|
85
|
+
# config/puma.rb
|
86
|
+
prometheus_exporter_url "tcp://127.0.0.1:9395/shmetrics"
|
87
|
+
```
|
88
|
+
|
89
|
+
Or by environment variables `PROMETHEUS_EXPORTER_URL`, `PROMETHEUS_EXPORTER_BIND`, `PROMETHEUS_EXPORTER_PORT`, and `PROMETHEUS_EXPORTER_PATH` (takes precedence over configuration option).
|
90
|
+
|
91
|
+
|
44
92
|
## Details
|
45
93
|
|
46
94
|
In accordance with the [architecture](https://github.com/puma/puma/blob/master/docs/architecture.md) of the puma web server lets look how it works:
|
@@ -84,7 +132,7 @@ docker-compose run app bundle exec rspec
|
|
84
132
|
|
85
133
|
## Contributing
|
86
134
|
|
87
|
-
Bug reports and pull requests are welcome on GitHub at https://github.com/
|
135
|
+
Bug reports and pull requests are welcome on GitHub at https://github.com/yabeda-rb/yabeda-puma-plugin.
|
88
136
|
|
89
137
|
## License
|
90
138
|
|
data/docs/diagram.png
CHANGED
Binary file
|
data/lib/puma/plugin/yabeda.rb
CHANGED
@@ -14,14 +14,14 @@ Puma::Plugin.create do
|
|
14
14
|
|
15
15
|
Yabeda.configure do
|
16
16
|
group :puma
|
17
|
-
|
18
|
-
gauge :backlog, comment: 'Number of established but unaccepted connections in the backlog'
|
19
|
-
gauge :running, comment: 'Number of running worker threads'
|
20
|
-
gauge :pool_capacity, comment: 'Number of allocatable worker threads'
|
21
|
-
gauge :max_threads, comment: 'Maximum number of worker threads'
|
22
|
-
gauge :workers, comment: 'Number of configured workers'
|
17
|
+
|
18
|
+
gauge :backlog, tags: %i[index], comment: 'Number of established but unaccepted connections in the backlog'
|
19
|
+
gauge :running, tags: %i[index], comment: 'Number of running worker threads'
|
20
|
+
gauge :pool_capacity, tags: %i[index], comment: 'Number of allocatable worker threads'
|
21
|
+
gauge :max_threads, tags: %i[index], comment: 'Maximum number of worker threads'
|
23
22
|
|
24
23
|
if clustered
|
24
|
+
gauge :workers, comment: 'Number of configured workers'
|
25
25
|
gauge :booted_workers, comment: 'Number of booted workers'
|
26
26
|
gauge :old_workers, comment: 'Number of old workers'
|
27
27
|
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
begin
|
4
|
+
require 'yabeda/prometheus/exporter'
|
5
|
+
rescue LoadError
|
6
|
+
raise LoadError, 'Please add either yabeda-prometheus or yabeda-prometheus-mmap gem into your Gemfile to use yabeda/prometheus/exporter!'
|
7
|
+
end
|
8
|
+
|
9
|
+
require 'uri'
|
10
|
+
require 'rack'
|
11
|
+
|
12
|
+
module Puma
|
13
|
+
class DSL
|
14
|
+
def prometheus_exporter_url(uri)
|
15
|
+
@options[:prometheus_exporter_url] = uri
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
Puma::Plugin.create do
|
21
|
+
def start(launcher)
|
22
|
+
events = launcher.events
|
23
|
+
|
24
|
+
uri = launcher.options.fetch(:prometheus_exporter_url, 'tcp://0.0.0.0:9394/metrics')
|
25
|
+
uri = URI.parse(ENV.fetch('PROMETHEUS_EXPORTER_URL', uri))
|
26
|
+
host = ENV.fetch('PROMETHEUS_EXPORTER_BIND', uri.host)
|
27
|
+
port = Integer(ENV.fetch('PROMETHEUS_EXPORTER_PORT', uri.port))
|
28
|
+
path = ENV.fetch('PROMETHEUS_EXPORTER_PATH', uri.path)
|
29
|
+
|
30
|
+
events.on_booted do
|
31
|
+
app = Yabeda::Prometheus::Exporter.rack_app(Yabeda::Prometheus::Exporter, path: path)
|
32
|
+
|
33
|
+
metrics = Puma::Server.new app, events
|
34
|
+
metrics.min_threads = 0
|
35
|
+
metrics.max_threads = 1
|
36
|
+
|
37
|
+
events.log "* Starting Yabeda Prometheus metrics exporter on http://#{host}:#{port}#{path}"
|
38
|
+
metrics.add_tcp_listener host, port
|
39
|
+
|
40
|
+
events.register(:state) do |state|
|
41
|
+
if %i[halt restart stop].include?(state)
|
42
|
+
metrics.stop(true) unless metrics.shutting_down?
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
metrics.run
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
@@ -2,8 +2,8 @@ module Yabeda
|
|
2
2
|
module Puma
|
3
3
|
module Plugin
|
4
4
|
module Statistics
|
5
|
-
METRICS = [:backlog, :running, :pool_capacity, :max_threads
|
6
|
-
CLUSTERED_METRICS = [:booted_workers, :old_workers]
|
5
|
+
METRICS = [:backlog, :running, :pool_capacity, :max_threads]
|
6
|
+
CLUSTERED_METRICS = [:booted_workers, :old_workers, :workers]
|
7
7
|
end
|
8
8
|
end
|
9
9
|
end
|
@@ -7,13 +7,29 @@ module Yabeda
|
|
7
7
|
module Statistics
|
8
8
|
class Fetcher
|
9
9
|
def self.call
|
10
|
-
|
11
|
-
|
12
|
-
|
10
|
+
control_url = Yabeda::Puma::Plugin.control_url
|
11
|
+
|
12
|
+
body = if control_url.start_with? "unix://"
|
13
|
+
path = control_url.gsub("unix://", '')
|
14
|
+
Socket.unix(path, &socket_block)
|
15
|
+
elsif control_url.start_with? "tcp://"
|
16
|
+
host, port = control_url.match(/^tcp:\/\/([a-z0-9\-._~%]+):([0-9]+)/).captures
|
17
|
+
Socket.tcp(host, port, &socket_block)
|
18
|
+
else
|
19
|
+
raise ArgumentError.new("Unknown puma control url type #{control_url}")
|
13
20
|
end
|
14
21
|
|
15
22
|
JSON.parse(body.split("\n").last)
|
16
23
|
end
|
24
|
+
|
25
|
+
private
|
26
|
+
|
27
|
+
def self.socket_block
|
28
|
+
Proc.new do |s|
|
29
|
+
s << "GET /stats?token=#{Yabeda::Puma::Plugin.control_auth_token} HTTP/1.0\r\n\r\n"
|
30
|
+
s.read
|
31
|
+
end
|
32
|
+
end
|
17
33
|
end
|
18
34
|
end
|
19
35
|
end
|
@@ -13,21 +13,33 @@ module Yabeda
|
|
13
13
|
end
|
14
14
|
|
15
15
|
def call
|
16
|
-
|
16
|
+
[].tap { |result| parse(data, {}, result) }
|
17
17
|
end
|
18
18
|
|
19
19
|
private
|
20
20
|
|
21
|
-
def parse(stats, labels
|
21
|
+
def parse(stats, labels, result)
|
22
22
|
stats.each do |key, value|
|
23
|
-
|
24
|
-
|
25
|
-
|
23
|
+
case key
|
24
|
+
when 'worker_status'
|
25
|
+
value.each { |s| parse(s, labels.merge(index: s['index']), result) }
|
26
|
+
when 'last_status'
|
27
|
+
parse(value, labels, result)
|
28
|
+
else
|
29
|
+
next unless metric?(key)
|
30
|
+
|
31
|
+
l = clustered_metric?(key) ? labels : { index: 0 }.merge(labels)
|
32
|
+
result << { name: key, value: value, labels: l }
|
33
|
+
end
|
26
34
|
end
|
27
35
|
end
|
28
36
|
|
29
37
|
def metric?(name)
|
30
|
-
Statistics::METRICS.include?(name.to_sym) ||
|
38
|
+
Statistics::METRICS.include?(name.to_sym) || clustered_metric?(name)
|
39
|
+
end
|
40
|
+
|
41
|
+
def clustered_metric?(name)
|
42
|
+
clustered && Statistics::CLUSTERED_METRICS.include?(name.to_sym)
|
31
43
|
end
|
32
44
|
end
|
33
45
|
end
|
data/yabeda-puma-plugin.gemspec
CHANGED
@@ -21,12 +21,12 @@ Gem::Specification.new do |spec|
|
|
21
21
|
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
22
22
|
spec.require_paths = ["lib"]
|
23
23
|
|
24
|
-
spec.add_runtime_dependency "yabeda"
|
24
|
+
spec.add_runtime_dependency "yabeda", "~> 0.2"
|
25
25
|
spec.add_runtime_dependency "puma"
|
26
26
|
spec.add_runtime_dependency "json"
|
27
27
|
|
28
28
|
spec.add_development_dependency "bundler"
|
29
|
-
spec.add_development_dependency "rake", "~>
|
29
|
+
spec.add_development_dependency "rake", "~> 13.0"
|
30
30
|
spec.add_development_dependency "rspec", "~> 3.0"
|
31
31
|
spec.add_development_dependency "rack"
|
32
32
|
end
|
metadata
CHANGED
@@ -1,29 +1,29 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: yabeda-puma-plugin
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.5.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Salahutdinov Dmitry
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2020-11-13 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: yabeda
|
15
15
|
requirement: !ruby/object:Gem::Requirement
|
16
16
|
requirements:
|
17
|
-
- - "
|
17
|
+
- - "~>"
|
18
18
|
- !ruby/object:Gem::Version
|
19
|
-
version: '0'
|
19
|
+
version: '0.2'
|
20
20
|
type: :runtime
|
21
21
|
prerelease: false
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
23
23
|
requirements:
|
24
|
-
- - "
|
24
|
+
- - "~>"
|
25
25
|
- !ruby/object:Gem::Version
|
26
|
-
version: '0'
|
26
|
+
version: '0.2'
|
27
27
|
- !ruby/object:Gem::Dependency
|
28
28
|
name: puma
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
@@ -72,14 +72,14 @@ dependencies:
|
|
72
72
|
requirements:
|
73
73
|
- - "~>"
|
74
74
|
- !ruby/object:Gem::Version
|
75
|
-
version: '
|
75
|
+
version: '13.0'
|
76
76
|
type: :development
|
77
77
|
prerelease: false
|
78
78
|
version_requirements: !ruby/object:Gem::Requirement
|
79
79
|
requirements:
|
80
80
|
- - "~>"
|
81
81
|
- !ruby/object:Gem::Version
|
82
|
-
version: '
|
82
|
+
version: '13.0'
|
83
83
|
- !ruby/object:Gem::Dependency
|
84
84
|
name: rspec
|
85
85
|
requirement: !ruby/object:Gem::Requirement
|
@@ -115,9 +115,10 @@ executables: []
|
|
115
115
|
extensions: []
|
116
116
|
extra_rdoc_files: []
|
117
117
|
files:
|
118
|
+
- ".github/workflows/build-release.yml"
|
119
|
+
- ".github/workflows/test.yml"
|
118
120
|
- ".gitignore"
|
119
121
|
- ".rspec"
|
120
|
-
- ".travis.yml"
|
121
122
|
- Gemfile
|
122
123
|
- LICENSE
|
123
124
|
- LICENSE.txt
|
@@ -129,6 +130,7 @@ files:
|
|
129
130
|
- docs/diagram.png
|
130
131
|
- docs/grafana.png
|
131
132
|
- lib/puma/plugin/yabeda.rb
|
133
|
+
- lib/puma/plugin/yabeda_prometheus.rb
|
132
134
|
- lib/yabeda/puma/plugin.rb
|
133
135
|
- lib/yabeda/puma/plugin/statistics.rb
|
134
136
|
- lib/yabeda/puma/plugin/statistics/fetcher.rb
|
@@ -154,8 +156,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
154
156
|
- !ruby/object:Gem::Version
|
155
157
|
version: '0'
|
156
158
|
requirements: []
|
157
|
-
|
158
|
-
rubygems_version: 2.7.7
|
159
|
+
rubygems_version: 3.1.4
|
159
160
|
signing_key:
|
160
161
|
specification_version: 4
|
161
162
|
summary: Puma web server plugin for collecting puma metrics with Yabeda framework.
|