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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 4676e97c70d4ea8a412d1835d5b341e86f7eb056
4
- data.tar.gz: 9f3cb9257f3d0e6c3edc152dd45f53e30ad2301c
3
+ metadata.gz: 852b64cbebc0760778a4d3332d4611f4652e64bd
4
+ data.tar.gz: fb0025c42ec04d18f7bdd1553d48dd75656e083f
5
5
  SHA512:
6
- metadata.gz: 272ec7539100efa9e4572114e20f36b6584f3a4a604b4574e7cf3b496c13a125691f102af649d8279317e86bd4758adacabc68b4d5a3c8949149ae18292747fa
7
- data.tar.gz: 97fa56c8eff9da0754b0f1ff437c27b365ab701cb3894023caa3eefbbb001a0a66d38d69a8cf52fd46a2061a882e221c71b33e2eb3abe9cdd6bb87b658047a40
6
+ metadata.gz: 58f1b9bf3fee6706e3bc543d4f0146dda3217c0dab443a624d9700f02083917d6a76169dbf0cd1d9372ed8f36ff9b51a9bfbf6cb121f62fa8c4e0798b48f86cc
7
+ data.tar.gz: f9a3abf6573ac684c6573c64dec24108ab0e6c56227ced05a6fa167dc0e4c4d0245b83dcdd53f2de2666e9138096b3668ed1f86529937ae06a70833e57849dc5
data/.gitignore CHANGED
@@ -1,3 +1,5 @@
1
+ .yardoc
2
+ doc
1
3
  *.gem
2
4
  .bundle
3
5
  Gemfile.lock
data/.travis.yml CHANGED
@@ -4,6 +4,11 @@ rvm:
4
4
  - 2.0.0
5
5
  - 2.1.1
6
6
  - ruby-head
7
+ - jruby-19mode
8
+ - rbx-2
9
+
7
10
  matrix:
8
11
  allow_failures:
9
12
  - rvm: ruby-head
13
+
14
+ sudo: false
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
- The fact that all of your stats data may not make it into statsd is no issue. Graphite (the graph database that statsd is built on) will only show you trends in your data. Internally it only keeps enough data to satisfy the levels of granularity we specify. As well as satisfying its requirement as a fixed size database. We can throw as much data at it as we want it and it will do its best to show us the trends over time and get rid of the fluff.
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
- ## Reliance on DNS
260
+ ## Notes
269
261
 
270
- Out of the box StatsD is set up to be unidirectional fire-and-forget over UDP. Configuring
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
- ### Common Workarounds
264
+ Tested using Travis CI against Ruby 1.9.3, Ruby 2.0.0, Ruby 2.1.1, Rubinius, and JRuby
275
265
 
276
- 1. Using an IP avoids the DNS lookup but generally requires an application deploy to change.
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
- ## Compatibility
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
- Tested on several Ruby versions using Travis CI:
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
- ## Contributing
277
+ ## Links
289
278
 
290
- This project is MIT licensed and welcomes outside contributions.
279
+ This library was developed for shopify.com and is MIT licensed.
291
280
 
292
- 1. Fork the repository, and create a feature branch.
293
- 2. Implement the feature, and add tests that cover the new changes functionality.
294
- 3. Update the README.
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.
@@ -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
- class << self
124
- attr_accessor :logger, :default_sample_rate, :prefix
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
- # glork:320|ms
132
- def measure(key, value = nil, *metric_options, &block)
133
- if value.is_a?(Hash) && metric_options.empty?
134
- metric_options = [value]
135
- value = nil
136
- end
230
+ def backend
231
+ @backend ||= StatsD::Instrument::Environment.default_backend
232
+ end
137
233
 
138
- result = nil
139
- value = 1000 * StatsD::Instrument.duration { result = block.call } if block_given?
140
- metric = collect_metric(hash_argument(metric_options).merge(type: :ms, name: key, value: value))
141
- result = metric unless block_given?
142
- result
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
- # gorets:1|c
146
- def increment(key, value = 1, *metric_options)
147
- if value.is_a?(Hash) && metric_options.empty?
148
- metric_options = [value]
149
- value = 1
150
- end
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
- collect_metric(hash_argument(metric_options).merge(type: :c, name: key, value: value))
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
- # gaugor:333|g
156
- # guagor:1234|kv|@1339864935 (statsite)
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
- # histogram:123.45|h
162
- def histogram(key, value, *metric_options)
163
- collect_metric(hash_argument(metric_options).merge(type: :h, name: key, value: value))
164
- end
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
- def key_value(key, value, *metric_options)
167
- collect_metric(hash_argument(metric_options).merge(type: :kv, name: key, value: value))
168
- end
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
- # uniques:765|s
171
- def set(key, value, *metric_options)
172
- collect_metric(hash_argument(metric_options).merge(type: :s, name: key, value: value))
173
- end
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
- private
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
- def hash_argument(args)
178
- return {} if args.length == 0
179
- return args.first if args.length == 1 && args.first.is_a?(Hash)
327
+ private
180
328
 
181
- order = [:sample_rate, :tags]
182
- hash = {}
183
- args.each_with_index do |value, index|
184
- hash[order[index]] = value
185
- end
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
- return hash
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
- def collect_metric(options)
191
- backend.collect_metric(metric = StatsD::Instrument::Metric.new(options))
192
- metric
193
- end
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,4 +1,5 @@
1
1
  module StatsD::Instrument::Backends
2
+ # The null backend does nothing when receiving a metric, effectively disabling the gem completely.
2
3
  class NullBackend < StatsD::Instrument::Backend
3
4
  def collect_metric(metric)
4
5
  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.default_sample_rate = ENV.fetch('STATSD_SAMPLE_RATE', 1.0).to_f
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.railtie' do
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
@@ -1,5 +1,5 @@
1
1
  module StatsD
2
2
  module Instrument
3
- VERSION = "2.0.5"
3
+ VERSION = "2.0.6"
4
4
  end
5
5
  end
@@ -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 appspec. Provides metaprogramming methods to inject StatsD instrumentation into your code.}
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.5
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: 2014-10-08 00:00:00.000000000 Z
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
- description: A StatsD client for Ruby appspec. Provides metaprogramming methods to
57
- inject StatsD instrumentation into your code.
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: