statsd-instrument 2.5.1 → 2.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.rubocop-https---shopify-github-io-ruby-style-guide-rubocop-yml +1 -1
- data/.rubocop.yml +11 -6
- data/.yardopts +5 -0
- data/CHANGELOG.md +75 -6
- data/README.md +54 -46
- data/benchmark/datagram-client +41 -0
- data/lib/statsd/instrument/assertions.rb +168 -57
- data/lib/statsd/instrument/backends/udp_backend.rb +20 -29
- data/lib/statsd/instrument/capture_sink.rb +27 -0
- data/lib/statsd/instrument/client.rb +313 -0
- data/lib/statsd/instrument/datagram.rb +75 -0
- data/lib/statsd/instrument/datagram_builder.rb +101 -0
- data/lib/statsd/instrument/dogstatsd_datagram_builder.rb +71 -0
- data/lib/statsd/instrument/environment.rb +106 -29
- data/lib/statsd/instrument/log_sink.rb +24 -0
- data/lib/statsd/instrument/null_sink.rb +13 -0
- data/lib/statsd/instrument/rubocop/measure_as_dist_argument.rb +39 -0
- data/lib/statsd/instrument/rubocop/metaprogramming_positional_arguments.rb +6 -10
- data/lib/statsd/instrument/rubocop/metric_prefix_argument.rb +37 -0
- data/lib/statsd/instrument/rubocop/metric_return_value.rb +7 -6
- data/lib/statsd/instrument/rubocop/metric_value_keyword_argument.rb +11 -20
- data/lib/statsd/instrument/rubocop/positional_arguments.rb +13 -13
- data/lib/statsd/instrument/rubocop/splat_arguments.rb +8 -14
- data/lib/statsd/instrument/rubocop.rb +64 -0
- data/lib/statsd/instrument/statsd_datagram_builder.rb +14 -0
- data/lib/statsd/instrument/strict.rb +112 -22
- data/lib/statsd/instrument/udp_sink.rb +62 -0
- data/lib/statsd/instrument/version.rb +1 -1
- data/lib/statsd/instrument.rb +191 -100
- data/test/assertions_test.rb +139 -176
- data/test/capture_sink_test.rb +44 -0
- data/test/client_test.rb +164 -0
- data/test/compatibility/dogstatsd_datagram_compatibility_test.rb +162 -0
- data/test/datagram_builder_test.rb +120 -0
- data/test/deprecations_test.rb +56 -10
- data/test/dogstatsd_datagram_builder_test.rb +32 -0
- data/test/environment_test.rb +73 -7
- data/test/log_sink_test.rb +37 -0
- data/test/null_sink_test.rb +13 -0
- data/test/rubocop/measure_as_dist_argument_test.rb +44 -0
- data/test/rubocop/metaprogramming_positional_arguments_test.rb +1 -1
- data/test/rubocop/metric_prefix_argument_test.rb +38 -0
- data/test/rubocop/metric_return_value_test.rb +1 -1
- data/test/rubocop/metric_value_keyword_argument_test.rb +1 -1
- data/test/rubocop/positional_arguments_test.rb +1 -1
- data/test/rubocop/splat_arguments_test.rb +1 -1
- data/test/statsd_datagram_builder_test.rb +22 -0
- data/test/statsd_instrumentation_test.rb +0 -24
- data/test/statsd_test.rb +0 -23
- data/test/test_helper.rb +0 -2
- data/test/udp_backend_test.rb +25 -8
- data/test/udp_sink_test.rb +85 -0
- metadata +38 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 0e0581a0deec7cbdfae1ca0b03d336b3e08cf1d14599087678f1a6a6ec35b321
|
4
|
+
data.tar.gz: f10c0f102d13bb71a8365a1cc9707c848af954adb02eea3b8bbb95a9bbc2f0b7
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 0fc4c808a257a72bf0d86edb1e9ec9ae6c700f80408f83d574e466e1ce7cf2d627aee5e71dc5fdbb998850395881421cba97172db446fc102d04314546d55f60
|
7
|
+
data.tar.gz: 748ffe0481def020345c33029a630b266f033d84dcf80429316e0ec95a7394b82e20f82e9ee568efa3f455ac853fcbb3fec99243da482f8af3967069dffae13e
|
data/.rubocop.yml
CHANGED
@@ -2,11 +2,7 @@ inherit_from:
|
|
2
2
|
- https://shopify.github.io/ruby-style-guide/rubocop.yml
|
3
3
|
|
4
4
|
require:
|
5
|
-
- ./lib/statsd/instrument/rubocop
|
6
|
-
- ./lib/statsd/instrument/rubocop/metric_value_keyword_argument.rb
|
7
|
-
- ./lib/statsd/instrument/rubocop/positional_arguments.rb
|
8
|
-
- ./lib/statsd/instrument/rubocop/splat_arguments.rb
|
9
|
-
- ./lib/statsd/instrument/rubocop/metaprogramming_positional_arguments.rb
|
5
|
+
- ./lib/statsd/instrument/rubocop.rb
|
10
6
|
|
11
7
|
AllCops:
|
12
8
|
TargetRubyVersion: 2.3
|
@@ -27,7 +23,10 @@ Style/ClassAndModuleChildren:
|
|
27
23
|
Style/MethodCallWithArgsParentheses:
|
28
24
|
Enabled: false # TODO: enable later
|
29
25
|
|
30
|
-
|
26
|
+
Lint/UnusedMethodArgument:
|
27
|
+
AllowUnusedKeywordArguments: true
|
28
|
+
|
29
|
+
# Enable our own cops on our own repo
|
31
30
|
|
32
31
|
StatsD/MetricReturnValue:
|
33
32
|
Enabled: true
|
@@ -43,3 +42,9 @@ StatsD/SplatArguments:
|
|
43
42
|
|
44
43
|
StatsD/MetaprogrammingPositionalArguments:
|
45
44
|
Enabled: true
|
45
|
+
|
46
|
+
StatsD/MeasureAsDistArgument:
|
47
|
+
Enabled: true
|
48
|
+
|
49
|
+
StatsD/MetricPrefixArgument:
|
50
|
+
Enabled: true
|
data/.yardopts
ADDED
data/CHANGELOG.md
CHANGED
@@ -6,6 +6,75 @@ section below.
|
|
6
6
|
|
7
7
|
### Unreleased changes
|
8
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
|
+
|
9
78
|
- Several improvements to `StatsD.event` and `StatsD.service_check` (both are
|
10
79
|
Datadog-only). The previous implementation would sometimes construct invalid
|
11
80
|
datagrams based on the input. The method signatures have been made more
|
@@ -19,7 +88,7 @@ section below.
|
|
19
88
|
Consider the following example:
|
20
89
|
|
21
90
|
``` ruby
|
22
|
-
assert_raises(RuntimeError)
|
91
|
+
assert_raises(RuntimeError) do
|
23
92
|
assert_statsd_increment('foo') do
|
24
93
|
raise 'something unexpected'
|
25
94
|
end
|
@@ -39,7 +108,7 @@ section below.
|
|
39
108
|
This means that the following test will fail:
|
40
109
|
|
41
110
|
``` ruby
|
42
|
-
assert_raises(RuntimeError)
|
111
|
+
assert_raises(RuntimeError) do
|
43
112
|
assert_statsd_increment('foo') do
|
44
113
|
StatsD.increment('foo')
|
45
114
|
raise 'something unexpected'
|
@@ -53,7 +122,7 @@ section below.
|
|
53
122
|
|
54
123
|
``` ruby
|
55
124
|
assert_statsd_increment('foo') do
|
56
|
-
assert_raises(RuntimeError)
|
125
|
+
assert_raises(RuntimeError) do
|
57
126
|
StatsD.increment('foo')
|
58
127
|
raise 'something unexpected'
|
59
128
|
end
|
@@ -62,7 +131,7 @@ section below.
|
|
62
131
|
|
63
132
|
See #193, #184, and #166 for more information.
|
64
133
|
|
65
|
-
##
|
134
|
+
## Verison 2.5.1
|
66
135
|
|
67
136
|
- **Bugfix:** when using metaprogramming methods, changes to `StatsD.prefix` after
|
68
137
|
the metaprogramming method was evaluated would not be respected. This
|
@@ -73,7 +142,7 @@ section below.
|
|
73
142
|
|
74
143
|
## Version 2.5.0
|
75
144
|
|
76
|
-
-
|
145
|
+
- **⚠️ DEPRECATION**: Providing a sample rate and tags to your metrics and method
|
77
146
|
instrumentation macros should be done using keyword arguments rather than
|
78
147
|
positional arguments. Also, previously you could provide `value` as a keyword
|
79
148
|
argument, but it should be provided as the second positional argument.
|
@@ -115,7 +184,7 @@ section below.
|
|
115
184
|
|
116
185
|
```
|
117
186
|
|
118
|
-
-
|
187
|
+
- **⚠️ DEPRECATION**: Relying on the return value of the StatsD metric methods
|
119
188
|
(e.g. `StatsD.increment`) is deprecated. StatsD is a fire-and-forget
|
120
189
|
protocol, so your code should not depend on the return value of these methods.
|
121
190
|
|
data/README.md
CHANGED
@@ -1,57 +1,51 @@
|
|
1
1
|
# StatsD client for Ruby apps
|
2
2
|
|
3
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
14
|
-
|
15
|
-
|
16
|
-
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
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',
|
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
|
-
|
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
|
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.
|
@@ -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
|
@@ -1,121 +1,232 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
# This module defines several assertion methods that can be used to verify that
|
4
|
+
# your application is emitting the right StatsD metrics.
|
5
|
+
#
|
6
|
+
# Every metric type has its own assertion method, like {#assert_statsd_increment}
|
7
|
+
# to assert `StatsD.increment` calls. You can also assert other properties of the
|
8
|
+
# metric that was emitted, lioke the sample rate or presence of tags.
|
9
|
+
# To check for the absence of metrics, use {#assert_no_statsd_calls}.
|
10
|
+
#
|
11
|
+
# @example Check for metric properties:
|
12
|
+
# assert_statsd_measure('foo', sample_rate: 0.1, tags: ["bar"]) do
|
13
|
+
# StatsD.measure('foo', sample_rate: 0.5, tags: ['bar','baz']) do
|
14
|
+
# some_code_to_measure
|
15
|
+
# end
|
16
|
+
# end
|
17
|
+
#
|
18
|
+
# @example Check for multiple occurrences:
|
19
|
+
# assert_statsd_increment('foo', times: 2) do
|
20
|
+
# StatsD.increment('foo')
|
21
|
+
# StatsD.increment('foo')
|
22
|
+
# end
|
23
|
+
#
|
24
|
+
# @example Absence of metrics
|
25
|
+
# assert_no_statsd_calls do
|
26
|
+
# foo
|
27
|
+
# end
|
28
|
+
#
|
29
|
+
# @example Handling exceptions
|
30
|
+
# assert_statsd_increment('foo.error') do
|
31
|
+
# # If we expect exceptions to occur, we have to handle them inside
|
32
|
+
# # the block we pass to assert_statsd_increment.
|
33
|
+
# assert_raises(RuntimeError) do
|
34
|
+
# begin
|
35
|
+
# attempt_foo
|
36
|
+
# rescue
|
37
|
+
# StatsD.increment('foo.error')
|
38
|
+
# raise 'foo failed'
|
39
|
+
# end
|
40
|
+
# end
|
41
|
+
# end
|
3
42
|
module StatsD::Instrument::Assertions
|
4
43
|
include StatsD::Instrument::Helpers
|
5
44
|
|
45
|
+
# Asserts no metric occurred during the execution of the provided block.
|
46
|
+
#
|
47
|
+
# @param [String] metric_name (default: nil) The metric name that is not allowed
|
48
|
+
# to happen inside the block. If this is set to `nil`, the assertion will fail
|
49
|
+
# if any metric occurs.
|
50
|
+
# @yield A block in which the specified metric should not occur. This block
|
51
|
+
# should not raise any exceptions.
|
52
|
+
# @return [void]
|
53
|
+
# @raise [Minitest::Assertion] If an exception occurs, or if any metric (with the
|
54
|
+
# provided name, or any), occurred during the execution of the provided block.
|
6
55
|
def assert_no_statsd_calls(metric_name = nil, &block)
|
7
56
|
metrics = capture_statsd_calls(&block)
|
8
57
|
metrics.select! { |m| m.name == metric_name } if metric_name
|
9
58
|
assert(metrics.empty?, "No StatsD calls for metric #{metrics.map(&:name).join(', ')} expected.")
|
59
|
+
rescue => exception
|
60
|
+
flunk(<<~MESSAGE)
|
61
|
+
An exception occurred in the block provided to the StatsD assertion.
|
62
|
+
|
63
|
+
#{exception.class.name}: #{exception.message}
|
64
|
+
\t#{exception.backtrace.join("\n\t")}
|
65
|
+
|
66
|
+
If this exception is expected, make sure to handle it using `assert_raises`
|
67
|
+
inside the block provided to the StatsD assertion.
|
68
|
+
MESSAGE
|
10
69
|
end
|
11
70
|
|
71
|
+
# Asserts that a given counter metric occurred inside the provided block.
|
72
|
+
#
|
73
|
+
# @param [String] metric_name The name of the metric that should occur.
|
74
|
+
# @param [Hash] options (see StatsD::Instrument::MetricExpectation.new)
|
75
|
+
# @yield A block in which the specified metric should occur. This block
|
76
|
+
# should not raise any exceptions.
|
77
|
+
# @return [void]
|
78
|
+
# @raise [Minitest::Assertion] If an exception occurs, or if the metric did
|
79
|
+
# not occur as specified during the execution the block.
|
12
80
|
def assert_statsd_increment(metric_name, options = {}, &block)
|
13
81
|
assert_statsd_call(:c, metric_name, options, &block)
|
14
82
|
end
|
15
83
|
|
84
|
+
# Asserts that a given timing metric occurred inside the provided block.
|
85
|
+
#
|
86
|
+
# @param metric_name (see #assert_statsd_increment)
|
87
|
+
# @param options (see #assert_statsd_increment)
|
88
|
+
# @yield (see #assert_statsd_increment)
|
89
|
+
# @return [void]
|
90
|
+
# @raise (see #assert_statsd_increment)
|
16
91
|
def assert_statsd_measure(metric_name, options = {}, &block)
|
17
92
|
assert_statsd_call(:ms, metric_name, options, &block)
|
18
93
|
end
|
19
94
|
|
95
|
+
# Asserts that a given gauge metric occurred inside the provided block.
|
96
|
+
#
|
97
|
+
# @param metric_name (see #assert_statsd_increment)
|
98
|
+
# @param options (see #assert_statsd_increment)
|
99
|
+
# @yield (see #assert_statsd_increment)
|
100
|
+
# @return [void]
|
101
|
+
# @raise (see #assert_statsd_increment)
|
20
102
|
def assert_statsd_gauge(metric_name, options = {}, &block)
|
21
103
|
assert_statsd_call(:g, metric_name, options, &block)
|
22
104
|
end
|
23
105
|
|
106
|
+
# Asserts that a given histogram metric occurred inside the provided block.
|
107
|
+
#
|
108
|
+
# @param metric_name (see #assert_statsd_increment)
|
109
|
+
# @param options (see #assert_statsd_increment)
|
110
|
+
# @yield (see #assert_statsd_increment)
|
111
|
+
# @return [void]
|
112
|
+
# @raise (see #assert_statsd_increment)
|
24
113
|
def assert_statsd_histogram(metric_name, options = {}, &block)
|
25
114
|
assert_statsd_call(:h, metric_name, options, &block)
|
26
115
|
end
|
27
116
|
|
117
|
+
# Asserts that a given distribution metric occurred inside the provided block.
|
118
|
+
#
|
119
|
+
# @param metric_name (see #assert_statsd_increment)
|
120
|
+
# @param options (see #assert_statsd_increment)
|
121
|
+
# @yield (see #assert_statsd_increment)
|
122
|
+
# @return [void]
|
123
|
+
# @raise (see #assert_statsd_increment)
|
28
124
|
def assert_statsd_distribution(metric_name, options = {}, &block)
|
29
125
|
assert_statsd_call(:d, metric_name, options, &block)
|
30
126
|
end
|
31
127
|
|
128
|
+
# Asserts that a given set metric occurred inside the provided block.
|
129
|
+
#
|
130
|
+
# @param metric_name (see #assert_statsd_increment)
|
131
|
+
# @param options (see #assert_statsd_increment)
|
132
|
+
# @yield (see #assert_statsd_increment)
|
133
|
+
# @return [void]
|
134
|
+
# @raise (see #assert_statsd_increment)
|
32
135
|
def assert_statsd_set(metric_name, options = {}, &block)
|
33
136
|
assert_statsd_call(:s, metric_name, options, &block)
|
34
137
|
end
|
35
138
|
|
139
|
+
# Asserts that a given key/value metric occurred inside the provided block.
|
140
|
+
#
|
141
|
+
# @param metric_name (see #assert_statsd_increment)
|
142
|
+
# @param options (see #assert_statsd_increment)
|
143
|
+
# @yield (see #assert_statsd_increment)
|
144
|
+
# @return [void]
|
145
|
+
# @raise (see #assert_statsd_increment)
|
36
146
|
def assert_statsd_key_value(metric_name, options = {}, &block)
|
37
147
|
assert_statsd_call(:kv, metric_name, options, &block)
|
38
148
|
end
|
39
149
|
|
150
|
+
# Asserts that the set of provided metric expectations came true.
|
151
|
+
#
|
152
|
+
# Generally, it's recommended to use more specific assertion methods, like
|
153
|
+
# {#assert_statsd_increment} and others.
|
154
|
+
#
|
40
155
|
# @private
|
41
|
-
|
156
|
+
# @param [Array<StatsD::Instrument::MetricExpectation>] expected_metrics The set of
|
157
|
+
# metric expectations to verify.
|
158
|
+
# @yield (see #assert_statsd_increment)
|
159
|
+
# @return [void]
|
160
|
+
# @raise (see #assert_statsd_increment)
|
161
|
+
def assert_statsd_calls(expected_metrics)
|
42
162
|
raise ArgumentError, "block must be given" unless block_given?
|
43
163
|
|
44
164
|
capture_backend = StatsD::Instrument::Backends::CaptureBackend.new
|
45
165
|
with_capture_backend(capture_backend) do
|
46
|
-
exception_occurred = nil
|
47
166
|
begin
|
48
|
-
|
167
|
+
yield
|
49
168
|
rescue => exception
|
50
|
-
|
51
|
-
|
52
|
-
ensure
|
53
|
-
metrics = capture_backend.collected_metrics
|
54
|
-
matched_expected_metrics = []
|
55
|
-
expected_metrics.each do |expected_metric|
|
56
|
-
expected_metric_times = expected_metric.times
|
57
|
-
expected_metric_times_remaining = expected_metric.times
|
58
|
-
filtered_metrics = metrics.select { |m| m.type == expected_metric.type && m.name == expected_metric.name }
|
59
|
-
|
60
|
-
if filtered_metrics.empty?
|
61
|
-
flunk_with_exception_info(exception_occurred, "No StatsD calls for metric #{expected_metric.name} " \
|
62
|
-
"of type #{expected_metric.type} were made.")
|
63
|
-
end
|
169
|
+
flunk(<<~MESSAGE)
|
170
|
+
An exception occurred in the block provided to the StatsD assertion.
|
64
171
|
|
65
|
-
|
66
|
-
|
172
|
+
#{exception.class.name}: #{exception.message}
|
173
|
+
\t#{exception.backtrace.join("\n\t")}
|
67
174
|
|
68
|
-
|
69
|
-
|
175
|
+
If this exception is expected, make sure to handle it using `assert_raises`
|
176
|
+
inside the block provided to the StatsD assertion.
|
177
|
+
MESSAGE
|
178
|
+
end
|
70
179
|
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
180
|
+
metrics = capture_backend.collected_metrics
|
181
|
+
matched_expected_metrics = []
|
182
|
+
expected_metrics.each do |expected_metric|
|
183
|
+
expected_metric_times = expected_metric.times
|
184
|
+
expected_metric_times_remaining = expected_metric.times
|
185
|
+
filtered_metrics = metrics.select { |m| m.type == expected_metric.type && m.name == expected_metric.name }
|
75
186
|
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
matched_expected_metrics << expected_metric
|
80
|
-
end
|
81
|
-
end
|
187
|
+
if filtered_metrics.empty?
|
188
|
+
flunk("No StatsD calls for metric #{expected_metric.name} of type #{expected_metric.type} were made.")
|
189
|
+
end
|
82
190
|
|
83
|
-
|
191
|
+
filtered_metrics.each do |metric|
|
192
|
+
next unless expected_metric.matches(metric)
|
84
193
|
|
85
|
-
|
86
|
-
"#{
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
194
|
+
assert(within_numeric_range?(metric.sample_rate),
|
195
|
+
"Unexpected sample rate type for metric #{metric.name}, must be numeric")
|
196
|
+
|
197
|
+
if expected_metric_times_remaining == 0
|
198
|
+
flunk("Unexpected StatsD call; number of times this metric " \
|
199
|
+
"was expected exceeded: #{expected_metric.inspect}")
|
200
|
+
end
|
92
201
|
|
93
|
-
|
94
|
-
|
95
|
-
|
202
|
+
expected_metric_times_remaining -= 1
|
203
|
+
metrics.delete(metric)
|
204
|
+
if expected_metric_times_remaining == 0
|
205
|
+
matched_expected_metrics << expected_metric
|
206
|
+
end
|
96
207
|
end
|
97
208
|
|
98
|
-
|
209
|
+
next if expected_metric_times_remaining == 0
|
210
|
+
|
211
|
+
msg = +"Metric expected #{expected_metric_times} times but seen " \
|
212
|
+
"#{expected_metric_times - expected_metric_times_remaining} " \
|
213
|
+
"times: #{expected_metric.inspect}."
|
214
|
+
msg << "\nCaptured metrics with the same key: #{filtered_metrics}" if filtered_metrics.any?
|
215
|
+
flunk(msg)
|
99
216
|
end
|
100
|
-
|
101
|
-
end
|
217
|
+
expected_metrics -= matched_expected_metrics
|
102
218
|
|
103
|
-
|
219
|
+
unless expected_metrics.empty?
|
220
|
+
flunk("Unexpected StatsD calls; the following metric expectations " \
|
221
|
+
"were not satisfied: #{expected_metrics.inspect}")
|
222
|
+
end
|
104
223
|
|
105
|
-
|
106
|
-
if exception
|
107
|
-
flunk(<<~EXCEPTION)
|
108
|
-
#{message}
|
109
|
-
|
110
|
-
This could be due to the exception that occurred inside the block:
|
111
|
-
#{exception.class.name}: #{exception.message}
|
112
|
-
\t#{exception.backtrace.join("\n\t")}
|
113
|
-
EXCEPTION
|
114
|
-
else
|
115
|
-
flunk(message)
|
224
|
+
pass
|
116
225
|
end
|
117
226
|
end
|
118
227
|
|
228
|
+
private
|
229
|
+
|
119
230
|
def assert_statsd_call(metric_type, metric_name, options = {}, &block)
|
120
231
|
options[:name] = metric_name
|
121
232
|
options[:type] = metric_type
|