statsd-instrument 2.3.2 → 2.6.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (87) hide show
  1. checksums.yaml +4 -4
  2. data/.github/CODEOWNERS +1 -0
  3. data/.github/workflows/benchmark.yml +32 -0
  4. data/.github/workflows/ci.yml +47 -0
  5. data/.gitignore +1 -0
  6. data/.rubocop-https---shopify-github-io-ruby-style-guide-rubocop-yml +1027 -0
  7. data/.rubocop.yml +50 -0
  8. data/.yardopts +5 -0
  9. data/CHANGELOG.md +288 -2
  10. data/CONTRIBUTING.md +28 -6
  11. data/Gemfile +5 -0
  12. data/README.md +54 -46
  13. data/Rakefile +4 -2
  14. data/benchmark/README.md +29 -0
  15. data/benchmark/datagram-client +41 -0
  16. data/benchmark/send-metrics-to-dev-null-log +47 -0
  17. data/benchmark/send-metrics-to-local-udp-receiver +57 -0
  18. data/lib/statsd/instrument/assertions.rb +179 -30
  19. data/lib/statsd/instrument/backend.rb +3 -2
  20. data/lib/statsd/instrument/backends/capture_backend.rb +4 -1
  21. data/lib/statsd/instrument/backends/logger_backend.rb +3 -3
  22. data/lib/statsd/instrument/backends/null_backend.rb +2 -0
  23. data/lib/statsd/instrument/backends/udp_backend.rb +39 -45
  24. data/lib/statsd/instrument/capture_sink.rb +27 -0
  25. data/lib/statsd/instrument/client.rb +313 -0
  26. data/lib/statsd/instrument/datagram.rb +75 -0
  27. data/lib/statsd/instrument/datagram_builder.rb +101 -0
  28. data/lib/statsd/instrument/dogstatsd_datagram_builder.rb +71 -0
  29. data/lib/statsd/instrument/environment.rb +108 -29
  30. data/lib/statsd/instrument/helpers.rb +16 -8
  31. data/lib/statsd/instrument/log_sink.rb +24 -0
  32. data/lib/statsd/instrument/matchers.rb +14 -11
  33. data/lib/statsd/instrument/metric.rb +72 -45
  34. data/lib/statsd/instrument/metric_expectation.rb +32 -18
  35. data/lib/statsd/instrument/null_sink.rb +13 -0
  36. data/lib/statsd/instrument/railtie.rb +2 -1
  37. data/lib/statsd/instrument/rubocop/measure_as_dist_argument.rb +39 -0
  38. data/lib/statsd/instrument/rubocop/metaprogramming_positional_arguments.rb +42 -0
  39. data/lib/statsd/instrument/rubocop/metric_prefix_argument.rb +37 -0
  40. data/lib/statsd/instrument/rubocop/metric_return_value.rb +32 -0
  41. data/lib/statsd/instrument/rubocop/metric_value_keyword_argument.rb +36 -0
  42. data/lib/statsd/instrument/rubocop/positional_arguments.rb +99 -0
  43. data/lib/statsd/instrument/rubocop/splat_arguments.rb +31 -0
  44. data/lib/statsd/instrument/rubocop.rb +64 -0
  45. data/lib/statsd/instrument/statsd_datagram_builder.rb +14 -0
  46. data/lib/statsd/instrument/strict.rb +235 -0
  47. data/lib/statsd/instrument/udp_sink.rb +62 -0
  48. data/lib/statsd/instrument/version.rb +3 -1
  49. data/lib/statsd/instrument.rb +340 -163
  50. data/lib/statsd-instrument.rb +2 -0
  51. data/statsd-instrument.gemspec +13 -10
  52. data/test/assertions_test.rb +167 -156
  53. data/test/benchmark/clock_gettime.rb +27 -0
  54. data/test/benchmark/default_tags.rb +47 -0
  55. data/test/benchmark/metrics.rb +9 -8
  56. data/test/benchmark/tags.rb +5 -3
  57. data/test/capture_backend_test.rb +4 -2
  58. data/test/capture_sink_test.rb +44 -0
  59. data/test/client_test.rb +164 -0
  60. data/test/compatibility/dogstatsd_datagram_compatibility_test.rb +162 -0
  61. data/test/datagram_builder_test.rb +120 -0
  62. data/test/deprecations_test.rb +132 -0
  63. data/test/dogstatsd_datagram_builder_test.rb +32 -0
  64. data/test/environment_test.rb +75 -8
  65. data/test/helpers/rubocop_helper.rb +47 -0
  66. data/test/helpers_test.rb +2 -1
  67. data/test/integration_test.rb +31 -7
  68. data/test/log_sink_test.rb +37 -0
  69. data/test/logger_backend_test.rb +10 -8
  70. data/test/matchers_test.rb +42 -28
  71. data/test/metric_test.rb +18 -22
  72. data/test/null_sink_test.rb +13 -0
  73. data/test/rubocop/measure_as_dist_argument_test.rb +44 -0
  74. data/test/rubocop/metaprogramming_positional_arguments_test.rb +58 -0
  75. data/test/rubocop/metric_prefix_argument_test.rb +38 -0
  76. data/test/rubocop/metric_return_value_test.rb +78 -0
  77. data/test/rubocop/metric_value_keyword_argument_test.rb +39 -0
  78. data/test/rubocop/positional_arguments_test.rb +110 -0
  79. data/test/rubocop/splat_arguments_test.rb +27 -0
  80. data/test/statsd_datagram_builder_test.rb +22 -0
  81. data/test/statsd_instrumentation_test.rb +109 -100
  82. data/test/statsd_test.rb +113 -79
  83. data/test/test_helper.rb +12 -1
  84. data/test/udp_backend_test.rb +38 -36
  85. data/test/udp_sink_test.rb +85 -0
  86. metadata +85 -5
  87. data/.travis.yml +0 -12
