dogapi-demo 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.
Files changed (61) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +10 -0
  3. data/.rspec +2 -0
  4. data/.tailor +106 -0
  5. data/.travis.yml +8 -0
  6. data/CHANGELOG.md +157 -0
  7. data/Gemfile +16 -0
  8. data/LICENSE +25 -0
  9. data/README.rdoc +143 -0
  10. data/Rakefile +50 -0
  11. data/dogapi-demo.gemspec +32 -0
  12. data/examples/Capfile +19 -0
  13. data/examples/custom_event.rb +37 -0
  14. data/examples/custom_metric.rb +35 -0
  15. data/lib/capistrano/README.md +13 -0
  16. data/lib/capistrano/datadog.rb +125 -0
  17. data/lib/capistrano/datadog/v2.rb +74 -0
  18. data/lib/capistrano/datadog/v3.rb +64 -0
  19. data/lib/dogapi-demo.rb +5 -0
  20. data/lib/dogapi-demo/common.rb +168 -0
  21. data/lib/dogapi-demo/event.rb +129 -0
  22. data/lib/dogapi-demo/facade.rb +475 -0
  23. data/lib/dogapi-demo/metric.rb +34 -0
  24. data/lib/dogapi-demo/v1.rb +13 -0
  25. data/lib/dogapi-demo/v1/alert.rb +112 -0
  26. data/lib/dogapi-demo/v1/comment.rb +62 -0
  27. data/lib/dogapi-demo/v1/dash.rb +94 -0
  28. data/lib/dogapi-demo/v1/embed.rb +106 -0
  29. data/lib/dogapi-demo/v1/event.rb +101 -0
  30. data/lib/dogapi-demo/v1/metric.rb +118 -0
  31. data/lib/dogapi-demo/v1/monitor.rb +264 -0
  32. data/lib/dogapi-demo/v1/screenboard.rb +110 -0
  33. data/lib/dogapi-demo/v1/search.rb +27 -0
  34. data/lib/dogapi-demo/v1/service_check.rb +32 -0
  35. data/lib/dogapi-demo/v1/snapshot.rb +30 -0
  36. data/lib/dogapi-demo/v1/tag.rb +141 -0
  37. data/lib/dogapi-demo/v1/user.rb +113 -0
  38. data/lib/dogapi-demo/version.rb +3 -0
  39. data/spec/alerts_spec.rb +33 -0
  40. data/spec/common_spec.rb +37 -0
  41. data/spec/facade_spec.rb +166 -0
  42. data/spec/spec_helper.rb +30 -0
  43. data/spec/support/cassettes/Alerts/create/returns_HTTP_code_200.yml +114 -0
  44. data/spec/support/cassettes/Alerts/create/returns_a_valid_event_ID.yml +114 -0
  45. data/spec/support/cassettes/Alerts/create/returns_the_same_query_as_sent.yml +114 -0
  46. data/spec/support/cassettes/Facade/Events/emits_aggregate_events.yml +193 -0
  47. data/spec/support/cassettes/Facade/Events/emits_events_and_retrieves_them.yml +100 -0
  48. data/spec/support/cassettes/Facade/Events/emits_events_with_specified_priority.yml +98 -0
  49. data/spec/support/cassettes/Facade/Tags/adds_updates_and_detaches_tags.yml +442 -0
  50. data/tests/test_alerts.rb +38 -0
  51. data/tests/test_base.rb +30 -0
  52. data/tests/test_client.rb +23 -0
  53. data/tests/test_comments.rb +39 -0
  54. data/tests/test_dashes.rb +85 -0
  55. data/tests/test_embed.rb +194 -0
  56. data/tests/test_monitors.rb +192 -0
  57. data/tests/test_screenboard.rb +90 -0
  58. data/tests/test_search.rb +20 -0
  59. data/tests/test_snapshot.rb +28 -0
  60. data/tests/test_users.rb +65 -0
  61. metadata +178 -0
