puma-plugin-statsd 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/MIT-LICENSE +20 -0
- data/README.md +110 -0
- data/lib/puma/plugin/statsd.rb +151 -0
- metadata +116 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: ed64454a76b4776e4362b9e149ecaa09f9212c4482e5c4e6d2a1a2a33fc06c80
|
4
|
+
data.tar.gz: e5ad995a36ee4ae265ff02a07172ede4f1614e5263777293e81bc620ed2013fd
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 924aec321ac03bff296515886d8e2ad67e111c6ad769be6a97732a51c45fa26179b0d2dea815f5351e3e5d844ecc6b6a7c684b0cbc643b6ff33f86a9f6d1c0d3
|
7
|
+
data.tar.gz: 0da8be2a3781dd14c6804054f7698eb7840c2dd3d7515b8ad9c7e642636f1231bd000570e4b7eff9497730d98add97e6948283fa0d7c57ead73fc4573c261a84
|
data/MIT-LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2018 James Healy
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,110 @@
|
|
1
|
+
# Puma Statsd Plugin
|
2
|
+
|
3
|
+
[Puma][puma] integration with [statsd](statsd) for easy tracking of key metrics
|
4
|
+
that puma can provide:
|
5
|
+
|
6
|
+
* puma.workers
|
7
|
+
* puma.booted_workers
|
8
|
+
* puma.running
|
9
|
+
* puma.backlog
|
10
|
+
* puma.pool_capacity
|
11
|
+
* puma.max_threads
|
12
|
+
|
13
|
+
Puma already natively supports [socket activation][socket-activation].
|
14
|
+
|
15
|
+
[puma]: https://github.com/puma/puma
|
16
|
+
[statsd]: https://github.com/etsy/statsd
|
17
|
+
|
18
|
+
## Installation
|
19
|
+
|
20
|
+
Add this gem to your Gemfile with puma and then bundle:
|
21
|
+
|
22
|
+
```ruby
|
23
|
+
gem "puma"
|
24
|
+
gem "puma-plugin-statsd"
|
25
|
+
```
|
26
|
+
|
27
|
+
Add it to your puma config:
|
28
|
+
|
29
|
+
```ruby
|
30
|
+
# config/puma.rb
|
31
|
+
|
32
|
+
bind "http://127.0.0.1:9292"
|
33
|
+
|
34
|
+
workers 1
|
35
|
+
threads 8, 16
|
36
|
+
|
37
|
+
plugin :statsd
|
38
|
+
```
|
39
|
+
|
40
|
+
## Usage
|
41
|
+
|
42
|
+
Ensure you have an environment variable set that points to a statsd host, then boot your puma app as usual
|
43
|
+
|
44
|
+
```
|
45
|
+
STATSD_HOST=127.0.0.1 bundle exec puma
|
46
|
+
```
|
47
|
+
|
48
|
+
```
|
49
|
+
STATSD_HOST=127.0.0.1 MY_POD_NAME=foo bundle exec puma
|
50
|
+
```
|
51
|
+
|
52
|
+
### Datadog Integration
|
53
|
+
|
54
|
+
metric tags are a non-standard addition to the statsd protocol, supported by
|
55
|
+
the datadog "dogstatsd" server.
|
56
|
+
|
57
|
+
Should you be reporting the puma metrics to a dogstatsd server, you can set
|
58
|
+
tags via the following two environment variables.
|
59
|
+
|
60
|
+
`MY_POD_NAME` adds a `pod_name` tag to the metrics. The `MY_POD_NAME`
|
61
|
+
environment variable is recommended in th datadog kubernetes setup
|
62
|
+
documentation, and for puma apps deployed to kubernetes it's very helpful to
|
63
|
+
have the option to report on specific pods.
|
64
|
+
|
65
|
+
You can set it on your pods like this:
|
66
|
+
|
67
|
+
```yaml
|
68
|
+
env:
|
69
|
+
- name: MY_POD_NAME
|
70
|
+
valueFrom:
|
71
|
+
fieldRef:
|
72
|
+
fieldPath: metadata.name
|
73
|
+
```
|
74
|
+
|
75
|
+
`STATSD_GROUPING` adds a `grouping` tag to the metrics, with a value equal to
|
76
|
+
the environment variable value. This is particularly helpful in a kubernetes
|
77
|
+
deployment where each pod has a unique name but you want the option to group
|
78
|
+
metrics across all pods in a deployment. Setting this on the pods in a
|
79
|
+
deployment might look something like:
|
80
|
+
|
81
|
+
```yaml
|
82
|
+
env:
|
83
|
+
- name: STATSD_GROUPING
|
84
|
+
value: deployment-foo
|
85
|
+
```
|
86
|
+
|
87
|
+
## Contributing
|
88
|
+
|
89
|
+
Bug reports and pull requests are welcome on GitHub at
|
90
|
+
https://github.com/yob/puma-plugin-statsd.
|
91
|
+
|
92
|
+
## Acknowledgements
|
93
|
+
|
94
|
+
This gem is a fork of the excellent [puma-plugin-statsd][puma-plugin-statsd] by
|
95
|
+
Sam Cochran.
|
96
|
+
|
97
|
+
[puma-plugin-statsd]: https://github.com/sj26/puma-plugin-systemd
|
98
|
+
|
99
|
+
Other puma plugins that were helpful references:
|
100
|
+
|
101
|
+
* [puma-heroku](https://github.com/evanphx/puma-heroku)
|
102
|
+
* [tmp-restart](https://github.com/puma/puma/blob/master/lib/puma/plugin/tmp_restart.rb)
|
103
|
+
|
104
|
+
The [puma docs](https://github.com/puma/puma/blob/master/docs/plugins.md) were also helpful.
|
105
|
+
|
106
|
+
## License
|
107
|
+
|
108
|
+
The gem is available as open source under the terms of the [MIT License][license].
|
109
|
+
|
110
|
+
[license]: http://opensource.org/licenses/MIT
|
@@ -0,0 +1,151 @@
|
|
1
|
+
# coding: utf-8, frozen_string_literal: true
|
2
|
+
|
3
|
+
require "json"
|
4
|
+
require 'socket'
|
5
|
+
require "puma"
|
6
|
+
require "puma/plugin"
|
7
|
+
|
8
|
+
|
9
|
+
Puma::Plugin.create do
|
10
|
+
|
11
|
+
class Statsd
|
12
|
+
ENV_NAME = "STATSD_HOST"
|
13
|
+
STATSD_PORT = 8125
|
14
|
+
STATSD_TYPES = { count: 'c', gauge: 'g' }
|
15
|
+
|
16
|
+
attr_reader :host
|
17
|
+
|
18
|
+
def initialize
|
19
|
+
@host = ENV.fetch(ENV_NAME, nil)
|
20
|
+
end
|
21
|
+
|
22
|
+
def enabled?
|
23
|
+
!!host
|
24
|
+
end
|
25
|
+
|
26
|
+
def send(metric_name:, value:, type:, tags: {})
|
27
|
+
data = "#{metric_name}:#{value}|#{STATSD_TYPES.fetch(type)}"
|
28
|
+
if tags.any?
|
29
|
+
tag_str = tags.map { |k,v| "#{k}:#{v}" }.join(",")
|
30
|
+
data = "#{data}|##{tag_str}"
|
31
|
+
end
|
32
|
+
|
33
|
+
UDPSocket.new.send(data, 0, host, STATSD_PORT)
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
# Wrap puma's stats in a safe API
|
38
|
+
class PumaStats
|
39
|
+
def initialize(stats)
|
40
|
+
@stats = stats
|
41
|
+
end
|
42
|
+
|
43
|
+
def clustered?
|
44
|
+
@stats.has_key? "workers"
|
45
|
+
end
|
46
|
+
|
47
|
+
def workers
|
48
|
+
@stats.fetch("workers", 1)
|
49
|
+
end
|
50
|
+
|
51
|
+
def booted_workers
|
52
|
+
@stats.fetch("booted_workers", 1)
|
53
|
+
end
|
54
|
+
|
55
|
+
def running
|
56
|
+
if clustered?
|
57
|
+
@stats["worker_status"].map { |s| s["last_status"].fetch("running", 0) }.inject(0, &:+)
|
58
|
+
else
|
59
|
+
@stats.fetch("running", 0)
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
def backlog
|
64
|
+
if clustered?
|
65
|
+
@stats["worker_status"].map { |s| s["last_status"].fetch("backlog", 0) }.inject(0, &:+)
|
66
|
+
else
|
67
|
+
@stats.fetch("backlog", 0)
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
def pool_capacity
|
72
|
+
if clustered?
|
73
|
+
@stats["worker_status"].map { |s| s["last_status"].fetch("pool_capacity", 0) }.inject(0, &:+)
|
74
|
+
else
|
75
|
+
@stats.fetch("pool_capacity", 0)
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
def max_threads
|
80
|
+
if clustered?
|
81
|
+
@stats["worker_status"].map { |s| s["last_status"].fetch("max_threads", 0) }.inject(0, &:+)
|
82
|
+
else
|
83
|
+
@stats.fetch("max_threads", 0)
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
# Puma creates the plugin when encountering `plugin` in the config.
|
89
|
+
def initialize(loader)
|
90
|
+
@loader = loader
|
91
|
+
end
|
92
|
+
|
93
|
+
# We can start doing something when we have a launcher:
|
94
|
+
def start(launcher)
|
95
|
+
@launcher = launcher
|
96
|
+
|
97
|
+
@statsd = Statsd.new
|
98
|
+
if @statsd.enabled?
|
99
|
+
@launcher.events.debug "statsd: enabled (host: #{@statsd.host})"
|
100
|
+
register_hooks
|
101
|
+
else
|
102
|
+
@launcher.events.debug "statsd: not enabled (no #{Statsd::ENV_NAME} env var found)"
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
private
|
107
|
+
|
108
|
+
def register_hooks
|
109
|
+
in_background(&method(:stats_loop))
|
110
|
+
end
|
111
|
+
|
112
|
+
def fetch_stats
|
113
|
+
JSON.parse(Puma.stats)
|
114
|
+
end
|
115
|
+
|
116
|
+
def stats
|
117
|
+
PumaStats.new(fetch_stats)
|
118
|
+
end
|
119
|
+
|
120
|
+
def tags
|
121
|
+
tags = {}
|
122
|
+
if ENV.has_key?("MY_POD_NAME")
|
123
|
+
tags[:pod_name] = ENV.fetch("MY_POD_NAME", "no_pod")
|
124
|
+
end
|
125
|
+
if ENV.has_key?("STATSD_GROUPING")
|
126
|
+
tags[:grouping] = ENV.fetch("STATSD_GROUPING", "no-group")
|
127
|
+
end
|
128
|
+
tags
|
129
|
+
end
|
130
|
+
|
131
|
+
# Send data to statsd every few seconds
|
132
|
+
def stats_loop
|
133
|
+
sleep 5
|
134
|
+
loop do
|
135
|
+
@launcher.events.debug "statsd: notify statsd"
|
136
|
+
begin
|
137
|
+
@statsd.send(metric_name: "puma.workers", value: stats.workers, type: :gauge, tags: tags)
|
138
|
+
@statsd.send(metric_name: "puma.booted_workers", value: stats.booted_workers, type: :gauge, tags: tags)
|
139
|
+
@statsd.send(metric_name: "puma.running", value: stats.running, type: :gauge, tags: tags)
|
140
|
+
@statsd.send(metric_name: "puma.backlog", value: stats.backlog, type: :gauge, tags: tags)
|
141
|
+
@statsd.send(metric_name: "puma.pool_capacity", value: stats.pool_capacity, type: :gauge, tags: tags)
|
142
|
+
@statsd.send(metric_name: "puma.max_threads", value: stats.max_threads, type: :gauge, tags: tags)
|
143
|
+
rescue StandardError => e
|
144
|
+
@launcher.events.error "! statsd: notify stats failed:\n #{e.to_s}\n #{e.backtrace.join("\n ")}"
|
145
|
+
ensure
|
146
|
+
sleep 2
|
147
|
+
end
|
148
|
+
end
|
149
|
+
end
|
150
|
+
|
151
|
+
end
|
metadata
ADDED
@@ -0,0 +1,116 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: puma-plugin-statsd
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- James Healy
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2018-07-17 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: puma
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '3.12'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '3.12'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: json
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ">="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0'
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ">="
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: bundler
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - "~>"
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '1.13'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - "~>"
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '1.13'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: rake
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - "~>"
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '10.0'
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - "~>"
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '10.0'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: minitest
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - "~>"
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '5.0'
|
76
|
+
type: :development
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - "~>"
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '5.0'
|
83
|
+
description:
|
84
|
+
email: james@yob.id.au
|
85
|
+
executables: []
|
86
|
+
extensions: []
|
87
|
+
extra_rdoc_files: []
|
88
|
+
files:
|
89
|
+
- MIT-LICENSE
|
90
|
+
- README.md
|
91
|
+
- lib/puma/plugin/statsd.rb
|
92
|
+
homepage: https://github.com/yob/puma-plugin-statsd
|
93
|
+
licenses:
|
94
|
+
- MIT
|
95
|
+
metadata: {}
|
96
|
+
post_install_message:
|
97
|
+
rdoc_options: []
|
98
|
+
require_paths:
|
99
|
+
- lib
|
100
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
101
|
+
requirements:
|
102
|
+
- - ">="
|
103
|
+
- !ruby/object:Gem::Version
|
104
|
+
version: '0'
|
105
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
106
|
+
requirements:
|
107
|
+
- - ">="
|
108
|
+
- !ruby/object:Gem::Version
|
109
|
+
version: '0'
|
110
|
+
requirements: []
|
111
|
+
rubyforge_project:
|
112
|
+
rubygems_version: 2.7.6
|
113
|
+
signing_key:
|
114
|
+
specification_version: 4
|
115
|
+
summary: Send puma metrics to statsd via a background thread
|
116
|
+
test_files: []
|