data/.rubocop.yml ADDED
@@ -0,0 +1,50 @@
1
+ inherit_from:
2
+ - https://shopify.github.io/ruby-style-guide/rubocop.yml
3
+
4
+ require:
5
+ - ./lib/statsd/instrument/rubocop.rb
6
+
7
+ AllCops:
8
+ TargetRubyVersion: 2.3
9
+ UseCache: true
10
+ CacheRootDirectory: tmp/rubocop
11
+ Exclude:
12
+ - statsd-instrument.gemspec
13
+
14
+ Naming/FileName:
15
+ Enabled: true
16
+ Exclude:
17
+ - lib/statsd-instrument.rb
18
+
19
+ Style/ClassAndModuleChildren:
20
+ Enabled: false # TODO: enable later
21
+
22
+
23
+ Style/MethodCallWithArgsParentheses:
24
+ Enabled: false # TODO: enable later
25
+
26
+ Lint/UnusedMethodArgument:
27
+ AllowUnusedKeywordArguments: true
28
+
29
+ # Enable our own cops on our own repo
30
+
31
+ StatsD/MetricReturnValue:
32
+ Enabled: true
33
+
34
+ StatsD/MetricValueKeywordArgument:
35
+ Enabled: true
36
+
37
+ StatsD/PositionalArguments:
38
+ Enabled: true
39
+
40
+ StatsD/SplatArguments:
41
+ Enabled: true
42
+
43
+ StatsD/MetaprogrammingPositionalArguments:
44
+ Enabled: true
45
+
46
+ StatsD/MeasureAsDistArgument:
47
+ Enabled: true
48
+
49
+ StatsD/MetricPrefixArgument:
50
+ Enabled: true
data/.yardopts ADDED
@@ -0,0 +1,5 @@
1
+ --markup=markdown
2
+ -
3
+ README.md
4
+ CHANGELOG.md
5
+ CONTRIBUTING.md
data/CHANGELOG.md CHANGED
@@ -1,10 +1,296 @@
1
1
  # Changelog
2
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.
3
+ This file documents the changes between releases of this library. When
4
+ creating a pull request, please add an entry to the "unreleased changes"
5
+ section below.
5
6
 
6
7
  ### Unreleased changes
7
8
 
