rollbar 2.8.3 → 3.6.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 +5 -5
- data/.codeclimate.yml +18 -0
- data/.github/pull_request_template.md +34 -0
- data/.github/workflows/ci.yml +67 -0
- data/.gitignore +3 -1
- data/.rubocop.yml +206 -7
- data/Appraisals +10 -10
- data/CHANGELOG.md +257 -3
- data/Gemfile +74 -13
- data/README.md +38 -833
- data/Rakefile +0 -0
- data/THANKS.md +1 -0
- data/data/rollbar.snippet.js +1 -1
- data/docs/configuration.md +64 -3
- data/docs/plugins.md +46 -0
- data/gemfiles/rails50.gemfile +56 -0
- data/gemfiles/rails51.gemfile +57 -0
- data/gemfiles/rails52.gemfile +56 -0
- data/gemfiles/rails60.gemfile +52 -0
- data/gemfiles/rails61.gemfile +52 -0
- data/gemfiles/rails70.gemfile +52 -0
- data/gemfiles/rails71.gemfile +52 -0
- data/lib/generators/rollbar/rollbar_generator.rb +24 -20
- data/lib/generators/rollbar/templates/{initializer.rb → initializer.erb} +19 -5
- data/lib/rails/rollbar_runner.rb +26 -22
- data/lib/rollbar/capistrano.rb +78 -38
- data/lib/rollbar/capistrano3.rb +62 -1
- data/lib/rollbar/capistrano_tasks.rb +166 -0
- data/lib/rollbar/configuration.rb +291 -71
- data/lib/rollbar/delay/active_job.rb +17 -0
- data/lib/rollbar/delay/delayed_job.rb +23 -0
- data/lib/rollbar/delay/girl_friday.rb +4 -9
- data/lib/rollbar/delay/resque.rb +3 -6
- data/lib/rollbar/delay/shoryuken.rb +36 -0
- data/lib/rollbar/delay/sidekiq.rb +6 -8
- data/lib/rollbar/delay/sucker_punch.rb +17 -22
- data/lib/rollbar/delay/thread.rb +74 -3
- data/lib/rollbar/deploy.rb +91 -0
- data/lib/rollbar/encoding/encoder.rb +22 -11
- data/lib/rollbar/encoding.rb +2 -7
- data/lib/rollbar/exception_reporter.rb +36 -12
- data/lib/rollbar/item/backtrace.rb +118 -0
- data/lib/rollbar/item/frame.rb +121 -0
- data/lib/rollbar/item/locals.rb +103 -0
- data/lib/rollbar/item.rb +314 -0
- data/lib/rollbar/js.rb +0 -28
- data/lib/rollbar/json.rb +7 -55
- data/lib/rollbar/language_support.rb +7 -19
- data/lib/rollbar/lazy_store.rb +8 -12
- data/lib/rollbar/logger.rb +71 -0
- data/lib/rollbar/logger_proxy.rb +18 -1
- data/lib/rollbar/middleware/js/json_value.rb +36 -0
- data/lib/rollbar/middleware/js.rb +297 -0
- data/lib/rollbar/middleware/rack/builder.rb +4 -4
- data/lib/rollbar/middleware/rack/test_session.rb +4 -4
- data/lib/rollbar/middleware/rack.rb +52 -0
- data/lib/rollbar/middleware/rails/rollbar.rb +19 -7
- data/lib/rollbar/middleware/rails/show_exceptions.rb +21 -9
- data/lib/rollbar/middleware/sinatra.rb +2 -40
- data/lib/rollbar/notifier/trace_with_bindings.rb +75 -0
- data/lib/rollbar/notifier.rb +913 -0
- data/lib/rollbar/plugin.rb +126 -0
- data/lib/rollbar/plugins/active_job.rb +54 -0
- data/lib/rollbar/plugins/basic_socket.rb +31 -0
- data/lib/rollbar/plugins/delayed_job/job_data.rb +50 -0
- data/lib/rollbar/plugins/delayed_job/plugin.rb +88 -0
- data/lib/rollbar/plugins/delayed_job.rb +12 -0
- data/lib/rollbar/plugins/error_context.rb +11 -0
- data/lib/rollbar/plugins/goalie.rb +65 -0
- data/lib/rollbar/plugins/rack.rb +18 -0
- data/lib/rollbar/plugins/rails/controller_methods.rb +56 -0
- data/lib/rollbar/plugins/rails/error_subscriber.rb +12 -0
- data/lib/rollbar/plugins/rails/railtie30.rb +18 -0
- data/lib/rollbar/plugins/rails/railtie32.rb +18 -0
- data/lib/rollbar/plugins/rails/railtie_mixin.rb +37 -0
- data/lib/rollbar/plugins/rails.rb +89 -0
- data/lib/rollbar/plugins/rake.rb +73 -0
- data/lib/rollbar/plugins/resque/failure.rb +39 -0
- data/lib/rollbar/plugins/resque.rb +11 -0
- data/lib/rollbar/plugins/sidekiq/plugin.rb +77 -0
- data/lib/rollbar/plugins/sidekiq.rb +37 -0
- data/lib/rollbar/plugins/thread.rb +14 -0
- data/lib/rollbar/plugins/validations.rb +45 -0
- data/lib/rollbar/plugins.rb +47 -0
- data/lib/rollbar/rails.rb +0 -1
- data/lib/rollbar/rake_tasks.rb +4 -66
- data/lib/rollbar/request_data_extractor.rb +157 -117
- data/lib/rollbar/rollbar_test.rb +38 -0
- data/lib/rollbar/scrubbers/params.rb +133 -0
- data/lib/rollbar/scrubbers/url.rb +90 -35
- data/lib/rollbar/scrubbers.rb +13 -0
- data/lib/rollbar/truncation/frames_strategy.rb +2 -1
- data/lib/rollbar/truncation/min_body_strategy.rb +3 -4
- data/lib/rollbar/truncation/mixin.rb +1 -1
- data/lib/rollbar/truncation/remove_any_key_strategy.rb +126 -0
- data/lib/rollbar/truncation/remove_extra_strategy.rb +37 -0
- data/lib/rollbar/truncation/remove_request_strategy.rb +21 -0
- data/lib/rollbar/truncation/strings_strategy.rb +6 -5
- data/lib/rollbar/truncation.rb +10 -4
- data/lib/rollbar/util/hash.rb +37 -6
- data/lib/rollbar/util/ip_anonymizer.rb +33 -0
- data/lib/rollbar/util/ip_obfuscator.rb +1 -1
- data/lib/rollbar/util.rb +101 -55
- data/lib/rollbar/version.rb +1 -1
- data/lib/rollbar.rb +91 -879
- data/lib/tasks/benchmark.rake +104 -0
- data/lib/tasks/tasks.rake +3 -3
- data/rollbar.gemspec +21 -32
- data/spec/support/rollbar_api.rb +67 -0
- metadata +78 -439
- data/.travis.yml +0 -155
- data/gemfiles/rails30.gemfile +0 -20
- data/gemfiles/rails31.gemfile +0 -16
- data/gemfiles/rails32.gemfile +0 -17
- data/gemfiles/rails40.gemfile +0 -17
- data/gemfiles/rails41.gemfile +0 -15
- data/gemfiles/rails42.gemfile +0 -15
- data/lib/rollbar/active_job.rb +0 -11
- data/lib/rollbar/active_record_extension.rb +0 -14
- data/lib/rollbar/core_ext/basic_socket.rb +0 -7
- data/lib/rollbar/core_ext/thread.rb +0 -9
- data/lib/rollbar/delayed_job.rb +0 -78
- data/lib/rollbar/encoding/legacy_encoder.rb +0 -20
- data/lib/rollbar/goalie.rb +0 -33
- data/lib/rollbar/js/frameworks/rails.rb +0 -29
- data/lib/rollbar/js/frameworks.rb +0 -6
- data/lib/rollbar/js/middleware.rb +0 -129
- data/lib/rollbar/js/version.rb +0 -5
- data/lib/rollbar/json/default.rb +0 -11
- data/lib/rollbar/json/oj.rb +0 -15
- data/lib/rollbar/rack.rb +0 -9
- data/lib/rollbar/rails/controller_methods.rb +0 -40
- data/lib/rollbar/railtie.rb +0 -46
- data/lib/rollbar/rake.rb +0 -38
- data/lib/rollbar/sidekiq.rb +0 -40
- data/lib/rollbar/tasks/rollbar.cap +0 -45
- data/spec/cacert.pem +0 -3988
- data/spec/controllers/home_controller_spec.rb +0 -455
- data/spec/delay/sidekiq_spec.rb +0 -61
- data/spec/delay/sucker_punch_spec.rb +0 -25
- data/spec/delayed/backend/test.rb +0 -139
- data/spec/delayed/serialization/test.rb +0 -0
- data/spec/dummyapp/.gitignore +0 -73
- data/spec/dummyapp/Rakefile +0 -7
- data/spec/dummyapp/app/assets/javascripts/application.js +0 -3
- data/spec/dummyapp/app/assets/stylesheets/application.css.scss +0 -37
- data/spec/dummyapp/app/controllers/application_controller.rb +0 -3
- data/spec/dummyapp/app/controllers/home_controller.rb +0 -60
- data/spec/dummyapp/app/controllers/users_controller.rb +0 -17
- data/spec/dummyapp/app/helpers/.gitkeep +0 -0
- data/spec/dummyapp/app/mailers/.gitkeep +0 -0
- data/spec/dummyapp/app/models/.gitkeep +0 -0
- data/spec/dummyapp/app/models/user.rb +0 -7
- data/spec/dummyapp/app/views/devise/registrations/edit.html.erb +0 -27
- data/spec/dummyapp/app/views/devise/registrations/new.html.erb +0 -20
- data/spec/dummyapp/app/views/devise/shared/_links.html.erb +0 -25
- data/spec/dummyapp/app/views/home/cause_exception.html.erb +0 -1
- data/spec/dummyapp/app/views/home/index.html.erb +0 -4
- data/spec/dummyapp/app/views/home/report_exception.html.erb +0 -1
- data/spec/dummyapp/app/views/js/test.html.erb +0 -1
- data/spec/dummyapp/app/views/layouts/_messages.html.erb +0 -5
- data/spec/dummyapp/app/views/layouts/_navigation.html.erb +0 -21
- data/spec/dummyapp/app/views/layouts/application.html.erb +0 -25
- data/spec/dummyapp/app/views/layouts/simple.html.erb +0 -18
- data/spec/dummyapp/app/views/users/index.html.erb +0 -8
- data/spec/dummyapp/app/views/users/show.html.erb +0 -3
- data/spec/dummyapp/config/application.rb +0 -59
- data/spec/dummyapp/config/boot.rb +0 -10
- data/spec/dummyapp/config/database.yml +0 -25
- data/spec/dummyapp/config/environment.rb +0 -5
- data/spec/dummyapp/config/environments/development.rb +0 -37
- data/spec/dummyapp/config/environments/production.rb +0 -67
- data/spec/dummyapp/config/environments/test.rb +0 -37
- data/spec/dummyapp/config/initializers/backtrace_silencers.rb +0 -7
- data/spec/dummyapp/config/initializers/inflections.rb +0 -15
- data/spec/dummyapp/config/initializers/mime_types.rb +0 -5
- data/spec/dummyapp/config/initializers/rollbar.rb +0 -23
- data/spec/dummyapp/config/initializers/secret_token.rb +0 -7
- data/spec/dummyapp/config/initializers/session_store.rb +0 -8
- data/spec/dummyapp/config/initializers/wrap_parameters.rb +0 -16
- data/spec/dummyapp/config/locales/devise.en.yml +0 -58
- data/spec/dummyapp/config/locales/en.yml +0 -5
- data/spec/dummyapp/config/routes.rb +0 -17
- data/spec/dummyapp/config.ru +0 -4
- data/spec/dummyapp/db/migrate/20121121184652_devise_create_users.rb +0 -46
- data/spec/dummyapp/db/migrate/20121121184654_add_name_to_users.rb +0 -5
- data/spec/dummyapp/db/schema.rb +0 -35
- data/spec/dummyapp/db/seeds.rb +0 -12
- data/spec/dummyapp/lib/assets/.gitkeep +0 -0
- data/spec/dummyapp/public/404.html +0 -26
- data/spec/dummyapp/public/422.html +0 -26
- data/spec/dummyapp/public/500.html +0 -25
- data/spec/dummyapp/public/favicon.ico +0 -0
- data/spec/dummyapp/script/rails +0 -6
- data/spec/fixtures/file1 +0 -1
- data/spec/fixtures/file2 +0 -1
- data/spec/fixtures/payloads/message.json +0 -25
- data/spec/fixtures/payloads/sample.trace.json +0 -275
- data/spec/fixtures/payloads/sample.trace_chain.json +0 -530
- data/spec/generators/rollbar/rollbar_generator_spec.rb +0 -24
- data/spec/requests/home_spec.rb +0 -49
- data/spec/rollbar/active_job_spec.rb +0 -33
- data/spec/rollbar/configuration_spec.rb +0 -24
- data/spec/rollbar/delay/girl_friday_spec.rb +0 -41
- data/spec/rollbar/delay/resque_spec.rb +0 -37
- data/spec/rollbar/delay/thread_spec.rb +0 -27
- data/spec/rollbar/delayed_job/job_data.rb +0 -35
- data/spec/rollbar/delayed_job_spec.rb +0 -90
- data/spec/rollbar/encoding/encoder_spec.rb +0 -63
- data/spec/rollbar/js/frameworks/rails_spec.rb +0 -19
- data/spec/rollbar/js/middleware_spec.rb +0 -162
- data/spec/rollbar/json/oj_spec.rb +0 -18
- data/spec/rollbar/json_spec.rb +0 -110
- data/spec/rollbar/lazy_store_spec.rb +0 -99
- data/spec/rollbar/logger_proxy_spec.rb +0 -34
- data/spec/rollbar/middleware/rack/builder_spec.rb +0 -151
- data/spec/rollbar/middleware/sinatra_spec.rb +0 -197
- data/spec/rollbar/rake_spec.rb +0 -34
- data/spec/rollbar/request_data_extractor_spec.rb +0 -82
- data/spec/rollbar/scrubbers/url_spec.rb +0 -111
- data/spec/rollbar/sidekiq_spec.rb +0 -90
- data/spec/rollbar/truncation/frames_strategy_spec.rb +0 -70
- data/spec/rollbar/truncation/min_body_strategy_spec.rb +0 -57
- data/spec/rollbar/truncation/strings_strategy_spec.rb +0 -89
- data/spec/rollbar/truncation_spec.rb +0 -27
- data/spec/rollbar/util/hash_spec.rb +0 -22
- data/spec/rollbar/util_spec.rb +0 -19
- data/spec/rollbar_bc_spec.rb +0 -380
- data/spec/rollbar_spec.rb +0 -2067
- data/spec/spec_helper.rb +0 -49
- data/spec/support/cause_exception.rb +0 -1
- data/spec/support/encoding_helpers.rb +0 -8
- data/spec/support/encodings/iso_8859_9 +0 -1
- data/spec/support/fixture_helpers.rb +0 -10
- data/spec/support/get_ip_raising.rb +0 -7
- data/spec/support/helpers.rb +0 -5
- data/spec/support/notifier_helpers.rb +0 -36
- data/spec/support/shared_contexts.rb +0 -12
data/lib/rollbar/delay/thread.rb
CHANGED
@@ -1,15 +1,86 @@
|
|
1
|
+
require 'timeout'
|
2
|
+
|
1
3
|
module Rollbar
|
2
4
|
module Delay
|
3
5
|
class Thread
|
4
|
-
|
5
|
-
|
6
|
+
EXIT_SIGNAL = :exit
|
7
|
+
EXIT_TIMEOUT = 6
|
8
|
+
|
9
|
+
Error = Class.new(StandardError)
|
10
|
+
TimeoutError = Class.new(Error)
|
11
|
+
|
12
|
+
DEFAULT_PRIORITY = 1
|
13
|
+
|
14
|
+
class << self
|
15
|
+
attr_writer :options
|
16
|
+
attr_reader :reaper
|
17
|
+
|
18
|
+
def call(payload)
|
19
|
+
spawn_threads_reaper
|
20
|
+
|
21
|
+
thread = new.call(payload)
|
22
|
+
threads << thread
|
23
|
+
thread
|
24
|
+
end
|
25
|
+
|
26
|
+
def options
|
27
|
+
@options || {}
|
28
|
+
end
|
29
|
+
|
30
|
+
private
|
31
|
+
|
32
|
+
def threads
|
33
|
+
@threads ||= Queue.new
|
34
|
+
end
|
35
|
+
|
36
|
+
def spawn_threads_reaper
|
37
|
+
return if @spawned
|
38
|
+
|
39
|
+
@spawned = true
|
40
|
+
|
41
|
+
@reaper ||= build_reaper_thread
|
42
|
+
configure_exit_handler
|
43
|
+
end
|
44
|
+
|
45
|
+
def build_reaper_thread
|
46
|
+
::Thread.start do
|
47
|
+
loop do
|
48
|
+
thread = threads.pop
|
49
|
+
|
50
|
+
break if thread == EXIT_SIGNAL
|
51
|
+
|
52
|
+
thread.join
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
def configure_exit_handler
|
58
|
+
at_exit do
|
59
|
+
begin
|
60
|
+
Timeout.timeout(EXIT_TIMEOUT) do
|
61
|
+
threads << EXIT_SIGNAL
|
62
|
+
reaper.join
|
63
|
+
end
|
64
|
+
rescue Timeout::Error
|
65
|
+
raise TimeoutError,
|
66
|
+
"unable to reap all threads within #{EXIT_TIMEOUT} seconds"
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
def priority
|
73
|
+
self.class.options[:priority] || DEFAULT_PRIORITY
|
6
74
|
end
|
7
75
|
|
8
76
|
def call(payload)
|
77
|
+
priority = self.priority
|
78
|
+
|
9
79
|
::Thread.new do
|
10
80
|
begin
|
81
|
+
::Thread.current.priority = priority
|
11
82
|
Rollbar.process_from_async_handler(payload)
|
12
|
-
rescue
|
83
|
+
rescue StandardError
|
13
84
|
# Here we swallow the exception:
|
14
85
|
# 1. The original report wasn't sent.
|
15
86
|
# 2. An internal error was sent and logged
|
@@ -0,0 +1,91 @@
|
|
1
|
+
require 'json'
|
2
|
+
|
3
|
+
module Rollbar
|
4
|
+
# Deploy Tracking API wrapper module
|
5
|
+
module Deploy
|
6
|
+
ENDPOINT = 'https://api.rollbar.com/api/1/deploy/'.freeze
|
7
|
+
|
8
|
+
def self.report(opts, access_token, environment, revision)
|
9
|
+
return {} unless access_token && !access_token.empty?
|
10
|
+
|
11
|
+
opts[:status] ||= :started
|
12
|
+
|
13
|
+
uri = ::URI.parse(::Rollbar::Deploy::ENDPOINT)
|
14
|
+
|
15
|
+
request_data = {
|
16
|
+
:access_token => access_token,
|
17
|
+
:environment => environment,
|
18
|
+
:revision => revision
|
19
|
+
}.merge(opts)
|
20
|
+
request_data.delete(:proxy)
|
21
|
+
request_data.delete(:dry_run)
|
22
|
+
|
23
|
+
request = ::Net::HTTP::Post.new(uri.request_uri)
|
24
|
+
request.body = ::JSON.dump(request_data)
|
25
|
+
|
26
|
+
send_request(opts, uri, request)
|
27
|
+
end
|
28
|
+
|
29
|
+
def self.update(opts, access_token, deploy_id, status)
|
30
|
+
return {} unless access_token && !access_token.empty?
|
31
|
+
|
32
|
+
uri = ::URI.parse(
|
33
|
+
"#{::Rollbar::Deploy::ENDPOINT}#{deploy_id}?access_token=#{access_token}"
|
34
|
+
)
|
35
|
+
|
36
|
+
request = ::Net::HTTP::Patch.new(uri.request_uri)
|
37
|
+
request.body = ::JSON.dump(:status => status.to_s, :comment => opts[:comment])
|
38
|
+
|
39
|
+
send_request(opts, uri, request)
|
40
|
+
end
|
41
|
+
|
42
|
+
class << self
|
43
|
+
private
|
44
|
+
|
45
|
+
def send_request(opts, uri, request)
|
46
|
+
::Net::HTTP.start(uri.host, uri.port, opts[:proxy], :use_ssl => true) do |http|
|
47
|
+
build_result(
|
48
|
+
uri,
|
49
|
+
request,
|
50
|
+
opts[:dry_run] ? nil : http.request(request),
|
51
|
+
opts[:dry_run]
|
52
|
+
)
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
def build_result(uri, request, response = nil, dry_run = false)
|
57
|
+
result = {}
|
58
|
+
result.merge!(request_result(uri, request))
|
59
|
+
result.merge!(response_result(response)) unless response.nil?
|
60
|
+
result[:success] = success?(result, dry_run)
|
61
|
+
result
|
62
|
+
end
|
63
|
+
|
64
|
+
def success?(result, dry_run = false)
|
65
|
+
return true if dry_run
|
66
|
+
|
67
|
+
result[:response] &&
|
68
|
+
result[:response].is_a?(::Net::HTTPSuccess) &&
|
69
|
+
result[:response].code == '200' &&
|
70
|
+
(result.key?('err') ? result['err'].to_i.zero? : true)
|
71
|
+
end
|
72
|
+
|
73
|
+
def request_result(uri, request)
|
74
|
+
{
|
75
|
+
:request_info => "#{uri.inspect}: #{request.body}",
|
76
|
+
:request => request
|
77
|
+
}
|
78
|
+
end
|
79
|
+
|
80
|
+
def response_result(response)
|
81
|
+
code = response.code
|
82
|
+
message = response.message
|
83
|
+
body = response.body.delete("\n")
|
84
|
+
{
|
85
|
+
:response => response,
|
86
|
+
:response_info => "#{code}; #{message}; #{body}"
|
87
|
+
}.merge(::JSON.parse(response.body, :symbolize_names => true))
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
@@ -1,9 +1,10 @@
|
|
1
1
|
module Rollbar
|
2
2
|
module Encoding
|
3
3
|
class Encoder
|
4
|
-
ALL_ENCODINGS = [::Encoding::UTF_8, ::Encoding::ISO_8859_1, ::Encoding::ASCII_8BIT,
|
5
|
-
|
6
|
-
|
4
|
+
ALL_ENCODINGS = [::Encoding::UTF_8, ::Encoding::ISO_8859_1, ::Encoding::ASCII_8BIT,
|
5
|
+
::Encoding::US_ASCII].freeze
|
6
|
+
ASCII_ENCODINGS = [::Encoding::US_ASCII, ::Encoding::ASCII_8BIT,
|
7
|
+
::Encoding::ISO_8859_1].freeze
|
7
8
|
UTF8 = 'UTF-8'.freeze
|
8
9
|
BINARY = 'binary'.freeze
|
9
10
|
|
@@ -18,13 +19,22 @@ module Rollbar
|
|
18
19
|
encoding = value.encoding
|
19
20
|
|
20
21
|
# This will be most of cases so avoid force anything for them
|
21
|
-
if encoding == ::Encoding::UTF_8 && value.valid_encoding?
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
22
|
+
encoded_value = if encoding == ::Encoding::UTF_8 && value.valid_encoding?
|
23
|
+
value
|
24
|
+
else
|
25
|
+
force_encoding(value).encode(
|
26
|
+
*encoding_args(value),
|
27
|
+
# Ruby 2.7 requires this to look like keyword args,
|
28
|
+
# and Ruby 1.9.3 doesn't understand keyword args, so
|
29
|
+
# don't use hash rockets here and both will be happy.
|
30
|
+
invalid: :replace, undef: :replace, replace: '' # rubocop:disable Style/HashSyntax
|
31
|
+
)
|
32
|
+
end
|
26
33
|
|
27
34
|
object.is_a?(Symbol) ? encoded_value.to_sym : encoded_value
|
35
|
+
rescue StandardError => e
|
36
|
+
# If encoding fails for any reason, replace the string with a diagnostic error.
|
37
|
+
"error encoding string: #{e.class}: #{e.message}"
|
28
38
|
end
|
29
39
|
|
30
40
|
private
|
@@ -32,7 +42,9 @@ module Rollbar
|
|
32
42
|
def force_encoding(value)
|
33
43
|
return value if value.frozen?
|
34
44
|
|
35
|
-
|
45
|
+
if value.encoding == ::Encoding::UTF_8
|
46
|
+
value.force_encoding(detect_encoding(value))
|
47
|
+
end
|
36
48
|
|
37
49
|
value
|
38
50
|
end
|
@@ -45,7 +57,7 @@ module Rollbar
|
|
45
57
|
# Seems #codepoints is faster than #valid_encoding?
|
46
58
|
value.force_encoding(encoding).encode(::Encoding::UTF_8).codepoints
|
47
59
|
true
|
48
|
-
rescue
|
60
|
+
rescue StandardError
|
49
61
|
false
|
50
62
|
end
|
51
63
|
end
|
@@ -54,7 +66,6 @@ module Rollbar
|
|
54
66
|
def encoding_args(value)
|
55
67
|
args = [UTF8]
|
56
68
|
args << BINARY if ASCII_ENCODINGS.include?(value.encoding)
|
57
|
-
args << ENCODING_OPTIONS
|
58
69
|
|
59
70
|
args
|
60
71
|
end
|
data/lib/rollbar/encoding.rb
CHANGED
@@ -5,13 +5,8 @@ module Rollbar
|
|
5
5
|
end
|
6
6
|
|
7
7
|
def self.setup
|
8
|
-
|
9
|
-
|
10
|
-
self.encoding_class = Rollbar::Encoding::Encoder
|
11
|
-
else
|
12
|
-
require 'rollbar/encoding/legacy_encoder'
|
13
|
-
self.encoding_class = Rollbar::Encoding::LegacyEncoder
|
14
|
-
end
|
8
|
+
require 'rollbar/encoding/encoder'
|
9
|
+
self.encoding_class = Rollbar::Encoding::Encoder
|
15
10
|
end
|
16
11
|
|
17
12
|
def self.encode(object)
|
@@ -1,21 +1,45 @@
|
|
1
1
|
module Rollbar
|
2
|
-
module ExceptionReporter
|
2
|
+
module ExceptionReporter # :nodoc:
|
3
3
|
def report_exception_to_rollbar(env, exception)
|
4
|
-
|
5
|
-
|
4
|
+
return unless capture_uncaught?
|
5
|
+
|
6
|
+
log_exception_message(exception)
|
6
7
|
|
7
|
-
exception_data =
|
8
|
+
exception_data = exception_data(exception)
|
8
9
|
|
9
|
-
|
10
|
+
case exception_data
|
11
|
+
when Hash
|
10
12
|
env['rollbar.exception_uuid'] = exception_data[:uuid]
|
11
|
-
Rollbar.log_debug
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
Rollbar.log_debug
|
13
|
+
Rollbar.log_debug(
|
14
|
+
"[Rollbar] Exception uuid saved in env: #{exception_data[:uuid]}"
|
15
|
+
)
|
16
|
+
when 'disabled'
|
17
|
+
Rollbar.log_debug(
|
18
|
+
'[Rollbar] Exception not reported because Rollbar is disabled'
|
19
|
+
)
|
20
|
+
when 'ignored'
|
21
|
+
Rollbar.log_debug '[Rollbar] Exception not reported because it was ignored'
|
16
22
|
end
|
17
|
-
rescue => e
|
18
|
-
Rollbar.log_warning
|
23
|
+
rescue StandardError => e
|
24
|
+
Rollbar.log_warning(
|
25
|
+
"[Rollbar] Exception while reporting exception to Rollbar: #{e.message}"
|
26
|
+
)
|
27
|
+
end
|
28
|
+
|
29
|
+
def capture_uncaught?
|
30
|
+
Rollbar.configuration.capture_uncaught != false &&
|
31
|
+
!Rollbar.configuration.enable_rails_error_subscriber
|
32
|
+
end
|
33
|
+
|
34
|
+
def log_exception_message(exception)
|
35
|
+
exception_message = exception.message if exception.respond_to?(:message)
|
36
|
+
exception_message ||= 'No Exception Message'
|
37
|
+
Rollbar.log_debug "[Rollbar] Reporting exception: #{exception_message}"
|
38
|
+
end
|
39
|
+
|
40
|
+
def exception_data(exception)
|
41
|
+
Rollbar.log(Rollbar.configuration.uncaught_exception_level, exception,
|
42
|
+
:use_exception_level_filters => true)
|
19
43
|
end
|
20
44
|
end
|
21
45
|
end
|
@@ -0,0 +1,118 @@
|
|
1
|
+
require 'rollbar/item/frame'
|
2
|
+
|
3
|
+
module Rollbar
|
4
|
+
class Item
|
5
|
+
class Backtrace
|
6
|
+
attr_reader :exception, :message, :extra, :configuration, :files
|
7
|
+
|
8
|
+
private :files
|
9
|
+
|
10
|
+
def initialize(exception, options = {})
|
11
|
+
@exception = exception
|
12
|
+
@message = options[:message]
|
13
|
+
@extra = options[:extra]
|
14
|
+
@configuration = options[:configuration]
|
15
|
+
@files = {}
|
16
|
+
end
|
17
|
+
|
18
|
+
def to_h
|
19
|
+
traces = trace_chain
|
20
|
+
|
21
|
+
traces[0][:exception][:description] = message if message
|
22
|
+
traces[0][:extra] = extra if extra
|
23
|
+
|
24
|
+
if traces.size > 1
|
25
|
+
{ :trace_chain => traces }
|
26
|
+
elsif traces.size == 1
|
27
|
+
{ :trace => traces[0] }
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
alias build to_h
|
32
|
+
|
33
|
+
def get_file_lines(filename)
|
34
|
+
files[filename] ||= read_file(filename)
|
35
|
+
end
|
36
|
+
|
37
|
+
private
|
38
|
+
|
39
|
+
def read_file(filename)
|
40
|
+
return unless File.exist?(filename)
|
41
|
+
|
42
|
+
File.read(filename).split("\n")
|
43
|
+
rescue StandardError
|
44
|
+
nil
|
45
|
+
end
|
46
|
+
|
47
|
+
def trace_chain
|
48
|
+
traces = [trace_data(exception)]
|
49
|
+
visited = [exception]
|
50
|
+
|
51
|
+
current_exception = exception
|
52
|
+
|
53
|
+
while current_exception.respond_to?(:cause) &&
|
54
|
+
(cause = current_exception.cause) &&
|
55
|
+
cause.is_a?(Exception) &&
|
56
|
+
!visited.include?(cause)
|
57
|
+
traces << trace_data(cause)
|
58
|
+
visited << cause
|
59
|
+
current_exception = cause
|
60
|
+
end
|
61
|
+
|
62
|
+
traces
|
63
|
+
end
|
64
|
+
|
65
|
+
def trace_data(current_exception)
|
66
|
+
{
|
67
|
+
:frames => map_frames(current_exception),
|
68
|
+
:exception => {
|
69
|
+
:class => current_exception.class.name,
|
70
|
+
:message => current_exception.message
|
71
|
+
}
|
72
|
+
}
|
73
|
+
end
|
74
|
+
|
75
|
+
def map_frames(current_exception)
|
76
|
+
frames = cleaned_backtrace(current_exception).map do |frame|
|
77
|
+
Rollbar::Item::Frame.new(self, frame,
|
78
|
+
:configuration => configuration).to_h
|
79
|
+
end
|
80
|
+
frames.reverse!
|
81
|
+
end
|
82
|
+
|
83
|
+
def cleaned_backtrace(current_exception)
|
84
|
+
normalized_backtrace = exception_backtrace(current_exception)
|
85
|
+
if configuration.backtrace_cleaner
|
86
|
+
configuration.backtrace_cleaner.clean(normalized_backtrace)
|
87
|
+
else
|
88
|
+
normalized_backtrace
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
# Returns the backtrace to be sent to our API. There are 3 options:
|
93
|
+
#
|
94
|
+
# 1. The exception received has a backtrace, then that backtrace is returned.
|
95
|
+
# 2. configuration.populate_empty_backtraces is disabled, we return [] here
|
96
|
+
# 3. The user has configuration.populate_empty_backtraces is enabled, then:
|
97
|
+
#
|
98
|
+
# We want to send the caller as backtrace, but the first lines of that array
|
99
|
+
# are those from the user's Rollbar.error line until this method. We want
|
100
|
+
# to remove those lines.
|
101
|
+
def exception_backtrace(current_exception)
|
102
|
+
if current_exception.backtrace.respond_to?(:map)
|
103
|
+
return current_exception.backtrace
|
104
|
+
end
|
105
|
+
return [] unless configuration.populate_empty_backtraces
|
106
|
+
|
107
|
+
caller_backtrace = caller
|
108
|
+
caller_backtrace.shift while caller_backtrace[0].include?(rollbar_lib_gem_dir)
|
109
|
+
caller_backtrace
|
110
|
+
end
|
111
|
+
|
112
|
+
def rollbar_lib_gem_dir
|
113
|
+
gem_dir = Gem::Specification.find_by_name('rollbar').gem_dir
|
114
|
+
"#{gem_dir}/lib"
|
115
|
+
end
|
116
|
+
end
|
117
|
+
end
|
118
|
+
end
|
@@ -0,0 +1,121 @@
|
|
1
|
+
# We want to use Gem.path
|
2
|
+
require 'rubygems'
|
3
|
+
require 'rollbar/item/locals'
|
4
|
+
|
5
|
+
module Rollbar
|
6
|
+
class Item
|
7
|
+
# Representation of the trace data per frame in the payload
|
8
|
+
class Frame
|
9
|
+
attr_reader :backtrace, :frame, :configuration
|
10
|
+
|
11
|
+
MAX_CONTEXT_LENGTH = 4
|
12
|
+
|
13
|
+
def initialize(backtrace, frame, options = {})
|
14
|
+
@backtrace = backtrace
|
15
|
+
@frame = frame
|
16
|
+
@configuration = options[:configuration]
|
17
|
+
end
|
18
|
+
|
19
|
+
def to_h
|
20
|
+
# parse the line
|
21
|
+
match = frame.match(/(.*):(\d+)(?::in `([^']+)')?/)
|
22
|
+
|
23
|
+
return unknown_frame unless match
|
24
|
+
|
25
|
+
filename = match[1]
|
26
|
+
lineno = match[2].to_i
|
27
|
+
frame_data = {
|
28
|
+
:filename => filename,
|
29
|
+
:lineno => lineno,
|
30
|
+
:method => match[3]
|
31
|
+
}
|
32
|
+
|
33
|
+
frame_data.merge(extra_frame_data(filename, lineno))
|
34
|
+
end
|
35
|
+
|
36
|
+
private
|
37
|
+
|
38
|
+
def unknown_frame
|
39
|
+
{ :filename => '<unknown>', :lineno => 0, :method => frame }
|
40
|
+
end
|
41
|
+
|
42
|
+
def extra_frame_data(filename, lineno)
|
43
|
+
file_lines = backtrace.get_file_lines(filename)
|
44
|
+
|
45
|
+
return {} if skip_extra_frame_data?(filename, file_lines)
|
46
|
+
|
47
|
+
{
|
48
|
+
:code => code_data(file_lines, lineno),
|
49
|
+
:context => context_data(file_lines, lineno),
|
50
|
+
:locals => locals_data(filename, lineno)
|
51
|
+
}
|
52
|
+
end
|
53
|
+
|
54
|
+
def skip_extra_frame_data?(filename, file_lines)
|
55
|
+
config = configuration.send_extra_frame_data
|
56
|
+
missing_file_lines = !file_lines || file_lines.empty?
|
57
|
+
|
58
|
+
return false if !missing_file_lines && config == :all
|
59
|
+
|
60
|
+
missing_file_lines ||
|
61
|
+
config == :none ||
|
62
|
+
config == :app && outside_project?(filename)
|
63
|
+
end
|
64
|
+
|
65
|
+
def outside_project?(filename)
|
66
|
+
project_gem_paths = configuration.project_gem_paths
|
67
|
+
inside_project_gem_paths = project_gem_paths.any? do |path|
|
68
|
+
filename.start_with?(path)
|
69
|
+
end
|
70
|
+
|
71
|
+
# The file is inside the configuration.project_gem_paths,
|
72
|
+
return false if inside_project_gem_paths
|
73
|
+
|
74
|
+
root = configuration.root
|
75
|
+
inside_root = root && filename.start_with?(root.to_s)
|
76
|
+
|
77
|
+
# The file is outside the configuration.root
|
78
|
+
return true unless inside_root
|
79
|
+
|
80
|
+
# At this point, the file is inside the configuration.root.
|
81
|
+
# Since it's common to have gems installed in {root}/vendor/bundle,
|
82
|
+
# let's check it's in any of the Gem.path paths
|
83
|
+
Gem.path.any? { |path| filename.start_with?(path) }
|
84
|
+
end
|
85
|
+
|
86
|
+
def code_data(file_lines, lineno)
|
87
|
+
file_lines[lineno - 1]
|
88
|
+
end
|
89
|
+
|
90
|
+
def context_data(file_lines, lineno)
|
91
|
+
{
|
92
|
+
:pre => pre_data(file_lines, lineno),
|
93
|
+
:post => post_data(file_lines, lineno)
|
94
|
+
}
|
95
|
+
end
|
96
|
+
|
97
|
+
def locals_data(filename, lineno)
|
98
|
+
return unless configuration.locals[:enabled]
|
99
|
+
|
100
|
+
::Rollbar::Item::Locals.locals_for_location(filename, lineno)
|
101
|
+
end
|
102
|
+
|
103
|
+
def post_data(file_lines, lineno)
|
104
|
+
from_line = lineno
|
105
|
+
number_of_lines = [from_line + MAX_CONTEXT_LENGTH,
|
106
|
+
file_lines.size].min - from_line
|
107
|
+
|
108
|
+
file_lines[from_line, number_of_lines]
|
109
|
+
end
|
110
|
+
|
111
|
+
def pre_data(file_lines, lineno)
|
112
|
+
to_line = lineno - 2
|
113
|
+
from_line = [to_line - MAX_CONTEXT_LENGTH + 1, 0].max
|
114
|
+
|
115
|
+
file_lines[from_line, (to_line - from_line + 1)].select do |line|
|
116
|
+
line && !line.empty?
|
117
|
+
end
|
118
|
+
end
|
119
|
+
end
|
120
|
+
end
|
121
|
+
end
|
@@ -0,0 +1,103 @@
|
|
1
|
+
require 'rollbar/scrubbers/params'
|
2
|
+
require 'rollbar/util'
|
3
|
+
|
4
|
+
module Rollbar
|
5
|
+
class Item
|
6
|
+
class Locals # :nodoc:
|
7
|
+
class << self
|
8
|
+
def exception_frames
|
9
|
+
Rollbar.notifier.exception_bindings
|
10
|
+
end
|
11
|
+
|
12
|
+
def locals_for_location(filename, lineno)
|
13
|
+
if (frame = frame_for_location(filename, lineno))
|
14
|
+
scrub(locals_for(frame[:binding]))
|
15
|
+
else
|
16
|
+
{}
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
def frame_for_location(filename, lineno)
|
21
|
+
while (frame = exception_frames.pop)
|
22
|
+
return nil unless frame
|
23
|
+
return frame if matching_frame?(frame, filename, lineno)
|
24
|
+
end
|
25
|
+
nil
|
26
|
+
end
|
27
|
+
|
28
|
+
private
|
29
|
+
|
30
|
+
def matching_frame?(frame, filename, lineno)
|
31
|
+
frame[:path] == filename && frame[:lineno].to_i <= lineno.to_i
|
32
|
+
end
|
33
|
+
|
34
|
+
def locals_for(frame)
|
35
|
+
return {} unless frame
|
36
|
+
|
37
|
+
{}.tap do |hash|
|
38
|
+
frame.local_variables.map do |var|
|
39
|
+
hash[var] = prepare_value(frame.local_variable_get(var))
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
# Prepare objects to be handled by the payload serializer.
|
45
|
+
#
|
46
|
+
# Hashes and Arrays are traversed. Then all types execpt strings and
|
47
|
+
# immediates are exported using #inspect. Sending the object itself to the
|
48
|
+
# serializer can result in large recursive expansions, especially in Rails
|
49
|
+
# environments with ActiveRecord, ActiveSupport, etc. on the stack.
|
50
|
+
# Other export options could be #to_s, #to_h, and #as_json. Several of these
|
51
|
+
# will omit the class name, or are not implemented for many types.
|
52
|
+
#
|
53
|
+
# #inspect has the advantage that it is specifically intended for debugging
|
54
|
+
# output. If the user wants more or different information in the payload
|
55
|
+
# about a specific type, #inspect is the correct place to implement it.
|
56
|
+
# Likewise the default implementation should be oriented toward usefulness
|
57
|
+
# in debugging.
|
58
|
+
#
|
59
|
+
# Because #inspect outputs a string, it can be handled well by the string
|
60
|
+
# truncation strategy for large payloads.
|
61
|
+
#
|
62
|
+
def prepare_value(value)
|
63
|
+
unless value.is_a?(Hash) || value.is_a?(Array)
|
64
|
+
return simple_value?(value) ? value : value.inspect
|
65
|
+
end
|
66
|
+
|
67
|
+
cloned_value = ::Rollbar::Util.deep_copy(value)
|
68
|
+
::Rollbar::Util.iterate_and_update_with_block(cloned_value) do |v|
|
69
|
+
simple_value?(v) ? v : v.inspect
|
70
|
+
end
|
71
|
+
|
72
|
+
cloned_value
|
73
|
+
end
|
74
|
+
|
75
|
+
def simple_classes
|
76
|
+
if Gem::Version.new(RUBY_VERSION) >= Gem::Version.new('2.4.0')
|
77
|
+
[String, Symbol, Integer, Float, TrueClass, FalseClass, NilClass]
|
78
|
+
else
|
79
|
+
[String, Symbol, Fixnum, Bignum, Float, TrueClass, FalseClass, NilClass] # rubocop:disable Lint/UnifiedInteger
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
def simple_value?(value)
|
84
|
+
simple_classes.each do |klass|
|
85
|
+
# Use instance_of? so that subclasses and module containers will
|
86
|
+
# be treated like complex object types, not simple values.
|
87
|
+
return true if value.instance_of?(klass)
|
88
|
+
end
|
89
|
+
|
90
|
+
false
|
91
|
+
end
|
92
|
+
|
93
|
+
def scrub(hash)
|
94
|
+
Rollbar::Scrubbers::Params.call(
|
95
|
+
:params => hash,
|
96
|
+
:config => Rollbar.configuration.scrub_fields,
|
97
|
+
:whitelist => Rollbar.configuration.scrub_whitelist
|
98
|
+
)
|
99
|
+
end
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|
103
|
+
end
|