exception_hunter 0.2.0 → 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +103 -6
- data/app/assets/stylesheets/exception_hunter/base.css +66 -8
- data/app/assets/stylesheets/exception_hunter/errors.css +188 -25
- data/app/assets/stylesheets/exception_hunter/navigation.css +20 -5
- data/app/assets/stylesheets/exception_hunter/sessions.css +71 -0
- data/app/controllers/concerns/exception_hunter/authorization.rb +23 -0
- data/app/controllers/exception_hunter/application_controller.rb +2 -0
- data/app/controllers/exception_hunter/errors_controller.rb +29 -4
- data/app/controllers/exception_hunter/ignored_errors_controller.rb +25 -0
- data/app/controllers/exception_hunter/resolved_errors_controller.rb +11 -0
- data/app/helpers/exception_hunter/application_helper.rb +22 -0
- data/app/helpers/exception_hunter/sessions_helper.rb +16 -0
- data/app/jobs/exception_hunter/send_notification_job.rb +15 -0
- data/app/models/exception_hunter/application_record.rb +8 -0
- data/app/models/exception_hunter/error.rb +24 -7
- data/app/models/exception_hunter/error_group.rb +24 -5
- data/app/presenters/exception_hunter/dashboard_presenter.rb +56 -0
- data/app/presenters/exception_hunter/error_group_presenter.rb +25 -0
- data/app/presenters/exception_hunter/error_presenter.rb +3 -2
- data/app/views/exception_hunter/devise/sessions/new.html.erb +24 -0
- data/app/views/exception_hunter/errors/_error_row.erb +52 -0
- data/app/views/exception_hunter/errors/_error_summary.erb +5 -5
- data/app/views/exception_hunter/errors/_errors_table.erb +1 -0
- data/app/views/exception_hunter/errors/_last_7_days_errors_table.erb +12 -0
- data/app/views/exception_hunter/errors/index.html.erb +84 -29
- data/app/views/exception_hunter/errors/pagy/_pagy_nav.html.erb +15 -15
- data/app/views/exception_hunter/errors/show.html.erb +58 -22
- data/app/views/layouts/exception_hunter/application.html.erb +67 -6
- data/app/views/layouts/exception_hunter/exception_hunter_logged_out.html.erb +24 -0
- data/config/rails_best_practices.yml +3 -3
- data/config/routes.rb +21 -1
- data/lib/exception_hunter.rb +44 -2
- data/lib/exception_hunter/config.rb +25 -1
- data/lib/exception_hunter/data_redacter.rb +27 -0
- data/lib/exception_hunter/devise.rb +19 -0
- data/lib/exception_hunter/engine.rb +6 -0
- data/lib/exception_hunter/error_creator.rb +72 -0
- data/lib/exception_hunter/error_reaper.rb +20 -0
- data/lib/exception_hunter/middleware/delayed_job_hunter.rb +70 -0
- data/lib/exception_hunter/middleware/request_hunter.rb +5 -2
- data/lib/exception_hunter/middleware/sidekiq_hunter.rb +1 -1
- data/lib/exception_hunter/notifiers/misconfigured_notifiers.rb +10 -0
- data/lib/exception_hunter/notifiers/slack_notifier.rb +42 -0
- data/lib/exception_hunter/notifiers/slack_notifier_serializer.rb +20 -0
- data/lib/exception_hunter/tracking.rb +35 -0
- data/lib/exception_hunter/user_attributes_collector.rb +21 -0
- data/lib/exception_hunter/version.rb +1 -1
- data/lib/generators/exception_hunter/create_users/create_users_generator.rb +8 -1
- data/lib/generators/exception_hunter/install/install_generator.rb +3 -1
- data/lib/generators/exception_hunter/install/templates/create_exception_hunter_error_groups.rb.erb +3 -0
- data/lib/generators/exception_hunter/install/templates/exception_hunter.rb.erb +52 -0
- data/lib/tasks/exception_hunter_tasks.rake +6 -4
- metadata +46 -12
- data/app/services/exception_hunter/error_creator.rb +0 -41
- data/config/initializers/exception_hunter.rb +0 -16
@@ -0,0 +1,10 @@
|
|
1
|
+
module ExceptionHunter
|
2
|
+
module Notifiers
|
3
|
+
# Error raised when there's a malformed notifier.
|
4
|
+
class MisconfiguredNotifiers < StandardError
|
5
|
+
def initialize(notifier)
|
6
|
+
super("Notifier has incorrect configuration: #{notifier.inspect}")
|
7
|
+
end
|
8
|
+
end
|
9
|
+
end
|
10
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
require 'slack-notifier'
|
2
|
+
|
3
|
+
module ExceptionHunter
|
4
|
+
module Notifiers
|
5
|
+
# Notifier that sends a message to a Slack channel every time an
|
6
|
+
# exception is tracked.
|
7
|
+
class SlackNotifier
|
8
|
+
attr_reader :error, :notifier
|
9
|
+
|
10
|
+
def initialize(error, notifier)
|
11
|
+
@error = error
|
12
|
+
@notifier = notifier
|
13
|
+
end
|
14
|
+
|
15
|
+
def notify
|
16
|
+
slack_notifier = Slack::Notifier.new(notifier[:options][:webhook])
|
17
|
+
slack_notifier.ping(slack_notification_message)
|
18
|
+
end
|
19
|
+
|
20
|
+
private
|
21
|
+
|
22
|
+
def slack_notification_message
|
23
|
+
{
|
24
|
+
blocks: [
|
25
|
+
{
|
26
|
+
type: 'section',
|
27
|
+
text: {
|
28
|
+
type: 'mrkdwn',
|
29
|
+
text: error_message
|
30
|
+
}
|
31
|
+
}
|
32
|
+
]
|
33
|
+
}
|
34
|
+
end
|
35
|
+
|
36
|
+
def error_message
|
37
|
+
"*#{error.class_name}*: #{error.message}. \n" \
|
38
|
+
"<#{ExceptionHunter::Engine.routes.url_helpers.error_url(error.error_group)}|Click to see the error>"
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
module ExceptionHunter
|
2
|
+
module Notifiers
|
3
|
+
# @private
|
4
|
+
class SlackNotifierSerializer
|
5
|
+
def self.serialize(slack_notifier)
|
6
|
+
{
|
7
|
+
error: slack_notifier.error,
|
8
|
+
notifier: slack_notifier.notifier.as_json
|
9
|
+
}
|
10
|
+
end
|
11
|
+
|
12
|
+
def self.deserialize(hash)
|
13
|
+
ExceptionHunter::Notifiers::SlackNotifier.new(
|
14
|
+
hash[:error],
|
15
|
+
hash[:notifier].deep_symbolize_keys
|
16
|
+
)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
module ExceptionHunter
|
2
|
+
# Mixin used to track manual exceptions.
|
3
|
+
module Tracking
|
4
|
+
# Used to manually track errors in cases where raising might
|
5
|
+
# not be adequate and but some insight is desired.
|
6
|
+
#
|
7
|
+
# @example Track the else clause on a case
|
8
|
+
# case user.status
|
9
|
+
# when :active then do_something()
|
10
|
+
# when :inactive then do_something_else()
|
11
|
+
# else
|
12
|
+
# ExceptionHunter.track(StandardError.new("User with unknown status"),
|
13
|
+
# custom_data: { status: user.status },
|
14
|
+
# user: user)
|
15
|
+
# end
|
16
|
+
#
|
17
|
+
# @param [Exception] exception to track.
|
18
|
+
# @param [Hash] custom_data to include and help debug the error. (optional)
|
19
|
+
# @param [User] user in the current session. (optional)
|
20
|
+
# @return [void]
|
21
|
+
def track(exception, custom_data: {}, user: nil)
|
22
|
+
ErrorCreator.call(
|
23
|
+
tag: ErrorCreator::MANUAL_TAG,
|
24
|
+
class_name: exception.class.to_s,
|
25
|
+
message: exception.message,
|
26
|
+
backtrace: exception.backtrace,
|
27
|
+
custom_data: custom_data,
|
28
|
+
user: user,
|
29
|
+
environment_data: {}
|
30
|
+
)
|
31
|
+
|
32
|
+
nil
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -1,13 +1,34 @@
|
|
1
1
|
module ExceptionHunter
|
2
|
+
# Utility module used to whitelist the user's attributes.
|
3
|
+
# Can be configured in {ExceptionHunter.setup ExceptionHunter.setup} to extract
|
4
|
+
# custom attributes.
|
5
|
+
#
|
6
|
+
# @example
|
7
|
+
# ExceptionHunter.setup do |config|
|
8
|
+
# config.user_attributes = [:id, :email, :role, :active?]
|
9
|
+
# end
|
10
|
+
#
|
2
11
|
module UserAttributesCollector
|
3
12
|
extend self
|
4
13
|
|
14
|
+
# Gets the attributes configured for the user.
|
15
|
+
#
|
16
|
+
# @example
|
17
|
+
# UserAttributesCollector.collect_attributes(current_user)
|
18
|
+
# # => { id: 42, email: "example@user.com" }
|
19
|
+
#
|
20
|
+
# @param user instance in your application
|
21
|
+
# @return [Hash] the whitelisted attributes from the user
|
5
22
|
def collect_attributes(user)
|
23
|
+
return {} unless user
|
24
|
+
|
6
25
|
attributes.reduce({}) do |data, attribute|
|
7
26
|
data.merge(attribute => user.try(attribute))
|
8
27
|
end
|
9
28
|
end
|
10
29
|
|
30
|
+
private
|
31
|
+
|
11
32
|
def attributes
|
12
33
|
Config.user_attributes
|
13
34
|
end
|
@@ -22,7 +22,14 @@ module ExceptionHunter
|
|
22
22
|
end
|
23
23
|
|
24
24
|
def create_admin_user
|
25
|
-
invoke 'devise', [name]
|
25
|
+
invoke 'devise', [name], routes: false
|
26
|
+
end
|
27
|
+
|
28
|
+
def remove_registerable_from_model
|
29
|
+
return if options[:registerable]
|
30
|
+
|
31
|
+
model_file = File.join(destination_root, 'app', 'models', "#{file_path}.rb")
|
32
|
+
gsub_file model_file, /\:registerable([.]*,)?/, ''
|
26
33
|
end
|
27
34
|
end
|
28
35
|
end
|
@@ -15,7 +15,9 @@ module ExceptionHunter
|
|
15
15
|
|
16
16
|
def setup_routes
|
17
17
|
if options[:users]
|
18
|
-
|
18
|
+
gsub_file 'config/routes.rb',
|
19
|
+
"\n devise_for :#{plural_table_name}, skip: :all",
|
20
|
+
"\n ExceptionHunter.routes(self)"
|
19
21
|
else
|
20
22
|
route 'ExceptionHunter.routes(self)'
|
21
23
|
end
|
data/lib/generators/exception_hunter/install/templates/create_exception_hunter_error_groups.rb.erb
CHANGED
@@ -5,10 +5,13 @@ class CreateExceptionHunterErrorGroups < ActiveRecord::Migration[<%= ActiveRecor
|
|
5
5
|
create_table :exception_hunter_error_groups do |t|
|
6
6
|
t.string :error_class_name, null: false
|
7
7
|
t.string :message
|
8
|
+
t.integer :status, default: 0
|
9
|
+
t.text :tags, array: true, default: []
|
8
10
|
|
9
11
|
t.timestamps
|
10
12
|
|
11
13
|
t.index :message, opclass: :gin_trgm_ops, using: :gin
|
14
|
+
t.index :status
|
12
15
|
end
|
13
16
|
end
|
14
17
|
end
|
@@ -1,4 +1,19 @@
|
|
1
1
|
ExceptionHunter.setup do |config|
|
2
|
+
# == Enabling
|
3
|
+
#
|
4
|
+
# This flag allows disabling error tracking, it's set to track in
|
5
|
+
# any environment but development or test by default
|
6
|
+
#
|
7
|
+
config.enabled = !(Rails.env.development? || Rails.env.test?)
|
8
|
+
|
9
|
+
# == Dashboard User
|
10
|
+
# Exception Hunter allows you to restrict users who can see the dashboard
|
11
|
+
# to the ones included in the database. You can change the table name in
|
12
|
+
# case you are not satisfied with the default one. You can also remove the
|
13
|
+
# configuration if you wish to have no access restrictions for the dashboard.
|
14
|
+
#
|
15
|
+
<%= @use_authentication_method ? "config.admin_user_class = '#{name}'" : "# config.admin_user_class = '#{name}'" %>
|
16
|
+
|
2
17
|
# == Current User
|
3
18
|
#
|
4
19
|
# Exception Hunter will include the user as part of the environment
|
@@ -15,4 +30,41 @@ ExceptionHunter.setup do |config|
|
|
15
30
|
# as part of the user information that is kept from the request.
|
16
31
|
#
|
17
32
|
config.user_attributes = [:id, :email]
|
33
|
+
|
34
|
+
# == Stale errors
|
35
|
+
#
|
36
|
+
# You can configure how long it takes for errors to go stale. This is
|
37
|
+
# taken into account when purging old error messages but nothing will
|
38
|
+
# happen automatically.
|
39
|
+
#
|
40
|
+
# config.errors_stale_time = 45.days
|
41
|
+
|
42
|
+
# == Slack notifications
|
43
|
+
#
|
44
|
+
# You can configure if you want to send notifications to slack for each error occurrence.
|
45
|
+
# You can enter multiple webhook urls.
|
46
|
+
# Default: []
|
47
|
+
#
|
48
|
+
# config.notifiers << {
|
49
|
+
# name: :slack,
|
50
|
+
# options: {
|
51
|
+
# webhook: 'SLACK_WEBHOOK_URL_1'
|
52
|
+
# }
|
53
|
+
# }
|
54
|
+
#
|
55
|
+
# config.notifiers << {
|
56
|
+
# name: :slack,
|
57
|
+
# options: {
|
58
|
+
# webhook: SLACK_WEBHOOK_URL_2'
|
59
|
+
# }
|
60
|
+
# }
|
61
|
+
|
62
|
+
# == Filter sensitive parameters
|
63
|
+
#
|
64
|
+
# You can configure if you want to filter some fields on the error's data for security or privacy issues.
|
65
|
+
# We use ActiveSupport::ParameterFilter for this, any accepted pattern will work.
|
66
|
+
# https://api.rubyonrails.org/classes/ActiveSupport/ParameterFilter.html
|
67
|
+
# Default: []
|
68
|
+
#
|
69
|
+
# config.sensitive_parameters = [:id, :name]
|
18
70
|
end
|
metadata
CHANGED
@@ -1,15 +1,15 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: exception_hunter
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 1.0.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Bruno Vezoli
|
8
8
|
- Tiziana Romani
|
9
|
-
autorequire:
|
9
|
+
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2020-05
|
12
|
+
date: 2020-11-05 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: pagy
|
@@ -17,14 +17,28 @@ dependencies:
|
|
17
17
|
requirements:
|
18
18
|
- - "~>"
|
19
19
|
- !ruby/object:Gem::Version
|
20
|
-
version: '3
|
20
|
+
version: '3'
|
21
21
|
type: :runtime
|
22
22
|
prerelease: false
|
23
23
|
version_requirements: !ruby/object:Gem::Requirement
|
24
24
|
requirements:
|
25
25
|
- - "~>"
|
26
26
|
- !ruby/object:Gem::Version
|
27
|
-
version: '3
|
27
|
+
version: '3'
|
28
|
+
- !ruby/object:Gem::Dependency
|
29
|
+
name: slack-notifier
|
30
|
+
requirement: !ruby/object:Gem::Requirement
|
31
|
+
requirements:
|
32
|
+
- - "~>"
|
33
|
+
- !ruby/object:Gem::Version
|
34
|
+
version: '2.3'
|
35
|
+
type: :runtime
|
36
|
+
prerelease: false
|
37
|
+
version_requirements: !ruby/object:Gem::Requirement
|
38
|
+
requirements:
|
39
|
+
- - "~>"
|
40
|
+
- !ruby/object:Gem::Version
|
41
|
+
version: '2.3'
|
28
42
|
- !ruby/object:Gem::Dependency
|
29
43
|
name: brakeman
|
30
44
|
requirement: !ruby/object:Gem::Requirement
|
@@ -115,15 +129,15 @@ dependencies:
|
|
115
129
|
requirements:
|
116
130
|
- - "~>"
|
117
131
|
- !ruby/object:Gem::Version
|
118
|
-
version: 0.
|
132
|
+
version: 0.17.1
|
119
133
|
type: :development
|
120
134
|
prerelease: false
|
121
135
|
version_requirements: !ruby/object:Gem::Requirement
|
122
136
|
requirements:
|
123
137
|
- - "~>"
|
124
138
|
- !ruby/object:Gem::Version
|
125
|
-
version: 0.
|
126
|
-
description:
|
139
|
+
version: 0.17.1
|
140
|
+
description:
|
127
141
|
email:
|
128
142
|
- bruno.vezoli@rootstrap.com
|
129
143
|
executables: []
|
@@ -139,32 +153,52 @@ files:
|
|
139
153
|
- app/assets/stylesheets/exception_hunter/base.css
|
140
154
|
- app/assets/stylesheets/exception_hunter/errors.css
|
141
155
|
- app/assets/stylesheets/exception_hunter/navigation.css
|
156
|
+
- app/assets/stylesheets/exception_hunter/sessions.css
|
157
|
+
- app/controllers/concerns/exception_hunter/authorization.rb
|
142
158
|
- app/controllers/exception_hunter/application_controller.rb
|
143
159
|
- app/controllers/exception_hunter/errors_controller.rb
|
160
|
+
- app/controllers/exception_hunter/ignored_errors_controller.rb
|
161
|
+
- app/controllers/exception_hunter/resolved_errors_controller.rb
|
144
162
|
- app/helpers/exception_hunter/application_helper.rb
|
145
163
|
- app/helpers/exception_hunter/errors_helper.rb
|
164
|
+
- app/helpers/exception_hunter/sessions_helper.rb
|
146
165
|
- app/jobs/exception_hunter/application_job.rb
|
166
|
+
- app/jobs/exception_hunter/send_notification_job.rb
|
147
167
|
- app/mailers/exception_hunter/application_mailer.rb
|
148
168
|
- app/models/exception_hunter/application_record.rb
|
149
169
|
- app/models/exception_hunter/error.rb
|
150
170
|
- app/models/exception_hunter/error_group.rb
|
171
|
+
- app/presenters/exception_hunter/dashboard_presenter.rb
|
172
|
+
- app/presenters/exception_hunter/error_group_presenter.rb
|
151
173
|
- app/presenters/exception_hunter/error_presenter.rb
|
152
|
-
- app/
|
174
|
+
- app/views/exception_hunter/devise/sessions/new.html.erb
|
153
175
|
- app/views/exception_hunter/errors/_error_backtrace.erb
|
176
|
+
- app/views/exception_hunter/errors/_error_row.erb
|
154
177
|
- app/views/exception_hunter/errors/_error_summary.erb
|
155
178
|
- app/views/exception_hunter/errors/_error_user_data.erb
|
179
|
+
- app/views/exception_hunter/errors/_errors_table.erb
|
180
|
+
- app/views/exception_hunter/errors/_last_7_days_errors_table.erb
|
156
181
|
- app/views/exception_hunter/errors/index.html.erb
|
157
182
|
- app/views/exception_hunter/errors/pagy/_pagy_nav.html.erb
|
158
183
|
- app/views/exception_hunter/errors/show.html.erb
|
159
184
|
- app/views/layouts/exception_hunter/application.html.erb
|
160
|
-
-
|
185
|
+
- app/views/layouts/exception_hunter/exception_hunter_logged_out.html.erb
|
161
186
|
- config/rails_best_practices.yml
|
162
187
|
- config/routes.rb
|
163
188
|
- lib/exception_hunter.rb
|
164
189
|
- lib/exception_hunter/config.rb
|
190
|
+
- lib/exception_hunter/data_redacter.rb
|
191
|
+
- lib/exception_hunter/devise.rb
|
165
192
|
- lib/exception_hunter/engine.rb
|
193
|
+
- lib/exception_hunter/error_creator.rb
|
194
|
+
- lib/exception_hunter/error_reaper.rb
|
195
|
+
- lib/exception_hunter/middleware/delayed_job_hunter.rb
|
166
196
|
- lib/exception_hunter/middleware/request_hunter.rb
|
167
197
|
- lib/exception_hunter/middleware/sidekiq_hunter.rb
|
198
|
+
- lib/exception_hunter/notifiers/misconfigured_notifiers.rb
|
199
|
+
- lib/exception_hunter/notifiers/slack_notifier.rb
|
200
|
+
- lib/exception_hunter/notifiers/slack_notifier_serializer.rb
|
201
|
+
- lib/exception_hunter/tracking.rb
|
168
202
|
- lib/exception_hunter/user_attributes_collector.rb
|
169
203
|
- lib/exception_hunter/version.rb
|
170
204
|
- lib/generators/exception_hunter/create_users/create_users_generator.rb
|
@@ -179,7 +213,7 @@ homepage: https://github.com/rootstrap/exception_hunter
|
|
179
213
|
licenses:
|
180
214
|
- MIT
|
181
215
|
metadata: {}
|
182
|
-
post_install_message:
|
216
|
+
post_install_message:
|
183
217
|
rdoc_options: []
|
184
218
|
require_paths:
|
185
219
|
- lib
|
@@ -195,7 +229,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
195
229
|
version: '0'
|
196
230
|
requirements: []
|
197
231
|
rubygems_version: 3.0.8
|
198
|
-
signing_key:
|
232
|
+
signing_key:
|
199
233
|
specification_version: 4
|
200
234
|
summary: Exception tracking engine
|
201
235
|
test_files: []
|
@@ -1,41 +0,0 @@
|
|
1
|
-
module ExceptionHunter
|
2
|
-
class ErrorCreator
|
3
|
-
class << self
|
4
|
-
def call(**error_attrs)
|
5
|
-
ActiveRecord::Base.transaction do
|
6
|
-
error_attrs = extract_user_data(error_attrs)
|
7
|
-
error = Error.new(error_attrs)
|
8
|
-
error_group = ErrorGroup.find_matching_group(error) || ErrorGroup.new
|
9
|
-
update_error_group(error_group, error)
|
10
|
-
error.error_group = error_group
|
11
|
-
error.save!
|
12
|
-
error
|
13
|
-
end
|
14
|
-
rescue ActiveRecord::RecordInvalid
|
15
|
-
false
|
16
|
-
end
|
17
|
-
|
18
|
-
private
|
19
|
-
|
20
|
-
def update_error_group(error_group, error)
|
21
|
-
error_group.error_class_name = error.class_name
|
22
|
-
error_group.message = error.message
|
23
|
-
|
24
|
-
error_group.save!
|
25
|
-
end
|
26
|
-
|
27
|
-
def extract_user_data(**error_attrs)
|
28
|
-
user = error_attrs[:user]
|
29
|
-
error_attrs[:user_data] =
|
30
|
-
if user.nil?
|
31
|
-
{}
|
32
|
-
else
|
33
|
-
UserAttributesCollector.collect_attributes(user)
|
34
|
-
end
|
35
|
-
|
36
|
-
error_attrs.delete(:user)
|
37
|
-
error_attrs
|
38
|
-
end
|
39
|
-
end
|
40
|
-
end
|
41
|
-
end
|