mouth-instrument 1.0.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.
- data/.gitignore +7 -0
- data/MIT-LICENSE +20 -0
- data/README.md +43 -0
- data/Rakefile +9 -0
- data/lib/mouth-instrument.rb +63 -0
- data/mouth-instrument.gemspec +14 -0
- data/test/mouth-instrument_test.rb +79 -0
- data/test/test_helper.rb +5 -0
- metadata +55 -0
data/.gitignore
ADDED
data/MIT-LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2012 Jonathan Novak - http://github.com/cypriss
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,43 @@
|
|
1
|
+
# mouth-instrument
|
2
|
+
|
3
|
+
Mouth-instrument is a very lightweight Ruby gem that you can use to instrument your apps to collect metrics. It sends these metrics via UDP to a [mouth](http://www.github.com/cypriss/mouth) daemon for storage and analysis.
|
4
|
+
|
5
|
+
## What is Mouth?
|
6
|
+
|
7
|
+
[Mouth](http://www.github.com/cypriss/mouth) is a Ruby daemon that collects metrics via UDP and stores them in Mongo. It comes with a modern UI that allows you to view graphs and create dashboards of these statistics.
|
8
|
+
|
9
|
+
## Using mouth-instrument
|
10
|
+
|
11
|
+
First, install the gem via your Gemfile or do so manually:
|
12
|
+
|
13
|
+
gem install mouth-instrument
|
14
|
+
|
15
|
+
Then, include it in your app:
|
16
|
+
|
17
|
+
require 'mouth-instrument'
|
18
|
+
|
19
|
+
Configure it to point to your daemon. In a Rails app, you might want to put this in config/initializers/mouth.rb
|
20
|
+
|
21
|
+
Mouth.daemon_hostport = "localhost:8889"
|
22
|
+
|
23
|
+
Then, instrument your app:
|
24
|
+
|
25
|
+
# Count things like this:
|
26
|
+
Mouth.increment("myapp.occurrences") # Basic
|
27
|
+
Mouth.increment("myapp.occurrences", 10) # Occurrences occurred 10 times!
|
28
|
+
Mouth.increment("myapp.occurrences", 1, 0.1) # Sample at a 1/10 rate. Lose a bit of accuracy, but sends less packets. Good for super-high volume metrics.
|
29
|
+
|
30
|
+
# Measure timings like this:
|
31
|
+
Mouth.measure("myapp.happening") do
|
32
|
+
do_happening!
|
33
|
+
end
|
34
|
+
|
35
|
+
# Or, like this (23.9 is in milliseconds):
|
36
|
+
Mouth.measure("myapp.happening", 23.9)
|
37
|
+
|
38
|
+
# Gauge values like this:
|
39
|
+
Mouth.gauge("myapp.subscriber_count", 1230) # I have 1230 customers right now!
|
40
|
+
|
41
|
+
## View your metrics
|
42
|
+
|
43
|
+
Please see the [Mouth](http://www.github.com/cypriss/mouth) project for more information! This gem plays a supporting role to core Mouth gem.
|
data/Rakefile
ADDED
@@ -0,0 +1,63 @@
|
|
1
|
+
require 'socket'
|
2
|
+
require 'benchmark'
|
3
|
+
|
4
|
+
module Mouth
|
5
|
+
|
6
|
+
INSTRUMENT_VERSION = '1.0.0'
|
7
|
+
|
8
|
+
unless self.respond_to?(:measure)
|
9
|
+
class << self
|
10
|
+
attr_accessor :daemon_host, :daemon_port, :disabled
|
11
|
+
|
12
|
+
# Mouth.server = 'localhost:1234'
|
13
|
+
def daemon_hostport=(hostport)
|
14
|
+
self.daemon_host, port = hostport.split(':')
|
15
|
+
self.daemon_port = port.to_i
|
16
|
+
end
|
17
|
+
|
18
|
+
def daemon_host
|
19
|
+
@daemon_host || "localhost"
|
20
|
+
end
|
21
|
+
|
22
|
+
def daemon_port
|
23
|
+
@daemon_port || 8889
|
24
|
+
end
|
25
|
+
|
26
|
+
def measure(key, milli = nil)
|
27
|
+
result = nil
|
28
|
+
ms = milli || (Benchmark.realtime { result = yield } * 1000).to_i
|
29
|
+
|
30
|
+
write(key, ms, :ms)
|
31
|
+
|
32
|
+
result
|
33
|
+
end
|
34
|
+
|
35
|
+
def increment(key, delta = 1, sample_rate = nil)
|
36
|
+
write(key, delta, :c, sample_rate)
|
37
|
+
end
|
38
|
+
|
39
|
+
def gauge(key, value)
|
40
|
+
write(key, value, :g)
|
41
|
+
end
|
42
|
+
|
43
|
+
protected
|
44
|
+
|
45
|
+
def socket
|
46
|
+
@socket ||= UDPSocket.new
|
47
|
+
end
|
48
|
+
|
49
|
+
def write(k, v, op, sample_rate = nil)
|
50
|
+
return if self.disabled
|
51
|
+
if sample_rate
|
52
|
+
sample_rate = 1 if sample_rate > 1
|
53
|
+
return if rand > sample_rate
|
54
|
+
end
|
55
|
+
|
56
|
+
command = "#{k}:#{v}|#{op}"
|
57
|
+
command << "|@#{sample_rate}" if sample_rate
|
58
|
+
|
59
|
+
socket.send(command, 0, self.daemon_host, self.daemon_port)
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
require './lib/mouth-instrument'
|
2
|
+
|
3
|
+
Gem::Specification.new do |s|
|
4
|
+
s.name = 'mouth-instrument'
|
5
|
+
s.version = Mouth::INSTRUMENT_VERSION
|
6
|
+
s.author = 'Jonathan Novak'
|
7
|
+
s.email = 'jnovak@gmail.com'
|
8
|
+
s.homepage = 'http://github.com/cypriss/mouth-instrument'
|
9
|
+
s.summary = 'Instrument your app to send metrics to a Mouth'
|
10
|
+
s.description = 'Instrument your app to send metrics to a Mouth. Sends UDP packets to Mouth for Counters, Timers, and Gauges.'
|
11
|
+
|
12
|
+
s.files = `git ls-files`.split("\n")
|
13
|
+
s.test_files = `git ls-files test`.split("\n")
|
14
|
+
end
|
@@ -0,0 +1,79 @@
|
|
1
|
+
$LOAD_PATH.unshift 'test'
|
2
|
+
require 'test_helper'
|
3
|
+
|
4
|
+
class MouthInstrumentTest < Test::Unit::TestCase
|
5
|
+
def setup
|
6
|
+
@@udp ||= UDPSocket.new.tap do |u|
|
7
|
+
u.bind("127.0.0.1", 32123)
|
8
|
+
end
|
9
|
+
@udp = @@udp
|
10
|
+
Mouth.daemon_hostport = "127.0.0.1:32123"
|
11
|
+
end
|
12
|
+
|
13
|
+
def test_increment
|
14
|
+
Mouth.increment("bob")
|
15
|
+
|
16
|
+
res = @udp.recvfrom(1024)
|
17
|
+
assert_equal "bob:1|c", res[0]
|
18
|
+
end
|
19
|
+
|
20
|
+
def test_increment_delta
|
21
|
+
Mouth.increment("bob.rob", 123)
|
22
|
+
|
23
|
+
res = @udp.recvfrom(1024)
|
24
|
+
assert_equal "bob.rob:123|c", res[0]
|
25
|
+
end
|
26
|
+
|
27
|
+
def test_increment_sample
|
28
|
+
# Clear out anything on the UDP packet
|
29
|
+
100.times { @udp.recvfrom_nonblock(1024) } rescue nil
|
30
|
+
|
31
|
+
pack = nil
|
32
|
+
nothing = false
|
33
|
+
|
34
|
+
100.times do
|
35
|
+
Mouth.increment("bob.rob/blah-haha", 123, 0.1)
|
36
|
+
|
37
|
+
begin
|
38
|
+
pack ||= @udp.recvfrom_nonblock(1024)
|
39
|
+
break if nothing
|
40
|
+
rescue IO::WaitReadable
|
41
|
+
nothing = true
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
assert nothing
|
46
|
+
assert_equal "bob.rob/blah-haha:123|c|@0.1", pack[0]
|
47
|
+
end
|
48
|
+
|
49
|
+
def test_gauge
|
50
|
+
Mouth.gauge("bob", 77)
|
51
|
+
|
52
|
+
res = @udp.recvfrom(1024)
|
53
|
+
assert_equal "bob:77|g", res[0]
|
54
|
+
end
|
55
|
+
|
56
|
+
def test_measure
|
57
|
+
Mouth.measure("bob", 1.2)
|
58
|
+
|
59
|
+
res = @udp.recvfrom(1024)
|
60
|
+
assert_equal "bob:1.2|ms", res[0]
|
61
|
+
end
|
62
|
+
|
63
|
+
def test_measure_block
|
64
|
+
result = Mouth.measure("bob") do
|
65
|
+
sleep 0.2
|
66
|
+
9876
|
67
|
+
end
|
68
|
+
|
69
|
+
assert_equal 9876, result
|
70
|
+
|
71
|
+
res = @udp.recvfrom(1024)[0]
|
72
|
+
m = res.match(/bob:(\d+)\|ms/)
|
73
|
+
assert m
|
74
|
+
|
75
|
+
# Make sure the number is within a resonable range
|
76
|
+
assert m[1].to_i > 180
|
77
|
+
assert m[1].to_i < 220
|
78
|
+
end
|
79
|
+
end
|
data/test/test_helper.rb
ADDED
metadata
ADDED
@@ -0,0 +1,55 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: mouth-instrument
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 1.0.0
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Jonathan Novak
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2012-05-07 00:00:00.000000000Z
|
13
|
+
dependencies: []
|
14
|
+
description: Instrument your app to send metrics to a Mouth. Sends UDP packets to
|
15
|
+
Mouth for Counters, Timers, and Gauges.
|
16
|
+
email: jnovak@gmail.com
|
17
|
+
executables: []
|
18
|
+
extensions: []
|
19
|
+
extra_rdoc_files: []
|
20
|
+
files:
|
21
|
+
- .gitignore
|
22
|
+
- MIT-LICENSE
|
23
|
+
- README.md
|
24
|
+
- Rakefile
|
25
|
+
- lib/mouth-instrument.rb
|
26
|
+
- mouth-instrument.gemspec
|
27
|
+
- test/mouth-instrument_test.rb
|
28
|
+
- test/test_helper.rb
|
29
|
+
homepage: http://github.com/cypriss/mouth-instrument
|
30
|
+
licenses: []
|
31
|
+
post_install_message:
|
32
|
+
rdoc_options: []
|
33
|
+
require_paths:
|
34
|
+
- lib
|
35
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
36
|
+
none: false
|
37
|
+
requirements:
|
38
|
+
- - ! '>='
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0'
|
41
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
42
|
+
none: false
|
43
|
+
requirements:
|
44
|
+
- - ! '>='
|
45
|
+
- !ruby/object:Gem::Version
|
46
|
+
version: '0'
|
47
|
+
requirements: []
|
48
|
+
rubyforge_project:
|
49
|
+
rubygems_version: 1.8.17
|
50
|
+
signing_key:
|
51
|
+
specification_version: 3
|
52
|
+
summary: Instrument your app to send metrics to a Mouth
|
53
|
+
test_files:
|
54
|
+
- test/mouth-instrument_test.rb
|
55
|
+
- test/test_helper.rb
|