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 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: