puma-plugin-statsd 0.2.0 → 1.2.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 +4 -4
  2. data/CHANGELOG.md +21 -0
  3. data/README.md +25 -6
  4. data/lib/puma/plugin/statsd.rb +65 -34
  5. metadata +4 -18
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: c327637905c572e3ed22e9936b440597f77d032abe2b155e73776bd845c9588c
4
- data.tar.gz: 5e57068f3dfaa9a5f9334825ff5bf7493a8286a2b46feb6da5b9fca36e605a92
3
+ metadata.gz: bb512a32b788a2df3fc7f9f222e7dcb510fdbda09bb1bb278466978d2f5b511d
4
+ data.tar.gz: 6274f07b78d32a6bddbc5d6d823114f6171df7fb3131186e35fbdd9416244ce1
5
5
  SHA512:
6
- metadata.gz: 7b5cd14ef13b733fe0488cb61dd63a74811566a467d45ec7f9a661515a61cd7b7a0cde4e4ff1e4f71f64d5f52e7c76a5a52e4addbc7085ec7159c90e88e62231
7
- data.tar.gz: 91c439d68efdf850d777bc380ee9ef3ca3c1233d04bb8891c1a204bbd038ff75f436c7bf3fb09caf8fa4576fed37385979aa94cd39f9288125c7d2eeccd7f1c2
6
+ metadata.gz: c568951db0c140e934d4cd40103c8b98160da02aeeda6b2549dd59a415ee2ee59ea171f6d818150fdbb66c99b88a912690c31f9bc3bc0715dc7478440136e76a
7
+ data.tar.gz: 2af0724eb7a83bae47caed6c3009a5fa5ed2d0ab98b2c98be250c03636750bdf48788f69fe8bd04fb83a7656fb15174ab356edd0581eed072e780b4fa0855aa8
@@ -1,5 +1,26 @@
1
1
  # CHANGELOG
2
2
 
