puma-plugin-statsd 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (5) hide show
  1. checksums.yaml +7 -0
  2. data/MIT-LICENSE +20 -0
  3. data/README.md +110 -0
  4. data/lib/puma/plugin/statsd.rb +151 -0
  5. 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: []