rollbar 1.1.0 → 1.2.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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +6 -1
- data/README.md +143 -71
- data/UPGRADING.md +45 -0
- data/lib/rollbar.rb +470 -374
- data/lib/rollbar/active_record_extension.rb +1 -1
- data/lib/rollbar/configuration.rb +17 -0
- data/lib/rollbar/core_ext/thread.rb +9 -0
- data/lib/rollbar/exception_reporter.rb +4 -5
- data/lib/rollbar/logger_proxy.rb +32 -0
- data/lib/rollbar/middleware/rack/builder.rb +22 -4
- data/lib/rollbar/middleware/rails/rollbar.rb +62 -0
- data/lib/rollbar/middleware/rails/show_exceptions.rb +3 -5
- data/lib/rollbar/middleware/sinatra.rb +21 -5
- data/lib/rollbar/railtie.rb +18 -15
- data/lib/rollbar/request_data_extractor.rb +19 -9
- data/lib/rollbar/util.rb +39 -0
- data/lib/rollbar/version.rb +1 -1
- data/spec/controllers/home_controller_spec.rb +119 -154
- data/spec/dummyapp/app/controllers/home_controller.rb +16 -6
- data/spec/dummyapp/config/routes.rb +1 -0
- data/spec/rollbar/logger_proxy_spec.rb +35 -0
- data/spec/rollbar/middleware/rack/builder_spec.rb +61 -0
- data/spec/rollbar/middleware/sinatra_spec.rb +83 -5
- data/spec/rollbar_bc_spec.rb +388 -0
- data/spec/rollbar_spec.rb +940 -430
- data/spec/spec_helper.rb +1 -0
- metadata +12 -3
- data/lib/rollbar/middleware/rails/rollbar_request_store.rb +0 -25
@@ -5,7 +5,7 @@ module Rollbar
|
|
5
5
|
def report_validation_errors_to_rollbar
|
6
6
|
self.errors.full_messages.each do |error|
|
7
7
|
Rollbar.log_info "[Rollbar] Reporting form validation error: #{error} for #{self.to_s}"
|
8
|
-
Rollbar.
|
8
|
+
Rollbar.warning("Form Validation Error: #{error} for #{self.to_s}")
|
9
9
|
end
|
10
10
|
end
|
11
11
|
end
|
@@ -20,6 +20,7 @@ module Rollbar
|
|
20
20
|
attr_accessor :framework
|
21
21
|
attr_accessor :ignored_person_ids
|
22
22
|
attr_accessor :logger
|
23
|
+
attr_accessor :payload_options
|
23
24
|
attr_accessor :person_method
|
24
25
|
attr_accessor :person_id_method
|
25
26
|
attr_accessor :person_username_method
|
@@ -28,6 +29,7 @@ module Rollbar
|
|
28
29
|
attr_accessor :request_timeout
|
29
30
|
attr_accessor :root
|
30
31
|
attr_accessor :scrub_fields
|
32
|
+
attr_accessor :uncaught_exception_level
|
31
33
|
attr_accessor :scrub_headers
|
32
34
|
attr_accessor :use_async
|
33
35
|
attr_accessor :use_eventmachine
|
@@ -57,6 +59,7 @@ module Rollbar
|
|
57
59
|
@failover_handlers = []
|
58
60
|
@framework = 'Plain'
|
59
61
|
@ignored_person_ids = []
|
62
|
+
@payload_options = {}
|
60
63
|
@person_method = 'current_user'
|
61
64
|
@person_id_method = 'id'
|
62
65
|
@person_username_method = 'username'
|
@@ -66,6 +69,7 @@ module Rollbar
|
|
66
69
|
@request_timeout = 3
|
67
70
|
@scrub_fields = [:passwd, :password, :password_confirmation, :secret,
|
68
71
|
:confirm_password, :password_confirmation, :secret_token]
|
72
|
+
@uncaught_exception_level = 'error'
|
69
73
|
@scrub_headers = ['Authorization']
|
70
74
|
@use_async = false
|
71
75
|
@use_eventmachine = false
|
@@ -73,6 +77,15 @@ module Rollbar
|
|
73
77
|
@write_to_file = false
|
74
78
|
end
|
75
79
|
|
80
|
+
def initialize_copy(orig)
|
81
|
+
super
|
82
|
+
|
83
|
+
instance_variables.each do |var|
|
84
|
+
instance_var = instance_variable_get(var)
|
85
|
+
instance_variable_set(var, Rollbar::Util::deep_copy(instance_var))
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
76
89
|
def use_sidekiq(options = {})
|
77
90
|
require 'rollbar/delay/sidekiq' if defined?(Sidekiq)
|
78
91
|
@use_async = true
|
@@ -133,5 +146,9 @@ module Rollbar
|
|
133
146
|
def [](option)
|
134
147
|
send(option)
|
135
148
|
end
|
149
|
+
|
150
|
+
def logger
|
151
|
+
@logger ||= default_logger.call
|
152
|
+
end
|
136
153
|
end
|
137
154
|
end
|
@@ -0,0 +1,9 @@
|
|
1
|
+
class Thread
|
2
|
+
def initialize_with_rollbar(*args, &block)
|
3
|
+
self[:_rollbar_notifier] ||= Rollbar.notifier.scope
|
4
|
+
initialize_without_rollbar(*args, &block)
|
5
|
+
end
|
6
|
+
|
7
|
+
alias_method :initialize_without_rollbar, :initialize
|
8
|
+
alias_method :initialize, :initialize_with_rollbar
|
9
|
+
end
|
@@ -3,10 +3,9 @@ module Rollbar
|
|
3
3
|
include RequestDataExtractor
|
4
4
|
|
5
5
|
def report_exception_to_rollbar(env, exception)
|
6
|
-
Rollbar.
|
7
|
-
|
8
|
-
|
9
|
-
exception_data = Rollbar.report_exception(exception, request_data, person_data)
|
6
|
+
Rollbar.log_debug "[Rollbar] Reporting exception: #{exception.try(:message)}"
|
7
|
+
|
8
|
+
exception_data = Rollbar.log(Rollbar.configuration.uncaught_exception_level, exception)
|
10
9
|
|
11
10
|
if exception_data.is_a?(Hash)
|
12
11
|
env['rollbar.exception_uuid'] = exception_data[:uuid]
|
@@ -17,7 +16,7 @@ module Rollbar
|
|
17
16
|
Rollbar.log_debug "[Rollbar] Exception not reported because it was ignored"
|
18
17
|
end
|
19
18
|
rescue => e
|
20
|
-
Rollbar.
|
19
|
+
Rollbar.log_warning "[Rollbar] Exception while reporting exception to Rollbar: #{e.message}"
|
21
20
|
end
|
22
21
|
end
|
23
22
|
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
module Rollbar
|
2
|
+
class LoggerProxy
|
3
|
+
attr_reader :object
|
4
|
+
|
5
|
+
def initialize(object)
|
6
|
+
@object = object
|
7
|
+
end
|
8
|
+
|
9
|
+
def debug(message)
|
10
|
+
log('debug', message)
|
11
|
+
end
|
12
|
+
|
13
|
+
def info(message)
|
14
|
+
log('info', message)
|
15
|
+
end
|
16
|
+
|
17
|
+
def warn(message)
|
18
|
+
log('warn', message)
|
19
|
+
end
|
20
|
+
|
21
|
+
def error(message)
|
22
|
+
log('error', message)
|
23
|
+
end
|
24
|
+
|
25
|
+
def log(level, message)
|
26
|
+
@object.send(level, message)
|
27
|
+
rescue
|
28
|
+
puts "[Rollbar] Error logging #{level}:"
|
29
|
+
puts "[Rollbar] #{message}"
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -1,14 +1,32 @@
|
|
1
|
+
require 'rollbar/exception_reporter'
|
2
|
+
require 'rollbar/request_data_extractor'
|
3
|
+
|
1
4
|
module Rollbar
|
2
5
|
module Middleware
|
3
6
|
module Rack
|
4
7
|
module Builder
|
5
8
|
include ExceptionReporter
|
9
|
+
include RequestDataExtractor
|
6
10
|
|
7
11
|
def call_with_rollbar(env)
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
+
Rollbar.reset_notifier!
|
13
|
+
|
14
|
+
Rollbar.scoped(fetch_scope(env)) do
|
15
|
+
begin
|
16
|
+
call_without_rollbar(env)
|
17
|
+
rescue ::Exception => exception
|
18
|
+
report_exception_to_rollbar(env, exception)
|
19
|
+
raise
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
def fetch_scope(env)
|
25
|
+
request_data = extract_request_data_from_rack(env)
|
26
|
+
{ :request => request_data }
|
27
|
+
rescue Exception => e
|
28
|
+
report_exception_to_rollbar(env, e)
|
29
|
+
raise
|
12
30
|
end
|
13
31
|
|
14
32
|
def self.included(base)
|
@@ -0,0 +1,62 @@
|
|
1
|
+
module Rollbar
|
2
|
+
module Middleware
|
3
|
+
module Rails
|
4
|
+
class RollbarMiddleware
|
5
|
+
include RequestDataExtractor
|
6
|
+
include ExceptionReporter
|
7
|
+
|
8
|
+
def initialize(app)
|
9
|
+
@app = app
|
10
|
+
end
|
11
|
+
|
12
|
+
def call(env)
|
13
|
+
Rollbar.reset_notifier!
|
14
|
+
|
15
|
+
Rollbar.scoped(fetch_scope(env)) do
|
16
|
+
begin
|
17
|
+
response = @app.call(env)
|
18
|
+
|
19
|
+
if (framework_exception = env['action_dispatch.exception'])
|
20
|
+
report_exception_to_rollbar(env, framework_exception)
|
21
|
+
end
|
22
|
+
|
23
|
+
response
|
24
|
+
rescue Exception => exception
|
25
|
+
report_exception_to_rollbar(env, exception)
|
26
|
+
raise
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
def fetch_scope(env)
|
32
|
+
request_data = extract_request_data_from_rack(env)
|
33
|
+
|
34
|
+
# Scope a new notifier with request data and a Proc for person data
|
35
|
+
# for any reports that happen while a controller is handling a request
|
36
|
+
{
|
37
|
+
:request => request_data,
|
38
|
+
:person => person_data_proc(env),
|
39
|
+
:context => context(request_data)
|
40
|
+
}
|
41
|
+
end
|
42
|
+
|
43
|
+
def person_data_proc(env)
|
44
|
+
block = proc { extract_person_data_from_controller(env) }
|
45
|
+
return block unless defined?(ActiveRecord::Base)
|
46
|
+
|
47
|
+
proc do
|
48
|
+
ActiveRecord::Base.connection_pool.with_connection(&block)
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
def context(request_data)
|
53
|
+
return unless request_data[:route]
|
54
|
+
|
55
|
+
route = request_data[:route]
|
56
|
+
# make sure route is a hash built by RequestDataExtractor
|
57
|
+
return "#{route[:controller]}" + '#' + "#{route[:action]}" if route.is_a?(Hash) && !route.empty?
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
@@ -6,18 +6,16 @@ module Rollbar
|
|
6
6
|
|
7
7
|
def render_exception_with_rollbar(env, exception)
|
8
8
|
key = 'action_dispatch.show_detailed_exceptions'
|
9
|
-
|
10
|
-
# don't report production exceptions here as it is done below
|
11
|
-
# in call_with_rollbar() when show_detailed_exception is false
|
12
|
-
if not env.has_key?(key) or env[key]
|
9
|
+
if exception.is_a? ActionController::RoutingError and env[key]
|
13
10
|
report_exception_to_rollbar(env, exception)
|
14
11
|
end
|
12
|
+
|
15
13
|
render_exception_without_rollbar(env, exception)
|
16
14
|
end
|
17
15
|
|
18
16
|
def call_with_rollbar(env)
|
19
17
|
call_without_rollbar(env)
|
20
|
-
rescue
|
18
|
+
rescue ActionController::RoutingError => exception
|
21
19
|
# won't reach here if show_detailed_exceptions is true
|
22
20
|
report_exception_to_rollbar(env, exception)
|
23
21
|
raise exception
|
@@ -1,21 +1,37 @@
|
|
1
1
|
require 'rollbar'
|
2
2
|
require 'rollbar/exception_reporter'
|
3
|
+
require 'rollbar/request_data_extractor'
|
3
4
|
|
4
5
|
module Rollbar
|
5
6
|
module Middleware
|
6
7
|
class Sinatra
|
7
8
|
include ::Rollbar::ExceptionReporter
|
9
|
+
include RequestDataExtractor
|
8
10
|
|
9
11
|
def initialize(app)
|
10
12
|
@app = app
|
11
13
|
end
|
12
14
|
|
13
15
|
def call(env)
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
16
|
+
Rollbar.reset_notifier!
|
17
|
+
|
18
|
+
Rollbar.scoped(fetch_scope(env)) do
|
19
|
+
begin
|
20
|
+
response = @app.call(env)
|
21
|
+
report_exception_to_rollbar(env, framework_error(env)) if framework_error(env)
|
22
|
+
response
|
23
|
+
rescue Exception => e
|
24
|
+
report_exception_to_rollbar(env, e)
|
25
|
+
raise
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def fetch_scope(env)
|
31
|
+
request_data = extract_request_data_from_rack(env)
|
32
|
+
{ :request => request_data }
|
33
|
+
rescue Exception => e
|
34
|
+
report_exception_to_rollbar(env, e)
|
19
35
|
raise
|
20
36
|
end
|
21
37
|
|
data/lib/rollbar/railtie.rb
CHANGED
@@ -6,11 +6,24 @@ module Rollbar
|
|
6
6
|
rake_tasks do
|
7
7
|
require 'rollbar/rake_tasks'
|
8
8
|
end
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
9
|
+
|
10
|
+
initializer 'rollbar.middleware.rails' do |app|
|
11
|
+
if defined?(ActionDispatch::DebugExceptions)
|
12
|
+
# Rails 3.2.x+
|
13
|
+
require 'rollbar/middleware/rails/rollbar'
|
14
|
+
require 'rollbar/middleware/rails/show_exceptions'
|
15
|
+
|
16
|
+
app.config.middleware.insert_after ActionDispatch::DebugExceptions,
|
17
|
+
Rollbar::Middleware::Rails::RollbarMiddleware
|
18
|
+
ActionDispatch::DebugExceptions.send(:include, Rollbar::Middleware::Rails::ShowExceptions)
|
19
|
+
elsif defined?(ActionDispatch::ShowExceptions)
|
20
|
+
# Rails 3.0.x and 3.1.x
|
21
|
+
require 'rollbar/middleware/rails/rollbar'
|
22
|
+
require 'rollbar/middleware/rails/show_exceptions'
|
23
|
+
|
24
|
+
app.config.middleware.insert_after ActionDispatch::ShowExceptions,
|
25
|
+
Rollbar::Middleware::Rails::RollbarMiddleware
|
26
|
+
ActionDispatch::ShowExceptions.send(:include, Rollbar::Middleware::Rails::ShowExceptions)
|
14
27
|
end
|
15
28
|
end
|
16
29
|
|
@@ -28,16 +41,6 @@ module Rollbar
|
|
28
41
|
require 'rollbar/rails/controller_methods'
|
29
42
|
include Rollbar::Rails::ControllerMethods
|
30
43
|
end
|
31
|
-
|
32
|
-
if defined?(ActionDispatch::DebugExceptions)
|
33
|
-
# Rails 3.2.x
|
34
|
-
require 'rollbar/middleware/rails/show_exceptions'
|
35
|
-
ActionDispatch::DebugExceptions.send(:include, Rollbar::Middleware::Rails::ShowExceptions)
|
36
|
-
elsif defined?(ActionDispatch::ShowExceptions)
|
37
|
-
# Rails 3.0.x and 3.1.x
|
38
|
-
require 'rollbar/middleware/rails/show_exceptions'
|
39
|
-
ActionDispatch::ShowExceptions.send(:include, Rollbar::Middleware::Rails::ShowExceptions)
|
40
|
-
end
|
41
44
|
end
|
42
45
|
end
|
43
46
|
end
|
@@ -1,10 +1,7 @@
|
|
1
|
+
require 'multi_json'
|
2
|
+
|
1
3
|
module Rollbar
|
2
4
|
module RequestDataExtractor
|
3
|
-
ATTACHMENT_CLASSES = %w[
|
4
|
-
ActionDispatch::Http::UploadedFile
|
5
|
-
Rack::Multipart::UploadedFile
|
6
|
-
].freeze
|
7
|
-
|
8
5
|
def extract_person_data_from_controller(env)
|
9
6
|
if env.has_key? 'rollbar.person_data'
|
10
7
|
person_data = env['rollbar.person_data'] || {}
|
@@ -23,11 +20,12 @@ module Rollbar
|
|
23
20
|
request_params = rollbar_filtered_params(sensitive_params, rollbar_request_params(env))
|
24
21
|
get_params = rollbar_filtered_params(sensitive_params, rollbar_get_params(rack_req))
|
25
22
|
post_params = rollbar_filtered_params(sensitive_params, rollbar_post_params(rack_req))
|
23
|
+
raw_post_params = rollbar_filtered_params(sensitive_params, rollbar_raw_post_params(rack_req))
|
26
24
|
cookies = rollbar_filtered_params(sensitive_params, rollbar_request_cookies(rack_req))
|
27
25
|
session = rollbar_filtered_params(sensitive_params, env['rack.session.options'])
|
28
26
|
route_params = rollbar_filtered_params(sensitive_params, rollbar_route_params(env))
|
29
27
|
|
30
|
-
params = request_params.merge(get_params).merge(post_params)
|
28
|
+
params = request_params.merge(get_params).merge(post_params).merge(raw_post_params)
|
31
29
|
|
32
30
|
data = {
|
33
31
|
:params => params,
|
@@ -101,6 +99,15 @@ module Rollbar
|
|
101
99
|
{}
|
102
100
|
end
|
103
101
|
|
102
|
+
def rollbar_raw_post_params(rack_req)
|
103
|
+
return {} unless rack_req.env['CONTENT_TYPE'] =~ %r{application/json}i
|
104
|
+
|
105
|
+
params = MultiJson.decode(rack_req.body.read)
|
106
|
+
rack_req.body.rewind
|
107
|
+
|
108
|
+
params
|
109
|
+
end
|
110
|
+
|
104
111
|
def rollbar_request_params(env)
|
105
112
|
env['action_dispatch.request.parameters'] || {}
|
106
113
|
end
|
@@ -125,18 +132,20 @@ module Rollbar
|
|
125
132
|
end
|
126
133
|
|
127
134
|
def rollbar_filtered_params(sensitive_params, params)
|
128
|
-
|
135
|
+
sensitive_params_regexp = Regexp.new(sensitive_params.map{ |val| Regexp.escape(val.to_s).to_s }.join('|'), true)
|
129
136
|
|
130
137
|
if params.nil?
|
131
138
|
{}
|
132
139
|
else
|
133
140
|
params.to_hash.inject({}) do |result, (key, value)|
|
134
|
-
if
|
141
|
+
if sensitive_params_regexp =~ key.to_s
|
135
142
|
result[key] = rollbar_scrubbed(value)
|
136
143
|
elsif value.is_a?(Hash)
|
137
144
|
result[key] = rollbar_filtered_params(sensitive_params, value)
|
138
145
|
elsif value.is_a?(Array)
|
139
|
-
result[key] = value.map
|
146
|
+
result[key] = value.map do |v|
|
147
|
+
v.is_a?(Hash) ? rollbar_filtered_params(sensitive_params, v) : v
|
148
|
+
end
|
140
149
|
elsif ATTACHMENT_CLASSES.include?(value.class.name)
|
141
150
|
result[key] = {
|
142
151
|
:content_type => value.content_type,
|
@@ -146,6 +155,7 @@ module Rollbar
|
|
146
155
|
else
|
147
156
|
result[key] = value
|
148
157
|
end
|
158
|
+
|
149
159
|
result
|
150
160
|
end
|
151
161
|
end
|
data/lib/rollbar/util.rb
CHANGED
@@ -37,6 +37,45 @@ module Rollbar
|
|
37
37
|
end
|
38
38
|
end
|
39
39
|
|
40
|
+
def self.iterate_and_update_hash(hash, block)
|
41
|
+
hash.each do |k, v|
|
42
|
+
if v.is_a?(Hash)
|
43
|
+
self.iterate_and_update_hash(v, block)
|
44
|
+
else
|
45
|
+
hash[k] = block.call(k, v)
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
def self.deep_copy(obj)
|
51
|
+
if obj.is_a?(Hash)
|
52
|
+
result = obj.clone
|
53
|
+
obj.each {|k, v| result[k] = deep_copy(v)}
|
54
|
+
result
|
55
|
+
elsif obj.is_a?(Array)
|
56
|
+
result = obj.clone
|
57
|
+
result.clear
|
58
|
+
obj.each {|v| result << deep_copy(v)}
|
59
|
+
result
|
60
|
+
else
|
61
|
+
obj
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
def self.deep_merge(hash1, hash2)
|
66
|
+
hash2.each_key do |k|
|
67
|
+
if hash1[k].is_a? Hash and hash2[k].is_a? Hash
|
68
|
+
hash1[k] = deep_merge(hash1[k], hash2[k])
|
69
|
+
elsif hash1[k].is_a? Array and hash2[k].is_a? Array
|
70
|
+
hash1[k] += deep_copy(hash2[k])
|
71
|
+
elsif hash2[k]
|
72
|
+
hash1[k] = deep_copy(hash2[k])
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
hash1
|
77
|
+
end
|
78
|
+
|
40
79
|
def self.truncate(str, length)
|
41
80
|
ellipsis = '...'
|
42
81
|
|