bugsnag 5.5.0 → 6.0.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/.travis.yml +33 -11
- data/CHANGELOG.md +23 -0
- data/Gemfile +20 -0
- data/Rakefile +19 -12
- data/UPGRADING.md +58 -0
- data/VERSION +1 -1
- data/bugsnag.gemspec +0 -9
- data/lib/bugsnag.rb +64 -86
- data/lib/bugsnag/configuration.rb +42 -26
- data/lib/bugsnag/delivery.rb +9 -0
- data/lib/bugsnag/delivery/synchronous.rb +3 -5
- data/lib/bugsnag/delivery/thread_queue.rb +4 -2
- data/lib/bugsnag/helpers.rb +5 -16
- data/lib/bugsnag/{delayed_job.rb → integrations/delayed_job.rb} +15 -7
- data/lib/bugsnag/{mailman.rb → integrations/mailman.rb} +13 -11
- data/lib/bugsnag/{que.rb → integrations/que.rb} +11 -11
- data/lib/bugsnag/{rack.rb → integrations/rack.rb} +22 -23
- data/lib/bugsnag/{rails → integrations/rails}/active_record_rescue.rb +9 -7
- data/lib/bugsnag/{rails → integrations/rails}/controller_methods.rb +0 -9
- data/lib/bugsnag/{railtie.rb → integrations/railtie.rb} +24 -21
- data/lib/bugsnag/{rake.rb → integrations/rake.rb} +12 -9
- data/lib/bugsnag/{resque.rb → integrations/resque.rb} +12 -9
- data/lib/bugsnag/integrations/shoryuken.rb +49 -0
- data/lib/bugsnag/{sidekiq.rb → integrations/sidekiq.rb} +13 -9
- data/lib/bugsnag/middleware/callbacks.rb +4 -8
- data/lib/bugsnag/middleware/classify_error.rb +7 -13
- data/lib/bugsnag/middleware/clearance_user.rb +8 -8
- data/lib/bugsnag/middleware/exception_meta_data.rb +34 -0
- data/lib/bugsnag/middleware/ignore_error_class.rb +21 -0
- data/lib/bugsnag/middleware/mailman.rb +4 -4
- data/lib/bugsnag/middleware/rack_request.rb +13 -13
- data/lib/bugsnag/middleware/rails3_request.rb +10 -10
- data/lib/bugsnag/middleware/rake.rb +5 -5
- data/lib/bugsnag/middleware/sidekiq.rb +5 -5
- data/lib/bugsnag/middleware/suggestion_data.rb +30 -0
- data/lib/bugsnag/middleware/warden_user.rb +6 -6
- data/lib/bugsnag/middleware_stack.rb +5 -5
- data/lib/bugsnag/report.rb +187 -0
- data/lib/bugsnag/stacktrace.rb +113 -0
- data/lib/bugsnag/tasks/bugsnag.rake +2 -70
- data/spec/cleaner_spec.rb +6 -0
- data/spec/configuration_spec.rb +1 -1
- data/spec/fixtures/middleware/internal_info_setter.rb +3 -3
- data/spec/fixtures/middleware/public_info_setter.rb +3 -3
- data/spec/fixtures/tasks/Rakefile +2 -3
- data/spec/integration_spec.rb +5 -20
- data/spec/{delayed_job_spec.rb → integrations/delayed_job_spec.rb} +0 -0
- data/spec/integrations/sidekiq_spec.rb +34 -0
- data/spec/middleware_spec.rb +108 -35
- data/spec/rack_spec.rb +1 -1
- data/spec/{notification_spec.rb → report_spec.rb} +226 -209
- data/spec/spec_helper.rb +18 -0
- data/spec/{code_spec.rb → stacktrace_spec.rb} +1 -1
- metadata +23 -139
- data/.document +0 -5
- data/lib/bugsnag/capistrano.rb +0 -7
- data/lib/bugsnag/capistrano2.rb +0 -32
- data/lib/bugsnag/delay/resque.rb +0 -21
- data/lib/bugsnag/deploy.rb +0 -35
- data/lib/bugsnag/middleware/rails2_request.rb +0 -52
- data/lib/bugsnag/notification.rb +0 -506
- data/lib/bugsnag/rails.rb +0 -70
- data/lib/bugsnag/rails/action_controller_rescue.rb +0 -74
- data/lib/bugsnag/shoryuken.rb +0 -41
- data/lib/bugsnag/tasks/bugsnag.cap +0 -48
- data/rails/init.rb +0 -7
data/spec/spec_helper.rb
CHANGED
@@ -1,3 +1,13 @@
|
|
1
|
+
if ARGV.include? "--coverage"
|
2
|
+
require 'simplecov'
|
3
|
+
require 'coveralls'
|
4
|
+
|
5
|
+
SimpleCov.formatter = Coveralls::SimpleCov::Formatter
|
6
|
+
SimpleCov.start do
|
7
|
+
add_filter 'spec'
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
1
11
|
require 'bugsnag'
|
2
12
|
|
3
13
|
require 'webmock/rspec'
|
@@ -20,6 +30,14 @@ def notify_test_exception(*args)
|
|
20
30
|
Bugsnag.notify(RuntimeError.new("test message"), *args)
|
21
31
|
end
|
22
32
|
|
33
|
+
def ruby_version_greater_equal?(version)
|
34
|
+
current_version = RUBY_VERSION.split "."
|
35
|
+
target_version = version.split "."
|
36
|
+
(Integer(current_version[0]) >= Integer(target_version[0])) &&
|
37
|
+
(Integer(current_version[1]) >= Integer(target_version[1])) &&
|
38
|
+
(Integer(current_version[2]) >= Integer(target_version[2]))
|
39
|
+
end
|
40
|
+
|
23
41
|
RSpec.configure do |config|
|
24
42
|
config.order = "random"
|
25
43
|
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: bugsnag
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version:
|
4
|
+
version: 6.0.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- James Smith
|
@@ -9,119 +9,7 @@ autorequire:
|
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
11
|
date: 2017-11-09 00:00:00.000000000 Z
|
12
|
-
dependencies:
|
13
|
-
- !ruby/object:Gem::Dependency
|
14
|
-
name: rake
|
15
|
-
requirement: !ruby/object:Gem::Requirement
|
16
|
-
requirements:
|
17
|
-
- - "~>"
|
18
|
-
- !ruby/object:Gem::Version
|
19
|
-
version: 10.1.1
|
20
|
-
type: :development
|
21
|
-
prerelease: false
|
22
|
-
version_requirements: !ruby/object:Gem::Requirement
|
23
|
-
requirements:
|
24
|
-
- - "~>"
|
25
|
-
- !ruby/object:Gem::Version
|
26
|
-
version: 10.1.1
|
27
|
-
- !ruby/object:Gem::Dependency
|
28
|
-
name: rspec
|
29
|
-
requirement: !ruby/object:Gem::Requirement
|
30
|
-
requirements:
|
31
|
-
- - ">="
|
32
|
-
- !ruby/object:Gem::Version
|
33
|
-
version: '0'
|
34
|
-
type: :development
|
35
|
-
prerelease: false
|
36
|
-
version_requirements: !ruby/object:Gem::Requirement
|
37
|
-
requirements:
|
38
|
-
- - ">="
|
39
|
-
- !ruby/object:Gem::Version
|
40
|
-
version: '0'
|
41
|
-
- !ruby/object:Gem::Dependency
|
42
|
-
name: rdoc
|
43
|
-
requirement: !ruby/object:Gem::Requirement
|
44
|
-
requirements:
|
45
|
-
- - ">="
|
46
|
-
- !ruby/object:Gem::Version
|
47
|
-
version: '0'
|
48
|
-
type: :development
|
49
|
-
prerelease: false
|
50
|
-
version_requirements: !ruby/object:Gem::Requirement
|
51
|
-
requirements:
|
52
|
-
- - ">="
|
53
|
-
- !ruby/object:Gem::Version
|
54
|
-
version: '0'
|
55
|
-
- !ruby/object:Gem::Dependency
|
56
|
-
name: pry
|
57
|
-
requirement: !ruby/object:Gem::Requirement
|
58
|
-
requirements:
|
59
|
-
- - ">="
|
60
|
-
- !ruby/object:Gem::Version
|
61
|
-
version: '0'
|
62
|
-
type: :development
|
63
|
-
prerelease: false
|
64
|
-
version_requirements: !ruby/object:Gem::Requirement
|
65
|
-
requirements:
|
66
|
-
- - ">="
|
67
|
-
- !ruby/object:Gem::Version
|
68
|
-
version: '0'
|
69
|
-
- !ruby/object:Gem::Dependency
|
70
|
-
name: addressable
|
71
|
-
requirement: !ruby/object:Gem::Requirement
|
72
|
-
requirements:
|
73
|
-
- - "~>"
|
74
|
-
- !ruby/object:Gem::Version
|
75
|
-
version: 2.3.8
|
76
|
-
type: :development
|
77
|
-
prerelease: false
|
78
|
-
version_requirements: !ruby/object:Gem::Requirement
|
79
|
-
requirements:
|
80
|
-
- - "~>"
|
81
|
-
- !ruby/object:Gem::Version
|
82
|
-
version: 2.3.8
|
83
|
-
- !ruby/object:Gem::Dependency
|
84
|
-
name: webmock
|
85
|
-
requirement: !ruby/object:Gem::Requirement
|
86
|
-
requirements:
|
87
|
-
- - '='
|
88
|
-
- !ruby/object:Gem::Version
|
89
|
-
version: 2.1.0
|
90
|
-
type: :development
|
91
|
-
prerelease: false
|
92
|
-
version_requirements: !ruby/object:Gem::Requirement
|
93
|
-
requirements:
|
94
|
-
- - '='
|
95
|
-
- !ruby/object:Gem::Version
|
96
|
-
version: 2.1.0
|
97
|
-
- !ruby/object:Gem::Dependency
|
98
|
-
name: delayed_job
|
99
|
-
requirement: !ruby/object:Gem::Requirement
|
100
|
-
requirements:
|
101
|
-
- - ">="
|
102
|
-
- !ruby/object:Gem::Version
|
103
|
-
version: '0'
|
104
|
-
type: :development
|
105
|
-
prerelease: false
|
106
|
-
version_requirements: !ruby/object:Gem::Requirement
|
107
|
-
requirements:
|
108
|
-
- - ">="
|
109
|
-
- !ruby/object:Gem::Version
|
110
|
-
version: '0'
|
111
|
-
- !ruby/object:Gem::Dependency
|
112
|
-
name: activesupport
|
113
|
-
requirement: !ruby/object:Gem::Requirement
|
114
|
-
requirements:
|
115
|
-
- - "~>"
|
116
|
-
- !ruby/object:Gem::Version
|
117
|
-
version: 4.2.10
|
118
|
-
type: :development
|
119
|
-
prerelease: false
|
120
|
-
version_requirements: !ruby/object:Gem::Requirement
|
121
|
-
requirements:
|
122
|
-
- - "~>"
|
123
|
-
- !ruby/object:Gem::Version
|
124
|
-
version: 4.2.10
|
12
|
+
dependencies: []
|
125
13
|
description: Ruby notifier for bugsnag.com
|
126
14
|
email: james@bugsnag.com
|
127
15
|
executables: []
|
@@ -131,7 +19,6 @@ extra_rdoc_files:
|
|
131
19
|
- README.md
|
132
20
|
- CHANGELOG.md
|
133
21
|
files:
|
134
|
-
- ".document"
|
135
22
|
- ".gitignore"
|
136
23
|
- ".rspec"
|
137
24
|
- ".travis.yml"
|
@@ -141,56 +28,50 @@ files:
|
|
141
28
|
- LICENSE.txt
|
142
29
|
- README.md
|
143
30
|
- Rakefile
|
31
|
+
- UPGRADING.md
|
144
32
|
- VERSION
|
145
33
|
- bugsnag.gemspec
|
146
34
|
- issue_template.md
|
147
35
|
- lib/bugsnag.rb
|
148
|
-
- lib/bugsnag/capistrano.rb
|
149
|
-
- lib/bugsnag/capistrano2.rb
|
150
36
|
- lib/bugsnag/cleaner.rb
|
151
37
|
- lib/bugsnag/configuration.rb
|
152
|
-
- lib/bugsnag/delay/resque.rb
|
153
|
-
- lib/bugsnag/delayed_job.rb
|
154
38
|
- lib/bugsnag/delivery.rb
|
155
39
|
- lib/bugsnag/delivery/synchronous.rb
|
156
40
|
- lib/bugsnag/delivery/thread_queue.rb
|
157
|
-
- lib/bugsnag/deploy.rb
|
158
41
|
- lib/bugsnag/helpers.rb
|
159
|
-
- lib/bugsnag/
|
42
|
+
- lib/bugsnag/integrations/delayed_job.rb
|
43
|
+
- lib/bugsnag/integrations/mailman.rb
|
44
|
+
- lib/bugsnag/integrations/que.rb
|
45
|
+
- lib/bugsnag/integrations/rack.rb
|
46
|
+
- lib/bugsnag/integrations/rails/active_record_rescue.rb
|
47
|
+
- lib/bugsnag/integrations/rails/controller_methods.rb
|
48
|
+
- lib/bugsnag/integrations/railtie.rb
|
49
|
+
- lib/bugsnag/integrations/rake.rb
|
50
|
+
- lib/bugsnag/integrations/resque.rb
|
51
|
+
- lib/bugsnag/integrations/shoryuken.rb
|
52
|
+
- lib/bugsnag/integrations/sidekiq.rb
|
160
53
|
- lib/bugsnag/meta_data.rb
|
161
54
|
- lib/bugsnag/middleware/callbacks.rb
|
162
55
|
- lib/bugsnag/middleware/classify_error.rb
|
163
56
|
- lib/bugsnag/middleware/clearance_user.rb
|
57
|
+
- lib/bugsnag/middleware/exception_meta_data.rb
|
58
|
+
- lib/bugsnag/middleware/ignore_error_class.rb
|
164
59
|
- lib/bugsnag/middleware/mailman.rb
|
165
60
|
- lib/bugsnag/middleware/rack_request.rb
|
166
|
-
- lib/bugsnag/middleware/rails2_request.rb
|
167
61
|
- lib/bugsnag/middleware/rails3_request.rb
|
168
62
|
- lib/bugsnag/middleware/rake.rb
|
169
63
|
- lib/bugsnag/middleware/sidekiq.rb
|
64
|
+
- lib/bugsnag/middleware/suggestion_data.rb
|
170
65
|
- lib/bugsnag/middleware/warden_user.rb
|
171
66
|
- lib/bugsnag/middleware_stack.rb
|
172
|
-
- lib/bugsnag/
|
173
|
-
- lib/bugsnag/
|
174
|
-
- lib/bugsnag/rack.rb
|
175
|
-
- lib/bugsnag/rails.rb
|
176
|
-
- lib/bugsnag/rails/action_controller_rescue.rb
|
177
|
-
- lib/bugsnag/rails/active_record_rescue.rb
|
178
|
-
- lib/bugsnag/rails/controller_methods.rb
|
179
|
-
- lib/bugsnag/railtie.rb
|
180
|
-
- lib/bugsnag/rake.rb
|
181
|
-
- lib/bugsnag/resque.rb
|
182
|
-
- lib/bugsnag/shoryuken.rb
|
183
|
-
- lib/bugsnag/sidekiq.rb
|
67
|
+
- lib/bugsnag/report.rb
|
68
|
+
- lib/bugsnag/stacktrace.rb
|
184
69
|
- lib/bugsnag/tasks.rb
|
185
|
-
- lib/bugsnag/tasks/bugsnag.cap
|
186
70
|
- lib/bugsnag/tasks/bugsnag.rake
|
187
71
|
- lib/bugsnag/version.rb
|
188
72
|
- lib/generators/bugsnag/bugsnag_generator.rb
|
189
|
-
- rails/init.rb
|
190
73
|
- spec/cleaner_spec.rb
|
191
|
-
- spec/code_spec.rb
|
192
74
|
- spec/configuration_spec.rb
|
193
|
-
- spec/delayed_job_spec.rb
|
194
75
|
- spec/fixtures/crashes/end_of_file.rb
|
195
76
|
- spec/fixtures/crashes/short_file.rb
|
196
77
|
- spec/fixtures/crashes/start_of_file.rb
|
@@ -199,11 +80,14 @@ files:
|
|
199
80
|
- spec/fixtures/tasks/Rakefile
|
200
81
|
- spec/helper_spec.rb
|
201
82
|
- spec/integration_spec.rb
|
83
|
+
- spec/integrations/delayed_job_spec.rb
|
84
|
+
- spec/integrations/sidekiq_spec.rb
|
202
85
|
- spec/middleware_spec.rb
|
203
|
-
- spec/notification_spec.rb
|
204
86
|
- spec/rack_spec.rb
|
205
87
|
- spec/rails3_request_spec.rb
|
88
|
+
- spec/report_spec.rb
|
206
89
|
- spec/spec_helper.rb
|
90
|
+
- spec/stacktrace_spec.rb
|
207
91
|
homepage: http://github.com/bugsnag/bugsnag-ruby
|
208
92
|
licenses:
|
209
93
|
- MIT
|
data/.document
DELETED
data/lib/bugsnag/capistrano.rb
DELETED
data/lib/bugsnag/capistrano2.rb
DELETED
@@ -1,32 +0,0 @@
|
|
1
|
-
module Bugsnag
|
2
|
-
module Capistrano
|
3
|
-
def self.load_into(configuration)
|
4
|
-
configuration.load do
|
5
|
-
after "deploy", "bugsnag:deploy"
|
6
|
-
after "deploy:migrations", "bugsnag:deploy"
|
7
|
-
|
8
|
-
namespace :bugsnag do
|
9
|
-
desc "Notify Bugsnag that new production code has been deployed"
|
10
|
-
task :deploy, :except => { :no_release => true }, :on_error => :continue do
|
11
|
-
begin
|
12
|
-
Bugsnag::Deploy.notify({
|
13
|
-
:api_key => fetch(:bugsnag_api_key, ENV["BUGSNAG_API_KEY"]),
|
14
|
-
:release_stage => ENV["BUGSNAG_RELEASE_STAGE"] || fetch(:rails_env, "production"),
|
15
|
-
:revision => fetch(:current_revision, ENV["BUGSNAG_REVISION"]),
|
16
|
-
:repository => fetch(:repository, ENV["BUGSNAG_REPOSITORY"]),
|
17
|
-
:branch => fetch(:branch, ENV["BUGSNAG_BRANCH"],
|
18
|
-
:app_version => fetch(:app_version, ENV["BUGSNAG_APP_VERSION"]))
|
19
|
-
})
|
20
|
-
rescue
|
21
|
-
logger.important("Bugsnag deploy notification failed, #{$!.inspect}")
|
22
|
-
end
|
23
|
-
|
24
|
-
logger.info "Bugsnag deploy notification complete."
|
25
|
-
end
|
26
|
-
end
|
27
|
-
end
|
28
|
-
end
|
29
|
-
end
|
30
|
-
end
|
31
|
-
|
32
|
-
Bugsnag::Capistrano.load_into(Capistrano::Configuration.instance) if Capistrano::Configuration.instance
|
data/lib/bugsnag/delay/resque.rb
DELETED
@@ -1,21 +0,0 @@
|
|
1
|
-
module Bugsnag
|
2
|
-
module Delay
|
3
|
-
class Resque
|
4
|
-
@queue = "bugsnag"
|
5
|
-
def self.perform(*args)
|
6
|
-
Bugsnag::Notification.deliver_exception_payload_without_resque(*args)
|
7
|
-
end
|
8
|
-
end
|
9
|
-
end
|
10
|
-
end
|
11
|
-
|
12
|
-
Bugsnag::Notification.class_eval do
|
13
|
-
class << self
|
14
|
-
def deliver_exception_payload_with_resque(*args)
|
15
|
-
Resque.enqueue(Bugsnag::Delay::Resque, *args)
|
16
|
-
end
|
17
|
-
|
18
|
-
alias_method :deliver_exception_payload_without_resque, :deliver_exception_payload
|
19
|
-
alias_method :deliver_exception_payload, :deliver_exception_payload_with_resque
|
20
|
-
end
|
21
|
-
end
|
data/lib/bugsnag/deploy.rb
DELETED
@@ -1,35 +0,0 @@
|
|
1
|
-
require "json"
|
2
|
-
|
3
|
-
module Bugsnag
|
4
|
-
class Deploy
|
5
|
-
def self.notify(opts = {})
|
6
|
-
|
7
|
-
configuration = Bugsnag.configuration.dup
|
8
|
-
|
9
|
-
# update configuration based on parameters passed in
|
10
|
-
[:api_key, :app_version, :release_stage, :endpoint, :use_ssl,
|
11
|
-
:proxy_host, :proxy_port, :proxy_user, :proxy_password].each do |param|
|
12
|
-
unless opts[param].nil?
|
13
|
-
configuration.send :"#{param}=", opts[param]
|
14
|
-
end
|
15
|
-
end
|
16
|
-
|
17
|
-
endpoint = (configuration.use_ssl ? "https://" : "http://") + configuration.endpoint + "/deploy"
|
18
|
-
|
19
|
-
parameters = {
|
20
|
-
"apiKey" => configuration.api_key,
|
21
|
-
"releaseStage" => configuration.release_stage,
|
22
|
-
"appVersion" => configuration.app_version,
|
23
|
-
"revision" => opts[:revision],
|
24
|
-
"repository" => opts[:repository],
|
25
|
-
"branch" => opts[:branch],
|
26
|
-
"provider" => opts[:provider]
|
27
|
-
}.reject {|k,v| v == nil}
|
28
|
-
|
29
|
-
raise RuntimeError.new("No API key found when notifying of deploy") if !parameters["apiKey"] || parameters["apiKey"].empty?
|
30
|
-
|
31
|
-
payload_string = ::JSON.dump(parameters)
|
32
|
-
Bugsnag::Delivery::Synchronous.deliver(endpoint, payload_string, configuration)
|
33
|
-
end
|
34
|
-
end
|
35
|
-
end
|
@@ -1,52 +0,0 @@
|
|
1
|
-
module Bugsnag::Middleware
|
2
|
-
class Rails2Request
|
3
|
-
def initialize(bugsnag)
|
4
|
-
@bugsnag = bugsnag
|
5
|
-
end
|
6
|
-
|
7
|
-
def call(notification)
|
8
|
-
if notification.request_data[:rails2_request]
|
9
|
-
request = notification.request_data[:rails2_request]
|
10
|
-
params = request.parameters || {}
|
11
|
-
session_data = request.session.respond_to?(:to_hash) ? request.session.to_hash : request.session.data
|
12
|
-
|
13
|
-
# Set the context
|
14
|
-
notification.context = "#{params[:controller]}##{params[:action]}"
|
15
|
-
|
16
|
-
# Set a sensible default for user_id
|
17
|
-
notification.user_id = request.remote_ip if request.respond_to?(:remote_ip)
|
18
|
-
|
19
|
-
# Build the clean url
|
20
|
-
url = "#{request.protocol}#{request.host}"
|
21
|
-
url << ":#{request.port}" unless [80, 443].include?(request.port)
|
22
|
-
url << Bugsnag::Cleaner.new(notification.configuration.params_filters).clean_url(request.fullpath)
|
23
|
-
|
24
|
-
# Add a request tab
|
25
|
-
notification.add_tab(:request, {
|
26
|
-
:url => url,
|
27
|
-
:params => params.to_hash,
|
28
|
-
:controller => params[:controller],
|
29
|
-
:action => params[:action]
|
30
|
-
})
|
31
|
-
|
32
|
-
# Add an environment tab
|
33
|
-
if request.env && notification.configuration.send_environment
|
34
|
-
notification.add_tab(:environment, request.env)
|
35
|
-
end
|
36
|
-
|
37
|
-
# Add a session tab
|
38
|
-
notification.add_tab(:session, session_data) if session_data
|
39
|
-
|
40
|
-
# Add a cookies tab
|
41
|
-
notification.add_tab(:cookies, request.cookies) if request.cookies
|
42
|
-
|
43
|
-
# Add the rails version
|
44
|
-
notification.add_tab(:environment, {
|
45
|
-
:railsVersion => Rails::VERSION::STRING
|
46
|
-
})
|
47
|
-
end
|
48
|
-
|
49
|
-
@bugsnag.call(notification)
|
50
|
-
end
|
51
|
-
end
|
52
|
-
end
|
data/lib/bugsnag/notification.rb
DELETED
@@ -1,506 +0,0 @@
|
|
1
|
-
require "json"
|
2
|
-
|
3
|
-
if RUBY_VERSION =~ /^1\.8/
|
4
|
-
begin
|
5
|
-
require "iconv"
|
6
|
-
rescue LoadError
|
7
|
-
end
|
8
|
-
end
|
9
|
-
|
10
|
-
require "pathname"
|
11
|
-
|
12
|
-
module Bugsnag
|
13
|
-
class Notification
|
14
|
-
NOTIFIER_NAME = "Ruby Bugsnag Notifier"
|
15
|
-
NOTIFIER_VERSION = Bugsnag::VERSION
|
16
|
-
NOTIFIER_URL = "http://www.bugsnag.com"
|
17
|
-
|
18
|
-
HANDLED_EXCEPTION = "handledException"
|
19
|
-
UNHANDLED_EXCEPTION = "unhandledException"
|
20
|
-
UNHANDLED_EXCEPTION_MIDDLEWARE = "unhandledExceptionMiddleware"
|
21
|
-
ERROR_CLASS = "errorClass"
|
22
|
-
USER_SPECIFIED_SEVERITY = "userSpecifiedSeverity"
|
23
|
-
USER_CALLBACK_SET_SEVERITY = "userCallbackSetSeverity"
|
24
|
-
|
25
|
-
API_KEY_REGEX = /[0-9a-f]{32}/i
|
26
|
-
|
27
|
-
# e.g. "org/jruby/RubyKernel.java:1264:in `catch'"
|
28
|
-
BACKTRACE_LINE_REGEX = /^((?:[a-zA-Z]:)?[^:]+):(\d+)(?::in `([^']+)')?$/
|
29
|
-
|
30
|
-
# e.g. "org.jruby.Ruby.runScript(Ruby.java:807)"
|
31
|
-
JAVA_BACKTRACE_REGEX = /^(.*)\((.*)(?::([0-9]+))?\)$/
|
32
|
-
|
33
|
-
MAX_EXCEPTIONS_TO_UNWRAP = 5
|
34
|
-
|
35
|
-
SUPPORTED_SEVERITIES = ["error", "warning", "info"]
|
36
|
-
|
37
|
-
CURRENT_PAYLOAD_VERSION = "2"
|
38
|
-
|
39
|
-
attr_accessor :context
|
40
|
-
attr_reader :user
|
41
|
-
attr_reader :severity
|
42
|
-
attr_accessor :severity_reason
|
43
|
-
attr_accessor :configuration
|
44
|
-
attr_accessor :meta_data
|
45
|
-
|
46
|
-
class << self
|
47
|
-
def deliver_exception_payload(url, payload, configuration=Bugsnag.configuration, delivery_method=nil)
|
48
|
-
payload_string = ::JSON.dump(Bugsnag::Helpers.trim_if_needed(payload))
|
49
|
-
delivery_method = delivery_method || configuration.delivery_method
|
50
|
-
Bugsnag::Delivery[delivery_method].deliver(url, payload_string, configuration)
|
51
|
-
end
|
52
|
-
end
|
53
|
-
|
54
|
-
def initialize(exception, configuration, overrides = nil, request_data = nil)
|
55
|
-
@configuration = configuration
|
56
|
-
@overrides = Bugsnag::Helpers.flatten_meta_data(overrides) || {}
|
57
|
-
@request_data = request_data
|
58
|
-
@meta_data = {}
|
59
|
-
@user = {}
|
60
|
-
@should_ignore = false
|
61
|
-
@severity = nil
|
62
|
-
@unhandled = false
|
63
|
-
@severity_reason = nil
|
64
|
-
@grouping_hash = nil
|
65
|
-
@delivery_method = nil
|
66
|
-
|
67
|
-
if @overrides.key? :unhandled
|
68
|
-
@unhandled = @overrides[:unhandled]
|
69
|
-
@overrides.delete :unhandled
|
70
|
-
end
|
71
|
-
|
72
|
-
valid_severity = @overrides.key?(:severity) && SUPPORTED_SEVERITIES.include?(@overrides[:severity])
|
73
|
-
has_reason = @overrides.key? :severity_reason
|
74
|
-
|
75
|
-
if valid_severity && has_reason
|
76
|
-
@severity = @overrides[:severity]
|
77
|
-
@severity_reason = @overrides[:severity_reason]
|
78
|
-
elsif valid_severity
|
79
|
-
@severity = @overrides[:severity]
|
80
|
-
@severity_reason = {
|
81
|
-
:type => USER_SPECIFIED_SEVERITY
|
82
|
-
}
|
83
|
-
elsif has_reason
|
84
|
-
@severity_reason = @overrides[:severity_reason]
|
85
|
-
else
|
86
|
-
@severity_reason = {
|
87
|
-
:type => HANDLED_EXCEPTION
|
88
|
-
}
|
89
|
-
end
|
90
|
-
|
91
|
-
@overrides.delete :severity_reason
|
92
|
-
@overrides.delete :severity
|
93
|
-
|
94
|
-
if @overrides.key? :grouping_hash
|
95
|
-
self.grouping_hash = @overrides[:grouping_hash]
|
96
|
-
@overrides.delete :grouping_hash
|
97
|
-
end
|
98
|
-
|
99
|
-
if @overrides.key? :api_key
|
100
|
-
self.api_key = @overrides[:api_key]
|
101
|
-
@overrides.delete :api_key
|
102
|
-
end
|
103
|
-
|
104
|
-
if @overrides.key? :delivery_method
|
105
|
-
@delivery_method = @overrides[:delivery_method]
|
106
|
-
@overrides.delete :delivery_method
|
107
|
-
end
|
108
|
-
|
109
|
-
# Unwrap exceptions
|
110
|
-
@exceptions = []
|
111
|
-
|
112
|
-
ex = exception
|
113
|
-
while ex != nil && !@exceptions.include?(ex) && @exceptions.length < MAX_EXCEPTIONS_TO_UNWRAP
|
114
|
-
|
115
|
-
unless ex.is_a? Exception
|
116
|
-
if ex.respond_to?(:to_exception)
|
117
|
-
ex = ex.to_exception
|
118
|
-
elsif ex.respond_to?(:exception)
|
119
|
-
ex = ex.exception
|
120
|
-
end
|
121
|
-
end
|
122
|
-
|
123
|
-
unless ex.is_a?(Exception) || (defined?(Java::JavaLang::Throwable) && ex.is_a?(Java::JavaLang::Throwable))
|
124
|
-
Bugsnag.warn("Converting non-Exception to RuntimeError: #{ex.inspect}")
|
125
|
-
ex = RuntimeError.new(ex.to_s)
|
126
|
-
ex.set_backtrace caller
|
127
|
-
end
|
128
|
-
|
129
|
-
@exceptions << ex
|
130
|
-
|
131
|
-
if ex.respond_to?(:cause) && ex.cause
|
132
|
-
ex = ex.cause
|
133
|
-
elsif ex.respond_to?(:continued_exception) && ex.continued_exception
|
134
|
-
ex = ex.continued_exception
|
135
|
-
elsif ex.respond_to?(:original_exception) && ex.original_exception
|
136
|
-
ex = ex.original_exception
|
137
|
-
else
|
138
|
-
ex = nil
|
139
|
-
end
|
140
|
-
end
|
141
|
-
end
|
142
|
-
|
143
|
-
# Add a single value as custom data, to this notification
|
144
|
-
def add_custom_data(name, value)
|
145
|
-
@meta_data[:custom] ||= {}
|
146
|
-
@meta_data[:custom][name.to_sym] = value
|
147
|
-
end
|
148
|
-
|
149
|
-
# Add a new tab to this notification
|
150
|
-
def add_tab(name, value)
|
151
|
-
return if name.nil?
|
152
|
-
|
153
|
-
if value.is_a? Hash
|
154
|
-
@meta_data[name.to_sym] ||= {}
|
155
|
-
@meta_data[name.to_sym].merge! value
|
156
|
-
else
|
157
|
-
self.add_custom_data(name, value)
|
158
|
-
Bugsnag.warn "Adding a tab requires a hash, adding to custom tab instead (name=#{name})"
|
159
|
-
end
|
160
|
-
end
|
161
|
-
|
162
|
-
# Remove a tab from this notification
|
163
|
-
def remove_tab(name)
|
164
|
-
return if name.nil?
|
165
|
-
|
166
|
-
@meta_data.delete(name.to_sym)
|
167
|
-
end
|
168
|
-
|
169
|
-
def user_id=(user_id)
|
170
|
-
@user[:id] = user_id
|
171
|
-
end
|
172
|
-
|
173
|
-
def user_id
|
174
|
-
@user[:id]
|
175
|
-
end
|
176
|
-
|
177
|
-
def user=(user = {})
|
178
|
-
return unless user.is_a? Hash
|
179
|
-
@user.merge!(user).delete_if{|k,v| v == nil}
|
180
|
-
end
|
181
|
-
|
182
|
-
def severity=(severity)
|
183
|
-
@severity = severity if SUPPORTED_SEVERITIES.include?(severity)
|
184
|
-
end
|
185
|
-
|
186
|
-
def severity
|
187
|
-
@severity || "warning"
|
188
|
-
end
|
189
|
-
|
190
|
-
def payload_version
|
191
|
-
CURRENT_PAYLOAD_VERSION
|
192
|
-
end
|
193
|
-
|
194
|
-
def grouping_hash=(grouping_hash)
|
195
|
-
@grouping_hash = grouping_hash
|
196
|
-
end
|
197
|
-
|
198
|
-
def grouping_hash
|
199
|
-
@grouping_hash || nil
|
200
|
-
end
|
201
|
-
|
202
|
-
def api_key=(api_key)
|
203
|
-
@api_key = api_key
|
204
|
-
end
|
205
|
-
|
206
|
-
def api_key
|
207
|
-
@api_key ||= @configuration.api_key
|
208
|
-
end
|
209
|
-
|
210
|
-
# Deliver this notification to bugsnag.com Also runs through the middleware as required.
|
211
|
-
def deliver
|
212
|
-
return unless @configuration.should_notify?
|
213
|
-
|
214
|
-
# Check we have at least an api_key
|
215
|
-
if api_key.nil?
|
216
|
-
Bugsnag.warn "No API key configured, couldn't notify"
|
217
|
-
return
|
218
|
-
elsif api_key !~ API_KEY_REGEX
|
219
|
-
Bugsnag.warn "Your API key (#{api_key}) is not valid, couldn't notify"
|
220
|
-
return
|
221
|
-
end
|
222
|
-
|
223
|
-
# Warn if no release_stage is set
|
224
|
-
Bugsnag.warn "You should set your app's release_stage (see https://bugsnag.com/docs/notifiers/ruby#release_stage)." unless @configuration.release_stage
|
225
|
-
|
226
|
-
@configuration.internal_middleware.run(self)
|
227
|
-
|
228
|
-
exceptions.each do |exception|
|
229
|
-
if exception.class.include?(Bugsnag::MetaData)
|
230
|
-
if exception.bugsnag_user_id.is_a?(String)
|
231
|
-
self.user_id = exception.bugsnag_user_id
|
232
|
-
end
|
233
|
-
if exception.bugsnag_context.is_a?(String)
|
234
|
-
self.context = exception.bugsnag_context
|
235
|
-
end
|
236
|
-
if exception.bugsnag_grouping_hash.is_a?(String)
|
237
|
-
self.grouping_hash = exception.bugsnag_grouping_hash
|
238
|
-
end
|
239
|
-
end
|
240
|
-
end
|
241
|
-
|
242
|
-
[:user_id, :context, :user, :grouping_hash].each do |symbol|
|
243
|
-
if @overrides[symbol]
|
244
|
-
self.send("#{symbol}=", @overrides[symbol])
|
245
|
-
@overrides.delete symbol
|
246
|
-
end
|
247
|
-
end
|
248
|
-
|
249
|
-
# make meta_data available to public middleware
|
250
|
-
@meta_data = generate_meta_data(@exceptions, @overrides)
|
251
|
-
|
252
|
-
initial_severity = self.severity
|
253
|
-
|
254
|
-
# Run the middleware here (including Bugsnag::Middleware::Callbacks)
|
255
|
-
# at the end of the middleware stack, execute the actual notification delivery
|
256
|
-
@configuration.middleware.run(self) do
|
257
|
-
# This supports self.ignore! for before_notify_callbacks.
|
258
|
-
return if @should_ignore
|
259
|
-
|
260
|
-
# Check to see if the severity has been changed
|
261
|
-
if initial_severity != self.severity
|
262
|
-
|
263
|
-
end
|
264
|
-
|
265
|
-
# Build the endpoint url
|
266
|
-
endpoint = (@configuration.use_ssl ? "https://" : "http://") + @configuration.endpoint
|
267
|
-
Bugsnag.log("Notifying #{endpoint} of #{@exceptions.last.class}")
|
268
|
-
|
269
|
-
# Deliver the payload
|
270
|
-
self.class.deliver_exception_payload(endpoint, build_exception_payload, @configuration, @delivery_method)
|
271
|
-
end
|
272
|
-
end
|
273
|
-
|
274
|
-
# Build an exception payload
|
275
|
-
def build_exception_payload
|
276
|
-
# Build the payload's exception event
|
277
|
-
payload_event = {
|
278
|
-
:app => {
|
279
|
-
:version => @configuration.app_version,
|
280
|
-
:releaseStage => @configuration.release_stage,
|
281
|
-
:type => @configuration.app_type
|
282
|
-
},
|
283
|
-
:context => self.context,
|
284
|
-
:user => @user,
|
285
|
-
:payloadVersion => payload_version,
|
286
|
-
:exceptions => exception_list,
|
287
|
-
:severity => self.severity,
|
288
|
-
:unhandled => @unhandled,
|
289
|
-
:severityReason => @severity_reason,
|
290
|
-
:groupingHash => self.grouping_hash,
|
291
|
-
}
|
292
|
-
|
293
|
-
payload_event[:device] = {:hostname => @configuration.hostname} if @configuration.hostname
|
294
|
-
|
295
|
-
# cleanup character encodings
|
296
|
-
payload_event = Bugsnag::Cleaner.clean_object_encoding(payload_event)
|
297
|
-
|
298
|
-
# filter out sensitive values in (and cleanup encodings) metaData
|
299
|
-
payload_event[:metaData] = Bugsnag::Cleaner.new(@configuration.params_filters).clean_object(@meta_data)
|
300
|
-
payload_event.reject! {|k,v| v.nil? }
|
301
|
-
|
302
|
-
# return the payload hash
|
303
|
-
{
|
304
|
-
:apiKey => api_key,
|
305
|
-
:notifier => {
|
306
|
-
:name => NOTIFIER_NAME,
|
307
|
-
:version => NOTIFIER_VERSION,
|
308
|
-
:url => NOTIFIER_URL
|
309
|
-
},
|
310
|
-
:events => [payload_event]
|
311
|
-
}
|
312
|
-
end
|
313
|
-
|
314
|
-
def ignore?
|
315
|
-
@should_ignore || ignore_exception_class? || ignore_user_agent?
|
316
|
-
end
|
317
|
-
|
318
|
-
def request_data
|
319
|
-
@request_data || Bugsnag.configuration.request_data
|
320
|
-
end
|
321
|
-
|
322
|
-
def exceptions
|
323
|
-
@exceptions
|
324
|
-
end
|
325
|
-
|
326
|
-
def ignore!
|
327
|
-
@should_ignore = true
|
328
|
-
end
|
329
|
-
|
330
|
-
private
|
331
|
-
|
332
|
-
def ignore_exception_class?
|
333
|
-
@exceptions.any? do |ex|
|
334
|
-
ancestor_chain = ex.class.ancestors.select { |ancestor| ancestor.is_a?(Class) }.map { |ancestor| error_class(ancestor) }.to_set
|
335
|
-
|
336
|
-
@configuration.ignore_classes.any? do |to_ignore|
|
337
|
-
to_ignore.is_a?(Proc) ? to_ignore.call(ex) : ancestor_chain.include?(to_ignore)
|
338
|
-
end
|
339
|
-
end
|
340
|
-
end
|
341
|
-
|
342
|
-
def ignore_user_agent?
|
343
|
-
if @configuration.request_data && @configuration.request_data[:rack_env] && (agent = @configuration.request_data[:rack_env]["HTTP_USER_AGENT"])
|
344
|
-
@configuration.ignore_user_agents.any? do |to_ignore|
|
345
|
-
agent =~ to_ignore
|
346
|
-
end
|
347
|
-
end
|
348
|
-
end
|
349
|
-
|
350
|
-
# Generate the meta data from both the request configuration, the overrides and the exceptions for this notification
|
351
|
-
def generate_meta_data(exceptions, overrides)
|
352
|
-
# Copy the request meta data so we dont edit it by mistake
|
353
|
-
meta_data = @meta_data.dup
|
354
|
-
|
355
|
-
exceptions.each do |exception|
|
356
|
-
if exception.respond_to?(:bugsnag_meta_data) && exception.bugsnag_meta_data
|
357
|
-
exception.bugsnag_meta_data.each do |key, value|
|
358
|
-
add_to_meta_data key, value, meta_data
|
359
|
-
end
|
360
|
-
end
|
361
|
-
end
|
362
|
-
|
363
|
-
overrides.each do |key, value|
|
364
|
-
add_to_meta_data key, value, meta_data
|
365
|
-
end
|
366
|
-
|
367
|
-
meta_data
|
368
|
-
end
|
369
|
-
|
370
|
-
def add_to_meta_data(key, value, meta_data)
|
371
|
-
# If its a hash, its a tab so we can just add it providing its not reserved
|
372
|
-
if value.is_a? Hash
|
373
|
-
key = key.to_sym
|
374
|
-
|
375
|
-
if meta_data[key]
|
376
|
-
# If its a clash, merge with the existing data
|
377
|
-
meta_data[key].merge! value
|
378
|
-
else
|
379
|
-
# Add it as is if its not special
|
380
|
-
meta_data[key] = value
|
381
|
-
end
|
382
|
-
else
|
383
|
-
meta_data[:custom] ||= {}
|
384
|
-
meta_data[:custom][key] = value
|
385
|
-
end
|
386
|
-
end
|
387
|
-
|
388
|
-
def exception_list
|
389
|
-
@exceptions.map do |exception|
|
390
|
-
{
|
391
|
-
:errorClass => error_class(exception),
|
392
|
-
:message => exception.message,
|
393
|
-
:stacktrace => stacktrace(exception.backtrace)
|
394
|
-
}
|
395
|
-
end
|
396
|
-
end
|
397
|
-
|
398
|
-
def error_class(exception)
|
399
|
-
# The "Class" check is for some strange exceptions like Timeout::Error
|
400
|
-
# which throw the error class instead of an instance
|
401
|
-
(exception.is_a? Class) ? exception.name : exception.class.name
|
402
|
-
end
|
403
|
-
|
404
|
-
def stacktrace(backtrace)
|
405
|
-
backtrace = caller if !backtrace || backtrace.empty?
|
406
|
-
backtrace.map do |trace|
|
407
|
-
if trace.match(BACKTRACE_LINE_REGEX)
|
408
|
-
file, line_str, method = [$1, $2, $3]
|
409
|
-
elsif trace.match(JAVA_BACKTRACE_REGEX)
|
410
|
-
method, file, line_str = [$1, $2, $3]
|
411
|
-
end
|
412
|
-
|
413
|
-
# Parse the stacktrace line
|
414
|
-
|
415
|
-
# Skip stacktrace lines inside lib/bugsnag
|
416
|
-
next(nil) if file.nil? || file =~ %r{lib/bugsnag(/|\.rb)}
|
417
|
-
|
418
|
-
# Expand relative paths
|
419
|
-
p = Pathname.new(file)
|
420
|
-
if p.relative?
|
421
|
-
file = p.realpath.to_s rescue file
|
422
|
-
end
|
423
|
-
|
424
|
-
# Generate the stacktrace line hash
|
425
|
-
trace_hash = {}
|
426
|
-
trace_hash[:inProject] = true if in_project?(file)
|
427
|
-
trace_hash[:lineNumber] = line_str.to_i
|
428
|
-
|
429
|
-
if @configuration.send_code
|
430
|
-
trace_hash[:code] = code(file, trace_hash[:lineNumber])
|
431
|
-
end
|
432
|
-
|
433
|
-
# Clean up the file path in the stacktrace
|
434
|
-
if defined?(Bugsnag.configuration.project_root) && Bugsnag.configuration.project_root.to_s != ''
|
435
|
-
file.sub!(/#{Bugsnag.configuration.project_root}\//, "")
|
436
|
-
end
|
437
|
-
|
438
|
-
# Strip common gem path prefixes
|
439
|
-
if defined?(Gem)
|
440
|
-
file = Gem.path.inject(file) {|line, path| line.sub(/#{path}\//, "") }
|
441
|
-
end
|
442
|
-
|
443
|
-
trace_hash[:file] = file
|
444
|
-
|
445
|
-
# Add a method if we have it
|
446
|
-
trace_hash[:method] = method if method && (method =~ /^__bind/).nil?
|
447
|
-
|
448
|
-
if trace_hash[:file] && !trace_hash[:file].empty?
|
449
|
-
trace_hash
|
450
|
-
else
|
451
|
-
nil
|
452
|
-
end
|
453
|
-
end.compact
|
454
|
-
end
|
455
|
-
|
456
|
-
def in_project?(line)
|
457
|
-
return false if @configuration.vendor_paths && @configuration.vendor_paths.any? do |vendor_path|
|
458
|
-
if vendor_path.is_a?(String)
|
459
|
-
line.include?(vendor_path)
|
460
|
-
else
|
461
|
-
line =~ vendor_path
|
462
|
-
end
|
463
|
-
end
|
464
|
-
@configuration.project_root && line.start_with?(@configuration.project_root.to_s)
|
465
|
-
end
|
466
|
-
|
467
|
-
def code(file, line_number, num_lines = 7)
|
468
|
-
code_hash = {}
|
469
|
-
|
470
|
-
from_line = [line_number - num_lines, 1].max
|
471
|
-
|
472
|
-
# don't try and open '(irb)' or '-e'
|
473
|
-
return unless File.exist?(file)
|
474
|
-
|
475
|
-
# Populate code hash with line numbers and code lines
|
476
|
-
File.open(file) do |f|
|
477
|
-
current_line_number = 0
|
478
|
-
f.each_line do |line|
|
479
|
-
current_line_number += 1
|
480
|
-
|
481
|
-
next if current_line_number < from_line
|
482
|
-
|
483
|
-
code_hash[current_line_number] = line[0...200].rstrip
|
484
|
-
|
485
|
-
break if code_hash.length >= ( num_lines * 1.5 ).ceil
|
486
|
-
end
|
487
|
-
end
|
488
|
-
|
489
|
-
while code_hash.length > num_lines
|
490
|
-
last_line = code_hash.keys.max
|
491
|
-
first_line = code_hash.keys.min
|
492
|
-
|
493
|
-
if (last_line - line_number) > (line_number - first_line)
|
494
|
-
code_hash.delete(last_line)
|
495
|
-
else
|
496
|
-
code_hash.delete(first_line)
|
497
|
-
end
|
498
|
-
end
|
499
|
-
|
500
|
-
code_hash
|
501
|
-
rescue
|
502
|
-
Bugsnag.warn("Error fetching code: #{$!.inspect}")
|
503
|
-
nil
|
504
|
-
end
|
505
|
-
end
|
506
|
-
end
|