ruby-metrics 0.9.0 → 0.9.4

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.
@@ -0,0 +1,80 @@
1
+ require 'ruby-metrics/version'
2
+ require 'ruby-metrics'
3
+
4
+ require 'gmetric'
5
+
6
+ module Metrics
7
+ module Reporters
8
+ class GangliaReporter
9
+
10
+ attr_reader :host_ip, :host_port
11
+
12
+ def initialize(options = {})
13
+ @host_ip = options[:host_ip]
14
+ @host_port = options[:host_port]
15
+ end
16
+
17
+ def send_data(data)
18
+ puts "Sending data: #{data.inspect}"
19
+ data_type =
20
+ case data[:value]
21
+ Fixnum
22
+ "uint32"
23
+ Float
24
+ "float"
25
+ String
26
+ "string"
27
+ else
28
+ "unknown"
29
+ end
30
+
31
+ Ganglia::GMetric.send(@host_ip, @host_port.to_i,
32
+ :spoof => 0,
33
+ :name => data[:name],
34
+ :units => data[:units],
35
+ :type => data_type,
36
+ :value => data[:value],
37
+ :tmax => 60,
38
+ :dmax => 300,
39
+ )
40
+ end
41
+
42
+ def report(agent)
43
+ agent.instruments.each do |name, instrument|
44
+ case instrument
45
+ when Metrics::Instruments::Counter
46
+ send_data :name => name,
47
+ :value => instrument.to_i,
48
+ :units => instrument.units
49
+ when Metrics::Instruments::Gauge
50
+ if instrument.get.is_a? Hash
51
+ instrument.get.each do |key, value|
52
+ send_data :name => "#{name}_#{key}",
53
+ :value => value,
54
+ :units => instrument.units
55
+ end
56
+ else
57
+ send_data :name => name,
58
+ :value => instrument.get,
59
+ :units => instrument.units
60
+ end
61
+ when Metrics::Instruments::Timer
62
+ [:count, :fifteen_minute_rate, :five_minute_rate, :one_minute_rate, :min, :max, :mean].each do |attribute|
63
+ send_data :name => "#{name}_#{attribute}",
64
+ :value => instrument.send(attribute),
65
+ :units => instrument.units
66
+ end
67
+ when Metrics::Instruments::Meter
68
+ [:count, :fifteen_minute_rate, :five_minute_rate, :one_minute_rate, :mean_rate].each do |attribute|
69
+ send_data :name => "#{name_attribute}",
70
+ :value => instrument.send(attribute),
71
+ :units => instrument.units
72
+ end
73
+ else
74
+ puts "Unhandled instrument"
75
+ end
76
+ end
77
+ end
78
+ end
79
+ end
80
+ end
@@ -0,0 +1,85 @@
1
+ require 'net/https'
2
+ require 'ruby-metrics/version'
3
+ require 'ruby-metrics'
4
+
5
+ module Metrics
6
+ module Reporters
7
+ class Librato
8
+ attr_reader :api_token
9
+ attr_reader :user
10
+
11
+ API_URL = "https://metrics-api.librato.com/v1"
12
+
13
+ def initialize(options = {})
14
+ @api_token = options[:api_token]
15
+ @user = options[:user]
16
+ @headers = {
17
+ 'User-Agent' => "ruby-metrics #{Metrics::VERSION}"
18
+ }
19
+ end
20
+
21
+ def send_data(post_url, post_data)
22
+ url = URI.parse(post_url)
23
+ req = Net::HTTP::Post.new(url.path)
24
+ req.basic_auth @user, @api_token
25
+ @headers.each do |k,v|
26
+ req.add_field(k, v)
27
+ end
28
+ req.set_form_data(post_data)
29
+ https = Net::HTTP.new(url.host, url.port)
30
+ https.use_ssl = true
31
+ #https.set_debug_output($stdout)
32
+
33
+ https.start do |http|
34
+ result = http.request(req)
35
+ case result
36
+ when Net::HTTPCreated
37
+ # OK
38
+ puts "SENT!"
39
+ else
40
+ puts "FAILED TO SEND: #{https.inspect}"
41
+ end
42
+ end
43
+ end
44
+
45
+ def report(agent)
46
+ agent.instruments.each do |name, instrument|
47
+ measure_time = Time.now.to_i
48
+
49
+ case instrument
50
+ when Metrics::Instruments::Counter
51
+ send_data "#{API_URL}/counters/#{name}.json",
52
+ :measure_time => measure_time,
53
+ :value => instrument.to_i
54
+
55
+ when Metrics::Instruments::Gauge
56
+ post_url = "#{API_URL}/gauges/#{name}.json"
57
+ if instrument.get.is_a? Hash
58
+ instrument.get.each do |key, value|
59
+ send_data post_url,
60
+ :measure_time => measure_time,
61
+ :source => key,
62
+ :value => value
63
+ end
64
+ else
65
+ send_data post_url,
66
+ :measure_time => measure_time,
67
+ :value => instrument.get
68
+ end
69
+
70
+ when Metrics::Instruments::Timer
71
+ [:count, :fifteen_minute_rate, :five_minute_rate, :one_minute_rate, :min, :max, :mean].each do |attribute|
72
+ send_data "#{API_URL}/gauges/#{name}.json",
73
+ :measure_time => measure_time,
74
+ :source => attribute,
75
+ :value => instrument.send(attribute)
76
+ end
77
+
78
+ else
79
+ puts "Unhandled instrument"
80
+ end
81
+ end
82
+ end
83
+ end
84
+ end
85
+ end
@@ -1,24 +1,23 @@
1
1
  module Metrics
2
2
  module Statistics
3
3
  class ExponentialSample
4
+ RESCALE_WINDOW_SECONDS = 60 * 60 # 1 hour
5
+
4
6
  def initialize(size = 1028, alpha = 0.015)
5
- @values = Hash.new
6
- @count = 0
7
7
  @size = size
8
8
  @alpha = alpha
9
- @rescale_window = 3600 #seconds -- 1 hour
10
- self.clear
9
+ clear
11
10
  end
12
11
 
13
12
  def clear
14
- @values = Hash.new
13
+ @values = { }
15
14
  @start_time = tick
16
- @next_scale_time = Time.now.to_f + @rescale_window
15
+ @next_scale_time = Time.now.to_f + RESCALE_WINDOW_SECONDS
17
16
  @count = 0
18
17
  end
19
18
 
20
19
  def size
21
- [@values.keys.length, @count].min
20
+ [@values.size, @count].min
22
21
  end
23
22
 
24
23
  def tick
@@ -32,59 +31,50 @@ module Metrics
32
31
  def update_with_timestamp(value, timestamp)
33
32
  priority = weight(timestamp.to_f - @start_time.to_f) / rand
34
33
  @count += 1
35
- newcount = @count
36
- if (newcount <= @size)
34
+ if @count <= @size
37
35
  @values[priority] = value
38
36
  else
39
- firstkey = @values.keys[0]
40
- if firstkey && (firstkey < priority)
37
+ first_key = @values.keys.first
38
+ if first_key && first_key < priority
41
39
  @values[priority] = value
42
40
 
43
- while(@values.delete(firstkey) == nil)
44
- firstkey = @values.keys[0]
41
+ while values.any? && @values.delete(first_key).nil?
42
+ first_key = @values.keys.first
45
43
  end
46
44
  end
47
45
  end
48
46
 
49
47
  now = Time.now.to_f
50
- next_scale_time = @next_scale_time
51
-
52
- if (now >= next_scale_time)
53
- self.rescale(now, next_scale_time)
48
+
49
+ if now >= @next_scale_time
50
+ rescale(now + RESCALE_WINDOW_SECONDS)
54
51
  end
55
52
  end
56
-
57
-
58
- def rescale(now, next_scale_time)
59
- if @next_scale_time == next_scale_time
60
- # writelock
61
- @next_scale_time = now + @rescale_window
62
- old_start_time = @start_time
63
- @start_time = tick
64
- time_delta = @start_time - old_start_time
65
- keys = @values.keys
66
- keys.each do |key|
67
- value = @values.delete(key)
68
- new_key = (key * Math.exp(-@alpha * time_delta))
69
- @values[new_key] = value
70
- end
71
- # unlock
72
- end
73
- end
74
-
75
- def weight(factor)
76
- return @alpha.to_f * factor.to_f
77
- end
78
-
53
+
79
54
  def values
80
55
  # read-lock?
81
- result = Array.new
82
- keys = @values.keys.sort
83
- keys.each do |key|
84
- result << @values[key]
56
+ @values.keys.sort.map do |key|
57
+ @values[key]
85
58
  end
86
-
87
- result
59
+ end
60
+
61
+ private
62
+ def rescale(next_scale_time)
63
+ # writelock
64
+ @next_scale_time = next_scale_time
65
+ old_start_time = @start_time
66
+ @start_time = tick
67
+ time_delta = @start_time - old_start_time
68
+ @values.keys.each do |key|
69
+ value = @values.delete(key)
70
+ new_key = key * Math.exp(-@alpha * time_delta)
71
+ @values[new_key] = value
72
+ end
73
+ # unlock
74
+ end
75
+
76
+ def weight(factor)
77
+ @alpha.to_f * factor.to_f
88
78
  end
89
79
  end
90
80
  end
@@ -3,22 +3,21 @@ module Metrics
3
3
  class UniformSample
4
4
  def initialize(size = 1028)
5
5
  @values = Array.new(size)
6
- @count = 0
7
6
  @size = size
8
- self.clear
7
+ clear
9
8
  end
10
-
9
+
11
10
  def clear
12
- (0..@values.length-1).each do |i|
11
+ (0...@values.size).each do |i|
13
12
  @values[i] = 0
14
13
  end
15
14
  @count = 0
16
15
  end
17
-
16
+
18
17
  def size
19
- @values.length
18
+ @values.size
20
19
  end
21
-
20
+
22
21
  def update(value)
23
22
  if @count < @values.length
24
23
  @values[@count] = value
@@ -28,10 +27,10 @@ module Metrics
28
27
  @values[index] = value
29
28
  end
30
29
  end
31
-
30
+
32
31
  def values
33
32
  @values.dup
34
33
  end
35
34
  end
36
35
  end
37
- end
36
+ end
@@ -1,42 +1,36 @@
1
1
  module Metrics
2
-
3
- class TimeUnit
4
- def self.to_nsec(mult = 1)
5
- raise NotImplementedError
6
- end
7
- end
8
-
9
- class Nanoseconds < TimeUnit
2
+
3
+ module Nanoseconds
10
4
  def self.to_nsec(mult = 1)
11
5
  mult
12
6
  end
13
7
  end
14
8
 
15
- class Microseconds < TimeUnit
9
+ module Microseconds
16
10
  def self.to_nsec(mult = 1)
17
11
  1000 * mult
18
12
  end
19
13
  end
20
14
 
21
- class Milliseconds < TimeUnit
15
+ module Milliseconds
22
16
  def self.to_nsec(mult = 1)
23
17
  1000000 * mult
24
18
  end
25
19
  end
26
20
 
27
- class Seconds < TimeUnit
21
+ module Seconds
28
22
  def self.to_nsec(mult = 1)
29
23
  1000000000 * mult
30
24
  end
31
25
  end
32
26
 
33
- class Minutes < TimeUnit
27
+ module Minutes
34
28
  def self.to_nsec(mult = 1)
35
29
  60000000000 * mult
36
30
  end
37
31
  end
38
32
 
39
- class Hours < TimeUnit
33
+ module Hours
40
34
  def self.to_nsec(mult = 1)
41
35
  3600000000000 * mult
42
36
  end
@@ -53,11 +47,11 @@ module Metrics
53
47
  }
54
48
 
55
49
  def convert_to_ns(value, unit)
56
- (UNITS[unit].to_nsec.to_f * value.to_f)
50
+ UNITS[unit].to_nsec.to_f * value.to_f
57
51
  end
58
52
 
59
53
  def scale_time_units(source, dest)
60
- (UNITS[source].to_nsec.to_f) / (UNITS[dest].to_nsec.to_f)
54
+ UNITS[source].to_nsec.to_f / UNITS[dest].to_nsec.to_f
61
55
  end
62
56
  end
63
57
  end
@@ -1,3 +1,3 @@
1
1
  module Metrics
2
- VERSION = '0.9.0'
2
+ VERSION = '0.9.4'
3
3
  end
@@ -16,6 +16,6 @@ Gem::Specification.new do |s|
16
16
  s.files = ['lib/ruby-metrics/reporters/opentsdb.rb']
17
17
  s.require_paths = ["lib"]
18
18
 
19
- s.add_dependency "opentsdb", '0.1.0'
19
+ s.add_dependency "opentsdb", '~> 0.1', '>= 0.1.0'
20
20
  s.add_dependency 'ruby-metrics', Metrics::VERSION
21
21
  end
data/ruby-metrics.gemspec CHANGED
@@ -17,7 +17,7 @@ Gem::Specification.new do |s|
17
17
  s.description = %q{A Ruby implementation of metrics inspired by @coda's JVM metrics for those of us in Ruby land}
18
18
  s.license = 'MIT'
19
19
 
20
- s.add_dependency 'json', '~> 1.5', '>= 1.5.5'
20
+ s.add_dependency 'json', '~> 2.3', '>= 2.3.0'
21
21
 
22
22
  s.add_development_dependency 'rspec', '~> 2.14', '>= 2.14.0'
23
23
  s.add_development_dependency 'simplecov', '~> 0.3', '>= 0.3.8'
@@ -1,78 +1,88 @@
1
1
  require 'spec_helper'
2
2
 
3
3
  describe Metrics::Instruments::Counter do
4
- before(:each) do
5
- @counter = Metrics::Instruments::Counter.new
4
+ let(:counter) {
5
+ Metrics::Instruments::Counter.new
6
+ }
7
+
8
+ it 'can be tagged' do
9
+ counter.tag(:foo, 'bar')
10
+ expect(counter.tags.keys).to include(:foo)
11
+ expect(counter.tags.values).to include('bar')
12
+ end
13
+
14
+ it 'does not need to be tagged' do
15
+ expect(counter.tags.keys.size).to be 0
6
16
  end
7
17
 
8
- it "should create a new entity with zero as its value" do
9
- @counter.to_i.should == 0
18
+ it 'should create a new entity with zero as its value' do
19
+ counter.to_i.should == 0
10
20
  end
11
21
 
12
22
  it "should increment its counter by the value specified" do
13
23
  value = 1
14
24
  lambda do
15
- @counter.inc(value)
16
- end.should change{ @counter.to_i }.by(value)
25
+ counter.inc(value)
26
+ end.should change{ counter.to_i }.by(value)
17
27
  end
18
28
 
19
29
  it "should increment its counter by one by default" do
20
30
  lambda do
21
- @counter.inc
22
- end.should change{ @counter.to_i }.by(1)
31
+ counter.inc
32
+ end.should change{ counter.to_i }.by(1)
23
33
  end
24
34
 
25
35
  it "should decrement its counter by the value specified" do
26
36
  value = 1
27
37
  lambda do
28
- @counter.dec(value)
29
- end.should change{ @counter.to_i }.by(-value)
38
+ counter.dec(value)
39
+ end.should change{ counter.to_i }.by(-value)
30
40
  end
31
41
 
32
42
  it "should decrement its counter by one by default" do
33
43
  lambda do
34
- @counter.dec
35
- end.should change{ @counter.to_i }.by(-1)
44
+ counter.dec
45
+ end.should change{ counter.to_i }.by(-1)
36
46
  end
37
47
 
38
48
  it "should alias #incr to #inc" do
39
49
  lambda do
40
- @counter.incr
41
- end.should change{ @counter.to_i }.by(1)
50
+ counter.incr
51
+ end.should change{ counter.to_i }.by(1)
42
52
  end
43
53
 
44
54
  it "should alias #decr to #dec" do
45
55
  lambda do
46
- @counter.decr
47
- end.should change{ @counter.to_i }.by(-1)
56
+ counter.decr
57
+ end.should change{ counter.to_i }.by(-1)
48
58
  end
49
59
 
50
60
  it "should clear the counter correctly" do
51
- @counter.clear
52
- @counter.to_i.should == 0
61
+ counter.clear
62
+ counter.to_i.should == 0
53
63
  end
54
64
 
55
65
  it "should correctly represent the value as a string" do
56
- @counter.clear
57
- @counter.to_i.should == 0
58
- @counter.to_s.should == "0"
66
+ counter.clear
67
+ counter.to_i.should == 0
68
+ counter.to_s.should == "0"
59
69
  end
60
70
 
61
71
  it "should return the new count when incrementing" do
62
- count = @counter.to_i
63
- @counter.inc(value = 1).should == count + value
72
+ count = counter.to_i
73
+ counter.inc(value = 1).should == count + value
64
74
  end
65
75
 
66
76
  it "should return the new count when decrementing" do
67
77
  lambda do
68
- @counter.dec(1)
69
- end.should change{ @counter.to_i }.by(-1)
78
+ counter.dec(1)
79
+ end.should change{ counter.to_i }.by(-1)
70
80
  end
71
81
 
72
82
  context "to_json" do
73
- let(:json) { @counter.to_json }
83
+ let(:json) { counter.to_json }
74
84
  it "should serialize to its current value" do
75
- json.should == @counter.to_s
85
+ json.should == counter.to_s
76
86
  end
77
87
  end
78
88
 
@@ -71,7 +71,7 @@ describe Metrics::Instruments::Histogram do
71
71
 
72
72
  it "should clear data correctly" do
73
73
  sample = Metrics::Statistics::UniformSample.new
74
- sample.should_receive(:clear)
74
+ sample.should_receive(:clear).twice
75
75
  Metrics::Statistics::UniformSample.should_receive(:new).and_return sample
76
76
 
77
77
  histogram = Metrics::Instruments::Histogram.new
@@ -29,7 +29,7 @@ describe Metrics::Instruments::Timer do
29
29
  end
30
30
 
31
31
  it "should have quantiles of zero" do
32
- @timer.quantiles.should == {0.99=>0.0, 0.97=>0.0, 0.95=>0.0, 0.75=>0.0, 0.5=>0.0, 0.25=>0.0}
32
+ @timer.quantiles.should == {0.25=>0.0, 0.50=>0.0, 0.75=>0.0, 0.95=>0.0, 0.97=>0.0, 0.98=>0.0, 0.99=>0.0 }
33
33
  end
34
34
 
35
35
  it "should have a mean rate of zero" do
@@ -85,7 +85,7 @@ describe Metrics::Instruments::Timer do
85
85
  end
86
86
 
87
87
  it "should accurately calculate percentiles" do
88
- @timer.quantiles.should == {0.99=>39.6, 0.97=>38.8, 0.95=>38.0, 0.75=>30.0, 0.5=>20.0, 0.25=>20.0}
88
+ @timer.quantiles.should == {0.25=>20.0, 0.5=>20.0, 0.75=>30.0, 0.95=>38.0, 0.97=>38.8, 0.98=>39.2, 0.99=>39.6}
89
89
  end
90
90
 
91
91
  it "should contain the series added" do
@@ -54,7 +54,7 @@ describe Metrics::Integration::Rack::Middleware do
54
54
  it "should count all requests" do
55
55
  lambda do
56
56
  get '/'
57
- end.should change{ app.requests.count }.by(1)
57
+ end.should change{ app.agent.timer(:_requests).count }.by(1)
58
58
  end
59
59
 
60
60
  it "should count uncaught exceptions" do
@@ -63,22 +63,22 @@ describe Metrics::Integration::Rack::Middleware do
63
63
  lambda do
64
64
  get '/'
65
65
  end.should raise_error
66
- end.should change{ app.uncaught_exceptions.to_i }.by(1)
66
+ end.should change{ app.agent.counter(:_uncaught_exceptions).to_i }.by(1)
67
67
  end
68
68
 
69
69
  it "should time request length" do
70
70
  length = 0.1
71
71
  @app = lambda{ |env| sleep(length); [200, {}, ['']] }
72
72
  get '/'
73
- app.requests.mean.should be_within(length / 10).of(length)
73
+ app.agent.timer(:_requests).mean.should be_within(length / 10).of(length)
74
74
  end
75
-
76
- [ 200, 304, 404, 500].each do |status|
75
+
76
+ [200, 304, 404, 500].each do |status|
77
77
  it "should count #{status} HTTP status code as #{status / 100}xx" do
78
78
  @app = lambda{ |env| [status, {}, []] }
79
79
  lambda do
80
80
  get '/'
81
- end.should change{ app.status_codes[status / 100].to_i }.by(1)
81
+ end.should change{ app.agent.counter(:"_status_#{status / 100}xx").to_i }.by(1)
82
82
  end
83
83
  end
84
84
 
@@ -0,0 +1,27 @@
1
+ require 'ruby-metrics/agent'
2
+ require 'ruby-metrics/reporter'
3
+ require 'ruby-metrics/reporters/opentsdb'
4
+
5
+ module Metrics
6
+ describe 'Reporter' do
7
+ let(:mock_reporter) {
8
+ double(Metrics::Reporters::OpenTSDBReporter)
9
+ }
10
+
11
+ let(:agent) {
12
+ agent = Metrics::Agent.new
13
+ agent.report_to 'opentsdb', mock_reporter
14
+ agent
15
+ }
16
+
17
+ it 'should report three times in 4 seconds with a 1 second interval' do
18
+ expect(mock_reporter).to receive(:report).exactly(3).times
19
+ agent.report_periodically(1)
20
+ puts "Sleeping"
21
+ sleep(4)
22
+ puts "Stopping..."
23
+ agent.stop_reporting
24
+ end
25
+
26
+ end
27
+ end