airbrakeV4rails5 4.3.8

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 (98) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG +1716 -0
  3. data/Gemfile +3 -0
  4. data/Guardfile +6 -0
  5. data/INSTALL +20 -0
  6. data/LICENSE +61 -0
  7. data/README.md +148 -0
  8. data/README_FOR_HEROKU_ADDON.md +102 -0
  9. data/Rakefile +179 -0
  10. data/TESTED_AGAINST +7 -0
  11. data/airbrake.gemspec +41 -0
  12. data/bin/airbrake +12 -0
  13. data/features/metal.feature +34 -0
  14. data/features/rack.feature +60 -0
  15. data/features/rails.feature +324 -0
  16. data/features/rake.feature +33 -0
  17. data/features/sinatra.feature +126 -0
  18. data/features/step_definitions/file_steps.rb +14 -0
  19. data/features/step_definitions/rack_steps.rb +27 -0
  20. data/features/step_definitions/rails_application_steps.rb +267 -0
  21. data/features/step_definitions/rake_steps.rb +22 -0
  22. data/features/support/airbrake_shim.rb.template +11 -0
  23. data/features/support/aruba.rb +5 -0
  24. data/features/support/env.rb +39 -0
  25. data/features/support/matchers.rb +35 -0
  26. data/features/support/rails.rb +156 -0
  27. data/features/support/rake/Rakefile +77 -0
  28. data/features/user_informer.feature +57 -0
  29. data/generators/airbrake/airbrake_generator.rb +94 -0
  30. data/generators/airbrake/lib/insert_commands.rb +34 -0
  31. data/generators/airbrake/lib/rake_commands.rb +24 -0
  32. data/generators/airbrake/templates/airbrake_tasks.rake +25 -0
  33. data/generators/airbrake/templates/capistrano_hook.rb +6 -0
  34. data/generators/airbrake/templates/initializer.rb +4 -0
  35. data/install.rb +1 -0
  36. data/lib/airbrake.rb +191 -0
  37. data/lib/airbrake/backtrace.rb +103 -0
  38. data/lib/airbrake/capistrano.rb +103 -0
  39. data/lib/airbrake/capistrano3.rb +3 -0
  40. data/lib/airbrake/cli/client.rb +76 -0
  41. data/lib/airbrake/cli/options.rb +45 -0
  42. data/lib/airbrake/cli/printer.rb +33 -0
  43. data/lib/airbrake/cli/project.rb +17 -0
  44. data/lib/airbrake/cli/project_factory.rb +33 -0
  45. data/lib/airbrake/cli/runner.rb +49 -0
  46. data/lib/airbrake/cli/validator.rb +8 -0
  47. data/lib/airbrake/configuration.rb +366 -0
  48. data/lib/airbrake/jobs/send_job.rb +7 -0
  49. data/lib/airbrake/notice.rb +411 -0
  50. data/lib/airbrake/rack.rb +64 -0
  51. data/lib/airbrake/rails.rb +45 -0
  52. data/lib/airbrake/rails/action_controller_catcher.rb +32 -0
  53. data/lib/airbrake/rails/controller_methods.rb +146 -0
  54. data/lib/airbrake/rails/error_lookup.rb +35 -0
  55. data/lib/airbrake/rails/middleware.rb +63 -0
  56. data/lib/airbrake/rails3_tasks.rb +126 -0
  57. data/lib/airbrake/railtie.rb +44 -0
  58. data/lib/airbrake/rake_handler.rb +75 -0
  59. data/lib/airbrake/response.rb +29 -0
  60. data/lib/airbrake/sender.rb +213 -0
  61. data/lib/airbrake/shared_tasks.rb +59 -0
  62. data/lib/airbrake/sidekiq.rb +8 -0
  63. data/lib/airbrake/sinatra.rb +40 -0
  64. data/lib/airbrake/tasks.rb +81 -0
  65. data/lib/airbrake/tasks/airbrake.cap +28 -0
  66. data/lib/airbrake/user_informer.rb +36 -0
  67. data/lib/airbrake/utils/params_cleaner.rb +141 -0
  68. data/lib/airbrake/utils/rack_filters.rb +45 -0
  69. data/lib/airbrake/version.rb +3 -0
  70. data/lib/airbrake_tasks.rb +62 -0
  71. data/lib/rails/generators/airbrake/airbrake_generator.rb +155 -0
  72. data/lib/templates/rescue.erb +91 -0
  73. data/rails/init.rb +1 -0
  74. data/resources/README.md +34 -0
  75. data/resources/airbrake_2_4.xsd +89 -0
  76. data/resources/airbrake_3_0.json +52 -0
  77. data/resources/ca-bundle.crt +3376 -0
  78. data/script/integration_test.rb +35 -0
  79. data/test/airbrake_tasks_test.rb +161 -0
  80. data/test/backtrace_test.rb +215 -0
  81. data/test/capistrano_test.rb +44 -0
  82. data/test/configuration_test.rb +303 -0
  83. data/test/controller_methods_test.rb +230 -0
  84. data/test/helper.rb +233 -0
  85. data/test/integration.rb +13 -0
  86. data/test/integration/catcher_test.rb +371 -0
  87. data/test/logger_test.rb +79 -0
  88. data/test/notice_test.rb +494 -0
  89. data/test/notifier_test.rb +288 -0
  90. data/test/params_cleaner_test.rb +204 -0
  91. data/test/rack_test.rb +62 -0
  92. data/test/rails_initializer_test.rb +36 -0
  93. data/test/recursion_test.rb +10 -0
  94. data/test/response_test.rb +18 -0
  95. data/test/sender_test.rb +335 -0
  96. data/test/support/response_shim.xml +4 -0
  97. data/test/user_informer_test.rb +29 -0
  98. metadata +469 -0
