airbrake 3.1.6 → 3.1.7
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.
- 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
|