Skip to content
This repository was archived by the owner on Jan 20, 2019. It is now read-only.
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions lib/AWS/Cloudwatch.rb
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ module Cloudwatch
DEFAULT_HOST = 'monitoring.amazonaws.com'
end

API_VERSION = '2009-05-15'
API_VERSION = '2010-08-01'

class Base < AWS::Base
def api_version
Expand All @@ -27,4 +27,4 @@ def default_host
end

end
end
end
81 changes: 81 additions & 0 deletions lib/AWS/Cloudwatch/monitoring.rb
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,87 @@ def get_metric_statistics ( options ={} )

end

# This method pushes custom metrics to AWS. See http://docs.amazonwebservices.com/AmazonCloudWatch/latest/APIReference/API_PutMetricData.html
#
# @option options [String] :namespace (nil) The namepsace of the metric you want to put data into. Cannot begin with 'AWS/'.
# @option options [String] :metric_name (nil) The name of the metric. No explicit creation is required, but it may take up to 15 minutes to show up.
# @option options [String] :unit ('None') One of the units that Amazon supports. See http://docs.amazonwebservices.com/AmazonCloudWatch/latest/APIReference/API_MetricDatum.html
# @option options [Double] :value (nil) The value of the metric. Very large (10 ** 126) and very small (10 ** -130) will be truncated.
# @option options [optional, Hash] :dimensions ({}) Hash like {'Dimension1' => 'value1', 'Dimension2' => 'value2', ...} that describe the dimensions.
# @option options [Time] :timestamp (Time.now) The timestamp that the point(s) will be recorded fora.
# @option options [Array] :metric_data ([{}]) An array of hashes for the data points that will be sent to CloudWatch. All previous options except :namespace can be specified and will override options specified outside the element. Can only be 20 items long.
# @example
# # Put a single point measured now
# cw.put_metric_data({
# :namespace => 'Foo',
# :metric_name => 'Clicks',
# :unit => 'Count',
# :value => 5
# })
# @example
# # Put one point for each of the last five minutes with one call
# points = (0..5).map do |min|
# {:timestamp => Time.now - min * 60, :value = rand()}
# end
#
# cw.put_metric_data({
# :namespace => 'Foo'
# :metric_name => 'Clicks',
# :unit => 'Count',
# :metric_data => points,
# :dimensions => {'InstanceID' => 'i-af23c4b9',
# 'InstanceType' => 'm2.4xlarge'}
# })
#
def put_metric_data( options={} )
options = {
:timestamp => Time.now,
:metric_data => [{}],
:dimensions => {},
:unit => 'None'
}.merge options
namespace = options.delete :namespace
metric_data = options.delete :metric_data
metric_data = [{}] unless metric_data.any?
merged_data = metric_data.map do |datum|
options.merge datum
end

params = {
"Namespace" => namespace
}
raise ArgumentError, ":namespace cannot be blank" if namespace.nil?

merged_data.each_with_index do |datum, idx|
prefix = "MetricData.member.#{idx+1}."
raise ArgumentError, ":metric_name cannot be blank" if datum[:metric_name].nil?
raise ArgumentError, ":unit cannot be blank" if datum[:unit].nil?
raise ArgumentError, ":value cannot be blank" if datum[:value].nil?
raise ArgumentError, ":timestamp cannot be blank" if datum[:timestamp].nil?

datum_params = {
prefix + "MetricName" => datum[:metric_name],
prefix + "Unit" => datum[:unit],
prefix + "Value" => datum[:value].to_s,
prefix + "Timestamp" => datum[:timestamp].iso8601
}
ii = 1
datum[:dimensions].each_pair do |dimension, value|
dimension_prefix = prefix + "Dimensions.member.#{ii}."
raise ArgumentError, "value cannot be blank for a dimension" if value.nil?
datum_params.merge!({
dimension_prefix + "Name" => dimension,
dimension_prefix + "Value" => value
})
ii += 1
end unless datum[:dimensions].nil?

params.merge! datum_params
end

return response_generator(:action => 'PutMetricData', :params => params)
end

end

end
Expand Down
138 changes: 138 additions & 0 deletions test/test_Cloudwatch_monitoring.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
#--
# Amazon Web Services EC2 Query API Ruby library
#
# Ruby Gem Name:: amazon-ec2
# Author:: Glenn Rempe (mailto:[email protected])
# Copyright:: Copyright (c) 2007-2008 Glenn Rempe
# License:: Distributes under the same terms as Ruby
# Home:: http://github.com/grempe/amazon-ec2/tree/master
#++

require File.dirname(__FILE__) + '/test_helper.rb'

context "CloudWatch monitoring" do

before do
@cw = AWS::Cloudwatch::Base.new( :access_key_id => "not a key", :secret_access_key => "not a secret" )

@success_response = <<-XML
<PutMetricDataResponse xmlns="http://monitoring.amazonaws.com/doc/2010-08-01/">
<ResponseMetadata>
<RequestId>e16fc4d3-9a04-11e0-9362-093a1cae5385</RequestId>
</ResponseMetadata>
</PutMetricDataResponse>
XML

@now = Time.now

