cloudtrapper 0.0.2.pre

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 (81) hide show
  1. data/CHANGELOG +823 -0
  2. data/Gemfile +12 -0
  3. data/Guardfile +6 -0
  4. data/INSTALL +20 -0
  5. data/MIT-LICENSE +22 -0
  6. data/README.md +465 -0
  7. data/README_FOR_HEROKU_ADDON.md +94 -0
  8. data/Rakefile +223 -0
  9. data/SUPPORTED_RAILS_VERSIONS +23 -0
  10. data/TESTING.md +33 -0
  11. data/cloudtrapper.gemspec +35 -0
  12. data/features/metal.feature +18 -0
  13. data/features/rack.feature +56 -0
  14. data/features/rails.feature +211 -0
  15. data/features/rails_with_js_notifier.feature +97 -0
  16. data/features/rake.feature +27 -0
  17. data/features/sinatra.feature +29 -0
  18. data/features/step_definitions/file_steps.rb +10 -0
  19. data/features/step_definitions/metal_steps.rb +23 -0
  20. data/features/step_definitions/rack_steps.rb +23 -0
  21. data/features/step_definitions/rails_application_steps.rb +433 -0
  22. data/features/step_definitions/rake_steps.rb +17 -0
  23. data/features/support/airbrake_shim.rb.template +11 -0
  24. data/features/support/env.rb +18 -0
  25. data/features/support/matchers.rb +35 -0
  26. data/features/support/rails.rb +201 -0
  27. data/features/support/rake/Rakefile +68 -0
  28. data/features/support/terminal.rb +107 -0
  29. data/features/user_informer.feature +63 -0
  30. data/generators/cloudtrapper/airbrake_generator.rb +94 -0
  31. data/generators/cloudtrapper/lib/insert_commands.rb +34 -0
  32. data/generators/cloudtrapper/lib/rake_commands.rb +24 -0
  33. data/generators/cloudtrapper/templates/capistrano_hook.rb +6 -0
  34. data/generators/cloudtrapper/templates/cloudtrapper_tasks.rake +25 -0
  35. data/generators/cloudtrapper/templates/initializer.rb +6 -0
  36. data/install.rb +1 -0
  37. data/lib/cloudtrapper/backtrace.rb +100 -0
  38. data/lib/cloudtrapper/capistrano.rb +44 -0
  39. data/lib/cloudtrapper/configuration.rb +281 -0
  40. data/lib/cloudtrapper/notice.rb +348 -0
  41. data/lib/cloudtrapper/rack.rb +55 -0
  42. data/lib/cloudtrapper/rails/action_controller_catcher.rb +30 -0
  43. data/lib/cloudtrapper/rails/controller_methods.rb +74 -0
  44. data/lib/cloudtrapper/rails/error_lookup.rb +33 -0
  45. data/lib/cloudtrapper/rails/javascript_notifier.rb +48 -0
  46. data/lib/cloudtrapper/rails/middleware/exceptions_catcher.rb +29 -0
  47. data/lib/cloudtrapper/rails.rb +40 -0
  48. data/lib/cloudtrapper/rails3_tasks.rb +85 -0
  49. data/lib/cloudtrapper/railtie.rb +48 -0
  50. data/lib/cloudtrapper/rake_handler.rb +66 -0
  51. data/lib/cloudtrapper/sender.rb +116 -0
  52. data/lib/cloudtrapper/shared_tasks.rb +36 -0
  53. data/lib/cloudtrapper/tasks.rb +83 -0
  54. data/lib/cloudtrapper/user_informer.rb +27 -0
  55. data/lib/cloudtrapper/version.rb +3 -0
  56. data/lib/cloudtrapper.rb +155 -0
  57. data/lib/cloudtrapper_tasks.rb +65 -0
  58. data/lib/rails/generators/cloudtrapper/cloudtrapper_generator.rb +100 -0
  59. data/lib/templates/javascript_notifier.erb +15 -0
  60. data/lib/templates/rescue.erb +91 -0
  61. data/rails/init.rb +1 -0
  62. data/resources/README.md +34 -0
  63. data/resources/ca-bundle.crt +3376 -0
  64. data/script/integration_test.rb +38 -0
  65. data/test/backtrace_test.rb +162 -0
  66. data/test/capistrano_test.rb +34 -0
  67. data/test/catcher_test.rb +333 -0
  68. data/test/cloudtrapper_2_2.xsd +78 -0
  69. data/test/cloudtrapper_tasks_test.rb +170 -0
  70. data/test/configuration_test.rb +221 -0
  71. data/test/helper.rb +263 -0
  72. data/test/javascript_notifier_test.rb +52 -0
  73. data/test/logger_test.rb +73 -0
  74. data/test/notice_test.rb +468 -0
  75. data/test/notifier_test.rb +246 -0
  76. data/test/rack_test.rb +58 -0
  77. data/test/rails_initializer_test.rb +36 -0
  78. data/test/recursion_test.rb +10 -0
  79. data/test/sender_test.rb +261 -0
  80. data/test/user_informer_test.rb +29 -0
  81. metadata +301 -0