@@ -0,0 +1,44 @@
1
+ require 'airbrake'
2
+ require 'rails'
3
+
4
+ require 'airbrake/rails/middleware'
5
+
6
+ module Airbrake
7
+ class Railtie < ::Rails::Railtie
8
+ rake_tasks do
9
+ require 'airbrake/rake_handler'
10
+ require 'airbrake/rails3_tasks'
11
+ end
12
+
13
+ initializer "airbrake.middleware" do |app|
14
+ middleware = if defined?(ActionDispatch::DebugExceptions)
15
+ # Rails >= 3.2.0
16
+ ActionDispatch::DebugExceptions
17
+ else
18
+ # Rails < 3.2.0
19
+ "ActionDispatch::ShowExceptions"
20
+ end
21
+
22
+ app.config.middleware.insert_after middleware, Airbrake::Rails::Middleware
23
+
24
+ app.config.middleware.insert 0, Airbrake::UserInformer
25
+ end
26
+
27
+ config.after_initialize do
28
+ Airbrake.configure(true) do |config|
29
+ config.logger ||= config.async? ? ::Logger.new(STDERR) : ::Rails.logger
30
+ config.environment_name ||= ::Rails.env
31
+ config.project_root ||= ::Rails.root
32
+ config.framework = "Rails: #{::Rails::VERSION::STRING}"
33
+ end
34
+
35
+ ActiveSupport.on_load(:action_controller) do
36
+ # Lazily load action_controller methods
37
+ #
38
+ require 'airbrake/rails/controller_methods'
39
+
40
+ include Airbrake::Rails::ControllerMethods
41
+ end
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,75 @@
1
+ # Patch Rake::Application to handle errors with Airbrake
2
+ module Airbrake::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_airbrake, :display_error_message
7
+ alias_method :display_error_message, :display_error_message_with_airbrake
8
+ end
9
+ end
10
+
11
+ def display_error_message_with_airbrake(exception)
12
+ if Airbrake.sender && Airbrake.configuration &&
13
+ (Airbrake.configuration.rescue_rake_exceptions ||
14
+ (Airbrake.configuration.rescue_rake_exceptions===nil && !self.tty_output?))
15
+
16
+ Airbrake.notify_or_ignore(exception,
17
+ :component => 'rake',
18
+ :action => reconstruct_command_line,
19
+ :cgi_data => environment_info,
20
+ :ignore => Airbrake.configuration.ignore_rake)
21
+ end
22
+
23
+ display_error_message_without_airbrake(exception)
24
+ end
25
+
26
+ def reconstruct_command_line
27
+ ARGV.join(' ')
28
+ end
29
+
30
+ def environment_info
31
+ ENV.reject do |k|
32
+ Airbrake.configuration.rake_environment_filters.include? k
33
+ end
34
+ end
35
+
36
+ # This module brings Rake 0.8.7 error handling to 0.9.0 standards
37
+ module Rake087Methods
38
+ # Method taken from Rake 0.9.0 source
39
+ #
40
+ # Provide standard exception handling for the given block.
41
+ def standard_exception_handling
42
+ begin
43
+ yield
44
+ rescue SystemExit => ex
45
+ # Exit silently with current status
46
+ raise
47
+ rescue OptionParser::InvalidOption => ex
48
+ $stderr.puts ex.message
49
+ exit(false)
50
+ rescue Exception => ex
51
+ # Exit with error message
52
+ display_error_message(ex)
53
+ exit(false)
54
+ end
55
+ end
56
+
57
+ # Method extracted from Rake 0.8.7 source
58
+ def display_error_message(ex)
59
+ $stderr.puts "#{name} aborted!"
60
+ $stderr.puts ex.message
61
+ if options.trace
62
+ $stderr.puts ex.backtrace.join("\n")
63
+ else
64
+ $stderr.puts ex.backtrace.find {|str| str =~ /#{@rakefile}/ } || ""
65
+ $stderr.puts "(See full trace by running task with --trace)"
66
+ end
67
+ end
68
+ end
69
+ end
70
+
71
+ Rake.application.instance_eval do
72
+ class << self
73
+ include Airbrake::RakeHandler
74
+ end
75
+ end
@@ -0,0 +1,29 @@
1
+ module Airbrake
2
+ class Response
3
+ def self.pretty_format(xml_body)
4
+ new(xml_body)
5
+ rescue
6
+ xml_body
7
+ end
8
+
9
+ def to_s
10
+ output = "\n"
11
+ output = "UUID: #{@id}"
12
+ output << "\n"
13
+ output << "URL: #{@url}"
14
+ output
15
+ end
16
+
17
+ private
18
+
19
+ def initialize(xml_body)
20
+ @xml_body = xml_body
21
+ @url = parse_tag("url")
22
+ @id = parse_tag("id")
23
+ end
24
+
25
+ def parse_tag(name)
26
+ @xml_body.match(%r{<#{name}[^>]*>(.*?)</#{name}>})[1]
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,213 @@
1
+ module Airbrake
2
+ # Sends out the notice to Airbrake
3
+ class Sender
4
+
5
+ NOTICES_URI = '/notifier_api/v2/notices'.freeze
6
+ HEADERS = {
7
+ :xml => {
8
+ 'Content-type' => 'text/xml',
9
+ 'Accept' => 'text/xml, application/xml'
10
+ },:json => {
11
+ 'Content-Type' => 'application/json',
12
+ 'Accept' => 'application/json'
13
+ }}
14
+
15
+ JSON_API_URI = '/api/v3/projects'.freeze
16
+ HTTP_ERRORS = [Timeout::Error,
17
+ Errno::EINVAL,
18
+ Errno::ECONNRESET,
19
+ EOFError,
20
+ Net::HTTPBadResponse,
21
+ Net::HTTPHeaderSyntaxError,
22
+ Net::ProtocolError,
23
+ Errno::ECONNREFUSED,
24
+ OpenSSL::SSL::SSLError].freeze
25
+
26
+ def initialize(options = {})
27
+ [ :proxy_host,
28
+ :proxy_port,
29
+ :proxy_user,
30
+ :proxy_pass,
31
+ :protocol,
32
+ :host,
33
+ :port,
34
+ :secure,
35
+ :use_system_ssl_cert_chain,
36
+ :http_open_timeout,
37
+ :http_read_timeout,
38
+ :project_id,
39
+ :api_key
40
+ ].each do |option|
41
+ instance_variable_set("@#{option}", options[option])
42
+ end
43
+ end
44
+
45
+
46
+ # Sends the notice data off to Airbrake for processing.
47
+ #
48
+ # @param [Notice or String] notice The notice to be sent off
49
+ def send_to_airbrake(notice)
50
+ data = prepare_notice(notice)
51
+ http = setup_http_connection
52
+
53
+ response = begin
54
+ http.post(request_uri,
55
+ data,
56
+ headers)
57
+ rescue *HTTP_ERRORS => e
58
+ log :level => :error,
59
+ :message => "Unable to contact the Airbrake server. HTTP Error=#{e}"
60
+ nil
61
+ end
62
+
63
+ case response
64
+ when Net::HTTPSuccess then
65
+ log :level => :info,
66
+ :message => "Success: #{response.class}",
67
+ :response => response
68
+ else
69
+ log :level => :error,
70
+ :message => "Failure: #{response.class}",
71
+ :response => response,
72
+ :notice => notice
73
+ end
74
+
75
+ if response && response.respond_to?(:body)
76
+ error_id = response.body.match(%r{<id[^>]*>(.*?)</id>})
77
+ error_id[1] if error_id
78
+ end
79
+ rescue => e
80
+ log :level => :error,
81
+ :message => "[Airbrake::Sender#send_to_airbrake] Cannot send notification. Error: #{e.class}" +
82
+ " - #{e.message}\nBacktrace:\n#{e.backtrace.join("\n\t")}"
83
+
84
+ nil
85
+ end
86
+
87
+ attr_reader :proxy_host,
88
+ :proxy_port,
89
+ :proxy_user,
90
+ :proxy_pass,
91
+ :protocol,
92
+ :host,
93
+ :port,
94
+ :secure,
95
+ :use_system_ssl_cert_chain,
96
+ :http_open_timeout,
97
+ :http_read_timeout,
98
+ :project_id,
99
+ :api_key
100
+
101
+ alias_method :secure?, :secure
102
+ alias_method :use_system_ssl_cert_chain?, :use_system_ssl_cert_chain
103
+
104
+ private
105
+
106
+ def prepare_notice(notice)
107
+ if json_api_enabled?
108
+ begin
109
+ JSON.parse(notice)
110
+ notice
111
+ rescue
112
+ notice.to_json
113
+ end
114
+ else
115
+ notice.respond_to?(:to_xml) ? notice.to_xml : notice
116
+ end
117
+ end
118
+
119
+ def api_url
120
+ if json_api_enabled?
121
+ "#{JSON_API_URI}/#{project_id}/notices?key=#{api_key}"
122
+ else
123
+ NOTICES_URI
124
+ end
125
+ end
126
+
127
+ def headers
128
+ if json_api_enabled?
129
+ HEADERS[:json]
130
+ else
131
+ HEADERS[:xml]
132
+ end
133
+ end
134
+
135
+ def url
136
+ URI.parse("#{protocol}://#{host}:#{port}").merge(api_url)
137
+ end
138
+
139
+ def log(opts = {})
140
+ (opts[:logger] || logger).send(opts[:level], LOG_PREFIX + opts[:message])
141
+ Airbrake.report_environment_info
142
+ Airbrake.report_response_body(opts[:response].body) if opts[:response] && opts[:response].respond_to?(:body)
143
+ Airbrake.report_notice(opts[:notice]) if opts[:notice]
144
+ end
145
+
146
+ def logger
147
+ Airbrake.logger
148
+ end
149
+
150
+ def setup_http_connection
151
+ http =
152
+ Net::HTTP::Proxy(proxy_host, proxy_port, proxy_user, proxy_pass).
153
+ new(url.host, url.port)
154
+
155
+ http.read_timeout = http_read_timeout
156
+ http.open_timeout = http_open_timeout
157
+
158
+ if secure?
159
+ http.use_ssl = true
160
+
161
+ http.ca_file = Airbrake.configuration.ca_bundle_path
162
+ http.verify_mode = OpenSSL::SSL::VERIFY_PEER
163
+ else
164
+ http.use_ssl = false
165
+ end
166
+
167
+ http
168
+ rescue => e
169
+ log :level => :error,
170
+ :message => "[Airbrake::Sender#setup_http_connection] Failure initializing the HTTP connection.\n" +
171
+ "Error: #{e.class} - #{e.message}\nBacktrace:\n#{e.backtrace.join("\n\t")}"
172
+ raise e
173
+ end
174
+
175
+ def json_api_enabled?
176
+ !!(host =~ /collect.airbrake.io/) &&
177
+ project_id =~ /\S/
178
+ end
179
+
180
+ # Use request_uri[0] to keep the query since it contains the API_KEY.
181
+ #
182
+ # [0] http://ruby-doc.org/stdlib-2.2.2/libdoc/uri/rdoc/URI/HTTP.html#method-i-request_uri
183
+ def request_uri
184
+ url.respond_to?(:request_uri) ? url.request_uri : url
185
+ end
186
+ end
187
+
188
+ class CollectingSender < Sender
189
+ # Used when test mode is enabled, to store the last XML notice locally
190
+
191
+ attr_writer :last_notice_path
192
+
193
+ def last_notice
194
+ File.read last_notice_path
195
+ end
196
+
197
+ def last_notice_path
198
+ File.expand_path(File.join("..", "..", "..", "resources", "notice.xml"), __FILE__)
199
+ end
200
+
201
+ def send_to_airbrake(notice)
202
+ data = prepare_notice(notice)
203
+
204
+ notices_file = File.open(last_notice_path, "w") do |file|
205
+ file.puts data
206
+ end
207
+
208
+ super(notice)
209
+ ensure
210
+ notices_file.close if notices_file
211
+ end
212
+ end
213
+ end
@@ -0,0 +1,59 @@
1
+ namespace :airbrake do
2
+ desc "Notify Airbrake of a new deploy."
3
+ task :deploy do
4
+ require 'airbrake_tasks'
5
+
6
+ if defined?(Rails.root)
7
+ initializer_file = Rails.root.join('config', 'initializers','airbrake.rb')
8
+
9
+ if initializer_file.exist?
10
+ load initializer_file
11
+ else
12
+ Rake::Task[:environment].invoke
13
+ end
14
+ end
15
+
16
+ AirbrakeTasks.deploy(:rails_env => ENV['TO'],
17
+ :scm_revision => ENV['REVISION'],
18
+ :scm_repository => ENV['REPO'],
19
+ :local_username => ENV['USER'],
20
+ :dry_run => ENV['DRY_RUN'])
21
+ end
22
+
23
+ task :log_stdout do
24
+ require 'logger'
25
+ RAILS_DEFAULT_LOGGER = Logger.new(STDOUT)
26
+ end
27
+
28
+ namespace :heroku do
29
+ desc "Install Heroku deploy notifications addon"
30
+ task :add_deploy_notification => [:environment] do
31
+
32
+ def get_heroku_vars
33
+ config = `heroku config --shell`
34
+ array_of_vars = config.split.map do |var|
35
+ var.partition("=").tap {|part| part.delete_at(1)}
36
+ end.flatten
37
+ @heroku_vars = Hash[*array_of_vars]
38
+ end
39
+
40
+ get_heroku_vars
41
+
42
+ heroku_rails_env = @heroku_vars["RAILS_ENV"] || ENV["RAILS_ENV"] || "production"
43
+ heroku_api_key = @heroku_vars["AIRBRAKE_API_KEY"] || Airbrake.configuration.api_key || ENV["AIRBRAKE_API_KEY"]
44
+ heroku_app = ENV["HEROKU_APP"]
45
+ repo = `git config --get remote.origin.url` || ENV["REPO"]
46
+
47
+ command = %Q(heroku addons:add deployhooks:http --url="http://airbrake.io/deploys.txt?api_key=#{heroku_api_key})
48
+ command << "&deploy[local_username]={{user}}"
49
+ command << "&deploy[scm_revision]={{head_long}}"
50
+ command << "&deploy[rails_env]=#{heroku_rails_env}" if heroku_rails_env
51
+ command << "&deploy[scm_repository]=#{repo}" if repo
52
+ command << '"'
53
+ command << " --app=#{heroku_app}" if heroku_app
54
+
55
+ puts "\nRunning:\n#{command}\n"
56
+ puts `#{command}`
57
+ end
58
+ end
59
+ end
@@ -0,0 +1,8 @@
1
+ gem 'sidekiq', '>= 3.0'
2
+ require 'sidekiq'
3
+
4
+ Sidekiq.configure_server do |config|
5
+ config.error_handlers << lambda do |exception, context|
6
+ Airbrake.notify_or_ignore(exception, :parameters => context)
7
+ end
8
+ end
@@ -0,0 +1,40 @@
1
+ module Airbrake
2
+ # Middleware for Sinatra applications. Any errors raised by the upstream
3
+ # application will be delivered to Airbrake and re-raised.
4
+
5
+ # Synopsis:
6
+
7
+ # require 'sinatra'
8
+ # require 'airbrake'
9
+
10
+ # Airbrake.configure do |config|
11
+ # config.api_key = 'my api key'
12
+ # end
13
+
14
+ # use Airbrake::Sinatra
15
+
16
+ # get '/' do
17
+ # raise "Sinatra has left the building"
18
+ # end
19
+ #
20
+ # Use a standard Airbrake.configure call to configure your api key.
21
+ class Sinatra < Rack
22
+
23
+ def initialize(app)
24
+ super
25
+ Airbrake.configuration.environment_name ||= environment_name(app)
26
+ Airbrake.configuration.framework = "Sinatra: #{::Sinatra::VERSION}"
27
+ end
28
+
29
+ def framework_exception(env)
30
+ env['sinatra.error']
31
+ end
32
+
33
+ def environment_name(app)
34
+ "#{app.settings.environment}"
35
+ rescue
36
+ ENV["RACK_ENV"] || warn("[Airbrake] Couldn't determine environment name automatically. "\
37
+ "Please set your environment name manually by setting 'config.environment_name='.")
38
+ end
39
+ end
40
+ end