statsd-instrument 2.0.5 → 2.0.6
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.gitignore +2 -0
- data/.travis.yml +5 -0
- data/CHANGELOG.md +43 -0
- data/CONTRIBUTING.md +34 -0
- data/README.md +29 -42
- data/lib/statsd/instrument.rb +213 -56
- data/lib/statsd/instrument/backend.rb +7 -0
- data/lib/statsd/instrument/backends/capture_backend.rb +12 -0
- data/lib/statsd/instrument/backends/logger_backend.rb +8 -2
- data/lib/statsd/instrument/backends/null_backend.rb +1 -0
- data/lib/statsd/instrument/environment.rb +31 -4
- data/lib/statsd/instrument/metric.rb +63 -2
- data/lib/statsd/instrument/railtie.rb +10 -2
- data/lib/statsd/instrument/version.rb +1 -1
- data/statsd-instrument.gemspec +3 -2
- metadata +22 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 852b64cbebc0760778a4d3332d4611f4652e64bd
|
4
|
+
data.tar.gz: fb0025c42ec04d18f7bdd1553d48dd75656e083f
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 58f1b9bf3fee6706e3bc543d4f0146dda3217c0dab443a624d9700f02083917d6a76169dbf0cd1d9372ed8f36ff9b51a9bfbf6cb121f62fa8c4e0798b48f86cc
|
7
|
+
data.tar.gz: f9a3abf6573ac684c6573c64dec24108ab0e6c56227ced05a6fa167dc0e4c4d0245b83dcdd53f2de2666e9138096b3668ed1f86529937ae06a70833e57849dc5
|
data/.travis.yml
CHANGED
data/CHANGELOG.md
ADDED
@@ -0,0 +1,43 @@
|
|
1
|
+
# Changelog
|
2
|
+
|
3
|
+
This file documents the changes between releases of this library. When creating a pull request,
|
4
|
+
please at an entry to the "unreleased changes" section below.
|
5
|
+
|
6
|
+
### Unreleased changes
|
7
|
+
|
8
|
+
- Nothing yet!
|
9
|
+
|
10
|
+
---
|
11
|
+
|
12
|
+
### Version 2.0.6
|
13
|
+
|
14
|
+
- Fix some loading order issues in Rails environments.
|
15
|
+
- Default behavior: in a **staging** environment, the defaults are now the same as in a **production environment**.
|
16
|
+
- Documentation overhaul
|
17
|
+
|
18
|
+
|
19
|
+
### Version 2.0.5
|
20
|
+
|
21
|
+
- Allow for nested assertions using the `assert_statsd_*` assertion methods.
|
22
|
+
|
23
|
+
### Version 2.0.4
|
24
|
+
|
25
|
+
- Add a Railtie to fix some initialization issues.
|
26
|
+
|
27
|
+
### Version 2.0.3
|
28
|
+
|
29
|
+
- Assertion method bugfixes
|
30
|
+
|
31
|
+
### Version 2.0.2
|
32
|
+
|
33
|
+
- Documentation fixes
|
34
|
+
|
35
|
+
### Version 2.0.1
|
36
|
+
|
37
|
+
- Add assertion methods `assert_statsd_histogram`, `assert_statsd_set`, and `assert_statsd_key_value`.
|
38
|
+
|
39
|
+
### Version 2.0.0
|
40
|
+
|
41
|
+
- Complete rewrite using pluggable backends.
|
42
|
+
- Add assertion methods in `StatsD::Instrument::Assertions` to make testing easier and less brittle.
|
43
|
+
- Drop support for Ruby 1.8
|
data/CONTRIBUTING.md
ADDED
@@ -0,0 +1,34 @@
|
|
1
|
+
# Contributing
|
2
|
+
|
3
|
+
This project is MIT licensed and welcomes outside contributions.
|
4
|
+
|
5
|
+
## Reporting issues
|
6
|
+
|
7
|
+
Report issues using the [Github issues tracker](https://github.com/Shopify/statsd-instrument/issues/new).
|
8
|
+
|
9
|
+
When reporting issues, please incldue the following information:
|
10
|
+
|
11
|
+
- Your Ruby interpreter version.
|
12
|
+
- The statsd-instrument version. **Note:** only the latest version is supported.
|
13
|
+
- The StatsD backend you are using.
|
14
|
+
|
15
|
+
## Pull request
|
16
|
+
|
17
|
+
1. Fork the repository, and create a branch.
|
18
|
+
2. Implement the feature or bugfix, and add tests that cover the changed functionality.
|
19
|
+
3. Create a pull request. Make sure that you get Travis CI passes.
|
20
|
+
4. Ping **@jstorimer** and/or **@wvanbergen** for a code review.
|
21
|
+
|
22
|
+
Some notes:
|
23
|
+
|
24
|
+
- Make sure to follow to coding style.
|
25
|
+
- Make sure your changes are properly documented using [yardoc syntax](http://www.rubydoc.info/gems/yard/file/docs/GettingStarted.md).
|
26
|
+
- Add an entry to the "unreleased changes" section of [CHANGELOG.md](./CHANGELOG.md).
|
27
|
+
- **Do not** update `StatsD::Instrument::VERSION`. This will be done during the release prodecure.
|
28
|
+
|
29
|
+
## Release procedure
|
30
|
+
|
31
|
+
1. Update the version number in `lib/statsd/instrument/version.rb`.
|
32
|
+
2. Move the "Unreleased changes" items in [CHANGELOG.md](./CHANGELOG.md) to a new section for the release.
|
33
|
+
3. Commit these changes.
|
34
|
+
4. Run `bundle exec rake release`.
|
data/README.md
CHANGED
@@ -2,29 +2,21 @@
|
|
2
2
|
|
3
3
|
[![Built on Travis](https://secure.travis-ci.org/Shopify/statsd-instrument.png?branch=master)](https://secure.travis-ci.org/Shopify/statsd-instrument)
|
4
4
|
|
5
|
-
This is a ruby client for statsd (http://github.com/etsy/statsd). It provides a lightweight way to track and measure metrics in your application.
|
5
|
+
This is a ruby client for statsd (http://github.com/etsy/statsd). It provides a lightweight way to track and measure metrics in your application.
|
6
6
|
|
7
7
|
We call out to statsd by sending data over a UDP socket. UDP sockets are fast, but unreliable, there is no guarantee that your data will ever arrive at its location. In other words, fire and forget. This is perfect for this use case because it means your code doesn't get bogged down trying to log statistics. We send data to statsd several times per request and haven't noticed a performance hit.
|
8
8
|
|
9
|
-
|
10
|
-
|
11
|
-
For Shopify, our retention periods are:
|
12
|
-
|
13
|
-
1. 10 seconds of granularity for the last 6 hours
|
14
|
-
2. 60 seconds of granularity for the last week
|
15
|
-
3. 10 minutes of granularity for the last 5 years
|
16
|
-
|
17
|
-
This is the same as what Etsy uses (mentioned in the README for http://github.com/etsy/statsd).
|
9
|
+
For more information about StatsD, see the [README of the Etsy project](http://github.com/etsy/statsd).
|
18
10
|
|
19
11
|
## Configuration
|
20
12
|
|
21
|
-
The library comes with different backends. Based on your environment (detected using environment
|
13
|
+
The library comes with different backends. Based on your environment (detected using environment
|
22
14
|
variables), it will select one of the following backends by default:
|
23
15
|
|
24
|
-
- **Production** environment: `StatsD::Instrument::Backends::UDPBackend` will actually send UDP packets.
|
25
|
-
It will configure itself using environment variables: it uses `STATSD_ADDR` for the address to connect
|
16
|
+
- **Production** and **staging** environment: `StatsD::Instrument::Backends::UDPBackend` will actually send UDP packets.
|
17
|
+
It will configure itself using environment variables: it uses `STATSD_ADDR` for the address to connect
|
26
18
|
to (default `"localhost:8125"`), and `STATSD_IMPLEMENTATION` to set the protocol variant. (See below)
|
27
|
-
- **Test** environment: `StatsD::Instrument::Backends::NullBackend` will swallow all calls. See below for
|
19
|
+
- **Test** environment: `StatsD::Instrument::Backends::NullBackend` will swallow all calls. See below for
|
28
20
|
notes on writing tests.
|
29
21
|
- **Development**, and all other, environments: `StatsD::Instrument::Backends::LoggerBackend` will log all
|
30
22
|
calls to stdout.
|
@@ -32,7 +24,7 @@ variables), it will select one of the following backends by default:
|
|
32
24
|
You can override the currently active backend by setting `StatsD.backend`:
|
33
25
|
|
34
26
|
``` ruby
|
35
|
-
# Sets up a UDP backend. First argument is the UDP address to send StatsD packets to,
|
27
|
+
# Sets up a UDP backend. First argument is the UDP address to send StatsD packets to,
|
36
28
|
# second argument specifies the protocol variant (i.e. `:statsd`, `:statsite`, or `:datadog`).
|
37
29
|
StatsD.backend = StatsD::Instrument::Backends::UDPBackend.new("1.2.3.4:8125", :statsite)
|
38
30
|
|
@@ -44,7 +36,7 @@ The other available settings, with their default, are
|
|
44
36
|
|
45
37
|
``` ruby
|
46
38
|
# Logger to which commands are logged when using the LoggerBackend, which is
|
47
|
-
# the default in development environment. Also, any errors or warnings will
|
39
|
+
# the default in development environment. Also, any errors or warnings will
|
48
40
|
# be logged here.
|
49
41
|
StatsD.logger = defined?(Rails) ? Rails.logger : Logger.new($stderr)
|
50
42
|
|
@@ -78,7 +70,7 @@ StatsD.measure('GoogleBase.insert') do
|
|
78
70
|
GoogleBase.insert(product)
|
79
71
|
end
|
80
72
|
```
|
81
|
-
|
73
|
+
|
82
74
|
#### StatsD.increment
|
83
75
|
|
84
76
|
Lets you increment a key in statsd to keep a count of something. If the specified key doesn't exist it will create it for you.
|
@@ -206,7 +198,7 @@ StatsD.increment('my.counter', tags: ['env:production', 'unicorn'])
|
|
206
198
|
GoogleBase.statsd_count :insert, 'GoogleBase.insert', tags: ['env:production']
|
207
199
|
```
|
208
200
|
|
209
|
-
If implementation is not set to `:datadog`, tags will not be included in the UDP packets, and a
|
201
|
+
If implementation is not set to `:datadog`, tags will not be included in the UDP packets, and a
|
210
202
|
warning is logged to `StatsD.logger`.
|
211
203
|
|
212
204
|
## Testing
|
@@ -232,24 +224,24 @@ class MyTestcase < Minitest::Test
|
|
232
224
|
StatsD.increment('unrelated') # doesn't match
|
233
225
|
StatsD.increment('counter.name', sample_rate: 1.0) # matches
|
234
226
|
StatsD.increment('counter.name', sample_rate: 0.1) # matches too
|
235
|
-
end
|
227
|
+
end
|
236
228
|
end
|
237
229
|
|
238
230
|
def test_no_udp_traffic
|
239
231
|
# Verifies no StatsD calls occured at all.
|
240
232
|
assert_no_statsd_calls do
|
241
|
-
do_some_work
|
233
|
+
do_some_work
|
242
234
|
end
|
243
235
|
|
244
236
|
# Verifies no StatsD calls occured for the given metric.
|
245
237
|
assert_no_statsd_calls('metric_name') do
|
246
238
|
do_some_work
|
247
|
-
end
|
239
|
+
end
|
248
240
|
end
|
249
241
|
|
250
242
|
def test_more_complicated_stuff
|
251
243
|
# capture_statsd_calls will capture all the StatsD calls in the
|
252
|
-
# given block, and returns them as an array. You can then run your
|
244
|
+
# given block, and returns them as an array. You can then run your
|
253
245
|
# own assertions on it.
|
254
246
|
metrics = capture_statsd_calls do
|
255
247
|
StatsD.increment('mycounter', sample_rate: 0.01)
|
@@ -265,32 +257,27 @@ end
|
|
265
257
|
|
266
258
|
```
|
267
259
|
|
268
|
-
##
|
260
|
+
## Notes
|
269
261
|
|
270
|
-
|
271
|
-
the StatsD host to be a non-ip will trigger a DNS lookup (i.e. a synchronous TCP round trip).
|
272
|
-
This can be particularly problematic in clouds that have a shared DNS infrastructure such as AWS.
|
262
|
+
### Compatibility
|
273
263
|
|
274
|
-
|
264
|
+
Tested using Travis CI against Ruby 1.9.3, Ruby 2.0.0, Ruby 2.1.1, Rubinius, and JRuby
|
275
265
|
|
276
|
-
|
277
|
-
2. Hardcoding the DNS/IP pair in /etc/hosts allows the IP to change without redeploying your application but fails to scale as the number of servers increases.
|
278
|
-
3. Installing caching software such as nscd that uses the DNS TTL avoids most DNS lookups but makes the exact moment of change indeterminate.
|
266
|
+
### Reliance on DNS
|
279
267
|
|
280
|
-
|
268
|
+
Out of the box StatsD is set up to be unidirectional fire-and-forget over UDP. Configuring
|
269
|
+
the StatsD host to be a non-ip will trigger a DNS lookup (i.e. a synchronous TCP round trip).
|
270
|
+
This can be particularly problematic in clouds that have a shared DNS infrastructure such as AWS.
|
281
271
|
|
282
|
-
|
272
|
+
1. Using a hardcoded IP avoids the DNS lookup but generally requires an application deploy to change.
|
273
|
+
2. Hardcoding the DNS/IP pair in /etc/hosts allows the IP to change without redeploying your application but fails to scale as the number of servers increases.
|
274
|
+
3. Installing caching software such as nscd that uses the DNS TTL avoids most DNS lookups but makes the exact moment of change indeterminate.
|
283
275
|
|
284
|
-
* Ruby 1.9.3
|
285
|
-
* Ruby 2.0.0
|
286
|
-
* Ruby 2.1.1
|
287
276
|
|
288
|
-
##
|
277
|
+
## Links
|
289
278
|
|
290
|
-
This
|
279
|
+
This library was developed for shopify.com and is MIT licensed.
|
291
280
|
|
292
|
-
|
293
|
-
|
294
|
-
|
295
|
-
4. Create a pull request. Make sure that you get a Travis CI pass on it.
|
296
|
-
5. Ping @jstorimer and/or @wvanbergen for review.
|
281
|
+
- [API documentation](http://www.rubydoc.info/gems/statsd-instrument/frames)
|
282
|
+
- [The changelog](./CHANGELOG.md) covers the changes between releases.
|
283
|
+
- [Contributing notes](./CONTRIBUTING.md) if you are interested in contributing to this library.
|
data/lib/statsd/instrument.rb
CHANGED
@@ -1,20 +1,60 @@
|
|
1
1
|
require 'socket'
|
2
2
|
require 'logger'
|
3
3
|
|
4
|
+
# The StatsD module contains low-level metrics for collecting metrics and sending them to the backend.
|
5
|
+
#
|
6
|
+
# @!attribute backend
|
7
|
+
# The backend that is being used to emit the metrics.
|
8
|
+
# @return [StatsD::Instrument::Backend] the currently active backend. If there is no active backend
|
9
|
+
# yet, it will call {StatsD::Instrument::Environment#default_backend} to obtain a
|
10
|
+
# default backend for the environment.
|
11
|
+
# @see StatsD::Instrument::Environment#default_backend
|
12
|
+
#
|
13
|
+
# @!attribute prefix
|
14
|
+
# The prefix to apply to metric names. This can be useful to group all the metrics
|
15
|
+
# for an application in a shared StatsD server.
|
16
|
+
#
|
17
|
+
# When using a prefix a dot will be included automatically to separate the prefix
|
18
|
+
# from the metric name.
|
19
|
+
#
|
20
|
+
# @return [String, nil] The prefix, or <tt>nil</tt> when no prefix is used
|
21
|
+
# @see StatsD::Instrument::Metric#name
|
22
|
+
#
|
23
|
+
# @!attribute default_sample_rate
|
24
|
+
# The sample rate to use if the sample rate is unspecified for a metric call.
|
25
|
+
# @return [Float] Default is 1.0.
|
26
|
+
#
|
27
|
+
# @!attribute logger
|
28
|
+
# The logger to use in case of any errors. The logger is also used as default logger
|
29
|
+
# for the LoggerBackend (although this can be overwritten).
|
30
|
+
#
|
31
|
+
# @see StatsD::Instrument::Backends::LoggerBackend
|
32
|
+
# @return [Logger]
|
33
|
+
#
|
34
|
+
# @see StatsD::Instrument <tt>StatsD::Instrument</tt> contains module to instrument
|
35
|
+
# existing methods with StatsD metrics.
|
4
36
|
module StatsD
|
37
|
+
extend self
|
38
|
+
|
39
|
+
# The StatsD::Instrument module provides metaprogramming methods to instrument your methods with
|
40
|
+
# StatsD metrics. E.g., yopu can create counters on how often a method is called, how often it is
|
41
|
+
# successful, the duration of the methods call, etc.
|
5
42
|
module Instrument
|
6
43
|
|
44
|
+
# @private
|
7
45
|
def self.generate_metric_name(metric_name, callee, *args)
|
8
46
|
metric_name.respond_to?(:call) ? metric_name.call(callee, args).gsub('::', '.') : metric_name.gsub('::', '.')
|
9
47
|
end
|
10
48
|
|
11
49
|
if Process.respond_to?(:clock_gettime)
|
50
|
+
# @private
|
12
51
|
def self.duration
|
13
52
|
start = Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
14
53
|
yield
|
15
54
|
Process.clock_gettime(Process::CLOCK_MONOTONIC) - start
|
16
55
|
end
|
17
56
|
else
|
57
|
+
# @private
|
18
58
|
def self.duration
|
19
59
|
start = Time.now
|
20
60
|
yield
|
@@ -22,6 +62,13 @@ module StatsD
|
|
22
62
|
end
|
23
63
|
end
|
24
64
|
|
65
|
+
# Adds execution duration instrumentation to a method.
|
66
|
+
#
|
67
|
+
# @param method [Symbol] The name of the method to instrument.
|
68
|
+
# @param name [String, #call] The name of the metric to use. You can also pass in a
|
69
|
+
# callable to dynamically generate a metric name
|
70
|
+
# @param metric_options (see StatsD#measure)
|
71
|
+
# @return [void]
|
25
72
|
def statsd_measure(method, name, *metric_options)
|
26
73
|
add_to_method(method, name, :measure) do |old_method, new_method, metric_name, *args|
|
27
74
|
define_method(new_method) do |*args, &block|
|
@@ -30,6 +77,21 @@ module StatsD
|
|
30
77
|
end
|
31
78
|
end
|
32
79
|
|
80
|
+
# Adds success and failure counter instrumentation to a method.
|
81
|
+
#
|
82
|
+
# A method call will be considered successful if it does not raise an exception, and the result is true-y.
|
83
|
+
# For successful calls, the metric <tt>[name].success</tt> will be incremented; for failed calls, the metric
|
84
|
+
# name is <tt>[name].failure</tt>.
|
85
|
+
#
|
86
|
+
# @param method (see #statsd_measure)
|
87
|
+
# @param name (see #statsd_measure)
|
88
|
+
# @param metric_options (see #statsd_measure)
|
89
|
+
# @yield You can pass a block to this method if you want to define yourself what is a successful call
|
90
|
+
# based on the return value of the method.
|
91
|
+
# @yieldparam result The return value of the instrumented method.
|
92
|
+
# @yieldreturn [Boolean] Return true iff the return value is consisered a success, false otherwise.
|
93
|
+
# @return [void]
|
94
|
+
# @see #statsd_count_if
|
33
95
|
def statsd_count_success(method, name, *metric_options)
|
34
96
|
add_to_method(method, name, :count_success) do |old_method, new_method, metric_name|
|
35
97
|
define_method(new_method) do |*args, &block|
|
@@ -49,6 +111,19 @@ module StatsD
|
|
49
111
|
end
|
50
112
|
end
|
51
113
|
|
114
|
+
# Adds success and failure counter instrumentation to a method.
|
115
|
+
#
|
116
|
+
# A method call will be considered successful if it does not raise an exception, and the result is true-y.
|
117
|
+
# Only for successful calls, the metric will be icnremented
|
118
|
+
#
|
119
|
+
# @param method (see #statsd_measure)
|
120
|
+
# @param name (see #statsd_measure)
|
121
|
+
# @param metric_options (see #statsd_measure)
|
122
|
+
# @yield (see #statsd_count_success)
|
123
|
+
# @yieldparam result (see #statsd_count_success)
|
124
|
+
# @yieldreturn (see #statsd_count_success)
|
125
|
+
# @return [void]
|
126
|
+
# @see #statsd_count_success
|
52
127
|
def statsd_count_if(method, name, *metric_options)
|
53
128
|
add_to_method(method, name, :count_if) do |old_method, new_method, metric_name|
|
54
129
|
define_method(new_method) do |*args, &block|
|
@@ -67,6 +142,15 @@ module StatsD
|
|
67
142
|
end
|
68
143
|
end
|
69
144
|
|
145
|
+
# Adds counter instrumentation to a method.
|
146
|
+
#
|
147
|
+
# The metric will be incremented for every call of the instrumented method, no matter
|
148
|
+
# whether what the method returns, or whether it raises an exception.
|
149
|
+
#
|
150
|
+
# @param method (see #statsd_measure)
|
151
|
+
# @param name (see #statsd_measure)
|
152
|
+
# @param metric_options (see #statsd_measure)
|
153
|
+
# @return [void]
|
70
154
|
def statsd_count(method, name, *metric_options)
|
71
155
|
add_to_method(method, name, :count) do |old_method, new_method, metric_name|
|
72
156
|
define_method(new_method) do |*args, &block|
|
@@ -76,18 +160,38 @@ module StatsD
|
|
76
160
|
end
|
77
161
|
end
|
78
162
|
|
163
|
+
# Removes StatsD counter instrumentation from a method
|
164
|
+
# @param method [Symbol] The method to remove instrumentation from.
|
165
|
+
# @param name [String] The name of the metric that was used.
|
166
|
+
# @return [void]
|
167
|
+
# @see #statsd_count
|
79
168
|
def statsd_remove_count(method, name)
|
80
169
|
remove_from_method(method, name, :count)
|
81
170
|
end
|
82
171
|
|
172
|
+
# Removes StatsD conditional counter instrumentation from a method
|
173
|
+
# @param method (see #statsd_remove_count)
|
174
|
+
# @param name (see #statsd_remove_count)
|
175
|
+
# @return [void]
|
176
|
+
# @see #statsd_count_if
|
83
177
|
def statsd_remove_count_if(method, name)
|
84
178
|
remove_from_method(method, name, :count_if)
|
85
179
|
end
|
86
180
|
|
181
|
+
# Removes StatsD success counter instrumentation from a method
|
182
|
+
# @param method (see #statsd_remove_count)
|
183
|
+
# @param name (see #statsd_remove_count)
|
184
|
+
# @return [void]
|
185
|
+
# @see #statsd_count_success
|
87
186
|
def statsd_remove_count_success(method, name)
|
88
187
|
remove_from_method(method, name, :count_success)
|
89
188
|
end
|
90
189
|
|
190
|
+
# Removes StatsD measure instrumentation from a method
|
191
|
+
# @param method (see #statsd_remove_count)
|
192
|
+
# @param name (see #statsd_remove_count)
|
193
|
+
# @return [void]
|
194
|
+
# @see #statsd_measure
|
91
195
|
def statsd_remove_measure(method, name)
|
92
196
|
remove_from_method(method, name, :measure)
|
93
197
|
end
|
@@ -120,83 +224,136 @@ module StatsD
|
|
120
224
|
end
|
121
225
|
end
|
122
226
|
|
123
|
-
|
124
|
-
|
125
|
-
attr_writer :backend
|
126
|
-
|
127
|
-
def backend
|
128
|
-
@backend ||= StatsD::Instrument::Environment.default_backend
|
129
|
-
end
|
227
|
+
attr_accessor :logger, :default_sample_rate, :prefix
|
228
|
+
attr_writer :backend
|
130
229
|
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
metric_options = [value]
|
135
|
-
value = nil
|
136
|
-
end
|
230
|
+
def backend
|
231
|
+
@backend ||= StatsD::Instrument::Environment.default_backend
|
232
|
+
end
|
137
233
|
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
234
|
+
# Emits a duration metric.
|
235
|
+
#
|
236
|
+
# @overload measure(key, value, metric_options = {})
|
237
|
+
# Emits a measure metric, by providing a duration in milliseconds.
|
238
|
+
# @param key [String] The name of the metric.
|
239
|
+
# @param value [Float] The measured duration in milliseconds
|
240
|
+
# @param metric_options [Hash] Options for the metric
|
241
|
+
# @return [StatsD::Instrument::Metric] The metric that was sent to the backend.
|
242
|
+
#
|
243
|
+
# @overload measure(key, metric_options = {}, &block)
|
244
|
+
# Emits a measure metric, after measuring the execution duration of the
|
245
|
+
# block passed to this method.
|
246
|
+
# @param key [String] The name of the metric.
|
247
|
+
# @param metric_options [Hash] Options for the metric
|
248
|
+
# @yield The method will yield the block that was passed to this emthod to measure its duration.
|
249
|
+
# @return The value that was returns by the block passed to this method.
|
250
|
+
#
|
251
|
+
# @example
|
252
|
+
# http_response = StatsD.measure('HTTP.call.duration') do
|
253
|
+
# HTTP.get(url)
|
254
|
+
# end
|
255
|
+
def measure(key, value = nil, *metric_options, &block)
|
256
|
+
if value.is_a?(Hash) && metric_options.empty?
|
257
|
+
metric_options = [value]
|
258
|
+
value = nil
|
143
259
|
end
|
144
260
|
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
261
|
+
result = nil
|
262
|
+
value = 1000 * StatsD::Instrument.duration { result = block.call } if block_given?
|
263
|
+
metric = collect_metric(hash_argument(metric_options).merge(type: :ms, name: key, value: value))
|
264
|
+
result = metric unless block_given?
|
265
|
+
result
|
266
|
+
end
|
151
267
|
|
152
|
-
|
268
|
+
# Emits a counter metric.
|
269
|
+
# @param key [String] The name of the metric.
|
270
|
+
# @param value [Integer] The value to increment the counter by.
|
271
|
+
#
|
272
|
+
# You should not compensate for the sample rate using the counter increment. E.g., if
|
273
|
+
# your sample rate is 0.01, you should <b>not</b> use 100 as increment to compensate for it.
|
274
|
+
# The sample rate is part of the packet that is being sent to the server, and the server
|
275
|
+
# should know how to handle it.
|
276
|
+
#
|
277
|
+
# @param metric_options [Hash] (default: {}) Metric options
|
278
|
+
# @return (see #collect_metric)
|
279
|
+
def increment(key, value = 1, *metric_options)
|
280
|
+
if value.is_a?(Hash) && metric_options.empty?
|
281
|
+
metric_options = [value]
|
282
|
+
value = 1
|
153
283
|
end
|
154
284
|
|
155
|
-
|
156
|
-
|
157
|
-
def gauge(key, value, *metric_options)
|
158
|
-
collect_metric(hash_argument(metric_options).merge(type: :g, name: key, value: value))
|
159
|
-
end
|
285
|
+
collect_metric(hash_argument(metric_options).merge(type: :c, name: key, value: value))
|
286
|
+
end
|
160
287
|
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
288
|
+
# Emits a gauge metric.
|
289
|
+
# @param key [String] The name of the metric.
|
290
|
+
# @param value [Numeric] The current value to record.
|
291
|
+
# @param metric_options [Hash] (default: {}) Metric options
|
292
|
+
# @return (see #collect_metric)
|
293
|
+
def gauge(key, value, *metric_options)
|
294
|
+
collect_metric(hash_argument(metric_options).merge(type: :g, name: key, value: value))
|
295
|
+
end
|
165
296
|
|
166
|
-
|
167
|
-
|
168
|
-
|
297
|
+
# Emits a histogram metric.
|
298
|
+
# @param key [String] The name of the metric.
|
299
|
+
# @param value [Numeric] The value to record.
|
300
|
+
# @param metric_options [Hash] (default: {}) Metric options
|
301
|
+
# @return (see #collect_metric)
|
302
|
+
# @note Supported by the datadog implementation only.
|
303
|
+
def histogram(key, value, *metric_options)
|
304
|
+
collect_metric(hash_argument(metric_options).merge(type: :h, name: key, value: value))
|
305
|
+
end
|
169
306
|
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
307
|
+
# Emits a key/value metric.
|
308
|
+
# @param key [String] The name of the metric.
|
309
|
+
# @param value [Numeric] The value to record.
|
310
|
+
# @param metric_options [Hash] (default: {}) Metric options
|
311
|
+
# @return (see #collect_metric)
|
312
|
+
# @note Supported by the statsite implementation only.
|
313
|
+
def key_value(key, value, *metric_options)
|
314
|
+
collect_metric(hash_argument(metric_options).merge(type: :kv, name: key, value: value))
|
315
|
+
end
|
174
316
|
|
175
|
-
|
317
|
+
# Emits a set metric.
|
318
|
+
# @param key [String] The name of the metric.
|
319
|
+
# @param value [Numeric] The value to record.
|
320
|
+
# @param metric_options [Hash] (default: {}) Metric options
|
321
|
+
# @return (see #collect_metric)
|
322
|
+
# @note Supported by the datadog implementation only.
|
323
|
+
def set(key, value, *metric_options)
|
324
|
+
collect_metric(hash_argument(metric_options).merge(type: :s, name: key, value: value))
|
325
|
+
end
|
176
326
|
|
177
|
-
|
178
|
-
return {} if args.length == 0
|
179
|
-
return args.first if args.length == 1 && args.first.is_a?(Hash)
|
327
|
+
private
|
180
328
|
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
|
329
|
+
# Converts old-style ordered arguments in an argument hash for backwards compatibility.
|
330
|
+
# @param args [Array] The list of non-required arguments.
|
331
|
+
# @return [Hash] The hash of optional arguments.
|
332
|
+
def hash_argument(args)
|
333
|
+
return {} if args.length == 0
|
334
|
+
return args.first if args.length == 1 && args.first.is_a?(Hash)
|
186
335
|
|
187
|
-
|
336
|
+
order = [:sample_rate, :tags]
|
337
|
+
hash = {}
|
338
|
+
args.each_with_index do |value, index|
|
339
|
+
hash[order[index]] = value
|
188
340
|
end
|
189
341
|
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
|
342
|
+
return hash
|
343
|
+
end
|
344
|
+
|
345
|
+
# Instantiates a metric, and sends it to the backend for further processing.
|
346
|
+
# @param options (see StatsD::Instrument::Metric#initialize)
|
347
|
+
# @return [StatsD::Instrument::Metric] The meric that was sent to the backend.
|
348
|
+
def collect_metric(options)
|
349
|
+
backend.collect_metric(metric = StatsD::Instrument::Metric.new(options))
|
350
|
+
metric
|
194
351
|
end
|
195
352
|
end
|
196
353
|
|
354
|
+
require 'statsd/instrument/version'
|
197
355
|
require 'statsd/instrument/metric'
|
198
356
|
require 'statsd/instrument/backend'
|
199
357
|
require 'statsd/instrument/assertions'
|
200
358
|
require 'statsd/instrument/environment'
|
201
|
-
require 'statsd/instrument/version'
|
202
359
|
require 'statsd/instrument/railtie' if defined?(Rails)
|
@@ -1,4 +1,11 @@
|
|
1
|
+
# This abstract class specifies the interface a backend implementation should conform to.
|
2
|
+
# @abstract
|
1
3
|
class StatsD::Instrument::Backend
|
4
|
+
|
5
|
+
# Collects a metric.
|
6
|
+
#
|
7
|
+
# @param metric [StatsD::Instrument::Metric] The metric to collect
|
8
|
+
# @return [void]
|
2
9
|
def collect_metric(metric)
|
3
10
|
raise NotImplementedError, "Use a concerete backend implementation"
|
4
11
|
end
|
@@ -1,4 +1,11 @@
|
|
1
1
|
module StatsD::Instrument::Backends
|
2
|
+
|
3
|
+
# The capture backend is used to capture the metrics that are collected, so you can
|
4
|
+
# run assertions on them.
|
5
|
+
#
|
6
|
+
# @!attribute collected_metrics [r]
|
7
|
+
# @return [Array<StatsD::Instrument::Metric>] The list of metrics that were collected.
|
8
|
+
# @see StatsD::Instrument::Assertions
|
2
9
|
class CaptureBackend < StatsD::Instrument::Backend
|
3
10
|
attr_reader :collected_metrics
|
4
11
|
|
@@ -6,10 +13,15 @@ module StatsD::Instrument::Backends
|
|
6
13
|
reset
|
7
14
|
end
|
8
15
|
|
16
|
+
# Adds a metric to the ist of collected metrics.
|
17
|
+
# @param metric [StatsD::Instrument::Metric] The metric to collect.
|
18
|
+
# @return [void]
|
9
19
|
def collect_metric(metric)
|
10
20
|
@collected_metrics << metric
|
11
21
|
end
|
12
22
|
|
23
|
+
# Resets the list of collected metrics to an empty list.
|
24
|
+
# @return [void]
|
13
25
|
def reset
|
14
26
|
@collected_metrics = []
|
15
27
|
end
|
@@ -1,12 +1,18 @@
|
|
1
1
|
module StatsD::Instrument::Backends
|
2
|
+
|
3
|
+
# The logger backend simply logs every metric to a logger
|
4
|
+
# @!attribute logger
|
5
|
+
# @return [Logger]
|
2
6
|
class LoggerBackend < StatsD::Instrument::Backend
|
3
|
-
|
7
|
+
|
4
8
|
attr_accessor :logger
|
5
|
-
|
9
|
+
|
6
10
|
def initialize(logger)
|
7
11
|
@logger = logger
|
8
12
|
end
|
9
13
|
|
14
|
+
# @param metric [StatsD::Instrument::Metric]
|
15
|
+
# @return [void]
|
10
16
|
def collect_metric(metric)
|
11
17
|
logger.info "[StatsD] #{metric}"
|
12
18
|
end
|
@@ -1,11 +1,17 @@
|
|
1
1
|
require 'logger'
|
2
2
|
|
3
|
+
# The environment module is used to detect, and initialize the environment in
|
4
|
+
# which this library is active. It will use different default values based on the environment.
|
3
5
|
module StatsD::Instrument::Environment
|
4
6
|
extend self
|
5
7
|
|
8
|
+
# Instantiates a default backend for the current environment.
|
9
|
+
#
|
10
|
+
# @return [StatsD::Instrument::Backend]
|
11
|
+
# @see #environment
|
6
12
|
def default_backend
|
7
13
|
case environment
|
8
|
-
when 'production'
|
14
|
+
when 'production', 'staging'
|
9
15
|
StatsD::Instrument::Backends::UDPBackend.new(ENV['STATSD_ADDR'], ENV['STATSD_IMPLEMENTATION'])
|
10
16
|
when 'test'
|
11
17
|
StatsD::Instrument::Backends::NullBackend.new
|
@@ -14,14 +20,35 @@ module StatsD::Instrument::Environment
|
|
14
20
|
end
|
15
21
|
end
|
16
22
|
|
23
|
+
# Detects the current environment, either by asking Rails, or by inspecting environment variables.
|
24
|
+
#
|
25
|
+
# - Within a Rails application, <tt>Rails.env</tt> is used.
|
26
|
+
# - It will check the following environment variables in order: <tt>RAILS_ENV</tt>, <tt>RACK_ENV</tt>, <tt>ENV</tt>.
|
27
|
+
# - If none of these are set, it will return <tt>development</tt>
|
28
|
+
#
|
29
|
+
# @return [String] The detected environment.
|
17
30
|
def environment
|
18
31
|
if defined?(Rails)
|
19
32
|
Rails.env.to_s
|
20
33
|
else
|
21
34
|
ENV['RAILS_ENV'] || ENV['RACK_ENV'] || ENV['ENV'] || 'development'
|
22
35
|
end
|
23
|
-
end
|
36
|
+
end
|
37
|
+
|
38
|
+
# Sets default values for sample rate and logger.
|
39
|
+
#
|
40
|
+
# - Default sample rate is set to the value in the STATSD_SAMPLE_RATE environment variable,
|
41
|
+
# or 1.0 otherwise. See {StatsD#default_sample_rate}
|
42
|
+
# - {StatsD#logger} is set to a logger that send output to stderr.
|
43
|
+
#
|
44
|
+
# If you are including this library inside a Rails environment, additional initialization will
|
45
|
+
# be done as part of the {StatsD::Instrument::Railtie}.
|
46
|
+
#
|
47
|
+
# @return [void]
|
48
|
+
def setup
|
49
|
+
StatsD.default_sample_rate = ENV.fetch('STATSD_SAMPLE_RATE', 1.0).to_f
|
50
|
+
StatsD.logger = Logger.new($stderr)
|
51
|
+
end
|
24
52
|
end
|
25
53
|
|
26
|
-
StatsD.
|
27
|
-
StatsD.logger = Logger.new($stderr)
|
54
|
+
StatsD::Instrument::Environment.setup
|
@@ -1,7 +1,48 @@
|
|
1
|
+
# The Metric class represents a metric sample to be send by a backend.
|
2
|
+
#
|
3
|
+
# @!attribute type
|
4
|
+
# @return [Symbol] The metric type. Must be one of {StatsD::Instrument::Metric::TYPES}
|
5
|
+
# @!attribute name
|
6
|
+
# @return [String] The name of the metric. {StatsD#prefix} will automatically be applied
|
7
|
+
# to the metric in the constructor, unless the <tt>:no_prefix</tt> option is set.
|
8
|
+
# @!attribute value
|
9
|
+
# @see #default_value
|
10
|
+
# @return [Numeric, String] The value to collect for the metric. Depending on the metric
|
11
|
+
# type, <tt>value</tt> can be a string, integer, or float.
|
12
|
+
# @!attribute sample_rate
|
13
|
+
# The sample rate to use for the metric. How the sample rate is handled differs per backend.
|
14
|
+
# The UDP backend will actually sample metric submissions based on the sample rate, while
|
15
|
+
# the logger backend will just include the sample rate in its output for debugging purposes.
|
16
|
+
# @see StatsD#default_sample_rate
|
17
|
+
# @return [Float] The sample rate to use for this metric. This should be a value between
|
18
|
+
# 0 and 1. If not set, it will use the default sample rate set to {StatsD#default_sample_rate}.
|
19
|
+
# @!attribute tags
|
20
|
+
# The tags to associate with the metric.
|
21
|
+
# @note Only the Datadog implementation supports tags.
|
22
|
+
# @see .normalize_tags
|
23
|
+
# @return [Array<String>, Hash<String, String>, nil] the tags to associate with the metric.
|
24
|
+
# You can either specify the tags as an array of strings, or a Hash of key/value pairs.
|
25
|
+
#
|
26
|
+
# @see StatsD The StatsD module contains methods that generate metric instances.
|
27
|
+
# @see StatsD::Instrument::Backend A StatsD::Instrument::Backend is used to collect metrics.
|
28
|
+
#
|
1
29
|
class StatsD::Instrument::Metric
|
2
30
|
|
3
31
|
attr_accessor :type, :name, :value, :sample_rate, :tags
|
4
32
|
|
33
|
+
# Initializes a new metric instance.
|
34
|
+
# Normally, you don't want to call this method directly, but use one of the metric collection
|
35
|
+
# methods on the {StatsD} module.
|
36
|
+
#
|
37
|
+
# @option options [Symbol] :type The type of the metric.
|
38
|
+
# @option options [String] :name The name of the metric without prefix.
|
39
|
+
# @option options [Boolean] :no_prefix Set to <tt>true</tt> if you don't want to apply {StatsD#prefix}
|
40
|
+
# @option options [Numeric, String, nil] :value The value to collect for the metric. If set to
|
41
|
+
# <tt>nil>/tt>, {#default_value} will be used.
|
42
|
+
# @option options [Numeric, nil] :sample_rate The sample rate to use. If not set, it will use
|
43
|
+
# {StatsD#default_sample_rate}.
|
44
|
+
# @option options [Array<String>, Hash<String, String>, nil] :tags The tags to apply to this metric.
|
45
|
+
# See {.normalize_tags} for more information.
|
5
46
|
def initialize(options = {})
|
6
47
|
@type = options[:type] or raise ArgumentError, "Metric :type is required."
|
7
48
|
@name = options[:name] or raise ArgumentError, "Metric :name is required."
|
@@ -12,6 +53,13 @@ class StatsD::Instrument::Metric
|
|
12
53
|
@tags = StatsD::Instrument::Metric.normalize_tags(options[:tags])
|
13
54
|
end
|
14
55
|
|
56
|
+
# The default value for this metric, which will be used if it is not set.
|
57
|
+
#
|
58
|
+
# A default value is only defined for counter metrics (<tt>1</tt>). For all other
|
59
|
+
# metric types, this emthod will raise an <tt>ArgumentError</tt>.
|
60
|
+
#
|
61
|
+
# @return [Numeric, String] The default value for this metric.
|
62
|
+
# @raise ArgumentError if the metric type doesn't have a default value
|
15
63
|
def default_value
|
16
64
|
case type
|
17
65
|
when :c; 1
|
@@ -19,6 +67,8 @@ class StatsD::Instrument::Metric
|
|
19
67
|
end
|
20
68
|
end
|
21
69
|
|
70
|
+
# @private
|
71
|
+
# @return [String]
|
22
72
|
def to_s
|
23
73
|
str = "#{TYPES[type]} #{name}:#{value}"
|
24
74
|
str << " @#{sample_rate}" if sample_rate != 1.0
|
@@ -26,10 +76,14 @@ class StatsD::Instrument::Metric
|
|
26
76
|
str
|
27
77
|
end
|
28
78
|
|
79
|
+
# @private
|
80
|
+
# @return [String]
|
29
81
|
def inspect
|
30
82
|
"#<StatsD::Instrument::Metric #{self.to_s}>"
|
31
83
|
end
|
32
84
|
|
85
|
+
# The metric types that are supported by this library. Note that every StatsD server
|
86
|
+
# implementation only supports a subset of them.
|
33
87
|
TYPES = {
|
34
88
|
c: 'increment',
|
35
89
|
ms: 'measure',
|
@@ -39,12 +93,19 @@ class StatsD::Instrument::Metric
|
|
39
93
|
s: 'set',
|
40
94
|
}
|
41
95
|
|
96
|
+
# Utility function to convert tags to the canonical form.
|
97
|
+
#
|
98
|
+
# - Tags specified as key value pairs will be converted into an array
|
99
|
+
# - Tags are normalized to only use word characters and underscores.
|
100
|
+
#
|
101
|
+
# @param tags [Array<String>, Hash<String, String>, nil] Tags specified in any form.
|
102
|
+
# @return [Array<String>, nil] the list of tags in canonical form.
|
42
103
|
def self.normalize_tags(tags)
|
43
104
|
return if tags.nil?
|
44
105
|
tags = tags.map { |k, v| "#{k}:#{v}" } if tags.is_a?(Hash)
|
45
|
-
tags.map do |tag|
|
106
|
+
tags.map do |tag|
|
46
107
|
components = tag.split(':', 2)
|
47
108
|
components.map { |c| c.gsub(/[^\w\.-]+/, '_') }.join(':')
|
48
109
|
end
|
49
110
|
end
|
50
|
-
end
|
111
|
+
end
|
@@ -1,6 +1,14 @@
|
|
1
|
+
# This Railtie runs some initializers that will set the logger to <tt>Rails#logger</tt>,
|
2
|
+
# and will initialize the {StatsD#backend} based on the Rails environment.
|
3
|
+
#
|
4
|
+
# @see StatsD::Instrument::Environment
|
1
5
|
class StatsD::Instrument::Railtie < Rails::Railtie
|
2
6
|
|
3
|
-
initializer 'statsd-instrument.
|
4
|
-
StatsD.logger = Rails.logger
|
7
|
+
initializer 'statsd-instrument.use_rails_logger' do
|
8
|
+
::StatsD.logger = Rails.logger
|
9
|
+
end
|
10
|
+
|
11
|
+
initializer 'statsd-instrument.setup_backend', after: 'statsd-instrument.use_rails_logger' do
|
12
|
+
::StatsD.backend = ::StatsD::Instrument::Environment.default_backend
|
5
13
|
end
|
6
14
|
end
|
data/statsd-instrument.gemspec
CHANGED
@@ -6,11 +6,11 @@ require 'statsd/instrument/version'
|
|
6
6
|
Gem::Specification.new do |spec|
|
7
7
|
spec.name = "statsd-instrument"
|
8
8
|
spec.version = StatsD::Instrument::VERSION
|
9
|
-
spec.authors = ["Jesse Storimer", "Tobias Lutke"]
|
9
|
+
spec.authors = ["Jesse Storimer", "Tobias Lutke", "Willem van Bergen"]
|
10
10
|
spec.email = ["jesse@shopify.com"]
|
11
11
|
spec.homepage = "https://github.com/Shopify/statsd-instrument"
|
12
12
|
spec.summary = %q{A StatsD client for Ruby apps}
|
13
|
-
spec.description = %q{A StatsD client for Ruby
|
13
|
+
spec.description = %q{A StatsD client for Ruby apps. Provides metaprogramming methods to inject StatsD instrumentation into your code.}
|
14
14
|
spec.license = "MIT"
|
15
15
|
|
16
16
|
spec.files = `git ls-files`.split($/)
|
@@ -21,4 +21,5 @@ Gem::Specification.new do |spec|
|
|
21
21
|
spec.add_development_dependency 'rake'
|
22
22
|
spec.add_development_dependency 'minitest'
|
23
23
|
spec.add_development_dependency 'mocha'
|
24
|
+
spec.add_development_dependency 'yard'
|
24
25
|
end
|
metadata
CHANGED
@@ -1,15 +1,16 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: statsd-instrument
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 2.0.
|
4
|
+
version: 2.0.6
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Jesse Storimer
|
8
8
|
- Tobias Lutke
|
9
|
+
- Willem van Bergen
|
9
10
|
autorequire:
|
10
11
|
bindir: bin
|
11
12
|
cert_chain: []
|
12
|
-
date:
|
13
|
+
date: 2015-02-02 00:00:00.000000000 Z
|
13
14
|
dependencies:
|
14
15
|
- !ruby/object:Gem::Dependency
|
15
16
|
name: rake
|
@@ -53,8 +54,22 @@ dependencies:
|
|
53
54
|
- - '>='
|
54
55
|
- !ruby/object:Gem::Version
|
55
56
|
version: '0'
|
56
|
-
|
57
|
-
|
57
|
+
- !ruby/object:Gem::Dependency
|
58
|
+
name: yard
|
59
|
+
requirement: !ruby/object:Gem::Requirement
|
60
|
+
requirements:
|
61
|
+
- - '>='
|
62
|
+
- !ruby/object:Gem::Version
|
63
|
+
version: '0'
|
64
|
+
type: :development
|
65
|
+
prerelease: false
|
66
|
+
version_requirements: !ruby/object:Gem::Requirement
|
67
|
+
requirements:
|
68
|
+
- - '>='
|
69
|
+
- !ruby/object:Gem::Version
|
70
|
+
version: '0'
|
71
|
+
description: A StatsD client for Ruby apps. Provides metaprogramming methods to inject
|
72
|
+
StatsD instrumentation into your code.
|
58
73
|
email:
|
59
74
|
- jesse@shopify.com
|
60
75
|
executables: []
|
@@ -63,6 +78,8 @@ extra_rdoc_files: []
|
|
63
78
|
files:
|
64
79
|
- .gitignore
|
65
80
|
- .travis.yml
|
81
|
+
- CHANGELOG.md
|
82
|
+
- CONTRIBUTING.md
|
66
83
|
- Gemfile
|
67
84
|
- LICENSE
|
68
85
|
- README.md
|
@@ -126,3 +143,4 @@ test_files:
|
|
126
143
|
- test/statsd_test.rb
|
127
144
|
- test/test_helper.rb
|
128
145
|
- test/udp_backend_test.rb
|
146
|
+
has_rdoc:
|