metriks-dogstatsd 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
data/.rvmrc ADDED
@@ -0,0 +1 @@
1
+ rvm --create use 1.9.3@metriks-dogstatsd
data/Gemfile ADDED
@@ -0,0 +1,3 @@
1
+ source :rubygems
2
+
3
+ gemspec
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,10 @@
1
+ require 'rake/testtask'
2
+
3
+ Rake::TestTask.new do |t|
4
+ t.libs << 'test'
5
+ t.test_files = FileList['test/test*.rb']
6
+ t.verbose = true
7
+ end
8
+
9
+ desc "Run tests"
10
+ task :default => :test
@@ -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: