ruby-metrics 0.9.0 → 0.9.4

Sign up to get free protection for your applications and to get access to all the features.
@@ -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