metriks-dogstatsd 0.1.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/.rvmrc +1 -0
- data/Gemfile +3 -0
- data/Gemfile.lock +30 -0
- data/Rakefile +10 -0
- data/lib/metriks-dogstatsd.rb +1 -0
- data/lib/metriks/reporter/dogstatsd.rb +149 -0
- data/lib/metriks/reporter/dogstatsd/version.rb +25 -0
- data/metriks-dogstatsd.gemspec +31 -0
- data/test/helper.rb +38 -0
- data/test/test_dogstatsd_reporter.rb +89 -0
- metadata +121 -0
data/.rvmrc
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
rvm --create use 1.9.3@metriks-dogstatsd
|
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
@@ -0,0 +1,30 @@
|
|
1
|
+
PATH
|
2
|
+
remote: .
|
3
|
+
specs:
|
4
|
+
metriks-dogstatsd (0.0.1alpha1)
|
5
|
+
dogstatsd-ruby (= 1.1.0)
|
6
|
+
metriks (~> 0.9.9.2)
|
7
|
+
|
8
|
+
GEM
|
9
|
+
remote: http://rubygems.org/
|
10
|
+
specs:
|
11
|
+
atomic (1.0.1)
|
12
|
+
avl_tree (1.1.3)
|
13
|
+
dogstatsd-ruby (1.1.0)
|
14
|
+
hitimes (1.1.1)
|
15
|
+
metaclass (0.0.1)
|
16
|
+
metriks (0.9.9.2)
|
17
|
+
atomic (~> 1.0)
|
18
|
+
avl_tree (~> 1.1.2)
|
19
|
+
hitimes (~> 1.1)
|
20
|
+
mocha (0.12.7)
|
21
|
+
metaclass (~> 0.0.1)
|
22
|
+
rake (0.9.5)
|
23
|
+
|
24
|
+
PLATFORMS
|
25
|
+
ruby
|
26
|
+
|
27
|
+
DEPENDENCIES
|
28
|
+
metriks-dogstatsd!
|
29
|
+
mocha (~> 0.12.3)
|
30
|
+
rake (~> 0.9.2)
|
data/Rakefile
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require 'metriks/reporter/dogstatsd'
|
@@ -0,0 +1,149 @@
|
|
1
|
+
require 'statsd' # from dogstatsd-ruby
|
2
|
+
|
3
|
+
module Metriks::Reporter
|
4
|
+
# Adapter class for dogstatsd-ruby and the metriks metric collection
|
5
|
+
# system.
|
6
|
+
class DogStatsd
|
7
|
+
|
8
|
+
attr_reader :client
|
9
|
+
|
10
|
+
def initialize host, port, options={}
|
11
|
+
|
12
|
+
@client = Statsd.new(host, port)
|
13
|
+
|
14
|
+
@prefix = options[:prefix]
|
15
|
+
|
16
|
+
@registry = options[:registry] || Metriks::Registry.default
|
17
|
+
@interval = options[:interval] || 60 # Seconds
|
18
|
+
@timeout_seconds = options[:timeout_seconds] || 45 # Seconds
|
19
|
+
@on_error = options[:on_error] || proc{|exception| } # by default, do nothing and just reconnect to the backend
|
20
|
+
Statsd.logger = options[:logger]
|
21
|
+
|
22
|
+
end
|
23
|
+
|
24
|
+
# Get the socket reference to the backend server.
|
25
|
+
# This is broken out to simplify testing.
|
26
|
+
def socket
|
27
|
+
@client.instance_variable_get(:@socket)
|
28
|
+
end
|
29
|
+
|
30
|
+
# Set the socket reference to the backend server.
|
31
|
+
# This is broken out to simplify testing.
|
32
|
+
def socket= io
|
33
|
+
@client.instance_variable_set(:@socket, io)
|
34
|
+
end
|
35
|
+
|
36
|
+
# Start the backend thread. The backend thread handles connections to
|
37
|
+
# the dogstatsd daemon.
|
38
|
+
def start
|
39
|
+
@thread ||= Thread.new do
|
40
|
+
loop do
|
41
|
+
sleep @interval # don't peg the cpu or wakeup continuously
|
42
|
+
spawn_threaded_write_system
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
def spawn_threaded_write_system
|
48
|
+
Thread.new do
|
49
|
+
begin
|
50
|
+
Timeout::timeout(@timeout_seconds) do
|
51
|
+
write
|
52
|
+
end
|
53
|
+
|
54
|
+
# Yes, this is dumb, but I need to asynchronously pass the
|
55
|
+
# exception up the thread stack and reconnect to the server seamlessly
|
56
|
+
rescue Exception => ex
|
57
|
+
@on_error.call(ex)
|
58
|
+
end
|
59
|
+
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
# Kill and release the backend thread.
|
64
|
+
def stop
|
65
|
+
@thread.kill if @thread
|
66
|
+
@thread = nil
|
67
|
+
end
|
68
|
+
|
69
|
+
# Restart the backend thread. This is implemented as a plain stop,
|
70
|
+
# followed by a plain start
|
71
|
+
def restart
|
72
|
+
stop
|
73
|
+
start
|
74
|
+
end
|
75
|
+
|
76
|
+
# Flush the metrics being aggregated by the registry to the
|
77
|
+
# statistics collection backend
|
78
|
+
def write
|
79
|
+
@registry.each do |name, metric|
|
80
|
+
case metric # handle all potential metric types
|
81
|
+
when Metriks::Meter
|
82
|
+
write_metric name, metric, :meter, [
|
83
|
+
# derived metrics
|
84
|
+
:count, :one_minute_rate, :five_minute_rate, :fifteen_minute_rate, :mean_rate
|
85
|
+
]
|
86
|
+
when Metriks::Counter
|
87
|
+
write_metric name, metric, :counter, [
|
88
|
+
:count
|
89
|
+
]
|
90
|
+
when Metriks::UtilizationTimer
|
91
|
+
write_metric name, metric, :utilization_timer, [
|
92
|
+
:count, :one_minute_rate, :five_minute_rate, :fifteen_minute_rate, :mean_rate, :min, :max, :mean, :stddev
|
93
|
+
], [
|
94
|
+
# snapshot metrics
|
95
|
+
:median, :get_95th_percentile
|
96
|
+
]
|
97
|
+
when Metriks::Timer
|
98
|
+
write_metric name, metric, :timer, [
|
99
|
+
:count, :one_minute_rate, :five_minute_rate, :fifteen_minute_rate, :mean_rate, :min, :max, :mean, :stddev
|
100
|
+
], [
|
101
|
+
:median, :get_95th_percentile
|
102
|
+
]
|
103
|
+
when Metriks::Histogram
|
104
|
+
write_metric name, metric, :histogram, [
|
105
|
+
:count, :min, :max, :mean, :stddev
|
106
|
+
], [
|
107
|
+
:median, :get_95th_percentile
|
108
|
+
]
|
109
|
+
end
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
def write_metric(base_name, metric, type, keys, snapshot_keys = [])
|
114
|
+
# squash all metrics base name whitespace to underscores
|
115
|
+
# the backend system doesn't do whitespace
|
116
|
+
base_name = base_name.to_s.gsub(/ +/, '_')
|
117
|
+
|
118
|
+
#prefix the base_name if we've got one set
|
119
|
+
base_name = "#{@prefix}.#{base_name}" if @prefix
|
120
|
+
|
121
|
+
keys.flatten.each do |key|
|
122
|
+
name = key.to_s.gsub(/^get_/, '')
|
123
|
+
value = metric.send(key)
|
124
|
+
if key == :count
|
125
|
+
case type
|
126
|
+
when :meter, :counter
|
127
|
+
@client.count(base_name, value)
|
128
|
+
when :timer, :utilization_timer, :histogram
|
129
|
+
@client.histogram("#{base_name}.#{name}", value)
|
130
|
+
end
|
131
|
+
else
|
132
|
+
@client.gauge("#{base_name}.#{name}", value)
|
133
|
+
end
|
134
|
+
end
|
135
|
+
|
136
|
+
unless snapshot_keys.empty?
|
137
|
+
snapshot = metric.snapshot
|
138
|
+
snapshot_keys.flatten.each do |key|
|
139
|
+
name = key.to_s.gsub(/^get_/, '')
|
140
|
+
value = snapshot.send(key)
|
141
|
+
@client.gauge("#{base_name}.#{name}", value)
|
142
|
+
end
|
143
|
+
end
|
144
|
+
|
145
|
+
end
|
146
|
+
end
|
147
|
+
end
|
148
|
+
|
149
|
+
require 'metriks/reporter/dogstatsd/version'
|
@@ -0,0 +1,25 @@
|
|
1
|
+
module Metriks
|
2
|
+
module Reporter
|
3
|
+
class DogStatsd
|
4
|
+
|
5
|
+
# Release version for metriks-dogstatsd. This versioning scheme adheres
|
6
|
+
# to the [semantic versioning specification](http://semver.org).
|
7
|
+
module Version
|
8
|
+
# The Major Version Number
|
9
|
+
MAJOR = '0'
|
10
|
+
# The Minor Version Number
|
11
|
+
MINOR = '1'
|
12
|
+
# The Patch Version Number
|
13
|
+
PATCH = '0'
|
14
|
+
|
15
|
+
# Converts the information stored in the Version module into a standards compliant
|
16
|
+
# version identification string
|
17
|
+
# @return [String] The standard version string
|
18
|
+
def self.to_standard_version_s
|
19
|
+
[ MAJOR, MINOR, PATCH ].join '.'
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__),'lib','metriks','reporter','dogstatsd','version')
|
2
|
+
|
3
|
+
Gem::Specification.new do |gem|
|
4
|
+
gem.name = 'metriks-dogstatsd'
|
5
|
+
gem.version = Metriks::Reporter::DogStatsd::Version.to_standard_version_s
|
6
|
+
|
7
|
+
gem.authors = ["Justin 'J' Lynn", "Sponsored by Mavenlink, Inc."]
|
8
|
+
gem.email = ["j@jaesharp.com"]
|
9
|
+
|
10
|
+
gem.summary = "A DogStatsD compatible reporter for the metriks metric reporting gem"
|
11
|
+
gem.description = gem.summary
|
12
|
+
|
13
|
+
gem.homepage = "http://github.com/mavenlink/metriks-dogstatsd"
|
14
|
+
|
15
|
+
gem.executables = `git ls-files -- bin/*`.split("\n").map{|f| File.basename f }
|
16
|
+
gem.test_files = `git ls-files -- {test,spec,features}/*`.split "\n"
|
17
|
+
gem.files = `git ls-files`.split "\n"
|
18
|
+
|
19
|
+
gem.require_paths = ['lib']
|
20
|
+
|
21
|
+
gem.required_ruby_version = '>= 1.9.3'
|
22
|
+
gem.required_rubygems_version = Gem::Requirement.new '>= 1.8.24'
|
23
|
+
|
24
|
+
gem.add_dependency 'metriks', '~> 0.9.9.2'
|
25
|
+
gem.add_dependency 'dogstatsd-ruby', '= 1.1.0' # we reach into some internals, hence the hard dep
|
26
|
+
|
27
|
+
# Testing
|
28
|
+
gem.add_development_dependency 'rake', '~> 0.9.2'
|
29
|
+
gem.add_development_dependency 'mocha', '~> 0.12.3'
|
30
|
+
|
31
|
+
end
|
data/test/helper.rb
ADDED
@@ -0,0 +1,38 @@
|
|
1
|
+
require 'bundler/setup'
|
2
|
+
|
3
|
+
require 'test/unit'
|
4
|
+
require 'pp'
|
5
|
+
|
6
|
+
require 'mocha'
|
7
|
+
|
8
|
+
require 'metriks'
|
9
|
+
|
10
|
+
# metriks uses threads so we need to ensure
|
11
|
+
# that the thread dies if things fail badly
|
12
|
+
Thread.abort_on_exception = true
|
13
|
+
|
14
|
+
module ThreadHelper
|
15
|
+
require 'thread'
|
16
|
+
|
17
|
+
# Run the given block on n threads in parallel. Returns an array of the
|
18
|
+
# return values of each thread's last invocation of block. Options:
|
19
|
+
|
20
|
+
# :n: call block n times per thread. Default 1.
|
21
|
+
def thread(threads = 2, option = {})
|
22
|
+
n = opts[:n] || 1
|
23
|
+
results = []
|
24
|
+
|
25
|
+
threads.times.map do |i|
|
26
|
+
Thread.new do
|
27
|
+
n.times do
|
28
|
+
results[i] = yield i
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end.each do |thread|
|
32
|
+
thread.join
|
33
|
+
end
|
34
|
+
|
35
|
+
results
|
36
|
+
end
|
37
|
+
|
38
|
+
end
|
@@ -0,0 +1,89 @@
|
|
1
|
+
require 'helper'
|
2
|
+
require 'metriks/reporter/dogstatsd'
|
3
|
+
|
4
|
+
class DogStatsdReporterTest < Test::Unit::TestCase
|
5
|
+
|
6
|
+
class UDPSocketInterceptor < UDPSocket
|
7
|
+
def self.send *args
|
8
|
+
# intercept and throw away any sent packets
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
def setup(options = {})
|
13
|
+
@registry = Metriks::Registry.new
|
14
|
+
|
15
|
+
@reporter = Metriks::Reporter::DogStatsd.new 'localhost', 8125, :registry => @registry, :prefix => options[:reporter_prefix], :timeout_seconds => options[:timeout_seconds], :on_error => options[:on_error]
|
16
|
+
@statsd_client = @reporter.client
|
17
|
+
|
18
|
+
# here we intercept the actual output routine so that we can inspect what would
|
19
|
+
# really be going to the DogStatsD host
|
20
|
+
@reporter.socket = UDPSocketInterceptor
|
21
|
+
end
|
22
|
+
|
23
|
+
def teardown
|
24
|
+
@reporter.stop
|
25
|
+
@registry.stop
|
26
|
+
end
|
27
|
+
|
28
|
+
def flush_reporter
|
29
|
+
@reporter.write
|
30
|
+
end
|
31
|
+
|
32
|
+
def test_meter
|
33
|
+
number_of_times_marked = 10
|
34
|
+
|
35
|
+
@statsd_client.expects(:count).once.with('meter.testing', number_of_times_marked)
|
36
|
+
|
37
|
+
number_of_times_marked.times do
|
38
|
+
@registry.meter('meter.testing').mark
|
39
|
+
end
|
40
|
+
|
41
|
+
flush_reporter
|
42
|
+
end
|
43
|
+
|
44
|
+
def test_counter
|
45
|
+
@statsd_client.expects(:count).once.with('counter.testing', 1)
|
46
|
+
|
47
|
+
@registry.counter('counter.testing').increment
|
48
|
+
flush_reporter
|
49
|
+
end
|
50
|
+
|
51
|
+
def test_timer
|
52
|
+
@statsd_client.expects(:histogram).once.with('timer.testing.count', 1)
|
53
|
+
|
54
|
+
@registry.timer('timer.testing').update(1.5)
|
55
|
+
flush_reporter
|
56
|
+
end
|
57
|
+
|
58
|
+
def test_histogram
|
59
|
+
@statsd_client.expects(:histogram).once.with('histogram.testing.count', 1)
|
60
|
+
|
61
|
+
@registry.histogram('histogram.testing').update(1.5)
|
62
|
+
flush_reporter
|
63
|
+
end
|
64
|
+
|
65
|
+
def test_utilization_timer
|
66
|
+
@statsd_client.expects(:histogram).once.with('utilization_timer.testing.count', 1)
|
67
|
+
|
68
|
+
@registry.utilization_timer('utilization_timer.testing').update(1.5)
|
69
|
+
flush_reporter
|
70
|
+
end
|
71
|
+
|
72
|
+
def test_prefixing
|
73
|
+
setup :reporter_prefix => 'test.prefix.xyzzy'
|
74
|
+
|
75
|
+
@statsd_client.expects(:count).once.with('test.prefix.xyzzy.test.prefixes', 1)
|
76
|
+
|
77
|
+
@registry.meter('test.prefixes').mark
|
78
|
+
flush_reporter
|
79
|
+
end
|
80
|
+
|
81
|
+
def test_timeout
|
82
|
+
error_handler_called = false
|
83
|
+
setup :timeout_seconds => 0.0, :on_error => proc{|ex| error_handler_called = true }
|
84
|
+
@registry.meter('test.timeout').mark
|
85
|
+
@reporter.spawn_threaded_write_system.join
|
86
|
+
assert error_handler_called, 'error handler was not called'
|
87
|
+
end
|
88
|
+
|
89
|
+
end
|
metadata
ADDED
@@ -0,0 +1,121 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: metriks-dogstatsd
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Justin 'J' Lynn
|
9
|
+
- Sponsored by Mavenlink, Inc.
|
10
|
+
autorequire:
|
11
|
+
bindir: bin
|
12
|
+
cert_chain: []
|
13
|
+
date: 2012-12-06 00:00:00.000000000 Z
|
14
|
+
dependencies:
|
15
|
+
- !ruby/object:Gem::Dependency
|
16
|
+
name: metriks
|
17
|
+
requirement: !ruby/object:Gem::Requirement
|
18
|
+
none: false
|
19
|
+
requirements:
|
20
|
+
- - ~>
|
21
|
+
- !ruby/object:Gem::Version
|
22
|
+
version: 0.9.9.2
|
23
|
+
type: :runtime
|
24
|
+
prerelease: false
|
25
|
+
version_requirements: !ruby/object:Gem::Requirement
|
26
|
+
none: false
|
27
|
+
requirements:
|
28
|
+
- - ~>
|
29
|
+
- !ruby/object:Gem::Version
|
30
|
+
version: 0.9.9.2
|
31
|
+
- !ruby/object:Gem::Dependency
|
32
|
+
name: dogstatsd-ruby
|
33
|
+
requirement: !ruby/object:Gem::Requirement
|
34
|
+
none: false
|
35
|
+
requirements:
|
36
|
+
- - '='
|
37
|
+
- !ruby/object:Gem::Version
|
38
|
+
version: 1.1.0
|
39
|
+
type: :runtime
|
40
|
+
prerelease: false
|
41
|
+
version_requirements: !ruby/object:Gem::Requirement
|
42
|
+
none: false
|
43
|
+
requirements:
|
44
|
+
- - '='
|
45
|
+
- !ruby/object:Gem::Version
|
46
|
+
version: 1.1.0
|
47
|
+
- !ruby/object:Gem::Dependency
|
48
|
+
name: rake
|
49
|
+
requirement: !ruby/object:Gem::Requirement
|
50
|
+
none: false
|
51
|
+
requirements:
|
52
|
+
- - ~>
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: 0.9.2
|
55
|
+
type: :development
|
56
|
+
prerelease: false
|
57
|
+
version_requirements: !ruby/object:Gem::Requirement
|
58
|
+
none: false
|
59
|
+
requirements:
|
60
|
+
- - ~>
|
61
|
+
- !ruby/object:Gem::Version
|
62
|
+
version: 0.9.2
|
63
|
+
- !ruby/object:Gem::Dependency
|
64
|
+
name: mocha
|
65
|
+
requirement: !ruby/object:Gem::Requirement
|
66
|
+
none: false
|
67
|
+
requirements:
|
68
|
+
- - ~>
|
69
|
+
- !ruby/object:Gem::Version
|
70
|
+
version: 0.12.3
|
71
|
+
type: :development
|
72
|
+
prerelease: false
|
73
|
+
version_requirements: !ruby/object:Gem::Requirement
|
74
|
+
none: false
|
75
|
+
requirements:
|
76
|
+
- - ~>
|
77
|
+
- !ruby/object:Gem::Version
|
78
|
+
version: 0.12.3
|
79
|
+
description: A DogStatsD compatible reporter for the metriks metric reporting gem
|
80
|
+
email:
|
81
|
+
- j@jaesharp.com
|
82
|
+
executables: []
|
83
|
+
extensions: []
|
84
|
+
extra_rdoc_files: []
|
85
|
+
files:
|
86
|
+
- .rvmrc
|
87
|
+
- Gemfile
|
88
|
+
- Gemfile.lock
|
89
|
+
- Rakefile
|
90
|
+
- lib/metriks-dogstatsd.rb
|
91
|
+
- lib/metriks/reporter/dogstatsd.rb
|
92
|
+
- lib/metriks/reporter/dogstatsd/version.rb
|
93
|
+
- metriks-dogstatsd.gemspec
|
94
|
+
- test/helper.rb
|
95
|
+
- test/test_dogstatsd_reporter.rb
|
96
|
+
homepage: http://github.com/mavenlink/metriks-dogstatsd
|
97
|
+
licenses: []
|
98
|
+
post_install_message:
|
99
|
+
rdoc_options: []
|
100
|
+
require_paths:
|
101
|
+
- lib
|
102
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
103
|
+
none: false
|
104
|
+
requirements:
|
105
|
+
- - ! '>='
|
106
|
+
- !ruby/object:Gem::Version
|
107
|
+
version: 1.9.3
|
108
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
109
|
+
none: false
|
110
|
+
requirements:
|
111
|
+
- - ! '>='
|
112
|
+
- !ruby/object:Gem::Version
|
113
|
+
version: 1.8.24
|
114
|
+
requirements: []
|
115
|
+
rubyforge_project:
|
116
|
+
rubygems_version: 1.8.24
|
117
|
+
signing_key:
|
118
|
+
specification_version: 3
|
119
|
+
summary: A DogStatsD compatible reporter for the metriks metric reporting gem
|
120
|
+
test_files: []
|
121
|
+
has_rdoc:
|