3
+ ## 1.2.1 2021-01-11
4
+
5
+ * Remove json from the gemspec
6
+
7
+ ## 1.2.0 2021-01-07
8
+
9
+ * 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))
10
+ * Require json at runtime to be extra sure we don't load the wrong version before bundler has initialised the LOAD_PATH
11
+
12
+ ## 1.1.0 2021-01-03
13
+
14
+ * Assume localhost for statsd host (PR #[20](https://github.com/yob/puma-plugin-statsd/pull/20))
15
+
16
+ ## 1.0.0 2020-11-03
17
+
18
+ * Added option to specify arbitrary datadog tags (PR #[18](https://github.com/yob/puma-plugin-statsd/pull/18))
19
+
20
+ ## 0.3.0 2020-09-24
21
+
22
+ * Support puma 5.x
23
+
3
24
  ## 0.2.0 2020-02-29
4
25
 
5
26
  * Added option to prefix stats metric (via STATSD_METRIC_PREFIX env var)
data/README.md CHANGED
@@ -37,12 +37,14 @@ plugin :statsd
37
37
 
38
38
  ## Usage
39
39
 
40
- Ensure you have an environment variable set that points to a statsd host, then boot your puma app as usual. Optionally you may specify a port (default is 8125).
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
- STATSD_HOST=127.0.0.1 bundle exec puma
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 two environment variables.
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
- `MY_POD_NAME` adds a `pod_name` tag to the metrics. The `MY_POD_NAME`
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
- `STATSD_GROUPING` adds a `grouping` tag to the metrics, with a value equal to
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
 
@@ -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'
@@ -12,20 +11,13 @@ class StatsdConnector
12
11
  attr_reader :host, :port
13
12
 
14
13
  def initialize
15
- @host = ENV.fetch(ENV_NAME, nil)
14
+ @host = ENV.fetch(ENV_NAME, "127.0.0.1")
16
15
  @port = ENV.fetch("STATSD_PORT", 8125)
17
16
  end
18
17
 
19
- def enabled?
20
- !!host
21
- end
22
-
23
- def send(metric_name:, value:, type:, tags: {})
18
+ def send(metric_name:, value:, type:, tags: nil)
24
19
  data = "#{metric_name}:#{value}|#{STATSD_TYPES.fetch(type)}"
25
- if tags.any?
26
- tag_str = tags.map { |k,v| "#{k}:#{v}" }.join(",")
27
- data = "#{data}|##{tag_str}"
28
- end
20
+ data = "#{data}|##{tags}" unless tags.nil?
29
21
 
30
22
  socket = UDPSocket.new
31
23
  socket.send(data, 0, host, port)
@@ -52,6 +44,10 @@ class PumaStats
52
44
  @stats.fetch(:booted_workers, 1)
53
45
  end
54
46
 
47
+ def old_workers
48
+ @stats.fetch(:old_workers, 0)
49
+ end
50
+
55
51
  def running
56
52
  if clustered?
57
53
  @stats[:worker_status].map { |s| s[:last_status].fetch(:running, 0) }.inject(0, &:+)
@@ -83,32 +79,31 @@ class PumaStats
83
79
  @stats.fetch(:max_threads, 0)
84
80
  end
85
81
  end
86
- end
87
82
 
88
- Puma::Plugin.create do
89
- # Puma creates the plugin when encountering `plugin` in the config.
90
- def initialize(loader)
91
- @loader = loader
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
92
89
  end
90
+ end
93
91
 
92
+ Puma::Plugin.create do
94
93
  # We can start doing something when we have a launcher:
95
94
  def start(launcher)
96
95
  @launcher = launcher
97
96
 
98
97
  @statsd = ::StatsdConnector.new
99
- if @statsd.enabled?
100
- @launcher.events.debug "statsd: enabled (host: #{@statsd.host})"
101
-
102
- # Fetch global metric prefix from env variable
103
- @metric_prefix = ENV.fetch("STATSD_METRIC_PREFIX", nil)
104
- if @metric_prefix && !@metric_prefix.end_with?(::StatsdConnector::METRIC_DELIMETER)
105
- @metric_prefix += ::StatsdConnector::METRIC_DELIMETER
106
- end
98
+ @launcher.events.debug "statsd: enabled (host: #{@statsd.host})"
107
99
 
108
- register_hooks
109
- else
110
- @launcher.events.debug "statsd: not enabled (no #{StatsdConnector::ENV_NAME} env var found)"
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
111
104
  end
105
+
106
+ register_hooks
112
107
  end
113
108
 
114
109
  private
@@ -117,19 +112,51 @@ Puma::Plugin.create do
117
112
  in_background(&method(:stats_loop))
118
113
  end
119
114
 
120
- def fetch_stats
121
- JSON.parse(Puma.stats, symbolize_names: true)
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
122
125
  end
123
126
 
124
- def tags
125
- tags = {}
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
+
126
136
  if ENV.has_key?("MY_POD_NAME")
127
- tags[:pod_name] = ENV.fetch("MY_POD_NAME", "no_pod")
137
+ tags << "pod_name:#{ENV['MY_POD_NAME']}"
128
138
  end
139
+
129
140
  if ENV.has_key?("STATSD_GROUPING")
130
- tags[:grouping] = ENV.fetch("STATSD_GROUPING", "no-group")
141
+ tags << "grouping:#{ENV['STATSD_GROUPING']}"
131
142
  end
132
- tags
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(",")
133
160
  end
134
161
 
135
162
  def prefixed_metric_name(puma_metric)
@@ -138,6 +165,8 @@ Puma::Plugin.create do
138
165
 
139
166
  # Send data to statsd every few seconds
140
167
  def stats_loop
168
+ tags = environment_variable_tags
169
+
141
170
  sleep 5
142
171
  loop do
143
172
  @launcher.events.debug "statsd: notify statsd"
@@ -145,10 +174,12 @@ Puma::Plugin.create do
145
174
  stats = ::PumaStats.new(fetch_stats)
146
175
  @statsd.send(metric_name: prefixed_metric_name("puma.workers"), value: stats.workers, type: :gauge, tags: tags)
147
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)
148
178
  @statsd.send(metric_name: prefixed_metric_name("puma.running"), value: stats.running, type: :gauge, tags: tags)
149
179
  @statsd.send(metric_name: prefixed_metric_name("puma.backlog"), value: stats.backlog, type: :gauge, tags: tags)
150
180
  @statsd.send(metric_name: prefixed_metric_name("puma.pool_capacity"), value: stats.pool_capacity, type: :gauge, tags: tags)
151
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)
152
183
  rescue StandardError => e
153
184
  @launcher.events.error "! statsd: notify stats failed:\n #{e.to_s}\n #{e.backtrace.join("\n ")}"
154
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: 0.2.0
4
+ version: 1.2.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - James Healy
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2020-02-29 00:00:00.000000000 Z
11
+ date: 2021-01-11 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: '5'
22
+ version: '6'
23
23
  type: :runtime
24
24
  prerelease: false
25
25
  version_requirements: !ruby/object:Gem::Requirement
@@ -29,21 +29,7 @@ dependencies:
29
29
  version: '3.12'
30
30
  - - "<"
31
31
  - !ruby/object:Gem::Version
32
- version: '5'
33
- - !ruby/object:Gem::Dependency
34
- name: json
35
- requirement: !ruby/object:Gem::Requirement
36
- requirements:
37
- - - ">="
38
- - !ruby/object:Gem::Version
39
- version: '0'
40
- type: :runtime
41
- prerelease: false
42
- version_requirements: !ruby/object:Gem::Requirement
43
- requirements:
44
- - - ">="
45
- - !ruby/object:Gem::Version
46
- version: '0'
32
+ version: '6'
47
33
  - !ruby/object:Gem::Dependency
48
34
  name: bundler
49
35
  requirement: !ruby/object:Gem::Requirement