statsd-instrument 1.5.0 → 1.6.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.travis.yml +1 -0
- data/README.md +12 -2
- data/Rakefile +1 -1
- data/lib/statsd/instrument.rb +100 -38
- data/lib/statsd/instrument/version.rb +5 -0
- data/statsd-instrument.gemspec +19 -11
- data/test/statsd_instrumentation_test.rb +189 -0
- data/test/statsd_test.rb +209 -0
- data/test/test_helper.rb +7 -0
- metadata +35 -19
- data/test/statsd-instrument_test.rb +0 -339
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: b9bc489157242e310ea28c0a1680fb34c608a8e6
|
4
|
+
data.tar.gz: 64ca871cc2a93f2fed3c5690fa6b53a1cd864925
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 7eb191b76130aef5e14cc7e9b74d22936809713339b5accfa25c995507fb8ebf2ba9d879cba31ed64c6773ec97b82c38e1ccbc9a0352597c3f972dc46afdc9c2
|
7
|
+
data.tar.gz: 3390177c19dfa7c9bca20f9d9a26fe36ead251bf8142132d0b866c2a09e435211c74cad4897156a4c914aa6b285d415896153a8ebd5447cda6076ae06a451cb4
|
data/.travis.yml
CHANGED
data/README.md
CHANGED
@@ -28,6 +28,13 @@ StatsD.default_sample_rate = 0.1 # Sample 10% of events. By default all events a
|
|
28
28
|
|
29
29
|
If you set the mode to anything besides production then the library will print its calls to the logger, rather than sending them over the wire.
|
30
30
|
|
31
|
+
There are several implementations of StatsD out there, all with slight protocol variations. You can this library to use the proper protocol by informing it about what implementation you use. By default, it will use the protocol of the original Etsy implementation.
|
32
|
+
|
33
|
+
```
|
34
|
+
StatsD.implementation = :datadog # Enable datadog extensions: tags and histograms
|
35
|
+
StatsD.implementation = :statsite # Enable keyvalue-style gauges
|
36
|
+
```
|
37
|
+
|
31
38
|
## StatsD keys
|
32
39
|
|
33
40
|
StatsD keys look like 'admin.logins.api.success'. Each dot in the key represents a 'folder' in the graphite interface. You can include any data you want in the keys.
|
@@ -148,20 +155,23 @@ GoogleBase.statsd_count :insert, lamdba{|object, args| object.class.to_s.downcas
|
|
148
155
|
```
|
149
156
|
|
150
157
|
## Reliance on DNS
|
158
|
+
|
151
159
|
Out of the box StatsD is set up to be unidirectional fire-and-forget over UDP. Configuring the StatsD host to be a non-ip will trigger a DNS lookup (ie synchronous round trip network call) for each metric sent. This can be particularly problematic in clouds that have a shared DNS infrastructure such as AWS.
|
152
160
|
|
153
161
|
### Common Workarounds
|
162
|
+
|
154
163
|
1. Using an IP avoids the DNS lookup but generally requires an application deploy to change.
|
155
164
|
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.
|
156
165
|
3. Installing caching software such as nscd that uses the DNS TTL avoids most DNS lookups but makes the exact moment of change indeterminate.
|
157
166
|
|
158
167
|
## Compatibility
|
159
168
|
|
160
|
-
Tested on:
|
169
|
+
Tested on several Ruby versions using Travis CI:
|
161
170
|
|
162
171
|
* Ruby 1.8.7
|
163
172
|
* Ruby Enterprise Edition 1.8.7
|
164
173
|
* Ruby 1.9.2
|
165
174
|
* Ruby 1.9.3
|
175
|
+
* Ruby 2.0.0
|
166
176
|
|
167
|
-
Ruby
|
177
|
+
Ruby 2.0 compatibility is planned for the long term. Your mileage may vary with other Ruby environments.
|
data/Rakefile
CHANGED
data/lib/statsd/instrument.rb
CHANGED
@@ -1,13 +1,7 @@
|
|
1
1
|
require 'socket'
|
2
2
|
require 'benchmark'
|
3
|
-
require 'timeout'
|
4
|
-
|
5
|
-
class << Benchmark
|
6
|
-
def ms
|
7
|
-
1000 * realtime { yield }
|
8
|
-
end
|
9
|
-
end
|
10
3
|
|
4
|
+
require 'statsd/instrument/version'
|
11
5
|
|
12
6
|
module StatsD
|
13
7
|
class << self
|
@@ -18,19 +12,33 @@ module StatsD
|
|
18
12
|
self.default_sample_rate = 1.0
|
19
13
|
self.implementation = :statsd
|
20
14
|
|
21
|
-
TimeoutClass = defined?(::SystemTimer) ? ::SystemTimer : ::Timeout
|
22
|
-
|
23
15
|
# StatsD.server = 'localhost:1234'
|
24
16
|
def self.server=(conn)
|
25
17
|
self.host, port = conn.split(':')
|
26
18
|
self.port = port.to_i
|
19
|
+
invalidate_socket
|
20
|
+
end
|
21
|
+
|
22
|
+
def self.host=(host)
|
23
|
+
@host = host
|
24
|
+
invalidate_socket
|
25
|
+
end
|
26
|
+
|
27
|
+
def self.port=(port)
|
28
|
+
@port = port
|
29
|
+
invalidate_socket
|
27
30
|
end
|
28
31
|
|
29
32
|
module Instrument
|
33
|
+
|
34
|
+
def self.generate_metric_name(metric_name, callee, *args)
|
35
|
+
metric_name.respond_to?(:call) ? metric_name.call(callee, args).gsub('::', '.') : metric_name.gsub('::', '.')
|
36
|
+
end
|
37
|
+
|
30
38
|
def statsd_measure(method, name, sample_rate = StatsD.default_sample_rate)
|
31
39
|
add_to_method(method, name, :measure) do |old_method, new_method, metric_name, *args|
|
32
40
|
define_method(new_method) do |*args, &block|
|
33
|
-
StatsD.measure(
|
41
|
+
StatsD.measure(StatsD::Instrument.generate_metric_name(metric_name, self, *args), nil, sample_rate) { send(old_method, *args, &block) }
|
34
42
|
end
|
35
43
|
end
|
36
44
|
end
|
@@ -47,10 +55,8 @@ module StatsD
|
|
47
55
|
truthiness = (yield(result) rescue false) if block_given?
|
48
56
|
result
|
49
57
|
ensure
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
StatsD.increment("#{key}.#{result}", sample_rate)
|
58
|
+
suffix = truthiness == false ? 'failure' : 'success'
|
59
|
+
StatsD.increment("#{StatsD::Instrument.generate_metric_name(metric_name, self, *args)}.#{suffix}", 1, sample_rate)
|
54
60
|
end
|
55
61
|
end
|
56
62
|
end
|
@@ -68,7 +74,7 @@ module StatsD
|
|
68
74
|
truthiness = (yield(result) rescue false) if block_given?
|
69
75
|
result
|
70
76
|
ensure
|
71
|
-
StatsD.increment(
|
77
|
+
StatsD.increment(StatsD::Instrument.generate_metric_name(metric_name, self, *args), sample_rate) if truthiness
|
72
78
|
end
|
73
79
|
end
|
74
80
|
end
|
@@ -77,13 +83,30 @@ module StatsD
|
|
77
83
|
def statsd_count(method, name, sample_rate = StatsD.default_sample_rate)
|
78
84
|
add_to_method(method, name, :count) do |old_method, new_method, metric_name|
|
79
85
|
define_method(new_method) do |*args, &block|
|
80
|
-
StatsD.increment(
|
86
|
+
StatsD.increment(StatsD::Instrument.generate_metric_name(metric_name, self, *args), 1, sample_rate)
|
81
87
|
send(old_method, *args, &block)
|
82
88
|
end
|
83
89
|
end
|
84
90
|
end
|
85
91
|
|
92
|
+
def statsd_remove_count(method, name)
|
93
|
+
remove_from_method(method, name, :count)
|
94
|
+
end
|
95
|
+
|
96
|
+
def statsd_remove_count_if(method, name)
|
97
|
+
remove_from_method(method, name, :count_if)
|
98
|
+
end
|
99
|
+
|
100
|
+
def statsd_remove_count_success(method, name)
|
101
|
+
remove_from_method(method, name, :count_success)
|
102
|
+
end
|
103
|
+
|
104
|
+
def statsd_remove_measure(method, name)
|
105
|
+
remove_from_method(method, name, :measure)
|
106
|
+
end
|
107
|
+
|
86
108
|
private
|
109
|
+
|
87
110
|
def add_to_method(method, name, action, &block)
|
88
111
|
metric_name = name
|
89
112
|
|
@@ -100,42 +123,85 @@ module StatsD
|
|
100
123
|
yield method_name_without_statsd, method_name_with_statsd, metric_name
|
101
124
|
alias_method method, method_name_with_statsd
|
102
125
|
end
|
126
|
+
|
127
|
+
def remove_from_method(method, name, action)
|
128
|
+
method_name_without_statsd = :"#{method}_for_#{action}_on_#{self.name}_without_#{name}"
|
129
|
+
method_name_with_statsd = :"#{method}_for_#{action}_on_#{self.name}_with_#{name}"
|
130
|
+
send(:remove_method, method_name_with_statsd)
|
131
|
+
alias_method method, method_name_without_statsd
|
132
|
+
send(:remove_method, method_name_without_statsd)
|
133
|
+
end
|
103
134
|
end
|
104
135
|
|
105
136
|
# glork:320|ms
|
106
|
-
def self.measure(key, milli = nil, sample_rate = default_sample_rate)
|
137
|
+
def self.measure(key, milli = nil, sample_rate = default_sample_rate, tags = nil)
|
107
138
|
result = nil
|
108
|
-
ms = milli || Benchmark.
|
139
|
+
ms = milli || 1000 * Benchmark.realtime do
|
109
140
|
result = yield
|
110
141
|
end
|
111
142
|
|
112
|
-
|
143
|
+
collect(key, ms, :ms, sample_rate, tags)
|
113
144
|
result
|
114
145
|
end
|
115
146
|
|
116
147
|
# gorets:1|c
|
117
|
-
def self.increment(key, delta = 1, sample_rate = default_sample_rate)
|
118
|
-
|
148
|
+
def self.increment(key, delta = 1, sample_rate = default_sample_rate, tags = nil)
|
149
|
+
collect(key, delta, :incr, sample_rate, tags)
|
119
150
|
end
|
120
151
|
|
121
152
|
# gaugor:333|g
|
122
153
|
# guagor:1234|kv|@1339864935 (statsite)
|
123
|
-
def self.gauge(key, value, sample_rate_or_epoch = default_sample_rate)
|
124
|
-
|
154
|
+
def self.gauge(key, value, sample_rate_or_epoch = default_sample_rate, tags = nil)
|
155
|
+
collect(key, value, :g, sample_rate_or_epoch, tags)
|
125
156
|
end
|
126
157
|
|
158
|
+
# histogram:123.45|h
|
159
|
+
def self.histogram(key, value, sample_rate_or_epoch = default_sample_rate, tags = nil)
|
160
|
+
collect(key, value, :h, sample_rate_or_epoch, tags)
|
161
|
+
end
|
162
|
+
|
127
163
|
private
|
128
164
|
|
165
|
+
def self.invalidate_socket
|
166
|
+
@socket = nil
|
167
|
+
end
|
168
|
+
|
129
169
|
def self.socket
|
130
|
-
@socket
|
170
|
+
if @socket.nil?
|
171
|
+
@socket = UDPSocket.new
|
172
|
+
@socket.connect(host, port)
|
173
|
+
end
|
174
|
+
@socket
|
131
175
|
end
|
132
176
|
|
133
|
-
def self.
|
177
|
+
def self.collect(k, v, op, sample_rate = default_sample_rate, tags = nil)
|
134
178
|
return unless enabled
|
135
179
|
return if sample_rate < 1 && rand > sample_rate
|
136
180
|
|
137
|
-
|
181
|
+
command = generate_packet(k, v, op, sample_rate, tags)
|
182
|
+
write_packet(command)
|
183
|
+
end
|
184
|
+
|
185
|
+
def self.write_packet(command)
|
186
|
+
if mode.to_s == 'production'
|
187
|
+
begin
|
188
|
+
socket.send(command, 0)
|
189
|
+
rescue SocketError, IOError, SystemCallError => e
|
190
|
+
logger.error e
|
191
|
+
end
|
192
|
+
else
|
193
|
+
logger.info "[StatsD] #{command}"
|
194
|
+
end
|
195
|
+
end
|
138
196
|
|
197
|
+
def self.clean_tags(tags)
|
198
|
+
tags.map do |tag|
|
199
|
+
components = tag.split(':', 2)
|
200
|
+
components.map { |c| c.gsub(/[^\w\.-]+/, '_') }.join(':')
|
201
|
+
end
|
202
|
+
end
|
203
|
+
|
204
|
+
def self.generate_packet(k, v, op, sample_rate = default_sample_rate, tags = nil)
|
139
205
|
command = "#{self.prefix + '.' if self.prefix}#{k}:#{v}"
|
140
206
|
case op
|
141
207
|
when :incr
|
@@ -144,22 +210,18 @@ module StatsD
|
|
144
210
|
command << '|ms'
|
145
211
|
when :g
|
146
212
|
command << (self.implementation == :statsite ? '|kv' : '|g')
|
213
|
+
when :h
|
214
|
+
raise NotImplementedError, "Histograms only supported on DataDog implementation." unless self.implementation == :datadog
|
215
|
+
command << '|h'
|
147
216
|
end
|
148
217
|
|
149
218
|
command << "|@#{sample_rate}" if sample_rate < 1 || (self.implementation == :statsite && sample_rate > 1)
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
socket_wrapper { socket.send(command, 0, host, port) }
|
154
|
-
else
|
155
|
-
logger.info "[StatsD] #{command}"
|
219
|
+
if tags
|
220
|
+
raise ArgumentError, "Tags are only supported on Datadog" unless self.implementation == :datadog
|
221
|
+
command << "|##{clean_tags(tags).join(',')}"
|
156
222
|
end
|
157
|
-
end
|
158
223
|
|
159
|
-
|
160
|
-
|
161
|
-
rescue Timeout::Error, SocketError, IOError, SystemCallError => e
|
162
|
-
logger.error e
|
224
|
+
command << "\n" if self.implementation == :statsite
|
225
|
+
return command
|
163
226
|
end
|
164
227
|
end
|
165
|
-
|
data/statsd-instrument.gemspec
CHANGED
@@ -1,15 +1,23 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
s.email = ["jesse@shopify.com"]
|
6
|
-
s.homepage = "http://github.com/shopify/statsd-instrument"
|
1
|
+
# encoding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'statsd/instrument/version'
|
7
5
|
|
8
|
-
|
9
|
-
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "statsd-instrument"
|
8
|
+
spec.version = StatsD::Instrument::VERSION
|
9
|
+
spec.authors = ["Jesse Storimer", "Tobias Lutke"]
|
10
|
+
spec.email = ["jesse@shopify.com"]
|
11
|
+
spec.homepage = "https://github.com/Shopify/statsd-instrument"
|
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.}
|
14
|
+
spec.license = "MIT"
|
10
15
|
|
11
|
-
|
12
|
-
|
16
|
+
spec.files = `git ls-files`.split($/)
|
17
|
+
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
18
|
+
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
19
|
+
spec.require_paths = ["lib"]
|
13
20
|
|
14
|
-
|
21
|
+
spec.add_development_dependency 'rake'
|
22
|
+
spec.add_development_dependency 'mocha'
|
15
23
|
end
|
@@ -0,0 +1,189 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
|
3
|
+
module ActiveMerchant; end
|
4
|
+
class ActiveMerchant::Base
|
5
|
+
def ssl_post(arg)
|
6
|
+
if arg
|
7
|
+
'OK'
|
8
|
+
else
|
9
|
+
raise 'Not OK'
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
def post_with_block(&block)
|
14
|
+
yield if block_given?
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
class ActiveMerchant::Gateway < ActiveMerchant::Base
|
19
|
+
def purchase(arg)
|
20
|
+
ssl_post(arg)
|
21
|
+
true
|
22
|
+
rescue
|
23
|
+
false
|
24
|
+
end
|
25
|
+
|
26
|
+
def self.sync
|
27
|
+
true
|
28
|
+
end
|
29
|
+
|
30
|
+
def self.singleton_class
|
31
|
+
class << self; self; end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
class ActiveMerchant::UniqueGateway < ActiveMerchant::Base
|
36
|
+
def ssl_post(arg)
|
37
|
+
{:success => arg}
|
38
|
+
end
|
39
|
+
|
40
|
+
def purchase(arg)
|
41
|
+
ssl_post(arg)
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
class GatewaySubClass < ActiveMerchant::Gateway
|
46
|
+
end
|
47
|
+
|
48
|
+
ActiveMerchant::Base.extend StatsD::Instrument
|
49
|
+
|
50
|
+
class StatsDInstrumentationTest < Test::Unit::TestCase
|
51
|
+
def test_statsd_count_if
|
52
|
+
ActiveMerchant::Gateway.statsd_count_if :ssl_post, 'ActiveMerchant.Gateway.if'
|
53
|
+
|
54
|
+
StatsD.expects(:increment).with('ActiveMerchant.Gateway.if', 1).once
|
55
|
+
ActiveMerchant::Gateway.new.purchase(true)
|
56
|
+
ActiveMerchant::Gateway.new.purchase(false)
|
57
|
+
|
58
|
+
ActiveMerchant::Gateway.statsd_remove_count_if :ssl_post, 'ActiveMerchant.Gateway.if'
|
59
|
+
end
|
60
|
+
|
61
|
+
def test_statsd_count_if_with_method_receiving_block
|
62
|
+
ActiveMerchant::Base.statsd_count_if :post_with_block, 'ActiveMerchant.Base.post_with_block' do |result|
|
63
|
+
result == 'true'
|
64
|
+
end
|
65
|
+
|
66
|
+
StatsD.expects(:collect).with('ActiveMerchant.Base.post_with_block', 1, :incr, 1.0, nil).once
|
67
|
+
assert_equal 'true', ActiveMerchant::Base.new.post_with_block { 'true' }
|
68
|
+
assert_equal 'false', ActiveMerchant::Base.new.post_with_block { 'false' }
|
69
|
+
|
70
|
+
ActiveMerchant::Base.statsd_remove_count_if :post_with_block, 'ActiveMerchant.Base.post_with_block'
|
71
|
+
end
|
72
|
+
|
73
|
+
def test_statsd_count_if_with_block
|
74
|
+
ActiveMerchant::UniqueGateway.statsd_count_if :ssl_post, 'ActiveMerchant.Gateway.block' do |result|
|
75
|
+
result[:success]
|
76
|
+
end
|
77
|
+
|
78
|
+
StatsD.expects(:increment).with('ActiveMerchant.Gateway.block', 1).once
|
79
|
+
ActiveMerchant::UniqueGateway.new.purchase(true)
|
80
|
+
ActiveMerchant::UniqueGateway.new.purchase(false)
|
81
|
+
|
82
|
+
ActiveMerchant::UniqueGateway.statsd_remove_count_if :ssl_post, 'ActiveMerchant.Gateway.block'
|
83
|
+
end
|
84
|
+
|
85
|
+
def test_statsd_count_success
|
86
|
+
ActiveMerchant::Gateway.statsd_count_success :ssl_post, 'ActiveMerchant.Gateway', 0.5
|
87
|
+
|
88
|
+
StatsD.expects(:increment).with('ActiveMerchant.Gateway.success', 1, 0.5).once
|
89
|
+
StatsD.expects(:increment).with('ActiveMerchant.Gateway.failure', 1, 0.5).once
|
90
|
+
|
91
|
+
ActiveMerchant::Gateway.new.purchase(true)
|
92
|
+
ActiveMerchant::Gateway.new.purchase(false)
|
93
|
+
|
94
|
+
ActiveMerchant::Gateway.statsd_remove_count_success :ssl_post, 'ActiveMerchant.Gateway'
|
95
|
+
end
|
96
|
+
|
97
|
+
def test_statsd_count_success_with_method_receiving_block
|
98
|
+
ActiveMerchant::Base.statsd_count_success :post_with_block, 'ActiveMerchant.Base.post_with_block' do |result|
|
99
|
+
result == 'successful'
|
100
|
+
end
|
101
|
+
|
102
|
+
StatsD.expects(:collect).with('ActiveMerchant.Base.post_with_block.success', 1, :incr, 1.0, nil).once
|
103
|
+
StatsD.expects(:collect).with('ActiveMerchant.Base.post_with_block.failure', 1, :incr, 1.0, nil).once
|
104
|
+
|
105
|
+
assert_equal 'successful', ActiveMerchant::Base.new.post_with_block { 'successful' }
|
106
|
+
assert_equal 'not so successful', ActiveMerchant::Base.new.post_with_block { 'not so successful' }
|
107
|
+
|
108
|
+
ActiveMerchant::Base.statsd_remove_count_success :post_with_block, 'ActiveMerchant.Base.post_with_block'
|
109
|
+
end
|
110
|
+
|
111
|
+
def test_statsd_count_success_with_block
|
112
|
+
ActiveMerchant::UniqueGateway.statsd_count_success :ssl_post, 'ActiveMerchant.Gateway' do |result|
|
113
|
+
result[:success]
|
114
|
+
end
|
115
|
+
|
116
|
+
StatsD.expects(:increment).with('ActiveMerchant.Gateway.success', 1, StatsD.default_sample_rate)
|
117
|
+
ActiveMerchant::UniqueGateway.new.purchase(true)
|
118
|
+
|
119
|
+
StatsD.expects(:increment).with('ActiveMerchant.Gateway.failure', 1, StatsD.default_sample_rate)
|
120
|
+
ActiveMerchant::UniqueGateway.new.purchase(false)
|
121
|
+
|
122
|
+
ActiveMerchant::UniqueGateway.statsd_remove_count_success :ssl_post, 'ActiveMerchant.Gateway'
|
123
|
+
end
|
124
|
+
|
125
|
+
def test_statsd_count
|
126
|
+
ActiveMerchant::Gateway.statsd_count :ssl_post, 'ActiveMerchant.Gateway.ssl_post'
|
127
|
+
|
128
|
+
StatsD.expects(:increment).with('ActiveMerchant.Gateway.ssl_post', 1, StatsD.default_sample_rate)
|
129
|
+
ActiveMerchant::Gateway.new.purchase(true)
|
130
|
+
|
131
|
+
ActiveMerchant::Gateway.statsd_remove_count :ssl_post, 'ActiveMerchant.Gateway.ssl_post'
|
132
|
+
end
|
133
|
+
|
134
|
+
def test_statsd_count_with_name_as_lambda
|
135
|
+
metric_namer = lambda { |object, args| object.class.to_s.downcase + ".insert." + args.first.to_s }
|
136
|
+
ActiveMerchant::Gateway.statsd_count(:ssl_post, metric_namer)
|
137
|
+
|
138
|
+
StatsD.expects(:increment).with('gatewaysubclass.insert.true', 1, StatsD.default_sample_rate)
|
139
|
+
GatewaySubClass.new.purchase(true)
|
140
|
+
|
141
|
+
ActiveMerchant::Gateway.statsd_remove_count(:ssl_post, metric_namer)
|
142
|
+
end
|
143
|
+
|
144
|
+
def test_statsd_count_with_method_receiving_block
|
145
|
+
ActiveMerchant::Base.statsd_count :post_with_block, 'ActiveMerchant.Base.post_with_block'
|
146
|
+
|
147
|
+
StatsD.expects(:collect).with('ActiveMerchant.Base.post_with_block', 1, :incr, 1.0, nil)
|
148
|
+
assert_equal 'block called', ActiveMerchant::Base.new.post_with_block { 'block called' }
|
149
|
+
|
150
|
+
ActiveMerchant::Base.statsd_remove_count :post_with_block, 'ActiveMerchant.Base.post_with_block'
|
151
|
+
end
|
152
|
+
|
153
|
+
def test_statsd_measure_with_nested_modules
|
154
|
+
ActiveMerchant::UniqueGateway.statsd_measure :ssl_post, 'ActiveMerchant::Gateway.ssl_post'
|
155
|
+
|
156
|
+
StatsD.expects(:measure).with('ActiveMerchant.Gateway.ssl_post', nil, StatsD.default_sample_rate)
|
157
|
+
ActiveMerchant::UniqueGateway.new.purchase(true)
|
158
|
+
|
159
|
+
ActiveMerchant::UniqueGateway.statsd_remove_measure :ssl_post, 'ActiveMerchant::Gateway.ssl_post'
|
160
|
+
end
|
161
|
+
|
162
|
+
def test_statsd_measure
|
163
|
+
ActiveMerchant::UniqueGateway.statsd_measure :ssl_post, 'ActiveMerchant.Gateway.ssl_post', 0.3
|
164
|
+
|
165
|
+
StatsD.expects(:measure).with('ActiveMerchant.Gateway.ssl_post', nil, 0.3)
|
166
|
+
ActiveMerchant::UniqueGateway.new.purchase(true)
|
167
|
+
|
168
|
+
ActiveMerchant::UniqueGateway.statsd_remove_measure :ssl_post, 'ActiveMerchant.Gateway.ssl_post'
|
169
|
+
end
|
170
|
+
|
171
|
+
def test_statsd_measure_with_method_receiving_block
|
172
|
+
ActiveMerchant::Base.statsd_measure :post_with_block, 'ActiveMerchant.Base.post_with_block'
|
173
|
+
|
174
|
+
StatsD.expects(:collect).with('ActiveMerchant.Base.post_with_block', is_a(Float), :ms, 1.0, nil)
|
175
|
+
assert_equal 'block called', ActiveMerchant::Base.new.post_with_block { 'block called' }
|
176
|
+
|
177
|
+
ActiveMerchant::Base.statsd_remove_measure :post_with_block, 'ActiveMerchant.Base.post_with_block'
|
178
|
+
end
|
179
|
+
|
180
|
+
def test_instrumenting_class_method
|
181
|
+
ActiveMerchant::Gateway.singleton_class.extend StatsD::Instrument
|
182
|
+
ActiveMerchant::Gateway.singleton_class.statsd_count :sync, 'ActiveMerchant.Gateway.sync'
|
183
|
+
|
184
|
+
StatsD.expects(:increment).with('ActiveMerchant.Gateway.sync', 1, StatsD.default_sample_rate)
|
185
|
+
ActiveMerchant::Gateway.sync
|
186
|
+
|
187
|
+
ActiveMerchant::Gateway.singleton_class.statsd_remove_count :sync, 'ActiveMerchant.Gateway.sync'
|
188
|
+
end
|
189
|
+
end
|
data/test/statsd_test.rb
ADDED
@@ -0,0 +1,209 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
|
3
|
+
class StatsDTest < Test::Unit::TestCase
|
4
|
+
|
5
|
+
def setup
|
6
|
+
StatsD.stubs(:rand).returns(0.0)
|
7
|
+
|
8
|
+
UDPSocket.stubs(:new).returns(@socket = mock('socket'))
|
9
|
+
@socket.stubs(:connect)
|
10
|
+
@socket.stubs(:send)
|
11
|
+
StatsD.invalidate_socket
|
12
|
+
|
13
|
+
StatsD.stubs(:logger).returns(@logger = mock('logger'))
|
14
|
+
@logger.stubs(:info)
|
15
|
+
@logger.stubs(:error)
|
16
|
+
end
|
17
|
+
|
18
|
+
def test_statsd_measure_with_explicit_value
|
19
|
+
StatsD.expects(:collect).with('values.foobar', 42, :ms, 1.0, nil)
|
20
|
+
StatsD.measure('values.foobar', 42)
|
21
|
+
end
|
22
|
+
|
23
|
+
def test_statsd_measure_with_explicit_value_and_sample_rate
|
24
|
+
StatsD.expects(:collect).with('values.foobar', 42, :ms, 0.1, nil)
|
25
|
+
StatsD.measure('values.foobar', 42, 0.1)
|
26
|
+
end
|
27
|
+
|
28
|
+
def test_statsd_measure_with_benchmarked_value
|
29
|
+
Benchmark.stubs(:realtime).returns(1.12)
|
30
|
+
StatsD.expects(:collect).with('values.foobar', 1120.0, :ms, 1.0, nil)
|
31
|
+
StatsD.measure('values.foobar', nil) do
|
32
|
+
#noop
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
def test_statsd_gauge
|
37
|
+
StatsD.expects(:collect).with('values.foobar', 12, :g, 1.0, nil)
|
38
|
+
StatsD.gauge('values.foobar', 12)
|
39
|
+
end
|
40
|
+
|
41
|
+
def test_statsd_histogram_on_datadog
|
42
|
+
StatsD.stubs(:implementation).returns(:datadog)
|
43
|
+
StatsD.expects(:collect).with('values.hg', 12.33, :h, 0.2, ['tag_123', 'key-name:value123'])
|
44
|
+
StatsD.histogram('values.hg', 12.33, 0.2, ['tag_123', 'key-name:value123'])
|
45
|
+
end
|
46
|
+
|
47
|
+
def test_collect_respects_enabled
|
48
|
+
StatsD.stubs(:enabled).returns(false)
|
49
|
+
StatsD.expects(:write_packet).never
|
50
|
+
StatsD.increment('counter')
|
51
|
+
end
|
52
|
+
|
53
|
+
def test_collect_respects_sampling_rate
|
54
|
+
StatsD.expects(:write_packet).once
|
55
|
+
|
56
|
+
StatsD.stubs(:rand).returns(0.6)
|
57
|
+
StatsD.increment('counter', 1, 0.5)
|
58
|
+
|
59
|
+
StatsD.stubs(:rand).returns(0.4)
|
60
|
+
StatsD.increment('counter', 1, 0.5)
|
61
|
+
end
|
62
|
+
|
63
|
+
def test_support_counter_syntax
|
64
|
+
StatsD.expects(:write_packet).with('counter:1|c')
|
65
|
+
StatsD.increment('counter')
|
66
|
+
|
67
|
+
StatsD.expects(:write_packet).with('counter:10|c|@0.5')
|
68
|
+
StatsD.increment('counter', 10, 0.5)
|
69
|
+
end
|
70
|
+
|
71
|
+
def test_supports_gauge_syntax
|
72
|
+
StatsD.expects(:write_packet).with('fooy:1.23|g')
|
73
|
+
StatsD.gauge('fooy', 1.23)
|
74
|
+
|
75
|
+
StatsD.expects(:write_packet).with('fooy:42|g|@0.01')
|
76
|
+
StatsD.gauge('fooy', 42, 0.01)
|
77
|
+
end
|
78
|
+
|
79
|
+
def test_support_timing_syntax
|
80
|
+
StatsD.expects(:write_packet).with('duration:1.23|ms')
|
81
|
+
StatsD.measure('duration', 1.23)
|
82
|
+
|
83
|
+
StatsD.expects(:write_packet).with('duration:0.42|ms|@0.01')
|
84
|
+
StatsD.measure('duration', 0.42, 0.01)
|
85
|
+
end
|
86
|
+
|
87
|
+
def test_histogram_syntax_on_datadog
|
88
|
+
StatsD.stubs(:implementation).returns(:datadog)
|
89
|
+
|
90
|
+
StatsD.expects(:write_packet).with('fooh:42.4|h')
|
91
|
+
StatsD.histogram('fooh', 42.4)
|
92
|
+
end
|
93
|
+
|
94
|
+
def test_support_tags_syntax_on_datadog
|
95
|
+
StatsD.stubs(:implementation).returns(:datadog)
|
96
|
+
|
97
|
+
StatsD.expects(:write_packet).with("fooc:3|c|#topic:foo,bar")
|
98
|
+
StatsD.increment('fooc', 3, 1.0, ['topic:foo', 'bar'])
|
99
|
+
end
|
100
|
+
|
101
|
+
def test_raise_when_using_tags_and_not_on_datadog
|
102
|
+
StatsD.stubs(:implementation).returns(:other)
|
103
|
+
assert_raises(ArgumentError) { StatsD.increment('fooc', 3, 1.0, ['nonempty']) }
|
104
|
+
end
|
105
|
+
|
106
|
+
def test_rewrite_shitty_tags
|
107
|
+
StatsD.stubs(:implementation).returns(:datadog)
|
108
|
+
|
109
|
+
assert_equal ['igno_red'], StatsD.clean_tags(['igno,red'])
|
110
|
+
assert_equal ['igno_red'], StatsD.clean_tags(['igno red'])
|
111
|
+
assert_equal ['test:test_test'], StatsD.clean_tags(['test:test:test'])
|
112
|
+
|
113
|
+
StatsD.expects(:write_packet).with("fooc:3|c|#topic:foo_foo,bar_")
|
114
|
+
StatsD.increment('fooc', 3, 1.0, ['topic:foo : foo', 'bar '])
|
115
|
+
end
|
116
|
+
|
117
|
+
def test_supports_gauge_syntax_on_statsite
|
118
|
+
StatsD.stubs(:implementation).returns(:statsite)
|
119
|
+
|
120
|
+
StatsD.expects(:write_packet).with("fooy:42|kv\n")
|
121
|
+
StatsD.gauge('fooy', 42)
|
122
|
+
end
|
123
|
+
|
124
|
+
def test_supports_gauge_timestamp_on_statsite
|
125
|
+
StatsD.stubs(:implementation).returns(:statsite)
|
126
|
+
|
127
|
+
StatsD.expects(:write_packet).with("fooy:42|kv|@123456\n")
|
128
|
+
StatsD.gauge('fooy', 42, 123456)
|
129
|
+
end
|
130
|
+
|
131
|
+
def test_support_key_prefix
|
132
|
+
StatsD.expects(:write_packet).with('prefix.counter:1|c').once
|
133
|
+
StatsD.expects(:write_packet).with('counter:1|c').once
|
134
|
+
|
135
|
+
StatsD.stubs(:prefix).returns('prefix')
|
136
|
+
StatsD.increment('counter')
|
137
|
+
StatsD.stubs(:prefix).returns(nil)
|
138
|
+
StatsD.increment('counter')
|
139
|
+
end
|
140
|
+
|
141
|
+
def test_development_mode_uses_logger
|
142
|
+
StatsD.stubs(:mode).returns(:development)
|
143
|
+
|
144
|
+
@logger.expects(:info).with(regexp_matches(/\A\[StatsD\] /))
|
145
|
+
StatsD.increment('counter')
|
146
|
+
end
|
147
|
+
|
148
|
+
def test_production_mode_uses_udp_socket
|
149
|
+
StatsD.stubs(:mode).returns(:production)
|
150
|
+
StatsD.server = "localhost:9815"
|
151
|
+
|
152
|
+
@socket.expects(:connect).with('localhost', 9815).once
|
153
|
+
@socket.expects(:send).with(is_a(String), 0).twice
|
154
|
+
StatsD.increment('counter')
|
155
|
+
StatsD.increment('counter')
|
156
|
+
end
|
157
|
+
|
158
|
+
def test_changing_host_or_port_should_create_new_socket
|
159
|
+
@socket.expects(:connect).with('localhost', 1234).once
|
160
|
+
@socket.expects(:connect).with('localhost', 2345).once
|
161
|
+
@socket.expects(:connect).with('127.0.0.1', 2345).once
|
162
|
+
|
163
|
+
StatsD.server = "localhost:1234"
|
164
|
+
StatsD.send(:socket)
|
165
|
+
|
166
|
+
StatsD.port = 2345
|
167
|
+
StatsD.send(:socket)
|
168
|
+
|
169
|
+
StatsD.host = '127.0.0.1'
|
170
|
+
StatsD.send(:socket)
|
171
|
+
end
|
172
|
+
|
173
|
+
def test_socket_error_should_not_raise_but_log
|
174
|
+
StatsD.stubs(:mode).returns(:production)
|
175
|
+
@socket.stubs(:connect).raises(SocketError)
|
176
|
+
|
177
|
+
@logger.expects(:error).with(instance_of(SocketError))
|
178
|
+
StatsD.measure('values.foobar', 42)
|
179
|
+
end
|
180
|
+
|
181
|
+
def test_system_call_error_should_not_raise_but_log
|
182
|
+
StatsD.stubs(:mode).returns(:production)
|
183
|
+
@socket.stubs(:send).raises(Errno::ETIMEDOUT)
|
184
|
+
|
185
|
+
@logger.expects(:error).with(instance_of(Errno::ETIMEDOUT))
|
186
|
+
StatsD.measure('values.foobar', 42)
|
187
|
+
end
|
188
|
+
|
189
|
+
def test_io_error_should_not_raise_but_log
|
190
|
+
StatsD.stubs(:mode).returns(:production)
|
191
|
+
@socket.stubs(:send).raises(IOError)
|
192
|
+
|
193
|
+
@logger.expects(:error).with(instance_of(IOError))
|
194
|
+
StatsD.measure('values.foobar', 42)
|
195
|
+
end
|
196
|
+
|
197
|
+
def test_live_local_udp_socket
|
198
|
+
UDPSocket.unstub(:new)
|
199
|
+
|
200
|
+
StatsD.stubs(:mode).returns(:production)
|
201
|
+
StatsD.server = "localhost:31798"
|
202
|
+
|
203
|
+
server = UDPSocket.new
|
204
|
+
server.bind('localhost', 31798)
|
205
|
+
|
206
|
+
StatsD.increment('counter')
|
207
|
+
assert_equal "counter:1|c", server.recvfrom(100).first
|
208
|
+
end
|
209
|
+
end
|
data/test/test_helper.rb
ADDED
metadata
CHANGED
@@ -1,8 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: statsd-instrument
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
5
|
-
prerelease:
|
4
|
+
version: 1.6.0
|
6
5
|
platform: ruby
|
7
6
|
authors:
|
8
7
|
- Jesse Storimer
|
@@ -10,26 +9,38 @@ authors:
|
|
10
9
|
autorequire:
|
11
10
|
bindir: bin
|
12
11
|
cert_chain: []
|
13
|
-
date:
|
12
|
+
date: 2014-01-21 00:00:00.000000000 Z
|
14
13
|
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: rake
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
17
|
+
requirements:
|
18
|
+
- - '>='
|
19
|
+
- !ruby/object:Gem::Version
|
20
|
+
version: '0'
|
21
|
+
type: :development
|
22
|
+
prerelease: false
|
23
|
+
version_requirements: !ruby/object:Gem::Requirement
|
24
|
+
requirements:
|
25
|
+
- - '>='
|
26
|
+
- !ruby/object:Gem::Version
|
27
|
+
version: '0'
|
15
28
|
- !ruby/object:Gem::Dependency
|
16
29
|
name: mocha
|
17
30
|
requirement: !ruby/object:Gem::Requirement
|
18
|
-
none: false
|
19
31
|
requirements:
|
20
|
-
- -
|
32
|
+
- - '>='
|
21
33
|
- !ruby/object:Gem::Version
|
22
34
|
version: '0'
|
23
35
|
type: :development
|
24
36
|
prerelease: false
|
25
37
|
version_requirements: !ruby/object:Gem::Requirement
|
26
|
-
none: false
|
27
38
|
requirements:
|
28
|
-
- -
|
39
|
+
- - '>='
|
29
40
|
- !ruby/object:Gem::Version
|
30
41
|
version: '0'
|
31
|
-
description: A StatsD client for Ruby
|
32
|
-
StatsD instrumentation into your code.
|
42
|
+
description: A StatsD client for Ruby appspec. Provides metaprogramming methods to
|
43
|
+
inject StatsD instrumentation into your code.
|
33
44
|
email:
|
34
45
|
- jesse@shopify.com
|
35
46
|
executables: []
|
@@ -44,31 +55,36 @@ files:
|
|
44
55
|
- Rakefile
|
45
56
|
- lib/statsd-instrument.rb
|
46
57
|
- lib/statsd/instrument.rb
|
58
|
+
- lib/statsd/instrument/version.rb
|
47
59
|
- statsd-instrument.gemspec
|
48
|
-
- test/
|
49
|
-
|
50
|
-
|
60
|
+
- test/statsd_instrumentation_test.rb
|
61
|
+
- test/statsd_test.rb
|
62
|
+
- test/test_helper.rb
|
63
|
+
homepage: https://github.com/Shopify/statsd-instrument
|
64
|
+
licenses:
|
65
|
+
- MIT
|
66
|
+
metadata: {}
|
51
67
|
post_install_message:
|
52
68
|
rdoc_options: []
|
53
69
|
require_paths:
|
54
70
|
- lib
|
55
71
|
required_ruby_version: !ruby/object:Gem::Requirement
|
56
|
-
none: false
|
57
72
|
requirements:
|
58
|
-
- -
|
73
|
+
- - '>='
|
59
74
|
- !ruby/object:Gem::Version
|
60
75
|
version: '0'
|
61
76
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
62
|
-
none: false
|
63
77
|
requirements:
|
64
|
-
- -
|
78
|
+
- - '>='
|
65
79
|
- !ruby/object:Gem::Version
|
66
80
|
version: '0'
|
67
81
|
requirements: []
|
68
82
|
rubyforge_project:
|
69
|
-
rubygems_version:
|
83
|
+
rubygems_version: 2.0.14
|
70
84
|
signing_key:
|
71
|
-
specification_version:
|
85
|
+
specification_version: 4
|
72
86
|
summary: A StatsD client for Ruby apps
|
73
87
|
test_files:
|
74
|
-
- test/
|
88
|
+
- test/statsd_instrumentation_test.rb
|
89
|
+
- test/statsd_test.rb
|
90
|
+
- test/test_helper.rb
|
@@ -1,339 +0,0 @@
|
|
1
|
-
# Allow testing with the SystemTimer gem on 1.8
|
2
|
-
if RUBY_VERSION =~ /^1.8/
|
3
|
-
puts "Loading SystemTimer gem"
|
4
|
-
require 'system_timer'
|
5
|
-
end
|
6
|
-
|
7
|
-
require 'statsd-instrument'
|
8
|
-
require 'test/unit'
|
9
|
-
require 'mocha'
|
10
|
-
require 'logger'
|
11
|
-
|
12
|
-
StatsD.logger = Logger.new('/dev/null')
|
13
|
-
|
14
|
-
module ActiveMerchant; end
|
15
|
-
class ActiveMerchant::Base
|
16
|
-
def ssl_post(arg)
|
17
|
-
if arg
|
18
|
-
'OK'
|
19
|
-
else
|
20
|
-
raise 'Not OK'
|
21
|
-
end
|
22
|
-
end
|
23
|
-
|
24
|
-
def post_with_block(&block)
|
25
|
-
yield if block_given?
|
26
|
-
end
|
27
|
-
end
|
28
|
-
|
29
|
-
class ActiveMerchant::Gateway < ActiveMerchant::Base
|
30
|
-
def purchase(arg)
|
31
|
-
ssl_post(arg)
|
32
|
-
true
|
33
|
-
rescue
|
34
|
-
false
|
35
|
-
end
|
36
|
-
|
37
|
-
def self.sync
|
38
|
-
true
|
39
|
-
end
|
40
|
-
|
41
|
-
def self.singleton_class
|
42
|
-
class << self; self; end
|
43
|
-
end
|
44
|
-
end
|
45
|
-
|
46
|
-
class ActiveMerchant::UniqueGateway < ActiveMerchant::Base
|
47
|
-
def ssl_post(arg)
|
48
|
-
{:success => arg}
|
49
|
-
end
|
50
|
-
|
51
|
-
def purchase(arg)
|
52
|
-
ssl_post(arg)
|
53
|
-
end
|
54
|
-
end
|
55
|
-
|
56
|
-
class GatewaySubClass < ActiveMerchant::Gateway
|
57
|
-
end
|
58
|
-
|
59
|
-
ActiveMerchant::Base.extend StatsD::Instrument
|
60
|
-
|
61
|
-
class StatsDTest < Test::Unit::TestCase
|
62
|
-
def setup
|
63
|
-
StatsD.mode = nil
|
64
|
-
StatsD.stubs(:increment)
|
65
|
-
end
|
66
|
-
|
67
|
-
def test_statsd_count_if
|
68
|
-
ActiveMerchant::Gateway.statsd_count_if :ssl_post, 'ActiveMerchant.Gateway.if'
|
69
|
-
|
70
|
-
StatsD.expects(:increment).with(includes('if'), 1).once
|
71
|
-
ActiveMerchant::Gateway.new.purchase(true)
|
72
|
-
ActiveMerchant::Gateway.new.purchase(false)
|
73
|
-
end
|
74
|
-
|
75
|
-
def test_statsd_count_if_with_method_receiving_block
|
76
|
-
ActiveMerchant::Base.statsd_count_if :post_with_block, 'ActiveMerchant.Base.post_with_block' do |result|
|
77
|
-
result[:success]
|
78
|
-
end
|
79
|
-
|
80
|
-
return_value = ActiveMerchant::Base.new.post_with_block {'block called'}
|
81
|
-
|
82
|
-
assert_equal 'block called', return_value
|
83
|
-
end
|
84
|
-
|
85
|
-
def test_statsd_count_if_with_block
|
86
|
-
ActiveMerchant::UniqueGateway.statsd_count_if :ssl_post, 'ActiveMerchant.Gateway.block' do |result|
|
87
|
-
result[:success]
|
88
|
-
end
|
89
|
-
|
90
|
-
StatsD.expects(:increment).with(includes('block'), 1).once
|
91
|
-
ActiveMerchant::UniqueGateway.new.purchase(true)
|
92
|
-
ActiveMerchant::UniqueGateway.new.purchase(false)
|
93
|
-
end
|
94
|
-
|
95
|
-
def test_statsd_count_success
|
96
|
-
ActiveMerchant::Gateway.statsd_count_success :ssl_post, 'ActiveMerchant.Gateway', 0.5
|
97
|
-
|
98
|
-
StatsD.expects(:increment).with(includes('success'), 0.5)
|
99
|
-
ActiveMerchant::Gateway.new.purchase(true)
|
100
|
-
|
101
|
-
StatsD.expects(:increment).with(includes('failure'), 0.5)
|
102
|
-
ActiveMerchant::Gateway.new.purchase(false)
|
103
|
-
end
|
104
|
-
|
105
|
-
def test_statsd_count_success_with_method_receiving_block
|
106
|
-
ActiveMerchant::Base.statsd_count_success :post_with_block, 'ActiveMerchant.Base.post_with_block' do |result|
|
107
|
-
result[:success]
|
108
|
-
end
|
109
|
-
|
110
|
-
return_value = ActiveMerchant::Base.new.post_with_block {'block called'}
|
111
|
-
|
112
|
-
assert_equal 'block called', return_value
|
113
|
-
end
|
114
|
-
|
115
|
-
def test_statsd_count_success_with_block
|
116
|
-
ActiveMerchant::UniqueGateway.statsd_count_success :ssl_post, 'ActiveMerchant.Gateway' do |result|
|
117
|
-
result[:success]
|
118
|
-
end
|
119
|
-
|
120
|
-
StatsD.expects(:increment).with(includes('success'), StatsD.default_sample_rate)
|
121
|
-
ActiveMerchant::UniqueGateway.new.purchase(true)
|
122
|
-
|
123
|
-
StatsD.expects(:increment).with(includes('failure'), StatsD.default_sample_rate)
|
124
|
-
ActiveMerchant::UniqueGateway.new.purchase(false)
|
125
|
-
end
|
126
|
-
|
127
|
-
def test_statsd_count
|
128
|
-
ActiveMerchant::Gateway.statsd_count :ssl_post, 'ActiveMerchant.Gateway.ssl_post'
|
129
|
-
|
130
|
-
StatsD.expects(:increment).with(includes('ssl_post'), 1)
|
131
|
-
ActiveMerchant::Gateway.new.purchase(true)
|
132
|
-
end
|
133
|
-
|
134
|
-
def test_statsd_count_with_name_as_lambda
|
135
|
-
ActiveMerchant::Gateway.statsd_count(:ssl_post, lambda {|object, args| object.class.to_s.downcase + ".insert." + args.first.to_s})
|
136
|
-
|
137
|
-
StatsD.expects(:increment).with('gatewaysubclass.insert.true', 1)
|
138
|
-
GatewaySubClass.new.purchase(true)
|
139
|
-
end
|
140
|
-
|
141
|
-
def test_statsd_count_with_method_receiving_block
|
142
|
-
ActiveMerchant::Base.statsd_count :post_with_block, 'ActiveMerchant.Base.post_with_block'
|
143
|
-
|
144
|
-
return_value = ActiveMerchant::Base.new.post_with_block {'block called'}
|
145
|
-
|
146
|
-
assert_equal 'block called', return_value
|
147
|
-
end
|
148
|
-
|
149
|
-
def test_statsd_measure_with_nested_modules
|
150
|
-
ActiveMerchant::UniqueGateway.statsd_measure :ssl_post, 'ActiveMerchant::Gateway.ssl_post'
|
151
|
-
|
152
|
-
StatsD.stubs(:mode).returns(:production)
|
153
|
-
UDPSocket.any_instance.expects(:send).with(regexp_matches(/ActiveMerchant\.Gateway\.ssl_post:\d\.\d{2,}\|ms/), 0, 'localhost', 123).at_least(1)
|
154
|
-
|
155
|
-
ActiveMerchant::UniqueGateway.new.purchase(true)
|
156
|
-
end
|
157
|
-
|
158
|
-
def test_statsd_measure
|
159
|
-
ActiveMerchant::UniqueGateway.statsd_measure :ssl_post, 'ActiveMerchant.Gateway.ssl_post', 0.3
|
160
|
-
|
161
|
-
StatsD.expects(:write).with('ActiveMerchant.Gateway.ssl_post', is_a(Float), :ms, 0.3).returns({:success => true})
|
162
|
-
ActiveMerchant::UniqueGateway.new.purchase(true)
|
163
|
-
end
|
164
|
-
|
165
|
-
|
166
|
-
def test_statsd_measure_with_method_receiving_block
|
167
|
-
ActiveMerchant::Base.statsd_measure :post_with_block, 'ActiveMerchant.Base.post_with_block'
|
168
|
-
|
169
|
-
return_value = ActiveMerchant::Base.new.post_with_block {'block called'}
|
170
|
-
|
171
|
-
assert_equal 'block called', return_value
|
172
|
-
end
|
173
|
-
|
174
|
-
def test_instrumenting_class_method
|
175
|
-
ActiveMerchant::Gateway.singleton_class.extend StatsD::Instrument
|
176
|
-
ActiveMerchant::Gateway.singleton_class.statsd_count :sync, 'ActiveMerchant.Gateway.sync'
|
177
|
-
|
178
|
-
StatsD.expects(:increment).with(includes('sync'), 1)
|
179
|
-
ActiveMerchant::Gateway.sync
|
180
|
-
end
|
181
|
-
|
182
|
-
def test_count_with_sampling
|
183
|
-
StatsD.unstub(:increment)
|
184
|
-
StatsD.stubs(:rand).returns(0.6)
|
185
|
-
StatsD.logger.expects(:info).never
|
186
|
-
|
187
|
-
StatsD.increment('sampling.foo.bar', 1, 0.1)
|
188
|
-
end
|
189
|
-
|
190
|
-
def test_count_with_successful_sample
|
191
|
-
StatsD.unstub(:increment)
|
192
|
-
StatsD.stubs(:rand).returns(0.01)
|
193
|
-
StatsD.logger.expects(:info).once.with do |string|
|
194
|
-
string.include?('@0.1')
|
195
|
-
end
|
196
|
-
|
197
|
-
StatsD.increment('sampling.foo.bar', 1, 0.1)
|
198
|
-
end
|
199
|
-
|
200
|
-
def test_production_mode_should_use_udp_socket
|
201
|
-
StatsD.unstub(:increment)
|
202
|
-
|
203
|
-
StatsD.mode = :production
|
204
|
-
StatsD.server = 'localhost:123'
|
205
|
-
UDPSocket.any_instance.expects(:send)
|
206
|
-
|
207
|
-
StatsD.increment('fooz')
|
208
|
-
StatsD.mode = :test
|
209
|
-
end
|
210
|
-
|
211
|
-
def test_write_supports_gauge_syntax
|
212
|
-
StatsD.unstub(:gauge)
|
213
|
-
|
214
|
-
StatsD.mode = :production
|
215
|
-
StatsD.server = 'localhost:123'
|
216
|
-
|
217
|
-
UDPSocket.any_instance.expects(:send).with('fooy:42|g', 0, 'localhost', 123)
|
218
|
-
|
219
|
-
StatsD.gauge('fooy', 42)
|
220
|
-
end
|
221
|
-
|
222
|
-
def test_write_supports_statsite_gauge_syntax
|
223
|
-
StatsD.unstub(:gauge)
|
224
|
-
|
225
|
-
StatsD.mode = :production
|
226
|
-
StatsD.server = 'localhost:123'
|
227
|
-
StatsD.implementation = :statsite
|
228
|
-
|
229
|
-
UDPSocket.any_instance.expects(:send).with("fooy:42|kv\n", 0, 'localhost', 123)
|
230
|
-
|
231
|
-
StatsD.gauge('fooy', 42)
|
232
|
-
end
|
233
|
-
|
234
|
-
def test_write_supports_statsite_gauge_timestamp
|
235
|
-
StatsD.unstub(:gauge)
|
236
|
-
|
237
|
-
StatsD.mode = :production
|
238
|
-
StatsD.server = 'localhost:123'
|
239
|
-
StatsD.implementation = :statsite
|
240
|
-
|
241
|
-
UDPSocket.any_instance.expects(:send).with("fooy:42|kv|@123456\n", 0, 'localhost', 123)
|
242
|
-
|
243
|
-
StatsD.gauge('fooy', 42, 123456)
|
244
|
-
end
|
245
|
-
|
246
|
-
def test_should_not_write_when_disabled
|
247
|
-
StatsD.enabled = false
|
248
|
-
StatsD.expects(:logger).never
|
249
|
-
StatsD.increment('fooz')
|
250
|
-
StatsD.enabled = true
|
251
|
-
end
|
252
|
-
|
253
|
-
def test_statsd_mode
|
254
|
-
StatsD.unstub(:increment)
|
255
|
-
StatsD.logger.expects(:info).once
|
256
|
-
StatsD.expects(:socket_wrapper).twice
|
257
|
-
StatsD.mode = :foo
|
258
|
-
StatsD.increment('foo')
|
259
|
-
StatsD.mode = :production
|
260
|
-
StatsD.increment('foo')
|
261
|
-
StatsD.mode = 'production'
|
262
|
-
StatsD.increment('foo')
|
263
|
-
end
|
264
|
-
|
265
|
-
def test_statsd_prefix
|
266
|
-
StatsD.unstub(:increment)
|
267
|
-
StatsD.prefix = 'my_app'
|
268
|
-
StatsD.logger.expects(:info).once.with do |string|
|
269
|
-
string.include?('my_app.foo')
|
270
|
-
end
|
271
|
-
StatsD.logger.expects(:info).once.with do |string|
|
272
|
-
string.include?('food')
|
273
|
-
end
|
274
|
-
StatsD.increment('foo')
|
275
|
-
StatsD.prefix = nil
|
276
|
-
StatsD.increment('food')
|
277
|
-
end
|
278
|
-
|
279
|
-
def test_statsd_measure_with_explicit_value
|
280
|
-
StatsD.expects(:write).with('values.foobar', 42, :ms, is_a(Numeric))
|
281
|
-
|
282
|
-
StatsD.measure('values.foobar', 42)
|
283
|
-
end
|
284
|
-
|
285
|
-
def test_statsd_measure_with_explicit_value_and_sample_rate
|
286
|
-
StatsD.expects(:write).with('values.foobar', 42, :ms, 0.1)
|
287
|
-
|
288
|
-
StatsD.measure('values.foobar', 42, 0.1)
|
289
|
-
end
|
290
|
-
|
291
|
-
def test_statsd_gauge
|
292
|
-
StatsD.expects(:write).with('values.foobar', 12, :g, 1)
|
293
|
-
|
294
|
-
StatsD.default_sample_rate = 1
|
295
|
-
|
296
|
-
StatsD.gauge('values.foobar', 12)
|
297
|
-
end
|
298
|
-
|
299
|
-
def test_socket_error_should_not_raise
|
300
|
-
StatsD.mode = :production
|
301
|
-
UDPSocket.any_instance.expects(:send).raises(SocketError)
|
302
|
-
StatsD.measure('values.foobar', 42)
|
303
|
-
StatsD.mode = :test
|
304
|
-
end
|
305
|
-
|
306
|
-
def test_timeout_error_should_not_raise
|
307
|
-
StatsD.mode = :production
|
308
|
-
UDPSocket.any_instance.expects(:send).raises(Timeout::Error)
|
309
|
-
StatsD.measure('values.foobar', 42)
|
310
|
-
StatsD.mode = :test
|
311
|
-
end
|
312
|
-
|
313
|
-
def test_system_call_error_should_not_raise
|
314
|
-
StatsD.mode = :production
|
315
|
-
UDPSocket.any_instance.expects(:send).raises(Errno::ETIMEDOUT)
|
316
|
-
StatsD.measure('values.foobar', 42)
|
317
|
-
StatsD.mode = :test
|
318
|
-
end
|
319
|
-
|
320
|
-
def test_io_error_should_not_raise
|
321
|
-
StatsD.mode = :production
|
322
|
-
UDPSocket.any_instance.expects(:send).raises(IOError)
|
323
|
-
StatsD.measure('values.foobar', 42)
|
324
|
-
StatsD.mode = :test
|
325
|
-
end
|
326
|
-
|
327
|
-
def test_long_request_should_timeout
|
328
|
-
StatsD.mode = :production
|
329
|
-
UDPSocket.any_instance.expects(:send).yields do
|
330
|
-
begin
|
331
|
-
Timeout.timeout(0.5) { sleep 1 }
|
332
|
-
rescue Timeout::Error
|
333
|
-
raise "Allowed long running request"
|
334
|
-
end
|
335
|
-
end
|
336
|
-
StatsD.measure('values.foobar', 42)
|
337
|
-
StatsD.mode = :test
|
338
|
-
end
|
339
|
-
end
|