librato-metrics 0.2.3 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore CHANGED
@@ -17,3 +17,5 @@ doc
17
17
 
18
18
  # rcov generated
19
19
  coverage
20
+
21
+ test_env.sh
data/README.md CHANGED
@@ -12,6 +12,12 @@ In your shell:
12
12
  Then, in your application or script:
13
13
 
14
14
  require 'librato/metrics'
15
+
16
+ ### Optional steps
17
+
18
+ For best performance we recommend installing [yajl-ruby](https://github.com/brianmario/yajl-ruby):
19
+
20
+ gem install yajl-ruby
15
21
 
16
22
  ## Quick Start
17
23
 
@@ -101,6 +107,10 @@ Get the 20 most recent 15 minute data point rollups for `temperature`:
101
107
 
102
108
  There are many more options supported for querying, take a look at the [REST API docs](http://dev.librato.com/v1/get/gauges/:name) or the [fetch documentation](http://rubydoc.info/github/librato/librato-metrics/master/Librato/Metrics.fetch) for more details.
103
109
 
110
+ ## Thread Safety
111
+
112
+ The `librato-metrics` gem currently does not do internal locking for thread safety. When used in multi-threaded applications, please add your own mutexes for sensitive operations.
113
+
104
114
  ## Known Issues & Coming Improvements
105
115
 
106
116
  This is an early release and as such is lacking some capabilities slated for future releases.
data/Rakefile CHANGED
@@ -5,8 +5,27 @@ Bundler::GemHelper.install_tasks
5
5
 
6
6
  # Testing
7
7
  require 'rspec/core/rake_task'
8
- RSpec::Core::RakeTask.new(:spec) do |t|
9
- t.rspec_opts = '--color'
8
+
9
+ desc "Run all tests"
10
+ task :spec do
11
+ Rake::Task['spec:unit'].execute
12
+ if ENV['TEST_API_USER'] && ENV['TEST_API_KEY']
13
+ Rake::Task['spec:integration'].execute
14
+ else
15
+ puts "TEST_API_USER and TEST_API_KEY not in environment, skipping integration tests..."
16
+ end
17
+ end
18
+
19
+ namespace :spec do
20
+ RSpec::Core::RakeTask.new(:unit) do |t|
21
+ t.rspec_opts = '--color'
22
+ t.pattern = 'spec/unit/**/*_spec.rb'
23
+ end
24
+
25
+ RSpec::Core::RakeTask.new(:integration) do |t|
26
+ t.rspec_opts = '--color'
27
+ t.pattern = 'spec/integration/**/*_spec.rb'
28
+ end
10
29
  end
11
30
 
12
31
  task :default => :spec
@@ -3,13 +3,14 @@ $:.unshift(File.dirname(__FILE__)) unless
3
3
 
4
4
  require 'base64'
5
5
  require 'excon'
6
- require 'json'
6
+ require 'multi_json'
7
7
 
8
8
  require 'metrics/errors'
9
9
  require 'metrics/persistence'
10
10
  require 'metrics/queue'
11
11
  require 'metrics/simple'
12
12
  require 'metrics/version'
13
+ require 'metrics/collect'
13
14
 
14
15
  module Librato
15
16
 
@@ -59,8 +60,6 @@ module Librato
59
60
  # @param [Symbol|String] metric Metric name
60
61
  # @param [Hash] options Query options
61
62
  def self.fetch(metric, options={})
62
- # TODO: look up type when not specified.
63
- type = options.delete(:type) || 'gauge'
64
63
  query = options.dup
65
64
  if query[:start_time].respond_to?(:year)
66
65
  query[:start_time] = query[:start_time].to_i
@@ -71,9 +70,9 @@ module Librato
71
70
  unless query.empty?
72
71
  query[:resolution] ||= 1
73
72
  end
74
- response = connection.get(:path => "v1/#{type}s/#{metric}.json",
73
+ response = connection.get(:path => "v1/metrics/#{metric}",
75
74
  :query => query, :expects => 200)
76
- parsed = JSON.parse(response.body)
75
+ parsed = MultiJson.decode(response.body)
77
76
  # TODO: pagination support
78
77
  query.empty? ? parsed : parsed["measurements"]
79
78
  end
@@ -90,10 +89,9 @@ module Librato
90
89
  def self.list(options={})
91
90
  query = {}
92
91
  query[:name] = options[:name] if options[:name]
93
- response = connection.get(:path => 'v1/metrics.json',
94
- :query => query, :expects => 200)
95
- # TODO: pagination support
96
- JSON.parse(response.body)["metrics"]
92
+ offset = 0
93
+ path = "v1/metrics"
94
+ Collect.paginated_metrics connection, path, query
97
95
  end
98
96
 
99
97
  end
@@ -0,0 +1,32 @@
1
+ module Librato
2
+ module Metrics
3
+ class Collect
4
+
5
+ MAX_RESULTS = 100
6
+
7
+ # Aggregates all results of paginated elements, requesting more collections as needed
8
+ #
9
+ # @param [Excon] connection Connection to Metrics service
10
+ # @param [String] path API uri
11
+ # @param [Hash] query Query options
12
+ def self.paginated_metrics connection, path, query
13
+ results = []
14
+ response = connection.get(:path => path,
15
+ :query => query, :expects => 200)
16
+ parsed = MultiJson.decode(response.body)
17
+ results = parsed["metrics"]
18
+ return results if parsed["query"]["found"] <= MAX_RESULTS
19
+ query[:offset] = MAX_RESULTS
20
+ begin
21
+ response = connection.get(:path => path,
22
+ :query => query, :expects => 200)
23
+ parsed = MultiJson.decode(response.body)
24
+ results.push(*parsed["metrics"])
25
+ query[:offset] += MAX_RESULTS
26
+ end while query[:offset] < parsed["query"]["found"]
27
+ results
28
+ end
29
+
30
+ end
31
+ end
32
+ end
@@ -10,8 +10,8 @@ module Librato
10
10
  # Metrics web API.
11
11
  #
12
12
  def persist(queued)
13
- payload = queued.to_json
14
- Simple.connection.post(:path => '/v1/metrics.json',
13
+ payload = MultiJson.encode(queued)
14
+ Simple.connection.post(:path => '/v1/metrics',
15
15
  :headers => {'Content-Type' => 'application/json'},
16
16
  :body => payload, :expects => 200)
17
17
  end
@@ -40,11 +40,19 @@ module Librato
40
40
  @queued[:counters] || []
41
41
  end
42
42
 
43
+ # Are any metrics currently queued?
44
+ #
45
+ # @return Boolean
46
+ def empty?
47
+ @queued.empty?
48
+ end
49
+
43
50
  # Remove all queued metrics
44
51
  #
45
52
  def flush
46
53
  @queued = {}
47
54
  end
55
+ alias :clear :flush
48
56
  alias :flush_queued :flush
49
57
 
50
58
  # The object this MetricSet will use to persist
@@ -67,6 +75,14 @@ module Librato
67
75
  @queued
68
76
  end
69
77
 
78
+ # Count of metrics currently queued
79
+ #
80
+ # @return Integer
81
+ def size
82
+ self.queued.inject(0) { |result, data| result + data.last.size }
83
+ end
84
+ alias :length :size
85
+
70
86
  # Persist currently queued metrics
71
87
  #
72
88
  # @return Boolean
@@ -1,5 +1,5 @@
1
1
  module Librato
2
2
  module Metrics
3
- VERSION = "0.2.3"
3
+ VERSION = "0.3.0"
4
4
  end
5
5
  end
@@ -25,6 +25,7 @@ Gem::Specification.new do |s|
25
25
 
26
26
  ## runtime dependencies
27
27
  s.add_dependency 'excon', '~>0.7.12'
28
+ s.add_dependency 'multi_json'
28
29
 
29
30
  ## development dependencies
30
31
  s.add_development_dependency 'rake'
@@ -0,0 +1,117 @@
1
+ require 'spec_helper'
2
+
3
+ module Librato
4
+ describe Metrics do
5
+ before(:all) { prep_integration_tests }
6
+
7
+ describe "#fetch" do
8
+ before(:all) do
9
+ delete_all_metrics
10
+ Metrics.submit :my_counter => {:type => :counter, :value => 0}
11
+ 1.upto(2).each do |i|
12
+ sleep 1
13
+ Metrics.submit :my_counter => {:type => :counter, :value => i}
14
+ Metrics.submit :my_counter => {:source => 'baz', :type => :counter, :value => i+1}
15
+ end
16
+ end
17
+
18
+ context "without arguments" do
19
+ it "should get metric attributes" do
20
+ metric = Metrics.fetch :my_counter
21
+ metric['name'].should == 'my_counter'
22
+ metric['type'].should == 'counter'
23
+ end
24
+ end
25
+
26
+ context "with a start_time" do
27
+ it "should return entries since that time" do
28
+ data = Metrics.fetch :my_counter, :start_time => Time.now-3600 # 1 hr ago
29
+ data['unassigned'].length.should == 3
30
+ data['baz'].length.should == 2
31
+ end
32
+ end
33
+
34
+ context "with a count limit" do
35
+ it "should return that number of entries per source" do
36
+ data = Metrics.fetch :my_counter, :count => 2
37
+ data['unassigned'].length.should == 2
38
+ data['baz'].length.should == 2
39
+ end
40
+ end
41
+
42
+ context "with a source limit" do
43
+ it "should only return that source" do
44
+ data = Metrics.fetch :my_counter, :source => 'baz', :start_time => Time.now-3600
45
+ data['baz'].length.should == 2
46
+ data['unassigned'].should be_nil
47
+ end
48
+ end
49
+
50
+ end
51
+
52
+ describe "#list" do
53
+ before(:all) do
54
+ delete_all_metrics
55
+ Metrics.submit :foo => 123, :bar => 345, :baz => 678, :foo_2 => 901
56
+ end
57
+
58
+ context "without arguments" do
59
+ it "should list all metrics" do
60
+ metric_names = Metrics.list.map { |metric| metric['name'] }
61
+ metric_names.sort.should == %w{foo bar baz foo_2}.sort
62
+ end
63
+ end
64
+
65
+ context "with a name argument" do
66
+ it "should list metrics that match" do
67
+ metric_names = Metrics.list(:name => 'foo').map { |metric| metric['name'] }
68
+ metric_names.sort.should == %w{foo foo_2}.sort
69
+ end
70
+ end
71
+
72
+ end
73
+
74
+ describe "#submit" do
75
+
76
+ context "with a gauge" do
77
+ before(:all) do
78
+ delete_all_metrics
79
+ Metrics.submit :foo => 123
80
+ end
81
+
82
+ it "should create the metrics" do
83
+ metric = Metrics.list[0]
84
+ metric['name'].should == 'foo'
85
+ metric['type'].should == 'gauge'
86
+ end
87
+
88
+ it "should store their data" do
89
+ data = Metrics.fetch :foo, :count => 1
90
+ data.should_not be_empty
91
+ data['unassigned'][0]['value'] == 123.0
92
+ end
93
+ end
94
+
95
+ context "with a counter" do
96
+ before(:all) do
97
+ delete_all_metrics
98
+ Metrics.submit :bar => {:type => :counter, :source => 'baz', :value => 456}
99
+ end
100
+
101
+ it "should create the metrics" do
102
+ metric = Metrics.list[0]
103
+ metric['name'].should == 'bar'
104
+ metric['type'].should == 'counter'
105
+ end
106
+
107
+ it "should store their data" do
108
+ data = Metrics.fetch :bar, :count => 1
109
+ data.should_not be_empty
110
+ data['baz'][0]['value'] == 456.0
111
+ end
112
+ end
113
+
114
+ end
115
+
116
+ end
117
+ end
data/spec/spec_helper.rb CHANGED
@@ -7,4 +7,23 @@ require 'librato/metrics'
7
7
 
8
8
  RSpec.configure do |config|
9
9
 
10
+ # set up test account credentials for integration tests
11
+ def prep_integration_tests
12
+ raise 'no TEST_API_USER specified in environment' unless ENV['TEST_API_USER']
13
+ raise 'no TEST_API_KEY specified in environment' unless ENV['TEST_API_KEY']
14
+ if ENV['TEST_API_ENDPOINT']
15
+ Librato::Metrics.api_endpoint = ENV['TEST_API_ENDPOINT']
16
+ end
17
+ Librato::Metrics.authenticate ENV['TEST_API_USER'], ENV['TEST_API_KEY']
18
+ end
19
+
20
+ # purge all metrics from test account
21
+ def delete_all_metrics
22
+ connection = Librato::Metrics.connection
23
+ Librato::Metrics.list.each do |metric|
24
+ #puts "deleting #{metric['name']}..."
25
+ connection.delete(:path => "v1/metrics/#{metric['name']}", :expects => 204)
26
+ end
27
+ end
28
+
10
29
  end
@@ -74,6 +74,17 @@ module Librato
74
74
  end
75
75
  end
76
76
 
77
+ describe "#empty?" do
78
+ it "should return true when nothing queued" do
79
+ subject.empty?.should be_true
80
+ end
81
+
82
+ it "should return false with queued items" do
83
+ subject.add :foo => {:type => :gauge, :value => 121212}
84
+ subject.empty?.should be_false
85
+ end
86
+ end
87
+
77
88
  describe "#gauges" do
78
89
  it "should return currently queued gauges" do
79
90
  subject.add :transactions => {:type => :counter, :value => 12345},
@@ -86,6 +97,19 @@ module Librato
86
97
  end
87
98
  end
88
99
 
100
+ describe "#size" do
101
+ it "should return empty if gauges and counters are emtpy" do
102
+ subject.size.should eq 0
103
+ end
104
+ it "should return count of gauges and counters if added" do
105
+ subject.add :transactions => {:type => :counter, :value => 12345},
106
+ :register_cents => {:type => :gauge, :value => 211101}
107
+ subject.add :transactions => {:type => :counter, :value => 12345},
108
+ :register_cents => {:type => :gauge, :value => 211101}
109
+ subject.size.should eql 4
110
+ end
111
+ end
112
+
89
113
  describe "#submit" do
90
114
  before(:all) do
91
115
  Librato::Metrics.authenticate 'me@librato.com', 'foo'
@@ -16,22 +16,6 @@ module Librato
16
16
 
17
17
  end
18
18
 
19
- describe "#list" do
20
-
21
- context "without arguments" do
22
-
23
- it "should list all metrics"
24
-
25
- end
26
-
27
- context "with a name argument" do
28
-
29
- it "should list metrics that match"
30
-
31
- end
32
-
33
- end
34
-
35
19
  describe "#persistence" do
36
20
 
37
21
  it "should allow configuration of persistence method" do
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: librato-metrics
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.3
4
+ version: 0.3.0
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,11 +9,11 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2012-01-02 00:00:00.000000000Z
12
+ date: 2012-02-15 00:00:00.000000000Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: excon
16
- requirement: &70249860959660 !ruby/object:Gem::Requirement
16
+ requirement: &70282326461040 !ruby/object:Gem::Requirement
17
17
  none: false
18
18
  requirements:
19
19
  - - ~>
@@ -21,10 +21,21 @@ dependencies:
21
21
  version: 0.7.12
22
22
  type: :runtime
23
23
  prerelease: false
24
- version_requirements: *70249860959660
24
+ version_requirements: *70282326461040
25
+ - !ruby/object:Gem::Dependency
26
+ name: multi_json
27
+ requirement: &70282326460600 !ruby/object:Gem::Requirement
28
+ none: false
29
+ requirements:
30
+ - - ! '>='
31
+ - !ruby/object:Gem::Version
32
+ version: '0'
33
+ type: :runtime
34
+ prerelease: false
35
+ version_requirements: *70282326460600
25
36
  - !ruby/object:Gem::Dependency
26
37
  name: rake
27
- requirement: &70249860958680 !ruby/object:Gem::Requirement
38
+ requirement: &70282326460120 !ruby/object:Gem::Requirement
28
39
  none: false
29
40
  requirements:
30
41
  - - ! '>='
@@ -32,10 +43,10 @@ dependencies:
32
43
  version: '0'
33
44
  type: :development
34
45
  prerelease: false
35
- version_requirements: *70249860958680
46
+ version_requirements: *70282326460120
36
47
  - !ruby/object:Gem::Dependency
37
48
  name: rspec
38
- requirement: &70249860956780 !ruby/object:Gem::Requirement
49
+ requirement: &70282326459480 !ruby/object:Gem::Requirement
39
50
  none: false
40
51
  requirements:
41
52
  - - ~>
@@ -43,10 +54,10 @@ dependencies:
43
54
  version: 2.6.0
44
55
  type: :development
45
56
  prerelease: false
46
- version_requirements: *70249860956780
57
+ version_requirements: *70282326459480
47
58
  - !ruby/object:Gem::Dependency
48
59
  name: yard
49
- requirement: &70249860955580 !ruby/object:Gem::Requirement
60
+ requirement: &70282326458940 !ruby/object:Gem::Requirement
50
61
  none: false
51
62
  requirements:
52
63
  - - ! '>='
@@ -54,10 +65,10 @@ dependencies:
54
65
  version: '0'
55
66
  type: :development
56
67
  prerelease: false
57
- version_requirements: *70249860955580
68
+ version_requirements: *70282326458940
58
69
  - !ruby/object:Gem::Dependency
59
70
  name: rdiscount
60
- requirement: &70249860954580 !ruby/object:Gem::Requirement
71
+ requirement: &70282326458280 !ruby/object:Gem::Requirement
61
72
  none: false
62
73
  requirements:
63
74
  - - ! '>='
@@ -65,7 +76,7 @@ dependencies:
65
76
  version: '0'
66
77
  type: :development
67
78
  prerelease: false
68
- version_requirements: *70249860954580
79
+ version_requirements: *70282326458280
69
80
  description: An easy to use ruby wrapper for Librato's Metrics API
70
81
  email: matt@librato.com
71
82
  executables: []
@@ -81,6 +92,7 @@ files:
81
92
  - README.md
82
93
  - Rakefile
83
94
  - lib/librato/metrics.rb
95
+ - lib/librato/metrics/collect.rb
84
96
  - lib/librato/metrics/errors.rb
85
97
  - lib/librato/metrics/persistence.rb
86
98
  - lib/librato/metrics/persistence/direct.rb
@@ -89,6 +101,7 @@ files:
89
101
  - lib/librato/metrics/simple.rb
90
102
  - lib/librato/metrics/version.rb
91
103
  - librato-metrics.gemspec
104
+ - spec/integration/metrics_spec.rb
92
105
  - spec/spec_helper.rb
93
106
  - spec/unit/metrics/queue_spec.rb
94
107
  - spec/unit/metrics/simple_spec.rb
@@ -119,6 +132,7 @@ signing_key:
119
132
  specification_version: 2
120
133
  summary: Ruby wrapper for Librato's Metrics API
121
134
  test_files:
135
+ - spec/integration/metrics_spec.rb
122
136
  - spec/spec_helper.rb
123
137
  - spec/unit/metrics/queue_spec.rb
124
138
  - spec/unit/metrics/simple_spec.rb