librato-metrics 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/.gitignore +19 -0
- data/Gemfile +3 -0
- data/LICENSE +11 -0
- data/README.md +114 -0
- data/Rakefile +24 -0
- data/lib/librato/metrics.rb +89 -0
- data/lib/librato/metrics/errors.rb +11 -0
- data/lib/librato/metrics/persistence.rb +3 -0
- data/lib/librato/metrics/persistence/direct.rb +22 -0
- data/lib/librato/metrics/persistence/test.rb +27 -0
- data/lib/librato/metrics/queue.rb +110 -0
- data/lib/librato/metrics/simple.rb +100 -0
- data/lib/librato/metrics/version.rb +5 -0
- data/librato-metrics.gemspec +36 -0
- data/spec/spec_helper.rb +10 -0
- data/spec/unit/metrics/queue_spec.rb +146 -0
- data/spec/unit/metrics/simple_spec.rb +81 -0
- data/spec/unit/metrics_spec.rb +67 -0
- metadata +113 -0
data/.gitignore
ADDED
data/Gemfile
ADDED
data/LICENSE
ADDED
@@ -0,0 +1,11 @@
|
|
1
|
+
Librato Software Confidential and Proprietary
|
2
|
+
|
3
|
+
Copyright (C) 2011, Librato Inc. ALL RIGHTS RESERVED.
|
4
|
+
|
5
|
+
Except as specifically permitted herein, no portion of the
|
6
|
+
information, including but not limited to object code and source
|
7
|
+
code, may be reproduced, modified, distributed, republished or
|
8
|
+
otherwise utilized in any form or by any means for any purpose
|
9
|
+
without the prior written permission of Librato Inc.
|
10
|
+
|
11
|
+
Visit http://librato.com for more information.
|
data/README.md
ADDED
@@ -0,0 +1,114 @@
|
|
1
|
+
Librato Metrics
|
2
|
+
=======
|
3
|
+
|
4
|
+
A convenient Ruby wrapper for the Librato Metrics API.
|
5
|
+
|
6
|
+
## Installation
|
7
|
+
|
8
|
+
In your shell:
|
9
|
+
|
10
|
+
gem install librato-metrics
|
11
|
+
|
12
|
+
Then, in your application or script:
|
13
|
+
|
14
|
+
require 'librato/metrics'
|
15
|
+
|
16
|
+
## Quick Start
|
17
|
+
|
18
|
+
If you are looking for the quickest possible route to getting a data into Metrics, you only need two lines:
|
19
|
+
|
20
|
+
Librato::Metrics.authenticate 'email', 'api_key'
|
21
|
+
Librato::Metrics.submit :my_metric => 42, :my_other_metric => 1002
|
22
|
+
|
23
|
+
Unspecified metrics will send a *gauge*, but if you need to send a different metric type or include additional properties, simply use a hash:
|
24
|
+
|
25
|
+
Librato::Metrics.submit :my_metric => {:type => :counter, :value => 1002, :source => 'myapp'}
|
26
|
+
|
27
|
+
While this is all you need to get started, if you are sending a number of metrics regularly a queue may be easier/more performant so read on...
|
28
|
+
|
29
|
+
## Authentication
|
30
|
+
|
31
|
+
Make sure you have [an account for Metrics](https://metrics.librato.com/) and then authenticate with your email and API key (on your account page):
|
32
|
+
|
33
|
+
Librato::Metrics.authenticate 'email', 'api_key'
|
34
|
+
|
35
|
+
## Sending Metrics
|
36
|
+
|
37
|
+
If you are sending very many metrics or sending them very often, it will be much higher performance to bundle them up together to reduce your request volume. Use `Queue` for this.
|
38
|
+
|
39
|
+
Queue up a simple gauge metric named `temperature`:
|
40
|
+
|
41
|
+
queue = Librato::Metrics::Queue.new
|
42
|
+
queue.add :temperature => 32.2
|
43
|
+
|
44
|
+
If you are tracking measurements over several seconds/minutes, the queue will handle storing measurement time for you (otherwise all metrics will be recorded as measured when they are submitted).
|
45
|
+
|
46
|
+
You can queue multiple metrics at once. Here's a gauge (`load`) and a counter (`visits`):
|
47
|
+
|
48
|
+
queue.add :load => 2.2, :visits => {:type => :counter, :value => 400}
|
49
|
+
|
50
|
+
Queue up a metric with a specified source:
|
51
|
+
|
52
|
+
queue.add :cpu => {:source => 'app1', :value => 92.6}
|
53
|
+
|
54
|
+
A complete [list of metric attributes](http://dev.librato.com/v1/metrics) is available in the [API documentation](http://dev.librato.com/v1).
|
55
|
+
|
56
|
+
Save all queued metrics:
|
57
|
+
|
58
|
+
queue.submit
|
59
|
+
|
60
|
+
## Benchmarking
|
61
|
+
|
62
|
+
If you have operations in your application you want to record execution time for, you can use the `#time` method:
|
63
|
+
|
64
|
+
queue.time :my_measurement do
|
65
|
+
# do work...
|
66
|
+
end
|
67
|
+
|
68
|
+
If you need extra attributes for the measurement, simply add them on:
|
69
|
+
|
70
|
+
queue.time :my_measurement, :source => 'app1' do
|
71
|
+
# do work...
|
72
|
+
end
|
73
|
+
|
74
|
+
## Querying Metrics
|
75
|
+
|
76
|
+
Get name and properties for all metrics you have in the system:
|
77
|
+
|
78
|
+
metrics = Librato::Metrics.list
|
79
|
+
|
80
|
+
Get only metrics whose name includes `time`:
|
81
|
+
|
82
|
+
metrics = Librato::Metrics.list :name => 'time'
|
83
|
+
|
84
|
+
## Querying Metric Data
|
85
|
+
|
86
|
+
Get attributes for metric `temperature`:
|
87
|
+
|
88
|
+
data = Librato::Metrics.fetch :temperature
|
89
|
+
|
90
|
+
Get the 20 most recent data points for `temperature`:
|
91
|
+
|
92
|
+
data = Librato::Metrics.fetch :temperature, :count => 20
|
93
|
+
|
94
|
+
Get the 20 most recent data points for `temperature` from a specific source:
|
95
|
+
|
96
|
+
data = Librato::Metrics.fetch :temperature, :count => 20, :source => 'app1'
|
97
|
+
|
98
|
+
Get the 20 most recent 15 minute data point rollups for `temperature`:
|
99
|
+
|
100
|
+
data = Librato::Metrics.fetch :temperature, :count => 20, :resolution => 900
|
101
|
+
|
102
|
+
There are many more options supported for querying, take a look at the [REST API docs](http://dev.librato.com/v1/get/gauges/:name) for more details.
|
103
|
+
|
104
|
+
## Contribution
|
105
|
+
|
106
|
+
* Check out the latest master to make sure the feature hasn't been implemented or the bug hasn't been fixed yet.
|
107
|
+
* Check out the issue tracker to make sure someone already hasn't requested it and/or contributed it.
|
108
|
+
* Fork the project and submit a pull request from a feature or bugfix branch.
|
109
|
+
* Please include specs. This is important so we don't break your changes unintentionally in a future version.
|
110
|
+
* Please don't modify the Rakefile, version, or history. If you do change these files, please isolate a separate commit so we can cherry-pick around it.
|
111
|
+
|
112
|
+
## Copyright
|
113
|
+
|
114
|
+
Copyright (c) 2011-2012 [Librato Inc.](http://librato.com) See LICENSE for details.
|
data/Rakefile
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
require 'bundler'
|
2
|
+
|
3
|
+
# Packaging
|
4
|
+
Bundler::GemHelper.install_tasks
|
5
|
+
|
6
|
+
# Testing
|
7
|
+
require 'rspec/core/rake_task'
|
8
|
+
RSpec::Core::RakeTask.new(:spec) do |t|
|
9
|
+
t.rspec_opts = '--color'
|
10
|
+
end
|
11
|
+
|
12
|
+
task :default => :spec
|
13
|
+
task :test => :spec
|
14
|
+
|
15
|
+
# Docs
|
16
|
+
require 'yard'
|
17
|
+
YARD::Rake::YardocTask.new
|
18
|
+
|
19
|
+
# IRB
|
20
|
+
desc "Open an irb session preloaded with this library"
|
21
|
+
task :console do
|
22
|
+
sh "irb -rubygems -r ./lib/librato/metrics.rb"
|
23
|
+
end
|
24
|
+
|
@@ -0,0 +1,89 @@
|
|
1
|
+
$:.unshift(File.dirname(__FILE__)) unless
|
2
|
+
$:.include?(File.dirname(__FILE__)) || $:.include?(File.expand_path(File.dirname(__FILE__)))
|
3
|
+
|
4
|
+
require 'base64'
|
5
|
+
require 'excon'
|
6
|
+
require 'json'
|
7
|
+
|
8
|
+
require 'metrics/errors'
|
9
|
+
require 'metrics/persistence'
|
10
|
+
require 'metrics/queue'
|
11
|
+
require 'metrics/simple'
|
12
|
+
require 'metrics/version'
|
13
|
+
|
14
|
+
module Librato
|
15
|
+
|
16
|
+
# Metrics provides a simple wrapper for the Metrics web API. Some
|
17
|
+
# of the methods Metrics provides will be documented below. Others
|
18
|
+
# are delegated to {Librato::Metrics::Simple} and will be
|
19
|
+
# documented there.
|
20
|
+
module Metrics
|
21
|
+
extend SingleForwardable
|
22
|
+
|
23
|
+
TYPES = [:counter, :gauge]
|
24
|
+
|
25
|
+
# Expose class methods of Simple via Metrics itself.
|
26
|
+
#
|
27
|
+
# TODO: Explain exposed interface with examples.
|
28
|
+
def_delegators Librato::Metrics::Simple, :api_endpoint, :api_endpoint=,
|
29
|
+
:authenticate, :connection, :persistence, :persistence=,
|
30
|
+
:persister, :submit
|
31
|
+
|
32
|
+
# Query metric data
|
33
|
+
#
|
34
|
+
# @example Get attributes for a metric
|
35
|
+
# attrs = Librato::Metrics.fetch :temperature
|
36
|
+
#
|
37
|
+
# @example Get 20 most recent data points for metric
|
38
|
+
# data = Librato::Metrics.fetch :temperature, :count => 20
|
39
|
+
#
|
40
|
+
# @example Get 20 most recent data points for a specific source
|
41
|
+
# data = Librato::Metrics.fetch :temperature, :count => 20,
|
42
|
+
# :source => 'app1'
|
43
|
+
#
|
44
|
+
# @example Get the 20 most recent 15 minute data point rollups
|
45
|
+
# data = Librato::Metrics.fetch :temperature, :count => 20,
|
46
|
+
# :resolution => 900
|
47
|
+
#
|
48
|
+
# A full list of query parameters can be found in the API
|
49
|
+
# documentation: {http://dev.librato.com/v1/get/gauges/:name}
|
50
|
+
#
|
51
|
+
# @param [Symbol|String] metric Metric name
|
52
|
+
# @param [Hash] options Query options
|
53
|
+
def self.fetch(metric, options={})
|
54
|
+
resolution = options.delete(:resolution) || 1
|
55
|
+
count = options.delete(:count)
|
56
|
+
query = {}
|
57
|
+
if count
|
58
|
+
query.merge!({:count => count, :resolution => resolution})
|
59
|
+
end
|
60
|
+
query.merge!(options)
|
61
|
+
# TODO: look up type when not specified.
|
62
|
+
type = options.delete(:type) || 'gauge'
|
63
|
+
response = connection.get(:path => "v1/#{type}s/#{metric}.json",
|
64
|
+
:query => query, :expects => 200)
|
65
|
+
parsed = JSON.parse(response.body)
|
66
|
+
# TODO: pagination support
|
67
|
+
query.empty? ? parsed : parsed["measurements"]
|
68
|
+
end
|
69
|
+
|
70
|
+
# List currently existing metrics
|
71
|
+
#
|
72
|
+
# @example List all metrics
|
73
|
+
# Librato::Metrics.list
|
74
|
+
#
|
75
|
+
# @example List metrics with 'foo' in the name
|
76
|
+
# Librato::Metrics.list :name => 'foo'
|
77
|
+
#
|
78
|
+
# @param [Hash] options
|
79
|
+
def self.list(options={})
|
80
|
+
query = {}
|
81
|
+
query[:name] = options[:name] if options[:name]
|
82
|
+
response = connection.get(:path => 'v1/metrics.json',
|
83
|
+
:query => query, :expects => 200)
|
84
|
+
# TODO: pagination support
|
85
|
+
JSON.parse(response.body)["metrics"]
|
86
|
+
end
|
87
|
+
|
88
|
+
end
|
89
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
|
2
|
+
# Manages direct persistence with the Librato Metrics web API
|
3
|
+
|
4
|
+
module Librato
|
5
|
+
module Metrics
|
6
|
+
module Persistence
|
7
|
+
class Direct
|
8
|
+
|
9
|
+
# Persist the queued metrics directly to the
|
10
|
+
# Metrics web API.
|
11
|
+
#
|
12
|
+
def persist(queued)
|
13
|
+
payload = queued.to_json
|
14
|
+
Simple.connection.post(:path => '/v1/metrics.json',
|
15
|
+
:headers => {'Content-Type' => 'application/json'},
|
16
|
+
:body => payload, :expects => 200)
|
17
|
+
end
|
18
|
+
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
# Use for testing the interface with persistence methods
|
2
|
+
|
3
|
+
module Librato
|
4
|
+
module Metrics
|
5
|
+
module Persistence
|
6
|
+
class Test
|
7
|
+
|
8
|
+
# persist the given metrics
|
9
|
+
def persist(metrics)
|
10
|
+
@persisted = metrics
|
11
|
+
return !@return_value.nil? ? @return_value : true
|
12
|
+
end
|
13
|
+
|
14
|
+
# return what was persisted
|
15
|
+
def persisted
|
16
|
+
@persisted
|
17
|
+
end
|
18
|
+
|
19
|
+
# force a return value from persistence
|
20
|
+
def return_value(value)
|
21
|
+
@return_value = value
|
22
|
+
end
|
23
|
+
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,110 @@
|
|
1
|
+
module Librato
|
2
|
+
module Metrics
|
3
|
+
class Queue
|
4
|
+
|
5
|
+
def initialize
|
6
|
+
@queued ||= {}
|
7
|
+
end
|
8
|
+
|
9
|
+
# Add a metric entry to the metric set:
|
10
|
+
#
|
11
|
+
# @param Hash metrics metrics to add
|
12
|
+
# @return Hash queued_metrics the currently queued metrics
|
13
|
+
def add(args)
|
14
|
+
args.each do |key, value|
|
15
|
+
if value.respond_to?(:each)
|
16
|
+
type = (value.delete(:type) || 'gauge')
|
17
|
+
type = ("#{type}s").to_sym
|
18
|
+
value[:name] = key.to_s
|
19
|
+
@queued[type] ||= []
|
20
|
+
@queued[type] << value
|
21
|
+
else
|
22
|
+
@queued[:gauges] ||= []
|
23
|
+
@queued[:gauges] << {:name => key.to_s, :value => value}
|
24
|
+
end
|
25
|
+
end
|
26
|
+
queued
|
27
|
+
end
|
28
|
+
|
29
|
+
# Currently queued counters
|
30
|
+
#
|
31
|
+
# @return Array
|
32
|
+
def counters
|
33
|
+
@queued[:counters] || []
|
34
|
+
end
|
35
|
+
|
36
|
+
# Remove all queued metrics
|
37
|
+
#
|
38
|
+
def flush
|
39
|
+
@queued = {}
|
40
|
+
end
|
41
|
+
alias :flush_queued :flush
|
42
|
+
|
43
|
+
# The object this MetricSet will use to persist
|
44
|
+
#
|
45
|
+
def persister
|
46
|
+
@persister ||= create_persister
|
47
|
+
end
|
48
|
+
|
49
|
+
# Currently queued gauges
|
50
|
+
#
|
51
|
+
# @return Array
|
52
|
+
def gauges
|
53
|
+
@queued[:gauges] || []
|
54
|
+
end
|
55
|
+
|
56
|
+
# All currently queued metrics
|
57
|
+
#
|
58
|
+
# @return Hash
|
59
|
+
def queued
|
60
|
+
@queued
|
61
|
+
end
|
62
|
+
|
63
|
+
# Persist currently queued metrics
|
64
|
+
#
|
65
|
+
# @return Boolean
|
66
|
+
def submit
|
67
|
+
raise NoMetricsQueued if self.queued.empty?
|
68
|
+
if persister.persist(self.queued)
|
69
|
+
flush and return true
|
70
|
+
end
|
71
|
+
false
|
72
|
+
end
|
73
|
+
|
74
|
+
# Capture execution time for a block and queue
|
75
|
+
# it as the value for a metric. Times are recorded
|
76
|
+
# in milliseconds.
|
77
|
+
#
|
78
|
+
# Options are the same as for {#add}.
|
79
|
+
#
|
80
|
+
# @example Queue API request response time
|
81
|
+
# queue.time :api_request_time do
|
82
|
+
# # API request..
|
83
|
+
# end
|
84
|
+
#
|
85
|
+
# @example Queue API request response time w/ source
|
86
|
+
# queue.time :api_request_time, :source => 'app1' do
|
87
|
+
# # API request..
|
88
|
+
# end
|
89
|
+
#
|
90
|
+
# @param [Symbol|String] name Metric name
|
91
|
+
# @param [Hash] options Metric options
|
92
|
+
def time(name, options={})
|
93
|
+
start = Time.now
|
94
|
+
yield
|
95
|
+
duration = (Time.now - start) * 1000.0 # milliseconds
|
96
|
+
metric = {name => options.merge({:value => duration})}
|
97
|
+
add metric
|
98
|
+
end
|
99
|
+
alias :benchmark :time
|
100
|
+
|
101
|
+
private
|
102
|
+
|
103
|
+
def create_persister
|
104
|
+
type = Simple.persistence.capitalize
|
105
|
+
Librato::Metrics::Persistence.const_get(type).new
|
106
|
+
end
|
107
|
+
|
108
|
+
end
|
109
|
+
end
|
110
|
+
end
|
@@ -0,0 +1,100 @@
|
|
1
|
+
module Librato
|
2
|
+
module Metrics
|
3
|
+
|
4
|
+
# Class-level methods for quick one-off submission of metrics.
|
5
|
+
#
|
6
|
+
# @example Send a quick metric
|
7
|
+
# Librato::Metrics::Simple.authenticate 'fred@foo.com', 'myapikey'
|
8
|
+
# Librato::Metrics::Simple.save :total_vists => {:type => counter, :value => 2311}
|
9
|
+
#
|
10
|
+
# For more than quick one-off use, take a look at {Queue}. For
|
11
|
+
# convenience, most of Simple's methods can be accessed directly from
|
12
|
+
# the {Metrics} module.
|
13
|
+
#
|
14
|
+
class Simple
|
15
|
+
|
16
|
+
class << self
|
17
|
+
# class instance vars
|
18
|
+
attr_accessor :email, :api_key
|
19
|
+
|
20
|
+
# API endpoint to use for queries and direct
|
21
|
+
# persistence.
|
22
|
+
#
|
23
|
+
# @return [String] api_endpoint
|
24
|
+
def api_endpoint
|
25
|
+
@api_endpoint ||= 'https://metrics-api.librato.com/v1/'
|
26
|
+
end
|
27
|
+
|
28
|
+
# Set API endpoint for use with queries and direct
|
29
|
+
# persistence. Generally you should not need to set this
|
30
|
+
# as it will default to the current Librato Metrics
|
31
|
+
# endpoint.
|
32
|
+
#
|
33
|
+
def api_endpoint=(endpoint)
|
34
|
+
@api_endpoint = endpoint
|
35
|
+
end
|
36
|
+
|
37
|
+
# Authenticate for direct persistence
|
38
|
+
#
|
39
|
+
# @param [String] email
|
40
|
+
# @param [String] api_key
|
41
|
+
def authenticate(email, api_key)
|
42
|
+
flush_authentication
|
43
|
+
self.email, self.api_key = email, api_key
|
44
|
+
end
|
45
|
+
|
46
|
+
def connection
|
47
|
+
@connection ||= Excon.new(self.api_endpoint, :headers => {'Authorization' => auth_header})
|
48
|
+
end
|
49
|
+
|
50
|
+
# Purge current credentials and connection
|
51
|
+
#
|
52
|
+
def flush_authentication
|
53
|
+
self.email = nil
|
54
|
+
self.api_key = nil
|
55
|
+
@connection = nil
|
56
|
+
end
|
57
|
+
|
58
|
+
# Persistence type to use when saving metrics.
|
59
|
+
# Default is :direct.
|
60
|
+
#
|
61
|
+
def persistence
|
62
|
+
@persistence ||= :direct
|
63
|
+
end
|
64
|
+
|
65
|
+
# Set persistence type to use when saving metrics.
|
66
|
+
#
|
67
|
+
# @param [Symbol] persistence_type
|
68
|
+
def persistence=(persist_method)
|
69
|
+
@persistence = persist_method
|
70
|
+
end
|
71
|
+
|
72
|
+
def persister
|
73
|
+
@queue ? @queue.persister : nil
|
74
|
+
end
|
75
|
+
|
76
|
+
# Submit all queued metrics
|
77
|
+
#
|
78
|
+
def submit(args)
|
79
|
+
@queue ||= Queue.new
|
80
|
+
@queue.add args
|
81
|
+
@queue.submit
|
82
|
+
end
|
83
|
+
|
84
|
+
private
|
85
|
+
|
86
|
+
def auth_header
|
87
|
+
raise CredentialsMissing unless (self.email and self.api_key)
|
88
|
+
encoded = Base64.encode64("#{email}:#{api_key}").gsub("\n", ' ')
|
89
|
+
"Basic #{encoded}"
|
90
|
+
end
|
91
|
+
|
92
|
+
def flush_persistence
|
93
|
+
@persistence = nil
|
94
|
+
end
|
95
|
+
|
96
|
+
end
|
97
|
+
|
98
|
+
end
|
99
|
+
end
|
100
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
lib = File.expand_path('../lib/', __FILE__)
|
2
|
+
$:.unshift lib unless $:.include?(lib)
|
3
|
+
|
4
|
+
require 'librato/metrics/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |s|
|
7
|
+
s.specification_version = 2 if s.respond_to? :specification_version=
|
8
|
+
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
9
|
+
s.rubygems_version = '1.3.5'
|
10
|
+
|
11
|
+
s.name = 'librato-metrics'
|
12
|
+
s.version = Librato::Metrics::VERSION
|
13
|
+
|
14
|
+
s.summary = "Ruby wrapper for Librato's Metrics API"
|
15
|
+
s.description = "An easy to use ruby wrapper for Librato's Metrics API"
|
16
|
+
|
17
|
+
s.authors = ["Matt Sanders"]
|
18
|
+
s.email = 'matt@librato.com'
|
19
|
+
s.homepage = 'http://metrics.librato.com'
|
20
|
+
|
21
|
+
s.require_paths = %w[lib]
|
22
|
+
|
23
|
+
s.rdoc_options = ["--charset=UTF-8"]
|
24
|
+
s.extra_rdoc_files = %w[LICENSE]
|
25
|
+
|
26
|
+
## runtime dependencies
|
27
|
+
s.add_dependency 'excon', '~>0.7.12'
|
28
|
+
|
29
|
+
## development dependencies
|
30
|
+
s.add_development_dependency 'rspec', '~>2.6.0'
|
31
|
+
s.add_development_dependency 'yard'
|
32
|
+
s.add_development_dependency 'rdiscount' # for yard
|
33
|
+
|
34
|
+
s.files = `git ls-files`.split("\n")
|
35
|
+
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
36
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,146 @@
|
|
1
|
+
require "spec_helper.rb"
|
2
|
+
|
3
|
+
module Librato
|
4
|
+
module Metrics
|
5
|
+
|
6
|
+
describe Queue do
|
7
|
+
|
8
|
+
describe "#add" do
|
9
|
+
|
10
|
+
context "with single hash argument" do
|
11
|
+
it "should record a key-value gauge" do
|
12
|
+
subject.add :foo => 3000
|
13
|
+
subject.queued.should eql({:gauges => [{:name => 'foo', :value => 3000}]})
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
context "with specified metric type" do
|
18
|
+
it "should record counters" do
|
19
|
+
subject.add :total_visits => {:type => :counter, :value => 4000}
|
20
|
+
expected = {:counters => [{:name => 'total_visits', :value => 4000}]}
|
21
|
+
subject.queued.should eql expected
|
22
|
+
end
|
23
|
+
|
24
|
+
it "should record gauges" do
|
25
|
+
subject.add :temperature => {:type => :gauge, :value => 34}
|
26
|
+
expected = {:gauges => [{:name => 'temperature', :value => 34}]}
|
27
|
+
subject.queued.should eql expected
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
context "with extra attributes" do
|
32
|
+
it "should record" do
|
33
|
+
measure_time = Time.now
|
34
|
+
subject.add :disk_use => {:value => 35.4, :period => 2,
|
35
|
+
:description => 'current disk utilization', :measure_time => measure_time,
|
36
|
+
:source => 'db2'}
|
37
|
+
expected = {:gauges => [{:value => 35.4, :name => 'disk_use', :period => 2,
|
38
|
+
:description => 'current disk utilization', :measure_time => measure_time,
|
39
|
+
:source => 'db2'}]}
|
40
|
+
subject.queued.should eql expected
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
context "with multiple metrics" do
|
45
|
+
it "should record" do
|
46
|
+
subject.add :foo => 123, :bar => 345, :baz => 567
|
47
|
+
expected = {:gauges=>[{:name=>"foo", :value=>123}, {:name=>"bar", :value=>345}, {:name=>"baz", :value=>567}]}
|
48
|
+
subject.queued.should eql expected
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
end
|
53
|
+
|
54
|
+
describe "#counters" do
|
55
|
+
|
56
|
+
it "should return currently queued counters" do
|
57
|
+
subject.add :transactions => {:type => :counter, :value => 12345},
|
58
|
+
:register_cents => {:type => :gauge, :value => 211101}
|
59
|
+
subject.counters.should eql [{:name => 'transactions', :value => 12345}]
|
60
|
+
end
|
61
|
+
|
62
|
+
it "should return [] when no queued counters" do
|
63
|
+
subject.counters.should eql []
|
64
|
+
end
|
65
|
+
|
66
|
+
end
|
67
|
+
|
68
|
+
describe "#gauges" do
|
69
|
+
|
70
|
+
it "should return currently queued gauges" do
|
71
|
+
subject.add :transactions => {:type => :counter, :value => 12345},
|
72
|
+
:register_cents => {:type => :gauge, :value => 211101}
|
73
|
+
subject.gauges.should eql [{:name => 'register_cents', :value => 211101}]
|
74
|
+
end
|
75
|
+
|
76
|
+
it "should return [] when no queued gauges" do
|
77
|
+
subject.gauges.should eql []
|
78
|
+
end
|
79
|
+
|
80
|
+
end
|
81
|
+
|
82
|
+
describe "#submit" do
|
83
|
+
|
84
|
+
before(:all) do
|
85
|
+
Librato::Metrics.authenticate 'me@librato.com', 'foo'
|
86
|
+
Librato::Metrics.persistence = :test
|
87
|
+
end
|
88
|
+
after(:all) { Librato::Metrics::Simple.flush_authentication }
|
89
|
+
|
90
|
+
context "when successful" do
|
91
|
+
it "should flush queued metrics and return true" do
|
92
|
+
subject.add :steps => 2042, :distance => 1234
|
93
|
+
subject.submit.should be_true
|
94
|
+
subject.queued.should be_empty
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
context "when failed" do
|
99
|
+
it "should preserve queue and return false" do
|
100
|
+
subject.add :steps => 2042, :distance => 1234
|
101
|
+
subject.persister.return_value(false)
|
102
|
+
subject.submit.should be_false
|
103
|
+
subject.queued.should_not be_empty
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
end
|
108
|
+
|
109
|
+
describe "#time" do
|
110
|
+
|
111
|
+
context "with metric name only" do
|
112
|
+
|
113
|
+
it "should queue metric with timed value" do
|
114
|
+
subject.time :sleeping do
|
115
|
+
sleep 0.1
|
116
|
+
end
|
117
|
+
queued = subject.queued[:gauges][0]
|
118
|
+
queued[:name].should == 'sleeping'
|
119
|
+
queued[:value].should be > 100
|
120
|
+
queued[:value].should be_within(30).of(100)
|
121
|
+
end
|
122
|
+
|
123
|
+
end
|
124
|
+
|
125
|
+
context "with metric and options" do
|
126
|
+
|
127
|
+
it "should queue metric with value and options" do
|
128
|
+
subject.time :sleep_two, :source => 'app1', :period => 2 do
|
129
|
+
sleep 0.05
|
130
|
+
end
|
131
|
+
queued = subject.queued[:gauges][0]
|
132
|
+
queued[:name].should == 'sleep_two'
|
133
|
+
queued[:period].should == 2
|
134
|
+
queued[:source].should == 'app1'
|
135
|
+
queued[:value].should be > 50
|
136
|
+
queued[:value].should be_within(30).of(50)
|
137
|
+
end
|
138
|
+
|
139
|
+
end
|
140
|
+
|
141
|
+
end
|
142
|
+
|
143
|
+
end # MetricSet
|
144
|
+
|
145
|
+
end
|
146
|
+
end
|
@@ -0,0 +1,81 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
module Librato
|
4
|
+
module Metrics
|
5
|
+
|
6
|
+
describe Simple do
|
7
|
+
|
8
|
+
describe "#api_endpoint" do
|
9
|
+
it "should default to metrics" do
|
10
|
+
Simple.api_endpoint.should == 'https://metrics-api.librato.com/v1/'
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
describe "#api_endpoint=" do
|
15
|
+
it "should set api_endpoint" do
|
16
|
+
@prior = Simple.api_endpoint
|
17
|
+
Simple.api_endpoint = 'http://test.com/'
|
18
|
+
Simple.api_endpoint.should == 'http://test.com/'
|
19
|
+
Simple.api_endpoint = @prior
|
20
|
+
end
|
21
|
+
|
22
|
+
# TODO:
|
23
|
+
# it "should ensure trailing slash"
|
24
|
+
# it "should ensure real URI"
|
25
|
+
end
|
26
|
+
|
27
|
+
describe "#authenticate" do
|
28
|
+
context "when given two arguments" do
|
29
|
+
it "should store them as email and api_key" do
|
30
|
+
Simple.authenticate 'test@librato.com', 'api_key'
|
31
|
+
Simple.email.should == 'test@librato.com'
|
32
|
+
Simple.api_key.should == 'api_key'
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
describe "#connection" do
|
38
|
+
it "should raise exception without authentication" do
|
39
|
+
Simple.flush_authentication
|
40
|
+
lambda{ Simple.connection }.should raise_error(Librato::Metrics::CredentialsMissing)
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
describe "#persistence" do
|
45
|
+
it "should default to direct" do
|
46
|
+
Simple.send(:flush_persistence)
|
47
|
+
Simple.persistence.should == :direct
|
48
|
+
end
|
49
|
+
|
50
|
+
it "should allow configuration of persistence method" do
|
51
|
+
current = Simple.persistence
|
52
|
+
Simple.persistence = :fake
|
53
|
+
Simple.persistence.should == :fake
|
54
|
+
Simple.persistence = current
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
describe "#submit" do
|
59
|
+
before(:all) do
|
60
|
+
Simple.persistence = :test
|
61
|
+
Simple.authenticate 'me@librato.com', 'foo'
|
62
|
+
end
|
63
|
+
after(:all) { Simple.flush_authentication }
|
64
|
+
|
65
|
+
it "should persist metrics immediately" do
|
66
|
+
Simple.persistence = :test
|
67
|
+
Simple.submit(:foo => 123).should eql true
|
68
|
+
Simple.persister.persisted.should eql({:gauges => [{:name => 'foo', :value => 123}]})
|
69
|
+
end
|
70
|
+
|
71
|
+
it "should tolerate muliple metrics" do
|
72
|
+
lambda{ Simple.submit :foo => 123, :bar => 456 }.should_not raise_error
|
73
|
+
expected = {:gauges => [{:name => 'foo', :value => 123}, {:name => 'bar', :value => 456}]}
|
74
|
+
Simple.persister.persisted.should eql expected
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
end
|
79
|
+
|
80
|
+
end
|
81
|
+
end
|
@@ -0,0 +1,67 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
module Librato
|
4
|
+
|
5
|
+
describe Metrics do
|
6
|
+
|
7
|
+
describe "#authorize" do
|
8
|
+
|
9
|
+
context "when given two arguments" do
|
10
|
+
it "should store them on simple" do
|
11
|
+
Metrics.authenticate 'tester@librato.com', 'api_key'
|
12
|
+
Metrics::Simple.email.should == 'tester@librato.com'
|
13
|
+
Metrics::Simple.api_key.should == 'api_key'
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
end
|
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
|
+
describe "#persistence" do
|
36
|
+
|
37
|
+
it "should allow configuration of persistence method" do
|
38
|
+
Metrics.persistence = :test
|
39
|
+
Metrics.persistence.should == :test
|
40
|
+
end
|
41
|
+
|
42
|
+
end
|
43
|
+
|
44
|
+
describe "#submit" do
|
45
|
+
before(:all) do
|
46
|
+
Librato::Metrics.persistence = :test
|
47
|
+
Librato::Metrics.authenticate 'me@librato.com', 'foo'
|
48
|
+
end
|
49
|
+
after(:all) { Librato::Metrics::Simple.flush_authentication }
|
50
|
+
|
51
|
+
it "should persist metrics immediately" do
|
52
|
+
Metrics.persistence = :test
|
53
|
+
Metrics.submit(:foo => 123).should eql true
|
54
|
+
Metrics.persister.persisted.should eql({:gauges => [{:name => 'foo', :value => 123}]})
|
55
|
+
end
|
56
|
+
|
57
|
+
it "should tolerate multiple metrics" do
|
58
|
+
lambda{ Librato::Metrics.submit :foo => 123, :bar => 456 }.should_not raise_error
|
59
|
+
expected = {:gauges => [{:name => 'foo', :value => 123}, {:name => 'bar', :value => 456}]}
|
60
|
+
Librato::Metrics.persister.persisted.should eql expected
|
61
|
+
end
|
62
|
+
|
63
|
+
end
|
64
|
+
|
65
|
+
end
|
66
|
+
|
67
|
+
end
|
metadata
ADDED
@@ -0,0 +1,113 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: librato-metrics
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Matt Sanders
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2011-12-08 00:00:00.000000000Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: excon
|
16
|
+
requirement: &70180001393620 !ruby/object:Gem::Requirement
|
17
|
+
none: false
|
18
|
+
requirements:
|
19
|
+
- - ~>
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: 0.7.12
|
22
|
+
type: :runtime
|
23
|
+
prerelease: false
|
24
|
+
version_requirements: *70180001393620
|
25
|
+
- !ruby/object:Gem::Dependency
|
26
|
+
name: rspec
|
27
|
+
requirement: &70180001393140 !ruby/object:Gem::Requirement
|
28
|
+
none: false
|
29
|
+
requirements:
|
30
|
+
- - ~>
|
31
|
+
- !ruby/object:Gem::Version
|
32
|
+
version: 2.6.0
|
33
|
+
type: :development
|
34
|
+
prerelease: false
|
35
|
+
version_requirements: *70180001393140
|
36
|
+
- !ruby/object:Gem::Dependency
|
37
|
+
name: yard
|
38
|
+
requirement: &70180001392760 !ruby/object:Gem::Requirement
|
39
|
+
none: false
|
40
|
+
requirements:
|
41
|
+
- - ! '>='
|
42
|
+
- !ruby/object:Gem::Version
|
43
|
+
version: '0'
|
44
|
+
type: :development
|
45
|
+
prerelease: false
|
46
|
+
version_requirements: *70180001392760
|
47
|
+
- !ruby/object:Gem::Dependency
|
48
|
+
name: rdiscount
|
49
|
+
requirement: &70180001392300 !ruby/object:Gem::Requirement
|
50
|
+
none: false
|
51
|
+
requirements:
|
52
|
+
- - ! '>='
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
55
|
+
type: :development
|
56
|
+
prerelease: false
|
57
|
+
version_requirements: *70180001392300
|
58
|
+
description: An easy to use ruby wrapper for Librato's Metrics API
|
59
|
+
email: matt@librato.com
|
60
|
+
executables: []
|
61
|
+
extensions: []
|
62
|
+
extra_rdoc_files:
|
63
|
+
- LICENSE
|
64
|
+
files:
|
65
|
+
- .gitignore
|
66
|
+
- Gemfile
|
67
|
+
- LICENSE
|
68
|
+
- README.md
|
69
|
+
- Rakefile
|
70
|
+
- lib/librato/metrics.rb
|
71
|
+
- lib/librato/metrics/errors.rb
|
72
|
+
- lib/librato/metrics/persistence.rb
|
73
|
+
- lib/librato/metrics/persistence/direct.rb
|
74
|
+
- lib/librato/metrics/persistence/test.rb
|
75
|
+
- lib/librato/metrics/queue.rb
|
76
|
+
- lib/librato/metrics/simple.rb
|
77
|
+
- lib/librato/metrics/version.rb
|
78
|
+
- librato-metrics.gemspec
|
79
|
+
- spec/spec_helper.rb
|
80
|
+
- spec/unit/metrics/queue_spec.rb
|
81
|
+
- spec/unit/metrics/simple_spec.rb
|
82
|
+
- spec/unit/metrics_spec.rb
|
83
|
+
homepage: http://metrics.librato.com
|
84
|
+
licenses: []
|
85
|
+
post_install_message:
|
86
|
+
rdoc_options:
|
87
|
+
- --charset=UTF-8
|
88
|
+
require_paths:
|
89
|
+
- lib
|
90
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
91
|
+
none: false
|
92
|
+
requirements:
|
93
|
+
- - ! '>='
|
94
|
+
- !ruby/object:Gem::Version
|
95
|
+
version: '0'
|
96
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
97
|
+
none: false
|
98
|
+
requirements:
|
99
|
+
- - ! '>='
|
100
|
+
- !ruby/object:Gem::Version
|
101
|
+
version: '0'
|
102
|
+
requirements: []
|
103
|
+
rubyforge_project:
|
104
|
+
rubygems_version: 1.8.10
|
105
|
+
signing_key:
|
106
|
+
specification_version: 2
|
107
|
+
summary: Ruby wrapper for Librato's Metrics API
|
108
|
+
test_files:
|
109
|
+
- spec/spec_helper.rb
|
110
|
+
- spec/unit/metrics/queue_spec.rb
|
111
|
+
- spec/unit/metrics/simple_spec.rb
|
112
|
+
- spec/unit/metrics_spec.rb
|
113
|
+
has_rdoc:
|