@simple_request = {
'Namespace' => 'App/Metrics',
'MetricData.member.1.MetricName' => 'FooBar',
'MetricData.member.1.Unit' => 'Count',
'MetricData.member.1.Value' => '56265313',
'MetricData.member.1.Timestamp' => Time.now.iso8601,
}
@more_complex_request = {
'Namespace' => 'App/Metrics',
'MetricData.member.1.MetricName' => 'FooBar',
'MetricData.member.1.Unit' => 'Count',
'MetricData.member.1.Value' => '56265313',
'MetricData.member.1.Timestamp' => Time.now.iso8601,
'Namespace' => 'App/Metrics',
'MetricData.member.2.MetricName' => 'FooBar',
'MetricData.member.2.Unit' => 'Count',
'MetricData.member.2.Value' => '56265313',
'MetricData.member.2.Timestamp' => Time.now.-(60).iso8601,
'Namespace' => 'App/Metrics',
'MetricData.member.3.MetricName' => 'FooBar',
'MetricData.member.3.Unit' => 'Count',
'MetricData.member.3.Value' => '56265313',
'MetricData.member.3.Timestamp' => Time.now.-(120).iso8601,
'Namespace' => 'App/Metrics',
'MetricData.member.4.MetricName' => 'FooBar',
'MetricData.member.4.Unit' => 'Count',
'MetricData.member.4.Value' => '56265313',
'MetricData.member.4.Timestamp' => Time.now.-(180).iso8601,
'Namespace' => 'App/Metrics',
'MetricData.member.5.MetricName' => 'FooBar',
'MetricData.member.5.Unit' => 'Count',
'MetricData.member.5.Value' => '56265313',
'MetricData.member.5.Timestamp' => Time.now.-(240).iso8601,
}
@most_complex_request = {
'Namespace' => 'App/Metrics',
'MetricData.member.1.MetricName' => 'FooBar',
'MetricData.member.1.Unit' => 'Count',
'MetricData.member.1.Value' => '56265313',
'MetricData.member.1.Timestamp' => Time.now.iso8601,
'MetricData.member.1.Dimensions.member.1.Name' => 'InstanceID',
'MetricData.member.1.Dimensions.member.1.Value' => 'i-12345678',
'Namespace' => 'App/Metrics',
'MetricData.member.2.MetricName' => 'FooBar',
'MetricData.member.2.Unit' => 'Count',
'MetricData.member.2.Value' => '56265313',
'MetricData.member.2.Timestamp' => Time.now.-(60).iso8601,
'MetricData.member.2.Dimensions.member.1.Name' => 'InstanceID',
'MetricData.member.2.Dimensions.member.1.Value' => 'i-12345678',
'Namespace' => 'App/Metrics',
'MetricData.member.3.MetricName' => 'FooBar',
'MetricData.member.3.Unit' => 'Count',
'MetricData.member.3.Value' => '43',
'MetricData.member.3.Timestamp' => Time.now.-(120).iso8601,
'MetricData.member.3.Dimensions.member.1.Name' => 'InstanceID',
'MetricData.member.3.Dimensions.member.1.Value' => 'i-12345678',
}
end

specify "should put a single point without nested hashes" do
@cw.stubs(:make_request).with('PutMetricData', @simple_request).
returns(:body => @success_response, :is_a => true)
@cw.put_metric_data({
:namespace => 'App/Metrics',
:metric_name => 'FooBar',
:unit => 'Count',
:value => 56265313
}).should.be.an.instance_of Hash
end

specify "should put several metric data points with in an array" do
@cw.stubs(:make_request).with('PutMetricData', @more_complex_request).
returns(:body => @success_response, :is_a => true)
@cw.put_metric_data({
:namespace => 'App/Metrics',
:metric_data => [
{:metric_name => 'FooBar', :unit => 'Count', :value => 56265313, :timestamp => @now},
{:metric_name => 'FooBar', :unit => 'Count', :value => 56265313, :timestamp => @now - 60},
{:metric_name => 'FooBar', :unit => 'Count', :value => 56265313, :timestamp => @now - 120},
{:metric_name => 'FooBar', :unit => 'Count', :value => 56265313, :timestamp => @now - 180},
{:metric_name => 'FooBar', :unit => 'Count', :value => 56265313, :timestamp => @now - 240}
]
}).should.be.an.instance_of Hash
end

specify "should allow the user to be DRY" do
@cw.stubs(:make_request).with('PutMetricData', @most_complex_request).
returns(:body => @success_response, :is_a => true)
@cw.put_metric_data({
:namespace => 'App/Metrics',
:metric_name => 'FooBar',
:unit => 'Count',
:dimensions => {'InstanceID' => 'i-12345678'}, # It can accept more dimensions,
# but their order is unpredictable
:value => 56265313,
:metric_data => [
{:timestamp => @now},
{:timestamp => @now - 60},
{:timestamp => @now - 120, :value => 43} # This was the odd one out
]
}).should.be.an.instance_of Hash
end

specify "should not allow bad arguments" do
lambda {@cw.put_metric_data()}.should.raise(AWS::ArgumentError)
lambda {@cw.put_metric_data({:namespace => 'TEST', :metric_name => 'FooBar', :unit => 'Click'})}.should.raise(AWS::ArgumentError)
lambda {@cw.put_metric_data({:metric_name => 'FooBar', :unit => 'Click', :value => 23})}.should.raise(AWS::ArgumentError)
lambda {@cw.put_metric_data({:namespace => 'TEST', :unit => 'Click', :value => 23})}.should.raise(AWS::ArgumentError)
end

end