9
+ _Nothing yet!_
10
+
11
+ ## Version 2.6.0
12
+
13
+ This release contains a new `StatsD::Instrument::Client` class, which is
14
+ slated to replace the current implementation in the next major release.
15
+
16
+ The main reasons for this rewrite are two folds:
17
+ - Improved performance.
18
+ - Being able to instantiate multiple clients.
19
+
20
+ We have worked hard to make the new client as compatible as possible. However,
21
+ to accomplish some of our goals we have deprecated some stuff that we think
22
+ is unlikely to be used. See the rest of the release notes of this version, and
23
+ version 2.5.0 to see what is deprecated.
24
+
25
+ You can test compatibility with the new client by replacing `StatsD` with
26
+ `StatsD.client`, which points to a client that will be instantiated using
27
+ the same environment variables that you can already use for this library. You
28
+ can also use strict mode, and rubocop rules to check whether you are using any
29
+ deprecated patterns. See below for more info.
30
+
31
+ - **⚠️ DEPRECATION**: Using the `prefix: "foo"` argument for `StatsD.metric`
32
+ calls (and the metaprogramming macros) is deprecated.
33
+
34
+ - You can include the prefix in the metric name.
35
+ - If you want to override the global prefix, set `no_prefix: true` and
36
+ include the desired prefix in the metric name
37
+
38
+ This library ships with a Rubocop rule to detect uses of this keyword
39
+ argument in your codebase:
40
+
41
+ ``` sh
42
+ # Check for the prefix arguments on your StatsD.metric calls
43
+ rubocop --only StatsD/MetricPrefixArgument \
44
+ -r `bundle show statsd-instrument`/lib/statsd/instrument/rubocop.rb
45
+ ```
46
+
47
+ Strict mode has also been updated to no longer allow this argument.
48
+
49
+ - **⚠️ DEPRECATION**: Using the `as_dist: true` argument for `StatsD.measure`
50
+ and `statsd_measure` methods is deprecated. This argument was only available
51
+ for internal use, but was exposed in the public API. It is unlikely that you
52
+ are usijng this argumenr, but you can check to make sure using this Rubocop
53
+ invocation:
54
+
55
+ ``` sh
56
+ # Check for the as_dist arguments on your StatsD.measure calls
57
+ rubocop --only StatsD/MeasureAsDistArgument \
58
+ -r `bundle show statsd-instrument`/lib/statsd/instrument/rubocop.rb
59
+ ```
60
+
61
+ Strict mode has also been updated to no longer allow this argument.
62
+
63
+ - You can now enable strict mode by setting the `STATSD_STRICT_MODE`
64
+ environment variable. No more need to change your Gemfile! Note that it is
65
+ still not recommended to enable strict mode in production due to the
66
+ performance penalty, but is recommended for development and test. E.g. use
67
+ `STATSD_STRICT_MODE=1 rails test` to run your test suite with strict mode
68
+ enabled to expose any deprecations in your codebase.
69
+
70
+ - Add support for `STATSD_PREFIX` and `STATSD_DEFAULT_TAGS` environment variables
71
+ to configure the prefix to use for metrics and the comma-separated list of tags
72
+ to apply to every metric, respectively.
73
+
74
+ These environment variables are preferred over using `StatsD.prefix` and
75
+ `StatsD.default_tags`: it's best practice to configure the StatsD library
76
+ using environment variables.
77
+
78
+ - Several improvements to `StatsD.event` and `StatsD.service_check` (both are
79
+ Datadog-only). The previous implementation would sometimes construct invalid
80
+ datagrams based on the input. The method signatures have been made more
81
+ explicit, and documentation of these methods is now also more clear.
82
+
83
+ - Slight behaviour change when using the `assert_statsd_*` assertion methods in
84
+ combination with `assert_raises`: we now do not allow the block passed to the
85
+ `assert_statsd_` call to raise an exception. This may cause tests to fail that
86
+ previousloy were succeeding.
87
+
88
+ Consider the following example:
89
+
90
+ ``` ruby
91
+ assert_raises(RuntimeError) do
92
+ assert_statsd_increment('foo') do
93
+ raise 'something unexpected'
94
+ end
95
+ end
96
+ ```
97
+
98
+ In versions before 2.3.3, the assert `assert_statsd_*` calls would silently
99
+ pass when an exception would occur, which would later be handled by
100
+ `assert_raises`. So the test would pass, even though no `foo` metric would be
101
+ emitted.
102
+
103
+ Version 2.3.3 changed this by failing the test because no metric was being
104
+ emitted. However, this would hide the the exception from the assertion message,
105
+ complicating debugging efforts.
106
+
107
+ Now, we fail the test because an unexpected exception occured inside the block.
108
+ This means that the following test will fail:
109
+
110
+ ``` ruby
111
+ assert_raises(RuntimeError) do
112
+ assert_statsd_increment('foo') do
113
+ StatsD.increment('foo')
114
+ raise 'something unexpected'
115
+ end
116
+ end
117
+ ```
118
+
119
+ To fix, you will need to nest the `assert_raises` inside the block passed to
120
+ `assert_statsd_instrument` so that `assert_statsd_increment` will not see any
121
+ exceptions:
122
+
123
+ ``` ruby
124
+ assert_statsd_increment('foo') do
125
+ assert_raises(RuntimeError) do
126
+ StatsD.increment('foo')
127
+ raise 'something unexpected'
128
+ end
129
+ end
130
+ ```
131
+
132
+ See #193, #184, and #166 for more information.
133
+
134
+ ## Verison 2.5.1
135
+
136
+ - **Bugfix:** when using metaprogramming methods, changes to `StatsD.prefix` after
137
+ the metaprogramming method was evaluated would not be respected. This
138
+ unfortunately is quite common when you set the StatsD prefix inside an
139
+ initializer. This issue is now addressed: the prefix is evaluated at the
140
+ mopment the metric is emitted, not when the metaprogramming method is being
141
+ evaluated. (#202)
142
+
143
+ ## Version 2.5.0
144
+
145
+ - **⚠️ DEPRECATION**: Providing a sample rate and tags to your metrics and method
146
+ instrumentation macros should be done using keyword arguments rather than
147
+ positional arguments. Also, previously you could provide `value` as a keyword
148
+ argument, but it should be provided as the second positional argument.
149
+
150
+ ``` ruby
151
+ # DEPRECATED
152
+ StatsD.increment 'counter', 1, 0.1, ['tag']
153
+ StatsD.increment 'counter', value: 123, tags: { foo: 'bar' }
154
+ StatsD.measure('duration', nil, 1.0) { foo }
155
+ statsd_count_success :method, 'metric-name', 0.1
156
+
157
+ # SUPPORTED
158
+ StatsD.increment 'counter', sample_rate: 0.1, tags: ['tag']
159
+ StatsD.increment 'counter', 123, tags: { foo: 'bar' }
160
+ StatsD.measure('duration', sample_rate: 1.0) { foo }
161
+ statsd_count_success :method, 'metric-name', sample_rate: 0.1
162
+ ```
163
+
164
+ The documentation of the methods has been updated to reflect this change.
165
+ The behavior of the library is not changed for the time being, so you can
166
+ safely upgrade to this version. However, in a future major release, we will
167
+ remove support for the positional arguments.
168
+
169
+ The library includes some cops to help with finding issues in your existing
170
+ codebase, and fixing them:
171
+
172
+ ``` sh
173
+ # Check for positional arguments on your StatsD.metric calls
174
+ rubocop --only StatsD/PositionalArguments \
175
+ -r `bundle show statsd-instrument`/lib/statsd/instrument/rubocop/positional_arguments.rb
176
+
177
+ # Check for positional arguments on your statsd_instrumentation macros
178
+ rubocop --only StatsD/MetaprogrammingPositionalArguments \
179
+ -r `bundle show statsd-instrument`/lib/statsd/instrument/rubocop/metaprogramming_positional_arguments.rb
180
+
181
+ # Check for value as keyword argument
182
+ rubocop --only StatsD/MetricValueKeywordArgument \
183
+ -r `bundle show statsd-instrument`/lib/statsd/instrument/rubocop/metric_value_keyword_argument.rb
184
+
185
+ ```
186
+
187
+ - **⚠️ DEPRECATION**: Relying on the return value of the StatsD metric methods
188
+ (e.g. `StatsD.increment`) is deprecated. StatsD is a fire-and-forget
189
+ protocol, so your code should not depend on the return value of these methods.
190
+
191
+ The documentation of the methods has been updated to reflect this change.
192
+ The behavior of the library is not changed for the time being, so you can
193
+ safely upgrade to this version. However, in a future major release, we will
194
+ start to explicitly return `nil`.
195
+
196
+ This gem comes with a Rubocop rule that can help verify that your
197
+ application is not relying on the return value of the metric methods. To use
198
+ this cop on your codebase, invoke Rubocop with the following arguments:
199
+
200
+ ``` sh
201
+ rubocop --only StatsD/MetricReturnValue \
202
+ -r `bundle show statsd-instrument`/lib/statsd/instrument/rubocop/metric_return_value.rb
203
+ ```
204
+
205
+ - **Strict mode**: These custom Rubocop rules will give you a quick indication
206
+ of the issues in your codebase, but are not airtight. This library now also
207
+ ships with strict mode, a mixin module that already disables this deprecated
208
+ behavior so it will raise exceptions if you are depending on deprecated
209
+ behavior. It will also do additional input validation, and make sure the
210
+ `StatsD` metric methods return `nil`.
211
+
212
+ You enable strict mode by requiring `statsd/instrument/strict`:
213
+
214
+ ``` ruby
215
+ # In your Gemfile
216
+ gem 'statd-instrument', require: 'statsd/instrument/strict'
217
+
218
+ # Or, in your test helper:
219
+ require 'statsd/instrument/strict'
220
+ ```
221
+
222
+ It is recommended to enable this in CI to find deprecation issues, but not
223
+ in production because enabling it comes with a performance penalty.
224
+
225
+ - **Performance improvements 🎉**: Several internal changes have made the
226
+ library run singificantly faster. The changes:
227
+
228
+ - Improve performance of duration calculations. (#168)
229
+ - Early exit when no changes are needed to bring tags and metric names to
230
+ normalized form. (#173)
231
+ - Refactor method argument handling to reduce object allocations and
232
+ processing. (#174)
233
+
234
+ A benchmark suite was added (#169) and it now runs as part of CI (#170) so we
235
+ can more easily spot performance regressions before they get merged into the
236
+ library.
237
+
238
+ The result of this work:
239
+
240
+ ```
241
+ Comparison:
242
+ StatsD metrics to local UDP receiver (branch: master, sha: 2f98046): 10344.9 i/s
243
+ StatsD metrics to local UDP receiver (branch: v2.4.0, sha: 371d22a): 8556.5 i/s - 1.21x (± 0.00) slower
244
+ ```
245
+
246
+ The deprecations mentioned above will allows us to provide an even greater
247
+ performance improvement, so update your code base to not use those
248
+ deprecations anymore, and keep your eyes open for future releases of the
249
+ library!
250
+
251
+ - _Bugfix:_ avoid deadlock when an error occurs in the integration test suite (#175)
252
+
253
+ ## Version 2.4.0
254
+
255
+ - Add `StatsD.default_tags` to specify tags that should be included in all metrics. (#159)
256
+ - Improve assertion message when asserting metrics whose tags do not match. (#100)
257
+ - Enforce the Shopify Ruby style guide. (#164)
258
+ - Migrate CI to Github actions. (#158)
259
+ - Make the library frozen string literal-compatible. (#161, #163)
260
+ - Fix all Ruby warnings. (#162)
261
+
262
+ ## Version 2.3.5
263
+
264
+ - Re-add `StatsD::Instrument.duration`, which was accidentally removed since verison 2.5.3 (#157)
265
+
266
+ ## Version 2.3.4
267
+
268
+ - Improve performance of `Metric#to_s` (#152)
269
+ - Fix bug in escaping newlines for events with Datadog Backend (#153)
270
+
271
+ ## Version 2.3.3
272
+
273
+ - Capture measure and distribution metrics on exception and early return (#134)
274
+
275
+ NOTE: Now that exceptions are measured statistics may behave differently. An exception example:
276
+ ```
277
+ StatsD.measure('myhttpcall') do
278
+ my_http_object.invoke
279
+ end
280
+ ```
281
+ Version 2.3.2 and below did not track metrics whenever a HTTP Timeout exception was raised.
282
+ 2.3.3 and above will include those metrics which may increase the values included.
283
+
284
+ A return example:
285
+ ```
286
+ StatsD.measure('myexpensivecalculation') do
287
+ return if expensive_calculation_disabled?
288
+ expensive_calculation
289
+ end
290
+ ```
291
+ If `expensive_calculation_disabled?` is true 50% of the time version 2.3.2 will drop the
292
+ average metric considerably.
293
+
8
294
  ## Version 2.3.2
9
295
 
10
296
  - Add option to override global prefix for metrics (#148)
data/CONTRIBUTING.md CHANGED
@@ -11,27 +11,49 @@ This project is MIT licensed.
11
11
 
12
12
  Report issues using the [Github issues tracker](https://github.com/Shopify/statsd-instrument/issues/new).
13
13
 
14
- When reporting issues, please incldue the following information:
14
+ When reporting issues, please include the following information:
15
15
 
16
16
  - Your Ruby interpreter version.
17
17
  - The statsd-instrument version. **Note:** only the latest version is supported.
18
18
  - The StatsD backend you are using.
19
19
 
20
- ## Pull request
20
+ ## Opening pull requests
21
21
 
22
22
  1. Fork the repository, and create a branch.
23
23
  2. Implement the feature or bugfix, and add tests that cover the changed functionality.
24
- 3. Create a pull request. Make sure that you get Travis CI passes.
24
+ 3. Create a pull request. Make sure that you get a green CI status on your commit.
25
25
 
26
26
  Some notes:
27
27
 
28
- - Make sure to follow to coding style.
28
+ - Make sure to follow to coding style. This is enforced by Rubocop
29
29
  - Make sure your changes are properly documented using [yardoc syntax](http://www.rubydoc.info/gems/yard/file/docs/GettingStarted.md).
30
30
  - Add an entry to the "unreleased changes" section of [CHANGELOG.md](./CHANGELOG.md).
31
31
  - **Do not** update `StatsD::Instrument::VERSION`. This will be done during the release prodecure.
32
32
 
33
- > **Important:** if you change anything in the hot code path (sending a StatsD metric), please
34
- > include benchmarks to show the performance impact of your changes.
33
+ ### On perfomance & benchmarking
34
+
35
+ This gem is used in production at Shopify, and is used to instrument some of
36
+ our hottest code paths. This means that we are very careful about not
37
+ introducing performance regressions in this library.
38
+
39
+ **Important:** Whenever you make changes to the metric emission code path in
40
+ this library, you **must** include benchmark results to show the impact of
41
+ your changes.
42
+
43
+ The `benchmark/` folder contains some example benchmark script that you can
44
+ use, or can serve as a starting point. The [benchmark README](benchmark/README.md)
45
+ has instructions on how to benchmark your changes.
46
+
47
+ ### On backwards compatibility
48
+
49
+ Shopify's codebases are heavily instrumented using this library. As a result, we cannot
50
+ accept changes that are backwards incompatible:
51
+
52
+ - Changes that will require us to update our codebases.
53
+ - Changes that will cause metrics emitted by this library to change in form or shape.
54
+
55
+ This means that we may not be able to accept fixes for what you consider a bug, because
56
+ we are depending on the current behavior of the library.
35
57
 
36
58
  ## Release procedure
37
59
 
data/Gemfile CHANGED
@@ -1,2 +1,7 @@
1
+ # frozen_string_literal: true
2
+
1
3
  source "https://rubygems.org"
2
4
  gemspec
5
+
6
+ # benchmark-ips save! method is not part of a released version yet.
7
+ gem 'benchmark-ips', git: 'https://github.com/evanphx/benchmark-ips', branch: 'master'
data/README.md CHANGED
@@ -1,57 +1,51 @@
1
1
  # StatsD client for Ruby apps
2
2
 
3
- [![Built on Travis](https://secure.travis-ci.org/Shopify/statsd-instrument.svg?branch=master)](https://secure.travis-ci.org/Shopify/statsd-instrument)
3
+ This is a ruby client for statsd (http://github.com/etsy/statsd). It provides
4
+ a lightweight way to track and measure metrics in your application.
4
5
 
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
+ We call out to statsd by sending data over a UDP socket. UDP sockets are fast,
7
+ but unreliable, there is no guarantee that your data will ever arrive at its
8
+ location. In other words, fire and forget. This is perfect for this use case
9
+ because it means your code doesn't get bogged down trying to log statistics.
10
+ We send data to statsd several times per request and haven't noticed a
11
+ performance hit.
6
12
 
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
-
9
- For more information about StatsD, see the [README of the Etsy project](http://github.com/etsy/statsd).
13
+ For more information about StatsD, see the [README of the Etsy
14
+ project](http://github.com/etsy/statsd).
10
15
 
11
16
  ## Configuration
12
17
 
13
- The library comes with different backends. Based on your environment (detected using environment
14
- variables), it will select one of the following backends by default:
15
-
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
18
- to (default `"localhost:8125"`), and `STATSD_IMPLEMENTATION` to set the protocol variant. (See below)
19
- - **Test** environment: `StatsD::Instrument::Backends::NullBackend` will swallow all calls. See below for
20
- notes on writing tests.
21
- - **Development**, and all other, environments: `StatsD::Instrument::Backends::LoggerBackend` will log all
22
- calls to stdout.
23
-
24
- You can override the currently active backend by setting `StatsD.backend`:
25
-
26
- ``` ruby
27
- # Sets up a UDP backend. First argument is the UDP address to send StatsD packets to,
28
- # second argument specifies the protocol variant (i.e. `:statsd`, `:statsite`, or `:datadog`).
29
- StatsD.backend = StatsD::Instrument::Backends::UDPBackend.new("1.2.3.4:8125", :statsite)
30
-
31
- # Sets up a logger backend
32
- StatsD.backend = StatsD::Instrument::Backends::LoggerBackend.new(Rails.logger)
33
- ```
34
-
35
- The other available settings, with their default, are
36
-
37
- ``` ruby
38
- # Logger to which commands are logged when using the LoggerBackend, which is
39
- # the default in development environment. Also, any errors or warnings will
40
- # be logged here.
41
- StatsD.logger = defined?(Rails) ? Rails.logger : Logger.new($stderr)
42
-
43
- # An optional prefix to be added to each metric.
44
- StatsD.prefix = nil # but can be set to any string
45
-
46
- # Sample 10% of events. By default all events are reported, which may overload your network or server.
47
- # You can, and should vary this on a per metric basis, depending on frequency and accuracy requirements
48
- StatsD.default_sample_rate = (ENV['STATSD_SAMPLE_RATE'] || 0.1 ).to_f
49
- ```
18
+ It's recommended to configure this librray by setting environment variables.
19
+ The following environment variables are supported:
20
+
21
+ - `STATSD_ADDR`: (default `localhost:8125`) The address to send the StatsD UDP
22
+ datagrams to.
23
+ - `STATSD_IMPLEMENTATION`: (default: `statsd`). The StatsD implementation you
24
+ are using. `statsd`, `statsite` and `datadog` are supported. Some features
25
+ are only available on certain implementations,
26
+ - `STATSD_ENV`: The environment StatsD will run in. If this is not set
27
+ explicitly, this will be determined based on other environment variables,
28
+ like `RAILS_ENV` or `ENV`. The library will behave differently:
29
+
30
+ - In the **production** and **staging** environment, thre librray will
31
+ actually send UDP packets.
32
+ - In the **test** environment, it will swallow all calls, but allows you to
33
+ capture them for testing purposes. See below for notes on writing tests.
34
+ - In **development** and all other environments, it will write all calls to
35
+ the log (`StatsD.logger`, which by default writes to STDOUT).
36
+
37
+ - `STATSD_SAMPLE_RATE`: (default: `1.0`) The default sample rate to use for all
38
+ metrics. This can be used to reduce the amount of network traffic and CPU
39
+ overhead the usage of this library generates. This can be overridden in a
40
+ metric method call.
41
+ - `STATSD_PREFIX`: The prefix to apply to all metric names. This can be
42
+ overridden in a metric method call.
43
+ - `STATSD_DEFAULT_TAGS`: A comma-separated list of tags to apply to all metrics.
44
+ (Note: tags are not supported by all iomplementations.)
50
45
 
51
46
  ## StatsD keys
52
47
 
53
48
  StatsD keys look like 'admin.logins.api.success'. Dots are used as namespace separators.
54
- In Graphite, they will show up as folders.
55
49
 
56
50
  ## Usage
57
51
 
@@ -82,7 +76,7 @@ StatsD.increment('GoogleBase.insert')
82
76
  StatsD.increment('GoogleBase.insert', 10)
83
77
  # you can also specify a sample rate, so only 1/10 of events
84
78
  # actually get to statsd. Useful for very high volume data
85
- StatsD.increment('GoogleBase.insert', 1, sample_rate: 0.1)
79
+ StatsD.increment('GoogleBase.insert', sample_rate: 0.1)
86
80
  ```
87
81
 
88
82
  #### StatsD.gauge
@@ -106,6 +100,18 @@ StatsD.set('GoogleBase.customers', "12345", sample_rate: 1.0)
106
100
 
107
101
  Because you are counting unique values, the results of using a sampling value less than 1.0 can lead to unexpected, hard to interpret results.
108
102
 
103
+ #### StatsD.histogram
104
+
105
+ Builds a histogram of numeric values.
106
+ ``` ruby
107
+
108
+ StatsD.histogram('Order.value', order.value_in_usd.to_f tags: { source: 'POS' })
109
+ ```
110
+
111
+ Because you are counting unique values, the results of using a sampling value less than 1.0 can lead to unexpected, hard to interpret results.
112
+
113
+ *Note: This is only supported by the beta datadog implementatation.*
114
+
109
115
  #### StatsD.distribution
110
116
 
111
117
  A modified gauge that submits a distribution of values over a sample period. Arithmetic and statistical calculations (percetiles, average, etc.) on the data set are peformed server side rather than client side like a histogram.
@@ -333,7 +339,9 @@ end
333
339
 
334
340
  ### Compatibility
335
341
 
336
- Tested using Travis CI against Ruby 2.0, 2.1, 2.2, Rubinius, and JRuby.
342
+ The library is tested against Ruby 2.3 and higher. We are not testing on
343
+ different Ruby implementations besides MRI, but we expect it to work on other
344
+ implementations as well.
337
345
 
338
346
  ### Reliance on DNS
339
347
 
@@ -350,6 +358,6 @@ This can be particularly problematic in clouds that have a shared DNS infrastruc
350
358
 
351
359
  This library was developed for shopify.com and is MIT licensed.
352
360
 
353
- - [API documentation](http://www.rubydoc.info/gems/statsd-instrument/frames)
361
+ - [API documentation](http://www.rubydoc.info/gems/statsd-instrument)
354
362
  - [The changelog](./CHANGELOG.md) covers the changes between releases.
355
363
  - [Contributing notes](./CONTRIBUTING.md) if you are interested in contributing to this library.
data/Rakefile CHANGED
@@ -1,10 +1,12 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'bundler/gem_tasks'
2
4
  require 'rake/testtask'
3
5
 
4
6
  Rake::TestTask.new('test') do |t|
5
7
  t.ruby_opts << '-r rubygems'
6
8
  t.libs << 'lib' << 'test'
7
- t.test_files = FileList['test/*.rb']
9
+ t.test_files = FileList['test/**/*_test.rb']
8
10
  end
9
11
 
10
- task :default => :test
12
+ task default: :test
@@ -0,0 +1,29 @@
1
+ # Benchmark scripts
2
+
3
+ This directory contains benchmark scripts that can be used to gauge the
4
+ performance impact of changes.
5
+
6
+ As mentioned in the contributing guidelines, this library is used heavily in
7
+ production at Shopify in many of our hot code paths. This means that we care a
8
+ lot about changes not introducing performance regressions. Every pull request
9
+ that changes the code path to send metrics should include benchmarks
10
+ demonstrating the performance impact of the changes.
11
+
12
+ This directory contains two scripts to help with benchmarking.
13
+
14
+ - `send-metrics-to-dev-null-log` exercises the code path to construct metrics.
15
+ - `send-metrics-to-local-udp-listener` will also exercise the code path to
16
+ actually send a StatsD packet over UDP.
17
+
18
+ To benchmark your changes:
19
+
20
+ 1. Make sure the benchmark script will actually cover your changes.
21
+ - If not, please create a new benchmark script that does.
22
+ - Do not commit this script to the repository (yet), so it will continue to
23
+ be available if you check out another branch.
24
+ 2. Run these scripts on your pull request branch. The results will be stored in
25
+ a temporary file.
26
+ 3. Checkout the latest version of `master`.
27
+ 4. Run the benchmark again. The benchmark script will now print a comparison
28
+ between your branch and master.
29
+ 5. Include the output in your pull request description.
@@ -0,0 +1,41 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ require 'bundler/setup'
5
+ require 'benchmark/ips'
6
+ require 'socket'
7
+
8
+ # Set up an UDP listener to which we can send StatsD packets
9
+ legacy_receiver = UDPSocket.new
10
+ legacy_receiver.bind('localhost', 0)
11
+
12
+ ENV['ENV'] = "production"
13
+ ENV['STATSD_ADDR'] = "#{legacy_receiver.addr[2]}:#{legacy_receiver.addr[1]}"
14
+ ENV['STATSD_IMPLEMENTATION'] ||= 'datadog'
15
+
16
+ require 'statsd-instrument'
17
+ require 'statsd/instrument/client'
18
+
19
+ legacy_client = StatsD
20
+
21
+ # Set up an UDP listener to which we can send StatsD packets
22
+ new_client_receiver = UDPSocket.new
23
+ new_client_receiver.bind('localhost', 0)
24
+
25
+ udp_sink = StatsD::Instrument::UDPSink.new(new_client_receiver.addr[2], new_client_receiver.addr[1])
26
+ new_client = StatsD::Instrument::Client.new(sink: udp_sink, default_sample_rate: StatsD.default_sample_rate)
27
+
28
+ Benchmark.ips do |bench|
29
+ bench.report("Legacy client (sample rate: #{StatsD.default_sample_rate})") do
30
+ legacy_client.increment('StatsD.increment')
31
+ end
32
+
33
+ bench.report("New client (sample rate: #{StatsD.default_sample_rate})") do
34
+ new_client.increment('StatsD.increment')
35
+ end
36
+
37
+ bench.compare!
38
+ end
39
+
40
+ legacy_receiver.close
41
+ new_client_receiver.close