airbrake 3.1.6 → 3.1.7
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG +116 -0
- data/LICENSE +61 -0
- data/README.md +28 -478
- data/README_FOR_HEROKU_ADDON.md +13 -5
- data/Rakefile +26 -103
- data/TESTED_AGAINST +6 -0
- data/airbrake.gemspec +13 -14
- data/features/metal.feature +27 -12
- data/features/rack.feature +59 -59
- data/features/rails.feature +79 -100
- data/features/rails_with_js_notifier.feature +9 -21
- data/features/rake.feature +6 -4
- data/features/sinatra.feature +32 -6
- data/features/step_definitions/file_steps.rb +4 -0
- data/features/step_definitions/rack_steps.rb +9 -5
- data/features/step_definitions/rails_application_steps.rb +46 -278
- data/features/step_definitions/rake_steps.rb +16 -11
- data/features/support/airbrake_shim.rb.template +0 -2
- data/features/support/aruba.rb +5 -0
- data/features/support/env.rb +12 -6
- data/features/support/rails.rb +30 -73
- data/features/support/rake/Rakefile +1 -2
- data/features/user_informer.feature +12 -19
- data/generators/airbrake/airbrake_generator.rb +1 -1
- data/lib/airbrake.rb +9 -8
- data/lib/airbrake/cli/project_factory.rb +6 -3
- data/lib/airbrake/configuration.rb +7 -0
- data/lib/airbrake/notice.rb +52 -3
- data/lib/airbrake/rack.rb +16 -7
- data/lib/airbrake/rails/action_controller_catcher.rb +5 -3
- data/lib/airbrake/rails/controller_methods.rb +6 -6
- data/lib/airbrake/rails/error_lookup.rb +4 -2
- data/lib/airbrake/rails/javascript_notifier.rb +7 -3
- data/lib/airbrake/rails/middleware.rb +65 -0
- data/lib/airbrake/rails3_tasks.rb +16 -21
- data/lib/airbrake/railtie.rb +9 -16
- data/lib/airbrake/rake_handler.rb +2 -2
- data/lib/airbrake/sender.rb +57 -6
- data/lib/airbrake/shared_tasks.rb +24 -11
- data/lib/airbrake/sinatra.rb +34 -0
- data/lib/airbrake/user_informer.rb +9 -0
- data/lib/airbrake/version.rb +1 -1
- data/lib/rails/generators/airbrake/airbrake_generator.rb +10 -10
- data/{test/airbrake_2_3.xsd → resources/airbrake_2_4.xsd} +1 -0
- data/resources/airbrake_3_0.json +52 -0
- data/test/catcher_test.rb +198 -240
- data/test/configuration_test.rb +2 -0
- data/test/helper.rb +123 -53
- data/test/notice_test.rb +45 -1
- data/test/sender_test.rb +63 -32
- metadata +60 -79
- data/MIT-LICENSE +0 -22
- data/SUPPORTED_RAILS_VERSIONS +0 -38
- data/TESTING.md +0 -41
- data/features/step_definitions/metal_steps.rb +0 -23
- data/features/support/terminal.rb +0 -107
- data/lib/airbrake/extensions/blank.rb +0 -73
- data/lib/airbrake/rails/middleware/exceptions_catcher.rb +0 -33
data/lib/airbrake/rack.rb
CHANGED
@@ -22,6 +22,7 @@ module Airbrake
|
|
22
22
|
class Rack
|
23
23
|
def initialize(app)
|
24
24
|
@app = app
|
25
|
+
Airbrake.configuration.framework = "Rack: #{::Rack.release}"
|
25
26
|
end
|
26
27
|
|
27
28
|
def ignored_user_agent?(env)
|
@@ -32,23 +33,31 @@ module Airbrake
|
|
32
33
|
any? { |ua| ua === env['HTTP_USER_AGENT'] }
|
33
34
|
end
|
34
35
|
|
35
|
-
def notify_airbrake(exception,env)
|
36
|
-
|
36
|
+
def notify_airbrake(exception, env)
|
37
|
+
unless ignored_user_agent?(env)
|
38
|
+
Airbrake.notify_or_ignore(exception, :rack_env => env)
|
39
|
+
end
|
37
40
|
end
|
38
41
|
|
39
42
|
def call(env)
|
43
|
+
@env = env
|
40
44
|
begin
|
41
|
-
response = @app.call(env)
|
45
|
+
response = @app.call(@env)
|
42
46
|
rescue Exception => raised
|
43
|
-
env['airbrake.error_id'] = notify_airbrake(raised,env)
|
44
|
-
raise
|
47
|
+
@env['airbrake.error_id'] = notify_airbrake(raised, @env)
|
48
|
+
raise raised
|
45
49
|
end
|
46
50
|
|
47
|
-
if
|
48
|
-
env['airbrake.error_id'] = notify_airbrake(
|
51
|
+
if framework_exception
|
52
|
+
@env['airbrake.error_id'] = notify_airbrake(framework_exception, @env)
|
49
53
|
end
|
50
54
|
|
51
55
|
response
|
52
56
|
end
|
57
|
+
|
58
|
+
def framework_exception
|
59
|
+
@env['rack.exception']
|
60
|
+
end
|
61
|
+
|
53
62
|
end
|
54
63
|
end
|
@@ -2,10 +2,12 @@ module Airbrake
|
|
2
2
|
module Rails
|
3
3
|
module ActionControllerCatcher
|
4
4
|
|
5
|
-
# Sets up an alias chain to catch exceptions
|
5
|
+
# Sets up an alias chain to catch exceptions for Rails 2
|
6
6
|
def self.included(base) #:nodoc:
|
7
|
-
base.
|
8
|
-
|
7
|
+
if base.method_defined?(:rescue_action_in_public)
|
8
|
+
base.send(:alias_method, :rescue_action_in_public_without_airbrake, :rescue_action_in_public)
|
9
|
+
base.send(:alias_method, :rescue_action_in_public, :rescue_action_in_public_with_airbrake)
|
10
|
+
end
|
9
11
|
end
|
10
12
|
|
11
13
|
private
|
@@ -20,7 +20,7 @@ module Airbrake
|
|
20
20
|
# inside the controller. Otherwise it works like Airbrake.notify.
|
21
21
|
def notify_airbrake(hash_or_exception)
|
22
22
|
unless airbrake_local_request?
|
23
|
-
Airbrake.
|
23
|
+
Airbrake.notify_or_ignore(hash_or_exception, airbrake_request_data)
|
24
24
|
end
|
25
25
|
end
|
26
26
|
|
@@ -74,11 +74,11 @@ module Airbrake
|
|
74
74
|
|
75
75
|
def airbrake_current_user
|
76
76
|
user = begin current_user rescue current_member end
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
77
|
+
h = {}
|
78
|
+
Airbrake.configuration.user_attributes.map(&:to_sym).each do |attr|
|
79
|
+
h[attr.to_sym] = user.send(attr) if user.respond_to? attr
|
80
|
+
end
|
81
|
+
h
|
82
82
|
rescue NoMethodError, NameError
|
83
83
|
{}
|
84
84
|
end
|
@@ -4,8 +4,10 @@ module Airbrake
|
|
4
4
|
|
5
5
|
# Sets up an alias chain to catch exceptions when Rails does
|
6
6
|
def self.included(base) #:nodoc:
|
7
|
-
base.
|
8
|
-
|
7
|
+
if base.method_defined?(:rescue_action_locally)
|
8
|
+
base.send(:alias_method, :rescue_action_locally_without_airbrake, :rescue_action_locally)
|
9
|
+
base.send(:alias_method, :rescue_action_locally, :rescue_action_locally_with_airbrake)
|
10
|
+
end
|
9
11
|
end
|
10
12
|
|
11
13
|
private
|
@@ -7,9 +7,7 @@ module Airbrake
|
|
7
7
|
|
8
8
|
private
|
9
9
|
|
10
|
-
def
|
11
|
-
return unless Airbrake.configuration.public?
|
12
|
-
|
10
|
+
def airbrake_javascript_notifier_options
|
13
11
|
path = File.join File.dirname(__FILE__), '..', '..', 'templates', 'javascript_notifier.erb'
|
14
12
|
host = Airbrake.configuration.host.dup
|
15
13
|
port = Airbrake.configuration.port
|
@@ -28,6 +26,12 @@ module Airbrake
|
|
28
26
|
:url => request.url
|
29
27
|
}
|
30
28
|
}
|
29
|
+
end
|
30
|
+
|
31
|
+
def airbrake_javascript_notifier
|
32
|
+
return unless Airbrake.configuration.public?
|
33
|
+
|
34
|
+
options = airbrake_javascript_notifier_options
|
31
35
|
|
32
36
|
res = if @template
|
33
37
|
@template.render(options)
|
@@ -0,0 +1,65 @@
|
|
1
|
+
module Airbrake
|
2
|
+
module Rails
|
3
|
+
# Rack middleware for Rails applications. Any errors raised by the upstream
|
4
|
+
# application will be delivered to Airbrake and re-raised.
|
5
|
+
#
|
6
|
+
class Middleware
|
7
|
+
def initialize(app)
|
8
|
+
@app = app
|
9
|
+
end
|
10
|
+
|
11
|
+
def call(env)
|
12
|
+
@env = env
|
13
|
+
|
14
|
+
begin
|
15
|
+
response = @app.call(@env)
|
16
|
+
rescue Exception => exception
|
17
|
+
@env['airbrake.error_id'] = notify_airbrake(exception)
|
18
|
+
raise exception
|
19
|
+
end
|
20
|
+
|
21
|
+
if framework_exception
|
22
|
+
@env["airbrake.error_id"] = notify_airbrake(framework_exception)
|
23
|
+
end
|
24
|
+
|
25
|
+
response
|
26
|
+
end
|
27
|
+
|
28
|
+
private
|
29
|
+
|
30
|
+
def controller
|
31
|
+
@env["action_controller.instance"]
|
32
|
+
end
|
33
|
+
|
34
|
+
def after_airbrake_handler(exception)
|
35
|
+
if defined?(controller.rescue_action_in_public_without_airbrake)
|
36
|
+
controller.rescue_action_in_public_without_airbrake(exception)
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
def notify_airbrake(exception)
|
41
|
+
unless ignored_user_agent?
|
42
|
+
error_id = Airbrake.notify_or_ignore(exception, request_data)
|
43
|
+
after_airbrake_handler(exception)
|
44
|
+
error_id
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
def request_data
|
49
|
+
controller.try(:airbrake_request_data) || {:rack_env => @env}
|
50
|
+
end
|
51
|
+
|
52
|
+
def ignored_user_agent?
|
53
|
+
true if Airbrake.
|
54
|
+
configuration.
|
55
|
+
ignore_user_agent.
|
56
|
+
flatten.
|
57
|
+
any? { |ua| ua === @env['HTTP_USER_AGENT'] }
|
58
|
+
end
|
59
|
+
|
60
|
+
def framework_exception
|
61
|
+
@env["action_dispatch.exception"]
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
@@ -20,20 +20,28 @@ namespace :airbrake do
|
|
20
20
|
end
|
21
21
|
end
|
22
22
|
|
23
|
+
# Sets up verbose logging
|
23
24
|
Rails.logger.level = Logger::DEBUG
|
24
25
|
Airbrake.configure(true) do |config|
|
25
26
|
config.logger = Rails.logger
|
26
27
|
end
|
27
28
|
|
29
|
+
# Override Rails exception middleware, so we stop cluttering STDOUT
|
30
|
+
class ActionDispatch::DebugExceptions; def call(env); @app.call(env); end; end
|
31
|
+
class ActionDispatch::ShowExceptions; def call(env); @app.call(env); end; end
|
32
|
+
|
28
33
|
require './app/controllers/application_controller'
|
29
34
|
|
30
35
|
class AirbrakeTestingException < RuntimeError; end
|
31
36
|
|
37
|
+
# Checks if api_key is set
|
32
38
|
unless Airbrake.configuration.api_key
|
33
39
|
puts "Airbrake needs an API key configured! Check the README to see how to add it."
|
34
40
|
exit
|
35
41
|
end
|
36
42
|
|
43
|
+
# Enables Airbrake reporting on all environments,
|
44
|
+
# so we don't have to worry about invoking the task in production
|
37
45
|
Airbrake.configuration.development_environments = []
|
38
46
|
|
39
47
|
puts "Configuration:"
|
@@ -52,44 +60,31 @@ namespace :airbrake do
|
|
52
60
|
prepend_before_filter :test_airbrake
|
53
61
|
def test_airbrake
|
54
62
|
puts "Raising '#{exception_class.name}' to simulate application failure."
|
55
|
-
raise exception_class.new,
|
63
|
+
raise exception_class.new, "\nTesting airbrake via \"rake airbrake:test\"."\
|
64
|
+
" If you can see this, it works."
|
56
65
|
end
|
57
66
|
|
58
|
-
# def rescue_action(exception)
|
59
|
-
# rescue_action_in_public exception
|
60
|
-
# end
|
61
|
-
|
62
67
|
# Ensure we actually have an action to go to.
|
63
68
|
def verify; end
|
64
69
|
|
65
|
-
# def consider_all_requests_local
|
66
|
-
# false
|
67
|
-
# end
|
68
|
-
|
69
|
-
# def local_request?
|
70
|
-
# false
|
71
|
-
# end
|
72
|
-
|
73
70
|
def exception_class
|
74
71
|
exception_name = ENV['EXCEPTION'] || "AirbrakeTestingException"
|
75
72
|
Object.const_get(exception_name)
|
76
73
|
rescue
|
77
74
|
Object.const_set(exception_name, Class.new(Exception))
|
78
75
|
end
|
79
|
-
|
80
|
-
def logger
|
81
|
-
nil
|
82
|
-
end
|
83
76
|
end
|
84
|
-
class AirbrakeVerificationController < ApplicationController; end
|
85
77
|
|
86
|
-
Rails.application.routes_reloader.execute_if_updated
|
87
78
|
Rails.application.routes.draw do
|
88
|
-
|
79
|
+
get 'verify' => 'application#verify', :as => 'verify'
|
89
80
|
end
|
90
81
|
|
91
82
|
puts 'Processing request.'
|
92
|
-
|
83
|
+
|
84
|
+
config = Rails.application.config
|
85
|
+
protocol = (config.respond_to?(:force_ssl) && config.force_ssl) ? 'https' : 'http'
|
86
|
+
|
87
|
+
env = Rack::MockRequest.env_for("#{protocol}://www.example.com/verify")
|
93
88
|
|
94
89
|
Rails.application.call(env)
|
95
90
|
|
data/lib/airbrake/railtie.rb
CHANGED
@@ -1,16 +1,18 @@
|
|
1
1
|
require 'airbrake'
|
2
2
|
require 'rails'
|
3
3
|
|
4
|
+
require 'airbrake/rails/middleware'
|
5
|
+
|
4
6
|
module Airbrake
|
5
7
|
class Railtie < ::Rails::Railtie
|
6
8
|
rake_tasks do
|
7
9
|
require 'airbrake/rake_handler'
|
8
|
-
require
|
10
|
+
require 'airbrake/rails3_tasks'
|
9
11
|
end
|
10
12
|
|
11
|
-
initializer "airbrake.
|
13
|
+
initializer "airbrake.middleware" do |app|
|
14
|
+
app.config.middleware.use "Airbrake::Rails::Middleware"
|
12
15
|
app.config.middleware.insert 0, "Airbrake::UserInformer"
|
13
|
-
app.config.middleware.insert_after "Airbrake::UserInformer","Airbrake::Rack"
|
14
16
|
end
|
15
17
|
|
16
18
|
config.after_initialize do
|
@@ -24,24 +26,15 @@ module Airbrake
|
|
24
26
|
ActiveSupport.on_load(:action_controller) do
|
25
27
|
# Lazily load action_controller methods
|
26
28
|
#
|
27
|
-
require 'airbrake/rails/javascript_notifier'
|
28
29
|
require 'airbrake/rails/controller_methods'
|
29
30
|
|
30
|
-
include Airbrake::Rails::JavascriptNotifier
|
31
31
|
include Airbrake::Rails::ControllerMethods
|
32
32
|
end
|
33
33
|
|
34
|
-
if defined?(::
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
::ActionDispatch::DebugExceptions.send(:include,Airbrake::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 'airbrake/rails/middleware/exceptions_catcher'
|
44
|
-
::ActionDispatch::ShowExceptions.send(:include,Airbrake::Rails::Middleware::ExceptionsCatcher)
|
34
|
+
if defined?(::ActionController::Base)
|
35
|
+
require 'airbrake/rails/javascript_notifier'
|
36
|
+
|
37
|
+
::ActionController::Base.send(:include, Airbrake::Rails::JavascriptNotifier)
|
45
38
|
end
|
46
39
|
end
|
47
40
|
end
|
@@ -13,14 +13,14 @@ module Airbrake::RakeHandler
|
|
13
13
|
(Airbrake.configuration.rescue_rake_exceptions ||
|
14
14
|
(Airbrake.configuration.rescue_rake_exceptions===nil && !self.tty_output?))
|
15
15
|
|
16
|
-
Airbrake.notify_or_ignore(ex, :component => reconstruct_command_line, :cgi_data => environment_info)
|
16
|
+
Airbrake.notify_or_ignore(ex, :component => 'rake', :action => reconstruct_command_line, :cgi_data => environment_info)
|
17
17
|
end
|
18
18
|
|
19
19
|
display_error_message_without_airbrake(ex)
|
20
20
|
end
|
21
21
|
|
22
22
|
def reconstruct_command_line
|
23
|
-
|
23
|
+
ARGV.join( ' ' )
|
24
24
|
end
|
25
25
|
|
26
26
|
def environment_info
|
data/lib/airbrake/sender.rb
CHANGED
@@ -2,7 +2,17 @@ module Airbrake
|
|
2
2
|
# Sends out the notice to Airbrake
|
3
3
|
class Sender
|
4
4
|
|
5
|
-
NOTICES_URI = '/notifier_api/v2/notices
|
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
|
6
16
|
HTTP_ERRORS = [Timeout::Error,
|
7
17
|
Errno::EINVAL,
|
8
18
|
Errno::ECONNRESET,
|
@@ -23,21 +33,26 @@ module Airbrake
|
|
23
33
|
:secure,
|
24
34
|
:use_system_ssl_cert_chain,
|
25
35
|
:http_open_timeout,
|
26
|
-
:http_read_timeout
|
36
|
+
:http_read_timeout,
|
37
|
+
:project_id,
|
38
|
+
:api_key
|
27
39
|
].each do |option|
|
28
40
|
instance_variable_set("@#{option}", options[option])
|
29
41
|
end
|
30
42
|
end
|
31
43
|
|
44
|
+
|
32
45
|
# Sends the notice data off to Airbrake for processing.
|
33
46
|
#
|
34
47
|
# @param [Notice or String] notice The notice to be sent off
|
35
48
|
def send_to_airbrake(notice)
|
36
|
-
data = notice
|
49
|
+
data = prepare_notice(notice)
|
37
50
|
http = setup_http_connection
|
38
51
|
|
39
52
|
response = begin
|
40
|
-
http.post(url.path
|
53
|
+
http.post(url.respond_to?(:path) ? url.path : url,
|
54
|
+
data,
|
55
|
+
headers)
|
41
56
|
rescue *HTTP_ERRORS => e
|
42
57
|
log :level => :error,
|
43
58
|
:message => "Unable to contact the Airbrake server. HTTP Error=#{e}"
|
@@ -78,15 +93,46 @@ module Airbrake
|
|
78
93
|
:secure,
|
79
94
|
:use_system_ssl_cert_chain,
|
80
95
|
:http_open_timeout,
|
81
|
-
:http_read_timeout
|
96
|
+
:http_read_timeout,
|
97
|
+
:project_id,
|
98
|
+
:api_key
|
82
99
|
|
83
100
|
alias_method :secure?, :secure
|
84
101
|
alias_method :use_system_ssl_cert_chain?, :use_system_ssl_cert_chain
|
85
102
|
|
86
103
|
private
|
87
104
|
|
105
|
+
def prepare_notice(notice)
|
106
|
+
if json_api_enabled?
|
107
|
+
begin
|
108
|
+
JSON.parse(notice)
|
109
|
+
notice
|
110
|
+
rescue
|
111
|
+
notice.to_json
|
112
|
+
end
|
113
|
+
else
|
114
|
+
notice.respond_to?(:to_xml) ? notice.to_xml : notice
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
def api_url
|
119
|
+
if json_api_enabled?
|
120
|
+
"#{JSON_API_URI}/#{project_id}/notices?key=#{api_key}"
|
121
|
+
else
|
122
|
+
NOTICES_URI
|
123
|
+
end
|
124
|
+
end
|
125
|
+
|
126
|
+
def headers
|
127
|
+
if json_api_enabled?
|
128
|
+
HEADERS[:json]
|
129
|
+
else
|
130
|
+
HEADERS[:xml]
|
131
|
+
end
|
132
|
+
end
|
133
|
+
|
88
134
|
def url
|
89
|
-
URI.parse("#{protocol}://#{host}:#{port}").merge(
|
135
|
+
URI.parse("#{protocol}://#{host}:#{port}").merge(api_url)
|
90
136
|
end
|
91
137
|
|
92
138
|
def log(opts = {})
|
@@ -124,5 +170,10 @@ module Airbrake
|
|
124
170
|
"Error: #{e.class} - #{e.message}\nBacktrace:\n#{e.backtrace.join("\n\t")}"
|
125
171
|
raise e
|
126
172
|
end
|
173
|
+
|
174
|
+
def json_api_enabled?
|
175
|
+
!!(host =~ /collect.airbrake.io/) &&
|
176
|
+
project_id.present?
|
177
|
+
end
|
127
178
|
end
|
128
179
|
end
|