puma-plugin-statsd 0.1.0 → 1.2.0
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 +4 -4
- data/CHANGELOG.md +21 -0
- data/README.md +25 -6
- data/lib/puma/plugin/statsd.rb +88 -45
- metadata +5 -5
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: d0b1cd40cd7e6d081b35483c8319f4b1eb3463ded57a1f015e5f78db3771f5a8
|
4
|
+
data.tar.gz: 8df635d04448770b411364182f7d6466f736065ce2b4f396b6daec14c2abf364
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 892042e3683ca475e062e2439189dd9a17c3fd595eae0e86bb1f687111e6d8f158ff9d8fba034f5fabc8dd066f55289bf0dc0013e2330be59f9bcb764eacfeaa
|
7
|
+
data.tar.gz: e2a05da7f70a78c584f34b1b45ca3a8655f20437828be9462d0e83b87c73cb7389ad72081136929c989a34e572601d1bf0208208aa571e29794a7a4f503aab88
|
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,26 @@
|
|
1
1
|
# CHANGELOG
|
2
2
|
|
3
|
+
## 1.2.0 2021-01-07
|
4
|
+
|
5
|
+
* New metrics: old_workers (PR #[21](https://github.com/yob/puma-plugin-statsd/pull/21)) and requsts_count (PR #[28](https://github.com/yob/puma-plugin-statsd/pull/28))
|
6
|
+
* Require json at runtime to be extra sure we don't load the wrong version before bundler has initialised the LOAD_PATH
|
7
|
+
|
8
|
+
## 1.1.0 2021-01-03
|
9
|
+
|
10
|
+
* Assume localhost for statsd host (PR #[20](https://github.com/yob/puma-plugin-statsd/pull/20))
|
11
|
+
|
12
|
+
## 1.0.0 2020-11-03
|
13
|
+
|
14
|
+
* Added option to specify arbitrary datadog tags (PR #[18](https://github.com/yob/puma-plugin-statsd/pull/18))
|
15
|
+
|
16
|
+
## 0.3.0 2020-09-24
|
17
|
+
|
18
|
+
* Support puma 5.x
|
19
|
+
|
20
|
+
## 0.2.0 2020-02-29
|
21
|
+
|
22
|
+
* Added option to prefix stats metric (via STATSD_METRIC_PREFIX env var)
|
23
|
+
|
3
24
|
## 0.1.0 2019-07-06
|
4
25
|
|
5
26
|
* The statsd port is now configurable
|
data/README.md
CHANGED
@@ -37,12 +37,14 @@ plugin :statsd
|
|
37
37
|
|
38
38
|
## Usage
|
39
39
|
|
40
|
-
|
40
|
+
By default the plugin assumes statsd is available at 127.0.0.1. If that's true in your environment, just start puma like normal:
|
41
41
|
|
42
42
|
```
|
43
|
-
|
43
|
+
bundle exec puma
|
44
44
|
```
|
45
45
|
|
46
|
+
If statsd isn't on 127.0.0.1 or the port is non-standard, you can configure them using optional environment variables:
|
47
|
+
|
46
48
|
```
|
47
49
|
STATSD_HOST=127.0.0.1 STATSD_PORT=9125 bundle exec puma
|
48
50
|
```
|
@@ -53,9 +55,24 @@ metric tags are a non-standard addition to the statsd protocol, supported by
|
|
53
55
|
the datadog "dogstatsd" server.
|
54
56
|
|
55
57
|
Should you be reporting the puma metrics to a dogstatsd server, you can set
|
56
|
-
tags via the following
|
58
|
+
tags via the following three environment variables.
|
59
|
+
|
60
|
+
#### DD_TAGS
|
61
|
+
|
62
|
+
`DD_TAGS`: Set this to a space-separated list of tags, using the
|
63
|
+
[datadog agent standard format](https://docs.datadoghq.com/agent/docker/?tab=standard#global-options).
|
64
|
+
|
65
|
+
For example, you could set this environment variable to set three datadog tags,
|
66
|
+
and then you can filter by in the datadog interface:
|
57
67
|
|
58
|
-
|
68
|
+
```bash
|
69
|
+
export DD_TAGS="env:test simple-tag-0 tag-key-1:tag-value-1"
|
70
|
+
bundle exec rails server
|
71
|
+
```
|
72
|
+
|
73
|
+
#### MY_POD_NAME
|
74
|
+
|
75
|
+
`MY_POD_NAME`: Set a `pod_name` tag to the metrics. The `MY_POD_NAME`
|
59
76
|
environment variable is recommended in the datadog kubernetes setup
|
60
77
|
documentation, and for puma apps deployed to kubernetes it's very helpful to
|
61
78
|
have the option to report on specific pods.
|
@@ -70,7 +87,9 @@ env:
|
|
70
87
|
fieldPath: metadata.name
|
71
88
|
```
|
72
89
|
|
73
|
-
|
90
|
+
#### STATSD_GROUPING
|
91
|
+
|
92
|
+
`STATSD_GROUPING`: add a `grouping` tag to the metrics, with a value equal to
|
74
93
|
the environment variable value. This is particularly helpful in a kubernetes
|
75
94
|
deployment where each pod has a unique name but you want the option to group
|
76
95
|
metrics across all pods in a deployment. Setting this on the pods in a
|
@@ -100,7 +119,7 @@ Start puma:
|
|
100
119
|
Throw some traffic at it, either with curl or a tool like ab:
|
101
120
|
|
102
121
|
curl http://127.0.0.1:9292/
|
103
|
-
ab -n 10000 -c 20 http://127.0.0.1:9292/
|
122
|
+
ab -n 10000 -c 20 http://127.0.0.1:9292/
|
104
123
|
|
105
124
|
Watch the output of the UDP server process - you should see statsd data printed to stdout.
|
106
125
|
|
data/lib/puma/plugin/statsd.rb
CHANGED
@@ -1,5 +1,4 @@
|
|
1
1
|
# coding: utf-8, frozen_string_literal: true
|
2
|
-
require "json"
|
3
2
|
require "puma"
|
4
3
|
require "puma/plugin"
|
5
4
|
require 'socket'
|
@@ -7,24 +6,18 @@ require 'socket'
|
|
7
6
|
class StatsdConnector
|
8
7
|
ENV_NAME = "STATSD_HOST"
|
9
8
|
STATSD_TYPES = { count: 'c', gauge: 'g' }
|
9
|
+
METRIC_DELIMETER = ".".freeze
|
10
10
|
|
11
11
|
attr_reader :host, :port
|
12
12
|
|
13
13
|
def initialize
|
14
|
-
@host = ENV.fetch(ENV_NAME,
|
14
|
+
@host = ENV.fetch(ENV_NAME, "127.0.0.1")
|
15
15
|
@port = ENV.fetch("STATSD_PORT", 8125)
|
16
16
|
end
|
17
17
|
|
18
|
-
def
|
19
|
-
!!host
|
20
|
-
end
|
21
|
-
|
22
|
-
def send(metric_name:, value:, type:, tags: {})
|
18
|
+
def send(metric_name:, value:, type:, tags: nil)
|
23
19
|
data = "#{metric_name}:#{value}|#{STATSD_TYPES.fetch(type)}"
|
24
|
-
|
25
|
-
tag_str = tags.map { |k,v| "#{k}:#{v}" }.join(",")
|
26
|
-
data = "#{data}|##{tag_str}"
|
27
|
-
end
|
20
|
+
data = "#{data}|##{tags}" unless tags.nil?
|
28
21
|
|
29
22
|
socket = UDPSocket.new
|
30
23
|
socket.send(data, 0, host, port)
|
@@ -40,67 +33,77 @@ class PumaStats
|
|
40
33
|
end
|
41
34
|
|
42
35
|
def clustered?
|
43
|
-
@stats.has_key?
|
36
|
+
@stats.has_key?(:workers)
|
44
37
|
end
|
45
38
|
|
46
39
|
def workers
|
47
|
-
@stats.fetch(
|
40
|
+
@stats.fetch(:workers, 1)
|
48
41
|
end
|
49
42
|
|
50
43
|
def booted_workers
|
51
|
-
@stats.fetch(
|
44
|
+
@stats.fetch(:booted_workers, 1)
|
45
|
+
end
|
46
|
+
|
47
|
+
def old_workers
|
48
|
+
@stats.fetch(:old_workers, 0)
|
52
49
|
end
|
53
50
|
|
54
51
|
def running
|
55
52
|
if clustered?
|
56
|
-
@stats[
|
53
|
+
@stats[:worker_status].map { |s| s[:last_status].fetch(:running, 0) }.inject(0, &:+)
|
57
54
|
else
|
58
|
-
@stats.fetch(
|
55
|
+
@stats.fetch(:running, 0)
|
59
56
|
end
|
60
57
|
end
|
61
58
|
|
62
59
|
def backlog
|
63
60
|
if clustered?
|
64
|
-
@stats[
|
61
|
+
@stats[:worker_status].map { |s| s[:last_status].fetch(:backlog, 0) }.inject(0, &:+)
|
65
62
|
else
|
66
|
-
@stats.fetch(
|
63
|
+
@stats.fetch(:backlog, 0)
|
67
64
|
end
|
68
65
|
end
|
69
66
|
|
70
67
|
def pool_capacity
|
71
68
|
if clustered?
|
72
|
-
@stats[
|
69
|
+
@stats[:worker_status].map { |s| s[:last_status].fetch(:pool_capacity, 0) }.inject(0, &:+)
|
73
70
|
else
|
74
|
-
@stats.fetch(
|
71
|
+
@stats.fetch(:pool_capacity, 0)
|
75
72
|
end
|
76
73
|
end
|
77
74
|
|
78
75
|
def max_threads
|
79
76
|
if clustered?
|
80
|
-
@stats[
|
77
|
+
@stats[:worker_status].map { |s| s[:last_status].fetch(:max_threads, 0) }.inject(0, &:+)
|
81
78
|
else
|
82
|
-
@stats.fetch(
|
79
|
+
@stats.fetch(:max_threads, 0)
|
83
80
|
end
|
84
81
|
end
|
85
|
-
end
|
86
82
|
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
83
|
+
def requests_count
|
84
|
+
if clustered?
|
85
|
+
@stats[:worker_status].map { |s| s[:last_status].fetch(:requests_count, 0) }.inject(0, &:+)
|
86
|
+
else
|
87
|
+
@stats.fetch(:requests_count, 0)
|
88
|
+
end
|
91
89
|
end
|
90
|
+
end
|
92
91
|
|
92
|
+
Puma::Plugin.create do
|
93
93
|
# We can start doing something when we have a launcher:
|
94
94
|
def start(launcher)
|
95
95
|
@launcher = launcher
|
96
96
|
|
97
97
|
@statsd = ::StatsdConnector.new
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
98
|
+
@launcher.events.debug "statsd: enabled (host: #{@statsd.host})"
|
99
|
+
|
100
|
+
# Fetch global metric prefix from env variable
|
101
|
+
@metric_prefix = ENV.fetch("STATSD_METRIC_PREFIX", nil)
|
102
|
+
if @metric_prefix && !@metric_prefix.end_with?(::StatsdConnector::METRIC_DELIMETER)
|
103
|
+
@metric_prefix += ::StatsdConnector::METRIC_DELIMETER
|
103
104
|
end
|
105
|
+
|
106
|
+
register_hooks
|
104
107
|
end
|
105
108
|
|
106
109
|
private
|
@@ -109,34 +112,74 @@ Puma::Plugin.create do
|
|
109
112
|
in_background(&method(:stats_loop))
|
110
113
|
end
|
111
114
|
|
112
|
-
|
113
|
-
|
115
|
+
if Puma.respond_to?(:stats_hash)
|
116
|
+
def fetch_stats
|
117
|
+
Puma.stats_hash
|
118
|
+
end
|
119
|
+
else
|
120
|
+
def fetch_stats
|
121
|
+
require "json"
|
122
|
+
stats = Puma.stats
|
123
|
+
JSON.parse(stats, symbolize_names: true)
|
124
|
+
end
|
114
125
|
end
|
115
126
|
|
116
|
-
def
|
117
|
-
|
127
|
+
def environment_variable_tags
|
128
|
+
# Tags are separated by spaces, and while they are normally a tag and
|
129
|
+
# value separated by a ':', they can also just be tagged without any
|
130
|
+
# associated value.
|
131
|
+
#
|
132
|
+
# Examples: simple-tag-0 tag-key-1:tag-value-1
|
133
|
+
#
|
134
|
+
tags = []
|
135
|
+
|
118
136
|
if ENV.has_key?("MY_POD_NAME")
|
119
|
-
tags
|
137
|
+
tags << "pod_name:#{ENV['MY_POD_NAME']}"
|
120
138
|
end
|
139
|
+
|
121
140
|
if ENV.has_key?("STATSD_GROUPING")
|
122
|
-
tags
|
141
|
+
tags << "grouping:#{ENV['STATSD_GROUPING']}"
|
123
142
|
end
|
124
|
-
|
143
|
+
|
144
|
+
# Standardised datadog tag attributes, so that we can share the metric
|
145
|
+
# tags with the application running
|
146
|
+
#
|
147
|
+
# https://docs.datadoghq.com/agent/docker/?tab=standard#global-options
|
148
|
+
#
|
149
|
+
if ENV.has_key?("DD_TAGS")
|
150
|
+
ENV["DD_TAGS"].split(/\s+/).each do |t|
|
151
|
+
tags << t
|
152
|
+
end
|
153
|
+
end
|
154
|
+
|
155
|
+
# Return nil if we have no environment variable tags. This way we don't
|
156
|
+
# send an unnecessary '|' on the end of each stat
|
157
|
+
return nil if tags.empty?
|
158
|
+
|
159
|
+
tags.join(",")
|
160
|
+
end
|
161
|
+
|
162
|
+
def prefixed_metric_name(puma_metric)
|
163
|
+
"#{@metric_prefix}#{puma_metric}"
|
125
164
|
end
|
126
165
|
|
127
166
|
# Send data to statsd every few seconds
|
128
167
|
def stats_loop
|
168
|
+
tags = environment_variable_tags
|
169
|
+
|
129
170
|
sleep 5
|
130
171
|
loop do
|
131
172
|
@launcher.events.debug "statsd: notify statsd"
|
132
173
|
begin
|
133
174
|
stats = ::PumaStats.new(fetch_stats)
|
134
|
-
@statsd.send(metric_name: "puma.workers", value: stats.workers, type: :gauge, tags: tags)
|
135
|
-
@statsd.send(metric_name: "puma.booted_workers", value: stats.booted_workers, type: :gauge, tags: tags)
|
136
|
-
@statsd.send(metric_name: "puma.
|
137
|
-
@statsd.send(metric_name: "puma.
|
138
|
-
@statsd.send(metric_name: "puma.
|
139
|
-
@statsd.send(metric_name: "puma.
|
175
|
+
@statsd.send(metric_name: prefixed_metric_name("puma.workers"), value: stats.workers, type: :gauge, tags: tags)
|
176
|
+
@statsd.send(metric_name: prefixed_metric_name("puma.booted_workers"), value: stats.booted_workers, type: :gauge, tags: tags)
|
177
|
+
@statsd.send(metric_name: prefixed_metric_name("puma.old_workers"), value: stats.old_workers, type: :gauge, tags: tags)
|
178
|
+
@statsd.send(metric_name: prefixed_metric_name("puma.running"), value: stats.running, type: :gauge, tags: tags)
|
179
|
+
@statsd.send(metric_name: prefixed_metric_name("puma.backlog"), value: stats.backlog, type: :gauge, tags: tags)
|
180
|
+
@statsd.send(metric_name: prefixed_metric_name("puma.pool_capacity"), value: stats.pool_capacity, type: :gauge, tags: tags)
|
181
|
+
@statsd.send(metric_name: prefixed_metric_name("puma.max_threads"), value: stats.max_threads, type: :gauge, tags: tags)
|
182
|
+
@statsd.send(metric_name: prefixed_metric_name("puma.requests_count"), value: stats.requests_count, type: :gauge, tags: tags)
|
140
183
|
rescue StandardError => e
|
141
184
|
@launcher.events.error "! statsd: notify stats failed:\n #{e.to_s}\n #{e.backtrace.join("\n ")}"
|
142
185
|
ensure
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: puma-plugin-statsd
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version:
|
4
|
+
version: 1.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- James Healy
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2021-01-06 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: puma
|
@@ -19,7 +19,7 @@ dependencies:
|
|
19
19
|
version: '3.12'
|
20
20
|
- - "<"
|
21
21
|
- !ruby/object:Gem::Version
|
22
|
-
version: '
|
22
|
+
version: '6'
|
23
23
|
type: :runtime
|
24
24
|
prerelease: false
|
25
25
|
version_requirements: !ruby/object:Gem::Requirement
|
@@ -29,7 +29,7 @@ dependencies:
|
|
29
29
|
version: '3.12'
|
30
30
|
- - "<"
|
31
31
|
- !ruby/object:Gem::Version
|
32
|
-
version: '
|
32
|
+
version: '6'
|
33
33
|
- !ruby/object:Gem::Dependency
|
34
34
|
name: json
|
35
35
|
requirement: !ruby/object:Gem::Requirement
|
@@ -143,7 +143,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
143
143
|
- !ruby/object:Gem::Version
|
144
144
|
version: '0'
|
145
145
|
requirements: []
|
146
|
-
rubygems_version: 3.0.
|
146
|
+
rubygems_version: 3.0.3
|
147
147
|
signing_key:
|
148
148
|
specification_version: 4
|
149
149
|
summary: Send puma metrics to statsd via a background thread
|