@@ -0,0 +1,85 @@
1
+ require 'cloudtrapper'
2
+ require File.join(File.dirname(__FILE__), 'shared_tasks')
3
+
4
+ namespace :cloudtrapper do
5
+ desc "Verify your gem installation by sending a test exception to the cloudtrapper service"
6
+ task :test => [:environment] do
7
+ Rails.logger = defined?(ActiveSupport::TaggedLogging) ?
8
+ ActiveSupport::TaggedLogging.new(Logger.new(STDOUT)) :
9
+ Logger.new(STDOUT)
10
+
11
+ Rails.logger.level = Logger::DEBUG
12
+ Cloudtrapper.configure(true) do |config|
13
+ config.logger = Rails.logger
14
+ end
15
+
16
+ require './app/controllers/application_controller'
17
+
18
+ class CloudtrapperTestingException < RuntimeError; end
19
+
20
+ unless Cloudtrapper.configuration.api_key
21
+ puts "Cloudtrapper needs an API key configured! Check the README to see how to add it."
22
+ exit
23
+ end
24
+
25
+ Cloudtrapper.configuration.development_environments = []
26
+
27
+ puts "Configuration:"
28
+ Cloudtrapper.configuration.to_hash.each do |key, value|
29
+ puts sprintf("%25s: %s", key.to_s, value.inspect.slice(0, 55))
30
+ end
31
+
32
+ unless defined?(ApplicationController)
33
+ puts "No ApplicationController found"
34
+ exit
35
+ end
36
+
37
+ puts 'Setting up the Controller.'
38
+ class ApplicationController
39
+ # This is to bypass any filters that may prevent access to the action.
40
+ prepend_before_filter :test_cloudtrapper
41
+ def test_cloudtrapper
42
+ puts "Raising '#{exception_class.name}' to simulate application failure."
43
+ raise exception_class.new, 'Testing cloudtrapper via "rake cloudtrapper:test". If you can see this, it works.'
44
+ end
45
+
46
+ # def rescue_action(exception)
47
+ # rescue_action_in_public exception
48
+ # end
49
+
50
+ # Ensure we actually have an action to go to.
51
+ def verify; end
52
+
53
+ # def consider_all_requests_local
54
+ # false
55
+ # end
56
+
57
+ # def local_request?
58
+ # false
59
+ # end
60
+
61
+ def exception_class
62
+ exception_name = ENV['EXCEPTION'] || "CloudtrapperTestingException"
63
+ Object.const_get(exception_name)
64
+ rescue
65
+ Object.const_set(exception_name, Class.new(Exception))
66
+ end
67
+
68
+ def logger
69
+ nil
70
+ end
71
+ end
72
+ class CloudtrapperVerificationController < ApplicationController; end
73
+
74
+ Rails.application.routes_reloader.execute_if_updated
75
+ Rails.application.routes.draw do
76
+ match 'verify' => 'application#verify', :as => 'verify'
77
+ end
78
+
79
+ puts 'Processing request.'
80
+ env = Rack::MockRequest.env_for("/verify")
81
+
82
+ Rails.application.call(env)
83
+ end
84
+ end
85
+
@@ -0,0 +1,48 @@
1
+ require 'cloudtrapper'
2
+ require 'rails'
3
+
4
+ module Cloudtrapper
5
+ class Railtie < ::Rails::Railtie
6
+ rake_tasks do
7
+ require 'cloudtrapper/rake_handler'
8
+ require "cloudtrapper/rails3_tasks"
9
+ end
10
+
11
+ initializer "cloudtrapper.use_rack_middleware" do |app|
12
+ app.config.middleware.insert 0, "Cloudtrapper::UserInformer"
13
+ app.config.middleware.insert_after "Cloudtrapper::UserInformer","Cloudtrapper::Rack"
14
+ end
15
+
16
+ config.after_initialize do
17
+ Cloudtrapper.configure(true) do |config|
18
+ config.logger ||= ::Rails.logger
19
+ config.environment_name ||= ::Rails.env
20
+ config.project_root ||= ::Rails.root
21
+ config.framework = "Rails: #{::Rails::VERSION::STRING}"
22
+ end
23
+
24
+ ActiveSupport.on_load(:action_controller) do
25
+ # Lazily load action_controller methods
26
+ #
27
+ require 'cloudtrapper/rails/javascript_notifier'
28
+ require 'cloudtrapper/rails/controller_methods'
29
+
30
+ include Cloudtrapper::Rails::JavascriptNotifier
31
+ include Cloudtrapper::Rails::ControllerMethods
32
+ end
33
+
34
+ if defined?(::ActionDispatch::DebugExceptions)
35
+ # We should catch the exceptions in ActionDispatch::DebugExceptions in Rails 3.2.x.
36
+ #
37
+ require 'cloudtrapper/rails/middleware/exceptions_catcher'
38
+ ::ActionDispatch::DebugExceptions.send(:include,Cloudtrapper::Rails::Middleware::ExceptionsCatcher)
39
+ elsif defined?(::ActionDispatch::ShowExceptions)
40
+ # ActionDispatch::DebugExceptions is not defined in Rails 3.0.x and 3.1.x so
41
+ # catch the exceptions in ShowExceptions.
42
+ #
43
+ require 'cloudtrapper/rails/middleware/exceptions_catcher'
44
+ ::ActionDispatch::ShowExceptions.send(:include,Cloudtrapper::Rails::Middleware::ExceptionsCatcher)
45
+ end
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,66 @@
1
+ # Patch Rake::Application to handle errors with Cloudtrapper
2
+ module Cloudtrapper::RakeHandler
3
+ def self.included(klass)
4
+ klass.class_eval do
5
+ include Rake087Methods unless defined?(Rake::VERSION) && Rake::VERSION >= '0.9.0'
6
+ alias_method :display_error_message_without_cloudtrapper, :display_error_message
7
+ alias_method :display_error_message, :display_error_message_with_cloudtrapper
8
+ end
9
+ end
10
+
11
+ def display_error_message_with_cloudtrapper(ex)
12
+ if Cloudtrapper.sender && Cloudtrapper.configuration &&
13
+ (Cloudtrapper.configuration.rescue_rake_exceptions ||
14
+ (Cloudtrapper.configuration.rescue_rake_exceptions===nil && !self.tty_output?))
15
+
16
+ Cloudtrapper.notify_or_ignore(ex, :component => reconstruct_command_line, :cgi_data => ENV)
17
+ end
18
+
19
+ display_error_message_without_cloudtrapper(ex)
20
+ end
21
+
22
+ def reconstruct_command_line
23
+ "rake #{ARGV.join( ' ' )}"
24
+ end
25
+
26
+ # This module brings Rake 0.8.7 error handling to 0.9.0 standards
27
+ module Rake087Methods
28
+ # Method taken from Rake 0.9.0 source
29
+ #
30
+ # Provide standard exception handling for the given block.
31
+ def standard_exception_handling
32
+ begin
33
+ yield
34
+ rescue SystemExit => ex
35
+ # Exit silently with current status
36
+ raise
37
+ rescue OptionParser::InvalidOption => ex
38
+ $stderr.puts ex.message
39
+ exit(false)
40
+ rescue Exception => ex
41
+ # Exit with error message
42
+ display_error_message(ex)
43
+ exit(false)
44
+ end
45
+ end
46
+
47
+ # Method extracted from Rake 0.8.7 source
48
+ def display_error_message(ex)
49
+ $stderr.puts "#{name} aborted!"
50
+ $stderr.puts ex.message
51
+ if options.trace
52
+ $stderr.puts ex.backtrace.join("\n")
53
+ else
54
+ $stderr.puts ex.backtrace.find {|str| str =~ /#{@rakefile}/ } || ""
55
+ $stderr.puts "(See full trace by running task with --trace)"
56
+ end
57
+ end
58
+ end
59
+ end
60
+
61
+ Rake.application.instance_eval do
62
+ class << self
63
+ include Cloudtrapper::RakeHandler
64
+ end
65
+ end
66
+
@@ -0,0 +1,116 @@
1
+ module Cloudtrapper
2
+ # Sends out the notice to Cloudtrapper
3
+ class Sender
4
+
5
+ NOTICES_URI = '/api/notices/'.freeze
6
+ HTTP_ERRORS = [Timeout::Error,
7
+ Errno::EINVAL,
8
+ Errno::ECONNRESET,
9
+ EOFError,
10
+ Net::HTTPBadResponse,
11
+ Net::HTTPHeaderSyntaxError,
12
+ Net::ProtocolError,
13
+ Errno::ECONNREFUSED].freeze
14
+
15
+ def initialize(options = {})
16
+ [ :proxy_host,
17
+ :proxy_port,
18
+ :proxy_user,
19
+ :proxy_pass,
20
+ :protocol,
21
+ :host,
22
+ :port,
23
+ :secure,
24
+ :use_system_ssl_cert_chain,
25
+ :http_open_timeout,
26
+ :http_read_timeout
27
+ ].each do |option|
28
+ instance_variable_set("@#{option}", options[option])
29
+ end
30
+ end
31
+
32
+ # Sends the notice data off to Cloudtrapper for processing.
33
+ #
34
+ # @param [String] data The XML notice to be sent off
35
+ def send_to_cloudtrapper(data)
36
+ http = setup_http_connection
37
+
38
+ response = begin
39
+ http.post(url.path, data, HEADERS)
40
+ rescue *HTTP_ERRORS => e
41
+ log :error, "Unable to contact the Cloudtrapper server. HTTP Error=#{e}"
42
+ nil
43
+ end
44
+
45
+ case response
46
+ when Net::HTTPSuccess then
47
+ log :info, "Success: #{response.class}", response
48
+ else
49
+ log :error, "Failure: #{response.class}", response
50
+ end
51
+
52
+ if response && response.respond_to?(:body)
53
+ error_id = response.body.match(%r{<id[^>]*>(.*?)</id>})
54
+ error_id[1] if error_id
55
+ end
56
+ rescue => e
57
+ log :error, "[Cloudtrapper::Sender#send_to_cloudtrapper] Cannot send notification. Error: #{e.class} - #{e.message}\nBacktrace:\n#{e.backtrace.join("\n\t")}"
58
+ nil
59
+ end
60
+
61
+ attr_reader :proxy_host,
62
+ :proxy_port,
63
+ :proxy_user,
64
+ :proxy_pass,
65
+ :protocol,
66
+ :host,
67
+ :port,
68
+ :secure,
69
+ :use_system_ssl_cert_chain,
70
+ :http_open_timeout,
71
+ :http_read_timeout
72
+
73
+ alias_method :secure?, :secure
74
+ alias_method :use_system_ssl_cert_chain?, :use_system_ssl_cert_chain
75
+
76
+ private
77
+
78
+ def url
79
+ URI.parse("#{protocol}://#{host}:#{port}").merge(NOTICES_URI)
80
+ end
81
+
82
+ def log(level, message, response = nil)
83
+ logger.send level, LOG_PREFIX + message if logger
84
+ Cloudtrapper.report_environment_info
85
+ Cloudtrapper.report_response_body(response.body) if response && response.respond_to?(:body)
86
+ end
87
+
88
+ def logger
89
+ Cloudtrapper.logger
90
+ end
91
+
92
+ def setup_http_connection
93
+ http =
94
+ Net::HTTP::Proxy(proxy_host, proxy_port, proxy_user, proxy_pass).
95
+ new(url.host, url.port)
96
+
97
+ http.read_timeout = http_read_timeout
98
+ http.open_timeout = http_open_timeout
99
+
100
+ if secure?
101
+ http.use_ssl = true
102
+
103
+ http.ca_file = Cloudtrapper.configuration.ca_bundle_path
104
+ http.verify_mode = OpenSSL::SSL::VERIFY_PEER
105
+ else
106
+ http.use_ssl = false
107
+ end
108
+
109
+ http
110
+ rescue => e
111
+ log :error, "[Cloudtrapper::Sender#setup_http_connection] Failure initializing the HTTP connection.\nError: #{e.class} - #{e.message}\nBacktrace:\n#{e.backtrace.join("\n\t")}"
112
+ raise e
113
+ end
114
+
115
+ end
116
+ end
@@ -0,0 +1,36 @@
1
+ namespace :cloudtrapper do
2
+ desc "Notify Cloudtrapper of a new deploy."
3
+ task :deploy => :environment do
4
+ require 'cloudtrapper_tasks'
5
+ CloudtrapperTasks.deploy(:rails_env => ENV['TO'],
6
+ :scm_revision => ENV['REVISION'],
7
+ :scm_repository => ENV['REPO'],
8
+ :local_username => ENV['USER'],
9
+ :api_key => ENV['API_KEY'],
10
+ :dry_run => ENV['DRY_RUN'])
11
+ end
12
+
13
+ task :log_stdout do
14
+ require 'logger'
15
+ RAILS_DEFAULT_LOGGER = Logger.new(STDOUT)
16
+ end
17
+
18
+ namespace :heroku do
19
+ desc "Install Heroku deploy notifications addon"
20
+ task :add_deploy_notification => [:environment] do
21
+
22
+ def heroku_var(var)
23
+ `heroku config | grep -E "#{var.upcase}" | awk '{ print $3; }'`.strip
24
+ end
25
+
26
+ heroku_rails_env = heroku_var("rails_env")
27
+ heroku_api_key = heroku_var("(hoptoad|cloudtrapper)_api_key").split.find {|x| x unless x.blank?} ||
28
+ Cloudtrapper.configuration.api_key
29
+
30
+ command = %Q(heroku addons:add deployhooks:http --url="http://cloudtrapper.io/deploys.txt?deploy[rails_env]=#{heroku_rails_env}&api_key=#{heroku_api_key}")
31
+
32
+ puts "\nRunning:\n#{command}\n"
33
+ puts `#{command}`
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,83 @@
1
+ require 'cloudtrapper'
2
+ require File.join(File.dirname(__FILE__), 'shared_tasks')
3
+
4
+ namespace :cloudtrapper do
5
+ desc "Verify your gem installation by sending a test exception to the cloudtrapper service"
6
+ task :test => ['cloudtrapper:log_stdout', :environment] do
7
+ RAILS_DEFAULT_LOGGER.level = Logger::DEBUG
8
+
9
+ require 'action_controller/test_process'
10
+
11
+ Dir["app/controllers/application*.rb"].each { |file| require(File.expand_path(file)) }
12
+
13
+ class CloudtrapperTestingException < RuntimeError; end
14
+
15
+ unless Cloudtrapper.configuration.api_key
16
+ puts "Cloudtrapper needs an API key configured! Check the README to see how to add it."
17
+ exit
18
+ end
19
+
20
+ Cloudtrapper.configuration.development_environments = []
21
+
22
+ catcher = Cloudtrapper::Rails::ActionControllerCatcher
23
+ in_controller = ApplicationController.included_modules.include?(catcher)
24
+ in_base = ActionController::Base.included_modules.include?(catcher)
25
+ if !in_controller || !in_base
26
+ puts "Rails initialization did not occur"
27
+ exit
28
+ end
29
+
30
+ puts "Configuration:"
31
+ Cloudtrapper.configuration.to_hash.each do |key, value|
32
+ puts sprintf("%25s: %s", key.to_s, value.inspect.slice(0, 55))
33
+ end
34
+
35
+ unless defined?(ApplicationController)
36
+ puts "No ApplicationController found"
37
+ exit
38
+ end
39
+
40
+ puts 'Setting up the Controller.'
41
+ class ApplicationController
42
+ # This is to bypass any filters that may prevent access to the action.
43
+ prepend_before_filter :test_cloudtrapper
44
+ def test_cloudtrapper
45
+ puts "Raising '#{exception_class.name}' to simulate application failure."
46
+ raise exception_class.new, 'Testing cloudtrapper via "rake cloudtrapper:test". If you can see this, it works.'
47
+ end
48
+
49
+ def rescue_action(exception)
50
+ rescue_action_in_public exception
51
+ end
52
+
53
+ # Ensure we actually have an action to go to.
54
+ def verify; end
55
+
56
+ def consider_all_requests_local
57
+ false
58
+ end
59
+
60
+ def local_request?
61
+ false
62
+ end
63
+
64
+ def exception_class
65
+ exception_name = ENV['EXCEPTION'] || "CloudtrapperTestingException"
66
+ exception_name.split("::").inject(Object){|klass, name| klass.const_get(name)}
67
+ rescue
68
+ Object.const_set(exception_name.gsub(/:+/, "_"), Class.new(Exception))
69
+ end
70
+
71
+ def logger
72
+ nil
73
+ end
74
+ end
75
+ class CloudtrapperVerificationController < ApplicationController; end
76
+
77
+ puts 'Processing request.'
78
+ request = ActionController::TestRequest.new("REQUEST_URI" => "/cloudtrapper_verification_controller")
79
+ response = ActionController::TestResponse.new
80
+ CloudtrapperVerificationController.new.process(request, response)
81
+ end
82
+ end
83
+
@@ -0,0 +1,27 @@
1
+ module Cloudtrapper
2
+ class UserInformer
3
+ def initialize(app)
4
+ @app = app
5
+ end
6
+
7
+ def replacement(with)
8
+ Cloudtrapper.configuration.user_information.gsub(/\{\{\s*error_id\s*\}\}/, with.to_s)
9
+ end
10
+
11
+ def call(env)
12
+ status, headers, body = @app.call(env)
13
+ if env['cloudtrapper.error_id'] && Cloudtrapper.configuration.user_information
14
+ new_body = []
15
+ replace = replacement(env['cloudtrapper.error_id'])
16
+ body.each do |chunk|
17
+ new_body << chunk.gsub("<!-- AIRBRAKE ERROR -->", replace)
18
+ end
19
+ body.close if body.respond_to?(:close)
20
+ headers['Content-Length'] = new_body.sum(&:bytesize).to_s
21
+ body = new_body
22
+ end
23
+ [status, headers, body]
24
+ end
25
+ end
26
+ end
27
+
@@ -0,0 +1,3 @@
1
+ module Cloudtrapper
2
+ VERSION = "0.0.2.pre".freeze
3
+ end
@@ -0,0 +1,155 @@
1
+ require 'net/http'
2
+ require 'net/https'
3
+ require 'rubygems'
4
+ begin
5
+ require 'active_support'
6
+ require 'active_support/core_ext'
7
+ rescue LoadError
8
+ require 'activesupport'
9
+ require 'activesupport/core_ext'
10
+ end
11
+ require 'cloudtrapper/version'
12
+ require 'cloudtrapper/configuration'
13
+ require 'cloudtrapper/notice'
14
+ require 'cloudtrapper/sender'
15
+ require 'cloudtrapper/backtrace'
16
+ require 'cloudtrapper/rack'
17
+ require 'cloudtrapper/user_informer'
18
+
19
+ require 'cloudtrapper/railtie' if defined?(Rails::Railtie)
20
+
21
+ module Cloudtrapper
22
+ API_VERSION = "2.2"
23
+ LOG_PREFIX = "** [Cloudtrapper] "
24
+
25
+ HEADERS = {
26
+ 'Content-type' => 'text/xml',
27
+ 'Accept' => 'text/xml, application/xml'
28
+ }
29
+
30
+ class << self
31
+ # The sender object is responsible for delivering formatted data to the Cloudtrapper server.
32
+ # Must respond to #send_to_cloudtrapper. See Cloudtrapper::Sender.
33
+ attr_accessor :sender
34
+
35
+ # A Cloudtrapper configuration object. Must act like a hash and return sensible
36
+ # values for all Cloudtrapper configuration options. See Cloudtrapper::Configuration.
37
+ attr_writer :configuration
38
+
39
+ # Tell the log that the Notifier is good to go
40
+ def report_ready
41
+ write_verbose_log("Notifier #{VERSION} ready to catch errors")
42
+ end
43
+
44
+ # Prints out the environment info to the log for debugging help
45
+ def report_environment_info
46
+ write_verbose_log("Environment Info: #{environment_info}")
47
+ end
48
+
49
+ # Prints out the response body from Cloudtrapper for debugging help
50
+ def report_response_body(response)
51
+ write_verbose_log("Response from Cloudtrapper: \n#{response}")
52
+ end
53
+
54
+ # Returns the Ruby version, Rails version, and current Rails environment
55
+ def environment_info
56
+ info = "[Ruby: #{RUBY_VERSION}]"
57
+ info << " [#{configuration.framework}]" if configuration.framework
58
+ info << " [Env: #{configuration.environment_name}]" if configuration.environment_name
59
+ end
60
+
61
+ # Writes out the given message to the #logger
62
+ def write_verbose_log(message)
63
+ logger.info LOG_PREFIX + message if logger
64
+ end
65
+
66
+ # Look for the Rails logger currently defined
67
+ def logger
68
+ self.configuration.logger
69
+ end
70
+
71
+ # Call this method to modify defaults in your initializers.
72
+ #
73
+ # @example
74
+ # Cloudtrapper.configure do |config|
75
+ # config.api_key = '1234567890abcdef'
76
+ # config.secure = false
77
+ # end
78
+ def configure(silent = false)
79
+ yield(configuration)
80
+ self.sender = Sender.new(configuration)
81
+ report_ready unless silent
82
+ self.sender
83
+ end
84
+
85
+ # The configuration object.
86
+ # @see Cloudtrapper.configure
87
+ def configuration
88
+ @configuration ||= Configuration.new
89
+ end
90
+
91
+ # Sends an exception manually using this method, even when you are not in a controller.
92
+ #
93
+ # @param [Exception] exception The exception you want to notify Cloudtrapper about.
94
+ # @param [Hash] opts Data that will be sent to Cloudtrapper.
95
+ #
96
+ # @option opts [String] :api_key The API key for this project. The API key is a unique identifier that Cloudtrapper uses for identification.
97
+ # @option opts [String] :error_message The error returned by the exception (or the message you want to log).
98
+ # @option opts [String] :backtrace A backtrace, usually obtained with +caller+.
99
+ # @option opts [String] :rack_env The Rack environment.
100
+ # @option opts [String] :session The contents of the user's session.
101
+ # @option opts [String] :environment_name The application environment name.
102
+ def notify(exception, opts = {})
103
+ send_notice(build_notice_for(exception, opts))
104
+ end
105
+
106
+ # Sends the notice unless it is one of the default ignored exceptions
107
+ # @see Cloudtrapper.notify
108
+ def notify_or_ignore(exception, opts = {})
109
+ notice = build_notice_for(exception, opts)
110
+ send_notice(notice) unless notice.ignore?
111
+ end
112
+
113
+ def build_lookup_hash_for(exception, options = {})
114
+ notice = build_notice_for(exception, options)
115
+
116
+ result = {}
117
+ result[:action] = notice.action rescue nil
118
+ result[:component] = notice.component rescue nil
119
+ result[:error_class] = notice.error_class if notice.error_class
120
+ result[:environment_name] = 'production'
121
+
122
+ unless notice.backtrace.lines.empty?
123
+ result[:file] = notice.backtrace.lines.first.file
124
+ result[:line_number] = notice.backtrace.lines.first.number
125
+ end
126
+
127
+ result
128
+ end
129
+
130
+ private
131
+
132
+ def send_notice(notice)
133
+ if configuration.public?
134
+ sender.send_to_cloudtrapper(notice.to_xml)
135
+ end
136
+ end
137
+
138
+ def build_notice_for(exception, opts = {})
139
+ exception = unwrap_exception(exception)
140
+ opts = opts.merge(:exception => exception) if exception.is_a?(Exception)
141
+ opts = opts.merge(exception.to_hash) if exception.respond_to?(:to_hash)
142
+ Notice.new(configuration.merge(opts))
143
+ end
144
+
145
+ def unwrap_exception(exception)
146
+ if exception.respond_to?(:original_exception)
147
+ exception.original_exception
148
+ elsif exception.respond_to?(:continued_exception)
149
+ exception.continued_exception
150
+ else
151
+ exception
152
+ end
153
+ end
154
+ end
155
+ end
@@ -0,0 +1,65 @@
1
+ require 'net/http'
2
+ require 'uri'
3
+ require 'active_support'
4
+
5
+ # Capistrano tasks for notifying Cloudtrapper of deploys
6
+ module CloudtrapperTasks
7
+
8
+ # Alerts Cloudtrapper of a deploy.
9
+ #
10
+ # @param [Hash] opts Data about the deploy that is set to Cloudtrapper
11
+ #
12
+ # @option opts [String] :api_key Api key of you Cloudtrapper application
13
+ # @option opts [String] :rails_env Environment of the deploy (production, staging)
14
+ # @option opts [String] :scm_revision The given revision/sha that is being deployed
15
+ # @option opts [String] :scm_repository Address of your repository to help with code lookups
16
+ # @option opts [String] :local_username Who is deploying
17
+ def self.deploy(opts = {})
18
+ api_key = opts.delete(:api_key) || Cloudtrapper.configuration.api_key
19
+ if api_key.blank?
20
+ puts "I don't seem to be configured with an API key. Please check your configuration."
21
+ return false
22
+ end
23
+
24
+ if opts[:rails_env].blank?
25
+ puts "I don't know to which Rails environment you are deploying (use the TO=production option)."
26
+ return false
27
+ end
28
+
29
+ dry_run = opts.delete(:dry_run)
30
+ params = {'api_key' => api_key}
31
+ opts.each {|k,v| params["deploy[#{k}]"] = v }
32
+
33
+ host = Cloudtrapper.configuration.host || 'api.cloudtrapper.io'
34
+ port = Cloudtrapper.configuration.port
35
+
36
+ proxy = Net::HTTP.Proxy(Cloudtrapper.configuration.proxy_host,
37
+ Cloudtrapper.configuration.proxy_port,
38
+ Cloudtrapper.configuration.proxy_user,
39
+ Cloudtrapper.configuration.proxy_pass)
40
+ http = proxy.new(host, port)
41
+
42
+
43
+
44
+ # Handle Security
45
+ if Cloudtrapper.configuration.secure?
46
+ http.use_ssl = true
47
+ http.ca_file = Cloudtrapper.configuration.ca_bundle_path
48
+ http.verify_mode = OpenSSL::SSL::VERIFY_PEER
49
+ end
50
+
51
+ post = Net::HTTP::Post.new("/deploys.txt")
52
+ post.set_form_data(params)
53
+
54
+ if dry_run
55
+ puts http.inspect, params.inspect
56
+ return true
57
+ else
58
+ response = http.request(post)
59
+
60
+ puts response.body
61
+ return Net::HTTPSuccess === response
62
+ end
63
+ end
64
+ end
65
+