@@ -0,0 +1,50 @@
1
+ require 'bundler/gem_tasks'
2
+ require 'rake/testtask'
3
+ require 'rdoc/task'
4
+ require 'rspec/core/rake_task'
5
+
6
+ default_tests = [:spec, :test]
7
+
8
+ case RbConfig::CONFIG['ruby_version']
9
+ when -> (version) { version.start_with?("2.3") }
10
+ # Not compatible with Ruby 2.3.x
11
+ else
12
+ require 'tailor/rake_task'
13
+ default_tests.unshift(:tailor)
14
+
15
+ Tailor::RakeTask.new do |task|
16
+ task.file_set 'lib/**/*.rb', :code do |style|
17
+ style.max_line_length 160, :level => :warn
18
+ style.max_code_lines_in_method 40, :level => :warn
19
+ end
20
+ task.file_set 'spec/**/*.rb', :tests do |style|
21
+ style.max_line_length 160, :level => :warn
22
+ style.max_code_lines_in_method 40, :level => :warn
23
+ end
24
+ end
25
+
26
+ end
27
+
28
+ task :default => default_tests
29
+
30
+ Rake::TestTask.new(:test) do |test|
31
+ test.libs.push 'lib'
32
+ test.libs.push 'tests'
33
+ test.test_files = FileList['tests/test_*.rb']
34
+ end
35
+
36
+ # Doc stuff
37
+ RDoc::Task.new do |rd|
38
+ rd.main = 'README.rdoc'
39
+ rd.rdoc_files.include('README.rdoc', 'lib/**/*.rb', 'CHANGELOG.md')
40
+ rd.options << '--line-numbers' << '--inline-source'
41
+ rd.rdoc_dir = 'doc'
42
+ rd.title = 'DogAPI -- DataDog Client'
43
+ end
44
+
45
+ RSpec::Core::RakeTask.new(:spec)
46
+
47
+ desc "Find notes in code"
48
+ task :notes do
49
+ puts `grep --exclude=Rakefile -r 'OPTIMIZE:\\|FIXME:\\|TODO:' .`
50
+ end
@@ -0,0 +1,32 @@
1
+ # -*- encoding: utf-8 -*-
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'dogapi-demo/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = 'dogapi-demo'
8
+ spec.version = DogapiDemo::VERSION
9
+ spec.authors = ['Datadog, Inc.']
10
+ spec.email = ['packages@datadoghq.com']
11
+ spec.description = 'Ruby bindings for Datadog\'s API'
12
+ spec.summary = spec.description
13
+ spec.homepage = 'http://datadoghq.com/'
14
+ spec.license = 'BSD'
15
+
16
+ spec.files = `git ls-files`.split($\)
17
+ spec.executables = spec.files.grep(%r{^bin/}).map { |f| File.basename(f) }
18
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
+ spec.require_paths = ["lib"]
20
+
21
+ spec.has_rdoc = true
22
+ spec.extra_rdoc_files = ['README.rdoc']
23
+ spec.rdoc_options << '--title' << 'DogAPI -- Datadog Client' <<
24
+ '--main' << 'README.rdoc' <<
25
+ '--line-numbers' << '--inline-source'
26
+
27
+ spec.add_dependency 'multi_json'
28
+
29
+ spec.add_development_dependency 'bundler', '~> 1.3'
30
+ spec.add_development_dependency 'rake', '~> 10'
31
+ spec.add_development_dependency 'rdoc'
32
+ end
@@ -0,0 +1,19 @@
1
+ require 'capistrano/setup'
2
+ require 'capistrano/datadog'
3
+
4
+ set :datadog_api_key, 'my_api_key'
5
+ set :stage, :test
6
+ # set :format, :pretty
7
+ # set :log_level, :info
8
+ # set :pty, true
9
+
10
+ server "host0", roles: ["thing"]
11
+ server "host1", roles: ["other_thing"]
12
+
13
+ desc "Hello world"
14
+ task :hello do
15
+ on roles(:all) do |host|
16
+ info capture('echo "$(date): Hello from $(whoami)@$(hostname) !"')
17
+ end
18
+ end
19
+
@@ -0,0 +1,37 @@
1
+ require 'rubygems'
2
+ require 'dogapi'
3
+
4
+ # Create a simple client
5
+ # The host is optional here, it's a shortcut to tie event and metrics to a given host
6
+ #
7
+ # You typically want to do:
8
+ # Dogapi::Client.new(your_actual_api_key_as_a_string, ...)
9
+ # We are using ENV to let you experiment via an environment variable.
10
+ dog = Dogapi::Client.new(ENV['DATADOG_KEY'])
11
+
12
+ # Let's use tags and aggregation
13
+ # We will send 2 related events, one error and one success.
14
+
15
+ dog.emit_event(Dogapi::Event.new("Uh-oh, something bad happened",
16
+ :msg_title => "Alert! Alert!",
17
+ :aggregation_key => "job-123",
18
+ :alert_type => "error",
19
+ :tags => ["ruby", "dogapi"]
20
+ ))
21
+
22
+ dog.emit_event(Dogapi::Event.new("Now that's better",
23
+ :msg_title => "All systems green",
24
+ :aggregation_key => "job-123",
25
+ :alert_type => "success",
26
+ :tags => ["ruby", "dogapi"]
27
+ ))
28
+
29
+ # You should see something like this in your stream:
30
+ # screenshot: https://img.skitch.com/20120705-3s4icgxfc689x1jtrx1yr1m5k.png
31
+
32
+ # And if you graph a metric and want to overlay these events on the graphs
33
+ # You can use the following query: "tags:dogapi tags:ruby" in the graph editor
34
+ # screenshot: https://img.skitch.com/20120705-m4h6f4377cbm2d3rctnd6biej9.png
35
+
36
+ # Then you can see the events neatly overlaid on your graph
37
+ # screenshot: https://img.skitch.com/20120705-xybhimti31u4s4m42ac854568h.png
@@ -0,0 +1,35 @@
1
+ require 'rubygems'
2
+ require 'dogapi'
3
+
4
+ # Create a simple client
5
+ # The host is optional here, it's a shortcut to tie event and metrics to a given host
6
+ #
7
+ # You typically want to do:
8
+ # Dogapi::Client.new(your_actual_api_key_as_a_string, ...)
9
+ # We are using ENV to let you experiment via an environment variable.
10
+ dog = Dogapi::Client.new(ENV['DATADOG_KEY'])
11
+
12
+ # Emit points one by one, timestamp is omitted and is the time this call is made.
13
+ dog.emit_point('test.api.test_metric', 4.0)
14
+
15
+ sleep 1
16
+
17
+ dog.emit_point('test.api.test_metric', 5.0)
18
+
19
+ # Emit a list of points in one go as a list of (timestamp, value)
20
+ # here we pretend to send a point a minute for the past hour
21
+ now = Time.now
22
+ points = (0...60).map do |i|
23
+ i = 60 - i
24
+ t = now - (i*60)
25
+ [t, Math.cos(i) + 1.0]
26
+ end
27
+
28
+ # And emit the data in one call
29
+ dog.emit_points('test.api.test_metric', points)
30
+
31
+ # Emit differents metrics in a single request to be more efficient
32
+ dog.batch_metrics do
33
+ dog.emit_point('test.api.test_metric',10)
34
+ dog.emit_point('test.api.this_other_metric', 1, :type => 'counter')
35
+ end
@@ -0,0 +1,13 @@
1
+ # capistrano/datadog
2
+
3
+ Instrument your [Capistrano](https://github.com/capistrano/capistrano) deploys, generating events in Datadog which you can correlate with metrics in order to figure out what caused production issues.
4
+
5
+ To set up your Capfile:
6
+
7
+ require "capistrano/datadog"
8
+ set :datadog_api_key, "my_api_key"
9
+
10
+ You can find your Datadog API key [here](https://app.datadoghq.com/account/settings#api). If you don't have a Datadog account, you can sign up for one [here](http://www.datadoghq.com/).
11
+
12
+ `capistrano/datadog` will capture each Capistrano task that that Capfile runs, including the roles that the task applies to and any logging output that it emits and submits them as events to Datadog at the end of the execution of all the tasks. If sending to Datadog fails for any reason, your scripts will still succeed.
13
+
@@ -0,0 +1,125 @@
1
+ require "etc"
2
+ require "digest/md5"
3
+ require "timeout"
4
+
5
+ require "dogapi"
6
+
7
+ module Capistrano
8
+ module Datadog
9
+ # Singleton method for Reporter
10
+ def self.reporter()
11
+ @reporter || @reporter = Reporter.new
12
+ end
13
+
14
+ def self.cap_version()
15
+ if @cap_version.nil? then
16
+ if Configuration.respond_to? :instance then
17
+ @cap_version = :v2
18
+ else
19
+ @cap_version = :v3
20
+ end
21
+ end
22
+ @cap_version
23
+ end
24
+
25
+ def self.submit(api_key)
26
+ begin
27
+ if api_key
28
+ dog = Dogapi::Client.new(api_key)
29
+ reporter.report.each do |event|
30
+ dog.emit_event event
31
+ end
32
+ else
33
+ puts "No api key set, not submitting to Datadog"
34
+ end
35
+ rescue Timeout::Error => e
36
+ puts "Could not submit to Datadog, request timed out."
37
+ rescue => e
38
+ puts "Could not submit to Datadog: #{e.inspect}\n#{e.backtrace.join("\n")}"
39
+ end
40
+ end
41
+
42
+ # Collects info about the tasks that ran in order to submit to Datadog
43
+ class Reporter
44
+ attr_accessor :current_task
45
+
46
+ def initialize()
47
+ @tasks = []
48
+ @current_task = nil
49
+ @logging_output = {}
50
+ end
51
+
52
+ def record_task(task_name, timing, roles, stage=nil, application_name=nil)
53
+ @tasks << {
54
+ :name => task_name,
55
+ :timing => timing,
56
+ :roles => roles,
57
+ :stage => stage,
58
+ :application => application_name
59
+ }
60
+ end
61
+
62
+ def record_log(message)
63
+ if not @logging_output[@current_task]
64
+ @logging_output[@current_task] = []
65
+ end
66
+ @logging_output[@current_task] << message
67
+ end
68
+
69
+ def report()
70
+ hostname = %x[hostname -f].strip
71
+ user = Etc.getlogin
72
+
73
+ # Lazy randomness
74
+ aggregation_key = Digest::MD5.hexdigest "#{Time.new}|#{rand}"
75
+
76
+ # Convert the tasks into Datadog events
77
+ @tasks.map do |task|
78
+ name = task[:name]
79
+ roles = Array(task[:roles]).map(&:to_s).sort
80
+ tags = ["#capistrano"] + (roles.map { |t| '#role:' + t })
81
+ if !task[:stage].nil? and !task[:stage].empty? then
82
+ tags << "#stage:#{task[:stage]}"
83
+ end
84
+ application = ''
85
+ if !task[:application].nil? and !task[:application].empty? then
86
+ application = ' for ' + task[:application]
87
+ end
88
+ title = "%s@%s ran %s%s on %s with capistrano in %.2f secs" % [user, hostname, name, application, roles.join(', '), task[:timing]]
89
+ type = "deploy"
90
+ alert_type = "success"
91
+ source_type = "capistrano"
92
+ message_content = (@logging_output[name] || []).join('')
93
+ message = if !message_content.empty? then
94
+ # Strip out color control characters
95
+ message_content = sanitize_encoding(message_content).gsub(/\e\[(\d+)m/, '')
96
+ "@@@\n#{message_content}@@@" else "" end
97
+
98
+ Dogapi::Event.new(message,
99
+ :msg_title => title,
100
+ :event_type => type,
101
+ :event_object => aggregation_key,
102
+ :alert_type => alert_type,
103
+ :source_type_name => source_type,
104
+ :tags => tags
105
+ )
106
+ end
107
+ end
108
+
109
+ def sanitize_encoding(string)
110
+ return string unless defined?(::Encoding) && string.encoding == Encoding::BINARY
111
+ string.encode(Encoding::UTF_8, Encoding::BINARY, invalid: :replace, undef: :replace, replace: '')
112
+ end
113
+ end
114
+
115
+ end
116
+ end
117
+
118
+ case Capistrano::Datadog::cap_version
119
+ when :v2
120
+ require 'capistrano/datadog/v2'
121
+ when :v3
122
+ require 'capistrano/datadog/v3'
123
+ else
124
+ puts "Unknown version: #{Capistrano::Datadog::cap_version.inspect}"
125
+ end
@@ -0,0 +1,74 @@
1
+ require "benchmark"
2
+ require "delegate"
3
+
4
+ # Capistrano v2
5
+
6
+ # Monkeypatch capistrano to collect data about the tasks that it's running
7
+ module Capistrano
8
+ class Configuration
9
+ module Execution
10
+ # Attempts to locate the task at the given fully-qualified path, and
11
+ # execute it. If no such task exists, a Capistrano::NoSuchTaskError
12
+ # will be raised.
13
+ # Also, capture the time the task took to execute, and the logs it
14
+ # outputted for submission to Datadog
15
+ def find_and_execute_task(path, hooks = {})
16
+ task = find_task(path) or raise NoSuchTaskError, "the task `#{path}' does not exist"
17
+ result = nil
18
+ reporter = Capistrano::Datadog.reporter
19
+ task_name = task.fully_qualified_name
20
+ timing = Benchmark.measure(task_name) do
21
+ # Set the current task so that the logger knows which task to
22
+ # associate the logs with
23
+ reporter.current_task = task_name
24
+ trigger(hooks[:before], task) if hooks[:before]
25
+ result = execute_task(task)
26
+ trigger(hooks[:after], task) if hooks[:after]
27
+ reporter.current_task = nil
28
+ end
29
+
30
+ # Record the task name, its timing and roles
31
+ roles = task.options[:roles]
32
+ if roles.is_a? Proc
33
+ roles = roles.call
34
+ end
35
+ reporter.record_task(task_name, timing.real, roles, task.namespace.variables[:stage], fetch(:application))
36
+
37
+ # Return the original result
38
+ result
39
+ end
40
+ end
41
+ end
42
+
43
+ class Logger
44
+ # Make the device attribute writeable so we can swap it out
45
+ # with something that captures logging out by task
46
+ attr_accessor :device
47
+ end
48
+ end
49
+
50
+
51
+ module Capistrano
52
+ module Datadog
53
+ class LogCapture < SimpleDelegator
54
+ def puts(message)
55
+ Capistrano::Datadog::reporter.record_log message
56
+ __getobj__.puts message
57
+ end
58
+ end
59
+
60
+ Configuration.instance(:must_exist).load do
61
+ # Wrap the existing logging target with the Datadog capture class
62
+ logger.device = Datadog::LogCapture.new logger.device
63
+
64
+ # Trigger the Datadog submission once all the tasks have run
65
+ on :exit, "datadog:submit"
66
+ namespace :datadog do
67
+ desc "Submit the tasks that have run to Datadog as events"
68
+ task :submit do |ns|
69
+ Capistrano::Datadog.submit variables[:datadog_api_key]
70
+ end
71
+ end
72
+ end
73
+ end
74
+ end
@@ -0,0 +1,64 @@
1
+ require "benchmark"
2
+ require "sshkit/formatters/pretty"
3
+ require "sshkit/formatters/simple_text"
4
+
5
+ # Capistrano v3 uses Rake's DSL instead of its own
6
+
7
+ module Rake
8
+ class Task
9
+ alias old_invoke invoke
10
+ def invoke(*args)
11
+ result = nil
12
+ reporter = Capistrano::Datadog.reporter
13
+ task_name = name
14
+ reporter.current_task = task_name
15
+ timing = Benchmark.measure(task_name) do
16
+ result = old_invoke(*args)
17
+ end
18
+ reporter.record_task(task_name, timing.real, roles,
19
+ Capistrano::Configuration.env.fetch(:stage), Capistrano::Configuration.env.fetch(:application))
20
+ result
21
+ end
22
+ end
23
+ end
24
+
25
+ module Capistrano
26
+ module Datadog
27
+ class CaptureIO
28
+ def initialize(wrapped)
29
+ @wrapped = wrapped
30
+ end
31
+
32
+ def write(*args)
33
+ @wrapped.write(*args)
34
+ args.each { |arg| Capistrano::Datadog.reporter.record_log(arg) }
35
+ end
36
+ alias :<< :write
37
+
38
+ def close
39
+ @wrapped.close
40
+ end
41
+ end
42
+ end
43
+ end
44
+
45
+ module SSHKit
46
+ module Formatter
47
+ class Pretty
48
+ def initialize(oio)
49
+ super(Capistrano::Datadog::CaptureIO.new(oio))
50
+ end
51
+ end
52
+
53
+ class SimpleText
54
+ def initialize(oio)
55
+ super(Capistrano::Datadog::CaptureIO.new(oio))
56
+ end
57
+ end
58
+ end
59
+ end
60
+
61
+ at_exit do
62
+ api_key = Capistrano::Configuration.env.fetch :datadog_api_key
63
+ Capistrano::Datadog.submit api_key
64
+ end