librato-metrics 0.2.3 → 0.3.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/.gitignore +2 -0
- data/README.md +10 -0
- data/Rakefile +21 -2
- data/lib/librato/metrics.rb +7 -9
- data/lib/librato/metrics/collect.rb +32 -0
- data/lib/librato/metrics/persistence/direct.rb +2 -2
- data/lib/librato/metrics/queue.rb +16 -0
- data/lib/librato/metrics/version.rb +1 -1
- data/librato-metrics.gemspec +1 -0
- data/spec/integration/metrics_spec.rb +117 -0
- data/spec/spec_helper.rb +19 -0
- data/spec/unit/metrics/queue_spec.rb +24 -0
- data/spec/unit/metrics_spec.rb +0 -16
- metadata +26 -12
data/.gitignore
CHANGED
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
|
-
|
9
|
-
|
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
|
data/lib/librato/metrics.rb
CHANGED
@@ -3,13 +3,14 @@ $:.unshift(File.dirname(__FILE__)) unless
|
|
3
3
|
|
4
4
|
require 'base64'
|
5
5
|
require 'excon'
|
6
|
-
require '
|
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/#{
|
73
|
+
response = connection.get(:path => "v1/metrics/#{metric}",
|
75
74
|
:query => query, :expects => 200)
|
76
|
-
parsed =
|
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
|
-
|
94
|
-
|
95
|
-
|
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
|
14
|
-
Simple.connection.post(:path => '/v1/metrics
|
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
|
data/librato-metrics.gemspec
CHANGED
@@ -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'
|
data/spec/unit/metrics_spec.rb
CHANGED
@@ -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.
|
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-
|
12
|
+
date: 2012-02-15 00:00:00.000000000Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: excon
|
16
|
-
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: *
|
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: &
|
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: *
|
46
|
+
version_requirements: *70282326460120
|
36
47
|
- !ruby/object:Gem::Dependency
|
37
48
|
name: rspec
|
38
|
-
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: *
|
57
|
+
version_requirements: *70282326459480
|
47
58
|
- !ruby/object:Gem::Dependency
|
48
59
|
name: yard
|
49
|
-
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: *
|
68
|
+
version_requirements: *70282326458940
|
58
69
|
- !ruby/object:Gem::Dependency
|
59
70
|
name: rdiscount
|
60
|
-
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: *
|
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
|