statsd-instrument 1.1.2 → 1.2.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.travis.yml +6 -0
- data/Gemfile +6 -0
- data/README.md +22 -0
- data/lib/statsd/instrument.rb +13 -5
- data/statsd-instrument.gemspec +1 -1
- data/test/statsd-instrument_test.rb +53 -1
- metadata +5 -4
data/.travis.yml
ADDED
data/Gemfile
CHANGED
data/README.md
CHANGED
@@ -1,5 +1,7 @@
|
|
1
1
|
# StatsD client for Ruby apps
|
2
2
|
|
3
|
+
![Built on Travis](https://secure.travis-ci.org/Shopify/statsd-instrument.png?branch=master)
|
4
|
+
|
3
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.
|
4
6
|
|
5
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 it's 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.
|
@@ -20,6 +22,7 @@ This is the same as what Etsy uses (mentioned in the README for http://github.co
|
|
20
22
|
StatsD.server = 'statsd.myservice.com:8125'
|
21
23
|
StatsD.logger = Rails.logger
|
22
24
|
StatsD.mode = :production
|
25
|
+
StatsD.prefix = 'my_app' # An optional prefix to be added to each stat.
|
23
26
|
StatsD.default_sample_rate = 0.1 # Sample 10% of events. By default all events are reported.
|
24
27
|
```
|
25
28
|
|
@@ -132,3 +135,22 @@ You can instrument class methods, just like instance methods, using the metaprog
|
|
132
135
|
AWS::S3::Base.singleton_class.extend StatsD::Instrument
|
133
136
|
AWS::S3::Base.singleton_class.statsd_measure :request, 'S3.request'
|
134
137
|
```
|
138
|
+
|
139
|
+
## Reliance on DNS
|
140
|
+
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.
|
141
|
+
|
142
|
+
### Common Workarounds
|
143
|
+
1. Using an IP avoids the DNS lookup but generally requires an application deploy to change.
|
144
|
+
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.
|
145
|
+
3. Installing caching software such as nscd that uses the DNS TTL avoids most DNS lookups but makes the exact moment of change indeterminate.
|
146
|
+
|
147
|
+
## Compatibility
|
148
|
+
|
149
|
+
Tested on:
|
150
|
+
|
151
|
+
* Ruby 1.8.7
|
152
|
+
* Ruby Enterprise Edition 1.8.7
|
153
|
+
* Ruby 1.9.2
|
154
|
+
* Ruby 1.9.3
|
155
|
+
|
156
|
+
Ruby 1.9 compatibility is planned for the long term. Your mileage may vary with other Ruby environments.
|
data/lib/statsd/instrument.rb
CHANGED
@@ -11,12 +11,13 @@ end
|
|
11
11
|
|
12
12
|
module StatsD
|
13
13
|
class << self
|
14
|
-
attr_accessor :host, :port, :mode, :logger, :enabled, :default_sample_rate
|
14
|
+
attr_accessor :host, :port, :mode, :logger, :enabled, :default_sample_rate,
|
15
|
+
:prefix
|
15
16
|
end
|
16
17
|
self.enabled = true
|
17
18
|
self.default_sample_rate = 1
|
18
19
|
|
19
|
-
|
20
|
+
TimeoutClass = defined?(::SystemTimer) ? ::SystemTimer : ::Timeout
|
20
21
|
|
21
22
|
# StatsD.server = 'localhost:1234'
|
22
23
|
def self.server=(conn)
|
@@ -113,6 +114,11 @@ module StatsD
|
|
113
114
|
write(key, delta, :incr, sample_rate)
|
114
115
|
end
|
115
116
|
|
117
|
+
#gaugor:333|g
|
118
|
+
def self.gauge(key, value, sample_rate = default_sample_rate)
|
119
|
+
write(key, value, :g, sample_rate)
|
120
|
+
end
|
121
|
+
|
116
122
|
private
|
117
123
|
|
118
124
|
def self.socket
|
@@ -123,17 +129,19 @@ module StatsD
|
|
123
129
|
return unless enabled
|
124
130
|
return if sample_rate < 1 && rand > sample_rate
|
125
131
|
|
126
|
-
command = "#{k}:#{v}"
|
132
|
+
command = "#{self.prefix + '.' if self.prefix}#{k}:#{v}"
|
127
133
|
case op
|
128
134
|
when :incr
|
129
135
|
command << '|c'
|
130
136
|
when :ms
|
131
137
|
command << '|ms'
|
138
|
+
when :g
|
139
|
+
command << '|g'
|
132
140
|
end
|
133
141
|
|
134
142
|
command << "|@#{sample_rate}" if sample_rate < 1
|
135
143
|
|
136
|
-
if mode ==
|
144
|
+
if mode.to_s == 'production'
|
137
145
|
socket_wrapper { socket.send(command, 0, host, port) }
|
138
146
|
else
|
139
147
|
logger.info "[StatsD] #{command}"
|
@@ -141,7 +149,7 @@ module StatsD
|
|
141
149
|
end
|
142
150
|
|
143
151
|
def self.socket_wrapper(options = {})
|
144
|
-
|
152
|
+
TimeoutClass.timeout(options.fetch(:timeout, 0.1)) { yield }
|
145
153
|
rescue Timeout::Error, SocketError, IOError, SystemCallError => e
|
146
154
|
logger.error e
|
147
155
|
end
|
data/statsd-instrument.gemspec
CHANGED
@@ -1,3 +1,9 @@
|
|
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
|
+
|
1
7
|
require 'statsd-instrument'
|
2
8
|
require 'test/unit'
|
3
9
|
require 'mocha'
|
@@ -47,6 +53,7 @@ ActiveMerchant::Base.extend StatsD::Instrument
|
|
47
53
|
|
48
54
|
class StatsDTest < Test::Unit::TestCase
|
49
55
|
def setup
|
56
|
+
StatsD.mode = nil
|
50
57
|
StatsD.stubs(:increment)
|
51
58
|
end
|
52
59
|
|
@@ -141,19 +148,64 @@ class StatsDTest < Test::Unit::TestCase
|
|
141
148
|
StatsD.mode = :test
|
142
149
|
end
|
143
150
|
|
151
|
+
def test_write_supports_gauge_syntax
|
152
|
+
StatsD.unstub(:gauge)
|
153
|
+
|
154
|
+
StatsD.mode = :production
|
155
|
+
StatsD.server = 'localhost:123'
|
156
|
+
|
157
|
+
UDPSocket.any_instance.expects(:send).with('fooy:42|g', 0, 'localhost', 123)
|
158
|
+
|
159
|
+
StatsD.gauge('fooy', 42)
|
160
|
+
end
|
161
|
+
|
144
162
|
def test_should_not_write_when_disabled
|
145
163
|
StatsD.enabled = false
|
146
164
|
StatsD.expects(:logger).never
|
147
165
|
StatsD.increment('fooz')
|
148
166
|
StatsD.enabled = true
|
149
167
|
end
|
150
|
-
|
168
|
+
|
169
|
+
def test_statsd_mode
|
170
|
+
StatsD.unstub(:increment)
|
171
|
+
StatsD.logger.expects(:info).once
|
172
|
+
StatsD.expects(:socket_wrapper).twice
|
173
|
+
StatsD.mode = :foo
|
174
|
+
StatsD.increment('foo')
|
175
|
+
StatsD.mode = :production
|
176
|
+
StatsD.increment('foo')
|
177
|
+
StatsD.mode = 'production'
|
178
|
+
StatsD.increment('foo')
|
179
|
+
end
|
180
|
+
|
181
|
+
def test_statsd_prefix
|
182
|
+
StatsD.unstub(:increment)
|
183
|
+
StatsD.prefix = 'my_app'
|
184
|
+
StatsD.logger.expects(:info).once.with do |string|
|
185
|
+
string.include?('my_app.foo')
|
186
|
+
end
|
187
|
+
StatsD.logger.expects(:info).once.with do |string|
|
188
|
+
string.include?('food')
|
189
|
+
end
|
190
|
+
StatsD.increment('foo')
|
191
|
+
StatsD.prefix = nil
|
192
|
+
StatsD.increment('food')
|
193
|
+
end
|
194
|
+
|
151
195
|
def test_statsd_measure_with_explicit_value
|
152
196
|
StatsD.expects(:write).with('values.foobar', 42, :ms)
|
153
197
|
|
154
198
|
StatsD.measure('values.foobar', 42)
|
155
199
|
end
|
156
200
|
|
201
|
+
def test_statsd_gauge
|
202
|
+
StatsD.expects(:write).with('values.foobar', 12, :g, 1)
|
203
|
+
|
204
|
+
StatsD.default_sample_rate = 1
|
205
|
+
|
206
|
+
StatsD.gauge('values.foobar', 12)
|
207
|
+
end
|
208
|
+
|
157
209
|
def test_socket_error_should_not_raise
|
158
210
|
StatsD.mode = :production
|
159
211
|
UDPSocket.any_instance.expects(:send).raises(SocketError)
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: statsd-instrument
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 1.2.0
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,11 +9,11 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2012-
|
12
|
+
date: 2012-04-20 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: mocha
|
16
|
-
requirement: &
|
16
|
+
requirement: &2178819120 !ruby/object:Gem::Requirement
|
17
17
|
none: false
|
18
18
|
requirements:
|
19
19
|
- - ! '>='
|
@@ -21,7 +21,7 @@ dependencies:
|
|
21
21
|
version: '0'
|
22
22
|
type: :development
|
23
23
|
prerelease: false
|
24
|
-
version_requirements: *
|
24
|
+
version_requirements: *2178819120
|
25
25
|
description: A StatsD client for Ruby apps. Provides metaprogramming methods to inject
|
26
26
|
StatsD instrumentation into your code.
|
27
27
|
email:
|
@@ -31,6 +31,7 @@ extensions: []
|
|
31
31
|
extra_rdoc_files: []
|
32
32
|
files:
|
33
33
|
- .gitignore
|
34
|
+
- .travis.yml
|
34
35
|
- Gemfile
|
35
36
|
- LICENSE
|
36
37
|
- README.md
|