dogapi 1.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,34 @@
1
+ Ruby client for Datadog API v1.0.0
2
+
3
+ The Ruby client is a library suitable for inclusion in existing Ruby projects or for development of standalone scripts. It provides an abstraction on top of Datadog's raw HTTP interface for reporting events and metrics.
4
+
5
+ = Installation
6
+
7
+ == Source
8
+
9
+ Available at: https://github.com/DataDog/dogapi-rb
10
+
11
+ $ cd dogapi-rb
12
+ $ rake gem
13
+ $ gem install pkg/dogapi-1.0.0.gem
14
+
15
+ == RubyGems
16
+
17
+ Gem page: https://rubygems.org/gems/dogapi
18
+
19
+ $ gem install dogapi
20
+
21
+ = Usage
22
+
23
+ The client expects the +DATADOG_HOST+ environment variable to be set to the host you will be submitting data to:
24
+
25
+ $ export DATADOG_HOST=http://app.datadoghq.com/
26
+
27
+ Minimal example:
28
+
29
+ require 'dogapi'
30
+
31
+ dog = Dogapi::Client.new(@api_key)
32
+ dog.emit_point 'some.metric.name', 50.0
33
+
34
+ Further usage examples are available in the source distribution under <code>examples/</code> and <code>tests/</code>.
@@ -0,0 +1,4 @@
1
+ require 'dogapi/common'
2
+ require 'dogapi/facade'
3
+ require 'dogapi/event'
4
+ require 'dogapi/metric'
@@ -0,0 +1,86 @@
1
+ require 'cgi'
2
+ require 'net/http'
3
+ require 'pp'
4
+ require 'socket'
5
+ require 'uri'
6
+
7
+ require 'rubygems'
8
+ require 'json'
9
+
10
+ module Dogapi
11
+
12
+ # Metadata class to hold the scope of an API call
13
+ class Scope
14
+ attr_reader :host, :device
15
+ def initialize(host=nil, device=nil)
16
+ @host = host
17
+ @device = device
18
+ end
19
+ end
20
+
21
+ # Superclass that deals with the details of communicating with the DataDog API
22
+ class Service
23
+ def initialize(api_host=find_datadog_host)
24
+ @host = api_host
25
+ end
26
+
27
+ # Manages the HTTP connection
28
+ def connect(api_key=nil, host=nil)
29
+
30
+ @api_key = api_key
31
+ host = host || @host
32
+
33
+ uri = URI.parse(host)
34
+ Net::HTTP.start(uri.host, uri.port) do |conn|
35
+ if 'https' == uri.scheme
36
+ conn.use_ssl = true
37
+ end
38
+ yield(conn)
39
+ end
40
+ end
41
+
42
+ # Prepares the request and handles the response
43
+ #
44
+ # +method+ is an implementation of Net::HTTP::Request (e.g. Net::HTTP::Post)
45
+ #
46
+ # +params+ is a Hash that will be converted to request parameters
47
+ def request(method, url, params)
48
+ if !params.has_key? :api_key
49
+ params[:api_key] = @api_key
50
+ end
51
+
52
+ resp_obj = nil
53
+ connect do |conn|
54
+ req = method.new(url)
55
+ req.set_form_data params
56
+ resp = conn.request(req)
57
+ begin
58
+ resp_obj = JSON.parse(resp.body)
59
+ rescue
60
+ raise 'Invalid JSON Response: ' + resp.body
61
+ end
62
+
63
+ if resp_obj.has_key? 'error'
64
+ request_string = params.pretty_inspect
65
+ error_string = resp_obj['error']
66
+ raise "Failed request\n#{request_string}#{error_string}"
67
+ end
68
+ end
69
+ resp_obj
70
+ end
71
+ end
72
+
73
+ private
74
+
75
+ def Dogapi.find_datadog_host
76
+ ENV['DATADOG_HOST'] rescue nil
77
+ end
78
+
79
+ def Dogapi.find_api_key
80
+ ENV['DATADOG_KEY'] rescue nil
81
+ end
82
+
83
+ def Dogapi.find_localhost
84
+ Socket.gethostname
85
+ end
86
+ end
@@ -0,0 +1,119 @@
1
+ require 'net/http'
2
+
3
+ require 'rubygems'
4
+ require 'json'
5
+
6
+ module Dogapi
7
+
8
+ # Metadata class for storing the details of an event
9
+ class Event
10
+ attr_reader :metric,
11
+ :date_detected,
12
+ :date_happened,
13
+ :alert_type,
14
+ :event_type,
15
+ :event_object,
16
+ :msg_title,
17
+ :msg_text,
18
+ :json_payload
19
+
20
+ # Optional arguments:
21
+ # :metric => String
22
+ # :date_detected => time in seconds since the epoch (defaults to now)
23
+ # :date_happened => time in seconds since the epoch (defaults to now)
24
+ # :alert_type => String
25
+ # :event_type => String
26
+ # :event_object => String
27
+ # :msg_title => String
28
+ # :json_payload => String
29
+ def initialize(msg_text, options={})
30
+ defaults = {
31
+ :metric => '',
32
+ :date_detected => Time.now.to_i,
33
+ :date_happened => Time.now.to_i,
34
+ :alert_type => '',
35
+ :event_type => '',
36
+ :event_object => '',
37
+ :msg_title => '',
38
+ :json_payload => ''
39
+ }
40
+ options = defaults.merge(options)
41
+
42
+ @msg_text = msg_text
43
+ @metric = options[:metric]
44
+ @date_detected = options[:date_detected]
45
+ @date_happened = options[:date_happened]
46
+ @alert_type = options[:alert_type]
47
+ @event_type = options[:event_type]
48
+ @event_object = options[:event_object]
49
+ @msg_title = options[:msg_title]
50
+ @json_payload = options[:json_payload]
51
+ end
52
+ end
53
+
54
+ # Event-specific client affording more granular control than the simple Dogapi::Client
55
+ class EventService < Dogapi::Service
56
+
57
+ API_VERSION = "1.0.0"
58
+
59
+ # Records an Event with no duration
60
+ def submit(api_key, event, scope=nil, source_type=nil)
61
+ scope = scope || Dogapi::Scope.new()
62
+ params = {
63
+ :api_key => api_key,
64
+ :api_version => API_VERSION,
65
+
66
+ :host => scope.host,
67
+ :device => scope.device,
68
+
69
+ :metric => event.metric,
70
+ :date_detected => event.date_detected,
71
+ :date_happened => event.date_happened,
72
+ :alert_type => event.alert_type,
73
+ :event_type => event.event_type,
74
+ :event_object => event.event_object,
75
+ :msg_title => event.msg_title,
76
+ :msg_text => event.msg_text,
77
+ :json_payload => event.json_payload,
78
+ }
79
+
80
+ if source_type
81
+ params[:source_type] = source_type
82
+ end
83
+
84
+ request Net::HTTP::Post, '/event/submit', params
85
+ end
86
+
87
+ # Manages recording an event with a duration
88
+ #
89
+ # 0. The start time is recorded immediately
90
+ # 0. The given block is executed with access to the response of the start request
91
+ # 0. The end time is recorded once the block completes execution
92
+ def start(api_key, event, scope, source_type=nil)
93
+ response = submit api_key, event, scope, source_type
94
+ success = nil
95
+
96
+ begin
97
+ yield response
98
+ rescue
99
+ success = false
100
+ raise
101
+ else
102
+ success = true
103
+ ensure
104
+ return finish api_key, response['id'], success
105
+ end
106
+ end
107
+
108
+ private
109
+
110
+ def finish(api_key, event_id, successful=nil)
111
+ params = {
112
+ :api_key => api_key,
113
+ :event_id => event_id
114
+ }
115
+
116
+ request Net::HTTP::Post, '/event/ended', params
117
+ end
118
+ end
119
+ end
@@ -0,0 +1,125 @@
1
+ require 'time'
2
+ require 'dogapi/metric'
3
+
4
+ module Dogapi
5
+
6
+ # A simple DogAPI client
7
+ #
8
+ # See Dogapi::EventService and Dogapi::MetricService for the thick underlying clients
9
+ class Client
10
+
11
+ # Create a new Client optionally specifying a default host and device
12
+ def initialize(api_key, host=nil, device=nil)
13
+
14
+ if api_key
15
+ @api_key = api_key
16
+ else
17
+ raise 'Please provide an API key to submit your data'
18
+ end
19
+
20
+ @datadog_host = Dogapi.find_datadog_host()
21
+ if !@datadog_host
22
+ raise 'DATADOG_HOST env variable not set'
23
+ end
24
+
25
+ @host = host
26
+ @device = device
27
+
28
+ @metric_svc = Dogapi::MetricService.new(@datadog_host)
29
+ @event_svc = Dogapi::EventService.new(@datadog_host)
30
+ end
31
+
32
+ # Record a single point of metric data
33
+ #
34
+ # Optional arguments:
35
+ # :timestamp => Ruby stdlib Time
36
+ # :host => String
37
+ # :device => String
38
+ def emit_point(metric, value, options={})
39
+ defaults = {:timestamp => Time.now, :host => nil, :device => nil}
40
+ options = defaults.merge(options)
41
+
42
+ self.emit_points metric,
43
+ [[options[:timestamp], value]],
44
+ :host => options[:host],
45
+ :device => options[:device]
46
+ end
47
+
48
+ # Record a set of points of metric data
49
+ #
50
+ # +points+ is an array of <tt>[Time, value]</tt> doubles
51
+ #
52
+ # Optional arguments:
53
+ # :host => String
54
+ # :device => String
55
+ def emit_points(metric, points, options={})
56
+ defaults = {:host => nil, :device => nil}
57
+ options = defaults.merge(options)
58
+
59
+ scope = override_scope options[:host], options[:device]
60
+
61
+ points.each do |p|
62
+ p[0].kind_of? Time or raise "Not a Time"
63
+ p[1].to_f # TODO: stupid to_f will never raise and exception
64
+ end
65
+
66
+ @metric_svc.submit @api_key, scope, metric, points
67
+ end
68
+
69
+ # Record an event with no duration
70
+ #
71
+ # If <tt>:source_type</tt> is unset, a generic "system" event will be reported
72
+ #
73
+ # Optional arguments:
74
+ # :host => String
75
+ # :device => String
76
+ # :source_type => String
77
+ def emit_event(event, options={})
78
+ defaults = {:host => nil, :device => nil, :source_type => nil}
79
+ options = defaults.merge(options)
80
+
81
+ scope = override_scope options[:host], options[:device]
82
+
83
+ @event_svc.submit(@api_key, event, scope, options[:source_type])
84
+ end
85
+
86
+ # Record an event with a duration
87
+ #
88
+ # 0. The start time is recorded immediately
89
+ # 0. The given block is executed
90
+ # 0. The end time is recorded once the block completes execution
91
+ #
92
+ # If <tt>:source_type</tt> is unset, a generic "system" event will be reported
93
+ #
94
+ # Optional arguments:
95
+ # :host => String
96
+ # :device => String
97
+ # :source_type => String
98
+ def start_event(event, options={})
99
+ defaults = {:host => nil, :device => nil, :source_type => nil}
100
+ options = defaults.merge(options)
101
+
102
+ scope = override_scope options[:host], options[:device]
103
+
104
+ @event_svc.start(@api_key, event, scope, options[:source_type]) do
105
+ yield
106
+ end
107
+ end
108
+
109
+ private
110
+
111
+ def override_scope(host, device)
112
+ if host
113
+ h = host
114
+ else
115
+ h = @host
116
+ end
117
+ if device
118
+ d = device
119
+ else
120
+ d = @device
121
+ end
122
+ Scope.new(h, d)
123
+ end
124
+ end
125
+ end
@@ -0,0 +1,30 @@
1
+ require 'net/http'
2
+
3
+ require 'rubygems'
4
+ require 'json'
5
+
6
+ module Dogapi
7
+
8
+ class MetricService < Dogapi::Service
9
+
10
+ API_VERSION = "1.0.0"
11
+
12
+ # Records some number of points for a given metric
13
+ def submit(api_key, scope, metric, points)
14
+ series = [{
15
+ :host => scope.host,
16
+ :device => scope.device,
17
+ :metric => metric,
18
+ :points => points.map { |p| [p[0].to_i, p[1]] }
19
+ }]
20
+
21
+ params = {
22
+ :api_key => api_key,
23
+ :api_version => API_VERSION,
24
+ :series => series.to_json
25
+ }
26
+
27
+ request Net::HTTP::Post, '/series/submit', params
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,47 @@
1
+ require 'test/unit'
2
+ require 'dogapi'
3
+
4
+ class TestClient < Test::Unit::TestCase
5
+
6
+ def config_client_test_env
7
+ @api_key = Dogapi.find_api_key
8
+ if !@api_key
9
+ @api_key = 'apikey_2'
10
+ ENV['DATADOG_KEY'] = @api_key
11
+ end
12
+
13
+ @host = Dogapi.find_datadog_host
14
+ if !@host
15
+ @host = 'localhost:5000'
16
+ ENV['DATADOG_HOST'] = @host
17
+ end
18
+ end
19
+
20
+ def setup
21
+ config_client_test_env
22
+ end
23
+
24
+ def test_simple_client
25
+ dog = Dogapi::Client.new(@api_key)
26
+
27
+ dog.emit_point 'test.dogclient.emit_point',
28
+ 999,
29
+ :host => 'test.dogclient.fake',
30
+ :device => 'eth0'
31
+
32
+ dog.emit_points 'test.dogclient.emit_points',
33
+ [[Time.now - 60, 10], [Time.now - 30, 20], [Time.now, 30]],
34
+ :host => 'test.dogclient.fake',
35
+ :device => 'eth0'
36
+
37
+ my_event = Dogapi::Event.new('Testing Event API')
38
+ dog.emit_event my_event,
39
+ :host => 'test.dogclient.fake',
40
+ :device => 'eth0'
41
+
42
+ my_event = Dogapi::Event.new('Testing Event API with a duration')
43
+ dog.start_event(my_event, :host =>'test.dogclient.fake', :device => 'eth0') do
44
+ sleep 1
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,29 @@
1
+ require 'test/unit'
2
+ require 'dogapi'
3
+
4
+ class TestEnvironment < Test::Unit::TestCase
5
+
6
+ def setup
7
+ @host = ENV['DATADOG_HOST']
8
+ @key = ENV['DATADOG_KEY']
9
+ end
10
+
11
+ def teardown
12
+ ENV['DATADOG_HOST'] = @host
13
+ ENV['DATADOG_KEY'] = @key
14
+ end
15
+
16
+ def test_unset
17
+ ENV['DATADOG_HOST'] = nil
18
+ ENV['DATADOG_KEY'] = nil
19
+ assert_equal nil, Dogapi.find_datadog_host
20
+ assert_equal nil, Dogapi.find_api_key
21
+ end
22
+
23
+ def test_set
24
+ ENV['DATADOG_HOST'] = 'test.host'
25
+ ENV['DATADOG_KEY'] = 'test_key'
26
+ assert_equal 'test.host', Dogapi.find_datadog_host
27
+ assert_equal 'test_key', Dogapi.find_api_key
28
+ end
29
+ end
@@ -0,0 +1,46 @@
1
+ require 'test/unit'
2
+ require 'dogapi'
3
+
4
+ class TestEventClient < Test::Unit::TestCase
5
+
6
+ def config_client_test_env
7
+ @api_key = Dogapi.find_api_key
8
+ if !@api_key
9
+ @api_key = 'apikey_2'
10
+ ENV['DATADOG_KEY'] = @api_key
11
+ end
12
+
13
+ @host = Dogapi.find_datadog_host
14
+ if !@host
15
+ @host = 'localhost:5000'
16
+ ENV['DATADOG_HOST'] = @host
17
+ end
18
+ end
19
+
20
+ def setup
21
+ config_client_test_env
22
+ end
23
+
24
+ def submit_event(scope=nil, source_type=nil)
25
+ event_service = Dogapi::EventService.new(@host)
26
+ tok = '#' + (rand(10000)+1).to_s
27
+ test_message = 'event_test_' + tok
28
+ event = Dogapi::Event.new(test_message, :event_type => 'test-event-type')
29
+ res = event_service.submit(@api_key, event, scope, source_type)
30
+ assert_not_equal(res['id'], nil)
31
+ end
32
+
33
+ def test_submit_event
34
+ submit_event
35
+ end
36
+
37
+ def test_submit_scoped_event
38
+ submit_event(Dogapi::Scope.new('scoped-testhost', 'testdevice'))
39
+ end
40
+
41
+ def test_submit_event
42
+ submit_event(Dogapi::Scope.new('typed-testhost', 'testdevice'), 'Nagios')
43
+ submit_event(Dogapi::Scope.new('typed-testhost', 'testdevice'), 'GitHub')
44
+ end
45
+
46
+ end
@@ -0,0 +1,47 @@
1
+ require 'test/unit'
2
+ require 'time'
3
+ require 'dogapi'
4
+
5
+ class TestEnvironment < Test::Unit::TestCase
6
+
7
+ def config_client_test_env
8
+ @api_key = Dogapi.find_api_key
9
+ if !@api_key
10
+ @api_key = 'apikey_2'
11
+ ENV['DATADOG_KEY'] = @api_key
12
+ end
13
+
14
+ @host = Dogapi.find_datadog_host
15
+ if !@host
16
+ @host = 'localhost:5000'
17
+ ENV['DATADOG_HOST'] = @host
18
+ end
19
+ end
20
+
21
+ def setup
22
+ config_client_test_env
23
+ end
24
+
25
+ def test_submit_metric
26
+ metric_service = Dogapi::MetricService.new(@host)
27
+ scope = Dogapi::Scope.new('test.dogclient.fake', 'eth0')
28
+ metric = 'test.dogclient.metric.submit_metric'
29
+ points = [
30
+ [Time.now-90, 1.0],
31
+ [Time.now-60, 2.0],
32
+ [Time.now-30, 4.0],
33
+ [Time.now, 8.0]
34
+ ]
35
+ res = metric_service.submit(@api_key, scope, metric, points)
36
+ assert_equal(res['status'], 'ok')
37
+ assert_equal(res['results'].size, 1)
38
+ r = res['results'][0]
39
+ assert_equal(r['host'], scope.host)
40
+ if scope.device
41
+ assert_equal(r['device'], scope.device)
42
+ end
43
+ assert_equal(r['metric'], metric)
44
+ assert_equal(r['length'], points.size)
45
+ end
46
+
47
+ end
@@ -0,0 +1,6 @@
1
+ require 'test/unit'
2
+
3
+ require 'tc_env'
4
+ require 'tc_client'
5
+ require 'tc_event'
6
+ require 'tc_metric'
metadata ADDED
@@ -0,0 +1,97 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: dogapi
3
+ version: !ruby/object:Gem::Version
4
+ hash: 21
5
+ prerelease:
6
+ segments:
7
+ - 1
8
+ - 0
9
+ - 1
10
+ version: 1.0.1
11
+ platform: ruby
12
+ authors:
13
+ - Datadog, Inc.
14
+ autorequire:
15
+ bindir: bin
16
+ cert_chain: []
17
+
18
+ date: 2011-03-21 00:00:00 -04:00
19
+ default_executable:
20
+ dependencies:
21
+ - !ruby/object:Gem::Dependency
22
+ name: json
23
+ prerelease: false
24
+ requirement: &id001 !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ">="
28
+ - !ruby/object:Gem::Version
29
+ hash: 1
30
+ segments:
31
+ - 1
32
+ - 5
33
+ - 1
34
+ version: 1.5.1
35
+ type: :runtime
36
+ version_requirements: *id001
37
+ description:
38
+ email: packages@datadoghq.com
39
+ executables: []
40
+
41
+ extensions: []
42
+
43
+ extra_rdoc_files:
44
+ - README.rdoc
45
+ files:
46
+ - lib/dogapi/common.rb
47
+ - lib/dogapi/event.rb
48
+ - lib/dogapi/facade.rb
49
+ - lib/dogapi/metric.rb
50
+ - lib/dogapi.rb
51
+ - tests/tc_client.rb
52
+ - tests/tc_env.rb
53
+ - tests/tc_event.rb
54
+ - tests/tc_metric.rb
55
+ - tests/ts_dogapi.rb
56
+ - README.rdoc
57
+ has_rdoc: true
58
+ homepage: http://datadoghq.com/
59
+ licenses:
60
+ - BSD
61
+ post_install_message:
62
+ rdoc_options:
63
+ - --title
64
+ - DogAPI -- DataDog Client
65
+ - --main
66
+ - README.rdoc
67
+ - --line-numbers
68
+ - --inline-source
69
+ require_paths:
70
+ - lib
71
+ required_ruby_version: !ruby/object:Gem::Requirement
72
+ none: false
73
+ requirements:
74
+ - - ">="
75
+ - !ruby/object:Gem::Version
76
+ hash: 3
77
+ segments:
78
+ - 0
79
+ version: "0"
80
+ required_rubygems_version: !ruby/object:Gem::Requirement
81
+ none: false
82
+ requirements:
83
+ - - ">="
84
+ - !ruby/object:Gem::Version
85
+ hash: 3
86
+ segments:
87
+ - 0
88
+ version: "0"
89
+ requirements: []
90
+
91
+ rubyforge_project:
92
+ rubygems_version: 1.6.2
93
+ signing_key:
94
+ specification_version: 3
95
+ summary: Ruby bindings for Datadog's API
96
+ test_files:
97
+ - tests/ts_dogapi.rb