exception_hunter 0.2.0 → 1.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.
Files changed (56) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +103 -6
  3. data/app/assets/stylesheets/exception_hunter/base.css +66 -8
  4. data/app/assets/stylesheets/exception_hunter/errors.css +188 -25
  5. data/app/assets/stylesheets/exception_hunter/navigation.css +20 -5
  6. data/app/assets/stylesheets/exception_hunter/sessions.css +71 -0
  7. data/app/controllers/concerns/exception_hunter/authorization.rb +23 -0
  8. data/app/controllers/exception_hunter/application_controller.rb +2 -0
  9. data/app/controllers/exception_hunter/errors_controller.rb +29 -4
  10. data/app/controllers/exception_hunter/ignored_errors_controller.rb +25 -0
  11. data/app/controllers/exception_hunter/resolved_errors_controller.rb +11 -0
  12. data/app/helpers/exception_hunter/application_helper.rb +22 -0
  13. data/app/helpers/exception_hunter/sessions_helper.rb +16 -0
  14. data/app/jobs/exception_hunter/send_notification_job.rb +15 -0
  15. data/app/models/exception_hunter/application_record.rb +8 -0
  16. data/app/models/exception_hunter/error.rb +24 -7
  17. data/app/models/exception_hunter/error_group.rb +24 -5
  18. data/app/presenters/exception_hunter/dashboard_presenter.rb +56 -0
  19. data/app/presenters/exception_hunter/error_group_presenter.rb +25 -0
  20. data/app/presenters/exception_hunter/error_presenter.rb +3 -2
  21. data/app/views/exception_hunter/devise/sessions/new.html.erb +24 -0
  22. data/app/views/exception_hunter/errors/_error_row.erb +52 -0
  23. data/app/views/exception_hunter/errors/_error_summary.erb +5 -5
  24. data/app/views/exception_hunter/errors/_errors_table.erb +1 -0
  25. data/app/views/exception_hunter/errors/_last_7_days_errors_table.erb +12 -0
  26. data/app/views/exception_hunter/errors/index.html.erb +84 -29
  27. data/app/views/exception_hunter/errors/pagy/_pagy_nav.html.erb +15 -15
  28. data/app/views/exception_hunter/errors/show.html.erb +58 -22
  29. data/app/views/layouts/exception_hunter/application.html.erb +67 -6
  30. data/app/views/layouts/exception_hunter/exception_hunter_logged_out.html.erb +24 -0
  31. data/config/rails_best_practices.yml +3 -3
  32. data/config/routes.rb +21 -1
  33. data/lib/exception_hunter.rb +44 -2
  34. data/lib/exception_hunter/config.rb +25 -1
  35. data/lib/exception_hunter/data_redacter.rb +27 -0
  36. data/lib/exception_hunter/devise.rb +19 -0
  37. data/lib/exception_hunter/engine.rb +6 -0
  38. data/lib/exception_hunter/error_creator.rb +72 -0
  39. data/lib/exception_hunter/error_reaper.rb +20 -0
  40. data/lib/exception_hunter/middleware/delayed_job_hunter.rb +70 -0
  41. data/lib/exception_hunter/middleware/request_hunter.rb +5 -2
  42. data/lib/exception_hunter/middleware/sidekiq_hunter.rb +1 -1
  43. data/lib/exception_hunter/notifiers/misconfigured_notifiers.rb +10 -0
  44. data/lib/exception_hunter/notifiers/slack_notifier.rb +42 -0
  45. data/lib/exception_hunter/notifiers/slack_notifier_serializer.rb +20 -0
  46. data/lib/exception_hunter/tracking.rb +35 -0
  47. data/lib/exception_hunter/user_attributes_collector.rb +21 -0
  48. data/lib/exception_hunter/version.rb +1 -1
  49. data/lib/generators/exception_hunter/create_users/create_users_generator.rb +8 -1
  50. data/lib/generators/exception_hunter/install/install_generator.rb +3 -1
  51. data/lib/generators/exception_hunter/install/templates/create_exception_hunter_error_groups.rb.erb +3 -0
  52. data/lib/generators/exception_hunter/install/templates/exception_hunter.rb.erb +52 -0
  53. data/lib/tasks/exception_hunter_tasks.rake +6 -4
  54. metadata +46 -12
  55. data/app/services/exception_hunter/error_creator.rb +0 -41
  56. data/config/initializers/exception_hunter.rb +0 -16
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: a48a18bd03288de4cf787931d9be95dea04f3f168224c87c971ad5b2cd9fb1bf
4
- data.tar.gz: 6194494c81ccd89d7f3ce0c2a00c607fe2c7836e872074d11b37454cf8f93122
3
+ metadata.gz: 6c804775027621de61eb328522e7a06259cef6f830d4f5b52a09da40ef99bb3a
4
+ data.tar.gz: d366989c30f3e76c587259bbce188b117b78dec40e7a41b8473751ef52b5c43e
5
5
  SHA512:
6
- metadata.gz: 9bd3b4b6410b75f39a43ce1a4f87234883f5a625d77e63f42dd217db865f31a515a95577d3def6d0de838905003640c348f9b43f8d1c4c9731b13e3d4dbff8a7
7
- data.tar.gz: 18744e0c947daa72a222449986d3c75d2e80b9993baa89ab9d90f137b2de9ac5b82272550096894dd248f4f7b337277ff94f61aa605a7229715a8fdf0049fb1e
6
+ metadata.gz: 74b86f508f6f88a42f857f1e62f137f94638c330a4a1c0416677c1ef427de0398e7c04250e06f6c41a452489b660277c02d12fdd3f666e17922d8e27c72c4357
7
+ data.tar.gz: ce57f0a4277381df8b27fc8015746c66abbde9ffc98e656f52e92259d9b263bc22ceadf85061cb4e836d1935b97473125d08f2802ef456c4cdedb20dcb2014ac
data/README.md CHANGED
@@ -1,14 +1,35 @@
1
1
  # ExceptionHunter
2
- Short description and motivation.
3
2
 
4
- ## Usage
5
- How to use my plugin.
3
+ ![CI](https://github.com/rootstrap/exception_hunter/workflows/Rails%20tests/badge.svg)
4
+ [![Maintainability](https://api.codeclimate.com/v1/badges/86f6aaa2377c894f8ee4/maintainability)](https://codeclimate.com/github/rootstrap/exception_hunter/maintainability)
5
+ [![Test Coverage](https://api.codeclimate.com/v1/badges/86f6aaa2377c894f8ee4/test_coverage)](https://codeclimate.com/github/rootstrap/exception_hunter/test_coverage)
6
+
7
+ ![Index screenshot](docs/index-screenshot.png)
8
+
9
+ Exception Hunter is a Rails engine meant to track errors in your Rails project. It works
10
+ by using your Postgres database to save errors with their corresponding metadata (like backtrace
11
+ or environment data at the time of failure).
12
+
13
+ To do so we hook to various points of your application where we can rescue from errors, track and
14
+ then re-raise those errors so they are handled normally. As such, the gem does not conflict with any
15
+ other service so you can have your favorite error tracking service running in parallel with Exception Hunter
16
+ while you decide which you like best.
17
+
18
+ ## Motivation
19
+
20
+ Error tracking is one of the most important tools a developer can have in their toolset. As such
21
+ we think it'd be nice to provide a way for everyone to have it in their project, be it a personal
22
+ project, and MVP or something else.
23
+
24
+ ## Docs
25
+
26
+ You can check the full documentation at [https://rootstrap.github.io/exception_hunter]().
6
27
 
7
28
  ## Installation
8
29
  Add Exception Hunter to your application's Gemfile:
9
30
 
10
31
  ```ruby
11
- gem 'exception_hunter', '~> 0.2.0'
32
+ gem 'exception_hunter', '~> 1.0.0'
12
33
  ```
13
34
 
14
35
  You may also need to add [Devise](https://github.com/heartcombo/devise) to your Gemfile
@@ -32,8 +53,84 @@ you can run the command with the `--skip-users` flag.
32
53
  Additionally it should add the 'ExceptionHunter.routes(self)' line to your routes, which means you can go to
33
54
  `/exception_hunter/errors` in your browser and start enjoying some good old fashioned exception tracking!
34
55
 
35
- ## Contributing
36
- Contribution directions go here.
56
+ #### Testing it on dev:
57
+
58
+ ExceptionHunter is disabled on dev by default so if you want to test it before shipping it to another
59
+ environment, which we highly recommend, you should enable it by going to the initializer and changing the
60
+ line that says `config.enabled = !(Rails.env.development? || Rails.env.test?)` with something like
61
+ `config.enabled = !(Rails.env.test?)` while you test. Don't forget to change it back if you don't
62
+ want a bunch of errors in your local DB!
63
+
64
+ You can then open a `rails console` and manually track an exception to check that it
65
+ works `ExceptionHunter.track(StandardError.new("It works!"))`. You should now see the exception
66
+ on [http://localhost:3000/exception_hunter]().
67
+
68
+
69
+ ## Stale data
70
+
71
+ You can get rid of stale errors by running the rake task to purge them:
72
+
73
+ ```bash
74
+ $ rake exception_hunter:purge_errors
75
+ ```
76
+
77
+ We recommend you run this task once in a while to de-clutter your DB, using a recurring tasks once
78
+ a week would be ideal. You can also purge errors by running `ExceptionHunter::ErrorReaper.purge`.
79
+
80
+ The time it takes for an error to go stale defaults to 45 days but it's configurable via the initializer.
81
+
82
+ ## Manual tracking
83
+
84
+ ExceptionHunter also includes a facility to manually log from anywhere in the code. Imagine the following case:
85
+
86
+ ```ruby
87
+ case current_user.status
88
+ when :inactive then do_something
89
+ when :active then do_something_else
90
+ when :banned then do_something_else_else
91
+ else
92
+ ExceptionHunter.track(ArgumentError.new('This should never happen'), custom_data: { status: current_user.status }, current_user: user)
93
+ end
94
+ ```
95
+
96
+ In this scenario we don't really want to raise an exception but we might want to be alerted if by any chance a user
97
+ has an invalid status.
98
+
99
+ ## Slack notifications
100
+
101
+ You can configure ExceptionHunter to send a message to slack every time an error occurs.
102
+ You have to do the following:
103
+
104
+ 1. Create a Slack app.
105
+ 2. Add it to your workspace.
106
+ 3. Add one or more webhooks linked to the channels you want to receive the notifications.
107
+ 4. Set the webhook urls in the `exception_hunter` initializer.
108
+
109
+ ```ruby
110
+ config.notifiers << {
111
+ name: :slack,
112
+ options: {
113
+ webhook: 'SLACK_WEBHOOK_URL_1'
114
+ }
115
+ }
116
+
117
+ config.notifiers << {
118
+ name: :slack,
119
+ options: {
120
+ webhook: 'SLACK_WEBHOOK_URL_2'
121
+ }
122
+ }
123
+ ```
124
+
125
+ 6. Add the code below to the environment config file where you are using ExceptionHunter with the correct server url.
126
+
127
+ ```ruby
128
+ ExceptionHunter::Engine.configure do |config|
129
+ config.routes.default_url_options = { host: "your_server_url" }
130
+ end
131
+ ```
132
+
133
+ This uses ActiveJob to send notification in the background, so [make sure you configure](https://guides.rubyonrails.org/active_job_basics.html#setting-the-backend) it with the adapter you are using, if not notifications will be sent synchronously.
37
134
 
38
135
  ## License
39
136
  The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
@@ -1,19 +1,77 @@
1
1
  :root {
2
- --main-color: #C8193C;
3
- --secondary-color: #F4F5F6;
4
- --link-color: #158FEF;
5
- --border-color: #D1D1D1;
6
- --file-name-color: #16AF90
2
+ --background-grey: #E5E5E5;
3
+ --highlight-color: #CF4031;
4
+ --highlight-good-color: #2CBB85;
5
+ --highlight-inactive-color: #808183;
6
+ --inactive-grey: #D1D1D3;
7
+ --focused-grey: #808183;
8
+ --highlighted-link-blue: #0036F7;
9
+ --header-grey: #F8F8F8;
10
+ --border-grey: #F1F2F5;
11
+ --file-name-color: #2CBB85;
12
+ --tag-color: #EAE639;
13
+ }
14
+
15
+ body {
16
+ font-family: 'Inter', sans-serif;
17
+ background-color: var(--background-grey);
18
+ }
19
+
20
+ .container {
21
+ padding: 0;
22
+ }
23
+
24
+ .row {
25
+ margin: 0;
26
+ width: 100%;
27
+ }
28
+
29
+ .row .column {
30
+ padding: 0;
31
+ margin-bottom: 0;
7
32
  }
8
33
 
9
34
  .wrapper {
10
- margin: 5.5rem auto auto;
35
+ margin: 6.5rem auto auto;
36
+ }
37
+
38
+ .text--underline {
39
+ text-decoration: underline;
11
40
  }
12
41
 
13
42
  a {
14
- color: var(--link-color);
43
+ color: inherit;
15
44
  }
16
45
 
17
46
  a:hover, a:focus, a:active {
18
- color: var(--main-color);
47
+ color: inherit;
48
+ }
49
+
50
+ form {
51
+ margin-bottom: 0;
52
+ }
53
+
54
+ .flash.flash--notice {
55
+ background-color: #FFF;
56
+ margin-bottom: 2rem;
57
+ padding: 0.7rem 2rem;
58
+ border-radius: 5px;
59
+ line-height: 3.8rem;
60
+ font-weight: 400;
61
+ }
62
+
63
+ .button.button-dismiss {
64
+ margin-bottom: 0;
65
+ padding: 0;
66
+ color: var(--focused-grey);
67
+ width: 100%;
68
+ text-align: right;
69
+ }
70
+
71
+ .button.button-dismiss:hover, .button.button-dismiss:focus, .button.button-dismiss:active {
72
+ color: var(--inactive-grey);
73
+ }
74
+
75
+ .mr-5{
76
+ margin-right: 5px;
19
77
  }
@@ -2,65 +2,228 @@
2
2
  Place all the styles related to the matching controller here.
3
3
  They will automatically be included in application.css.
4
4
  */
5
+ .errors__container {
6
+ background-color: #FFF;
7
+ padding: 32px;
8
+ border-radius: 0 5px 5px 5px;
9
+ }
10
+
11
+ .errors-tabs {
12
+ display: flex;
13
+ }
14
+
15
+ ul.errors-tabs {
16
+ list-style-type: none;
17
+ margin-bottom: 0;
18
+ }
19
+
20
+ .errors-tab {
21
+ border-radius: 5px 5px 0 0;
22
+ margin-right: 1rem;
23
+ background-color: var(--inactive-grey);
24
+ }
5
25
 
6
- .row.statistics-row {
7
- padding-top: 1rem;
26
+ li.errors-tab {
27
+ margin-bottom: 0;
8
28
  }
9
29
 
10
- .statistics__cell {
11
- color: var(--main-color);
12
- background-color: var(--secondary-color);
13
- border: 1px solid var(--border-color);
14
- height: 5rem;
15
- border-radius: 10px;
30
+ .errors-tab--active, li.errors-tab a[aria-selected="true"] .errors-tab__content {
31
+ background-color: #FFF;
32
+ color: var(--highlight-color);
33
+ }
34
+
35
+ .errors-tab__resolved.errors-tab--active {
36
+ color: var(--highlight-good-color);
37
+ }
38
+
39
+ .errors-tab__content {
40
+ padding: 1rem;
41
+ display: flex;
42
+ }
43
+
44
+ .errors-tab__badge {
45
+ background: var(--highlight-inactive-color);
46
+ border-radius: 5px;
47
+ color: #FFF;
48
+ padding: 4px 8px;
49
+ }
50
+
51
+ .errors-tab--active .errors-tab__badge {
52
+ background-color: var(--highlight-color);
53
+ }
54
+
55
+ .errors-tab__resolved.errors-tab--active .errors-tab__badge {
56
+ background-color: var(--highlight-good-color);
57
+ }
58
+
59
+ .errors-tab__description {
60
+ margin-left: 1.3rem;
16
61
  display: flex;
17
- align-items: center;
18
62
  justify-content: center;
19
- font-size: 2.2rem;
63
+ align-items: center;
64
+ }
65
+
66
+ .button.purge-button {
67
+ background-color: var(--highlight-color);
68
+ border: none;
69
+ }
70
+
71
+ .button.purge-button:hover, .button.purge-button:focus {
72
+ background: var(--focused-grey);
73
+ }
74
+
75
+ .errors-date-group {
76
+ background-color: var(--header-grey);
77
+ padding: 0.5rem 1rem;
78
+ }
79
+
80
+ .errors-date-group ~ .errors-date-group {
81
+ border-bottom: 1px solid var(--border-grey);
20
82
  }
21
83
 
22
84
  .row.error-row {
23
- padding-top: 1rem;
24
- padding-bottom: 1rem;
25
- border-bottom: 1px solid;
85
+ padding: 1rem;
86
+ }
87
+
88
+ .error-row:not(.error-row--header) {
89
+ border: 1px solid var(--border-grey);
26
90
  }
27
91
 
28
- .row.error-row-no-border {
29
- padding-top: 1rem;
30
- padding-bottom: 1rem;
92
+ .error-message, .error-message:hover, .error-message:focus, .error-message:active {
93
+ color: var(--highlighted-link-blue);
31
94
  }
32
95
 
33
96
  .error-row.error-row--header {
34
- font-weight: bold;
97
+ background-color: var(--header-grey);
98
+ font-weight: 500;
99
+ color: #000;
100
+ }
101
+
102
+ .error-cell__message {
103
+ overflow-x: hidden;
104
+ }
105
+
106
+ .error-cell__tags {
107
+ font-weight: 500;
108
+ font-size: 12px;
109
+ color: #000;
110
+ padding-bottom: 3px;
35
111
  }
36
112
 
37
- .error-cell.error-cell--highlight {
38
- color: var(--main-color);
113
+ .error-cell__tags .error-tag {
114
+ background-color: var(--tag-color);
115
+ border-radius: 5px;
116
+ padding: 1px 5px 1px 5px;
39
117
  }
40
118
 
41
119
  .error-title {
42
- font-size: 20px;
43
- border-bottom: 1px solid var(--border-color);
120
+ color: var(--highlighted-link-blue);
121
+ padding-left: 2rem;
44
122
  }
45
123
 
124
+ .error-occurrence__header {
125
+ background: #FFF;
126
+ padding: 2rem 1rem;
127
+ margin-bottom: 2rem;
128
+ border-radius: 5px;
129
+ }
130
+
131
+ .button_to .button.button-outlined.resolve-button {
132
+ color: var(--highlight-good-color);
133
+ border-color: var(--highlight-good-color);
134
+ font-size: 10px;
135
+ padding: 0 1rem;
136
+ height: 3rem;
137
+ line-height: 3rem;
138
+ }
139
+
140
+ .button_to .button.resolve-button {
141
+ color: #FFF;
142
+ background-color: var(--highlight-good-color);
143
+ border-color: var(--highlight-good-color);
144
+ font-size: 10px;
145
+ padding: 0 1rem;
146
+ height: 3rem;
147
+ line-height: 3rem;
148
+ margin-bottom: 0;
149
+ }
150
+
151
+ .button.resolve-button:hover, .button.resolve-button:focus {
152
+ color: var(--focused-grey);
153
+ border-color: var(--focused-grey);
154
+ }
155
+
156
+ .button_to .button.button-outlined.ignore-button {
157
+ color: var(--highlight-color);
158
+ border-color: var(--highlight-color);
159
+ font-size: 10px;
160
+ padding: 0 1rem;
161
+ height: 3rem;
162
+ line-height: 3rem;
163
+ }
164
+
165
+ .button_to .button.ignore-button {
166
+ color: #FFF;
167
+ background-color: var(--highlight-color);
168
+ border-color: var(--highlight-color);
169
+ font-size: 10px;
170
+ padding: 0 1rem;
171
+ height: 3rem;
172
+ line-height: 3rem;
173
+ margin-bottom: 0;
174
+ }
175
+
176
+ .button.ignore-button:hover, .button.ignore-button:focus {
177
+ color: var(--focused-grey);
178
+ border-color: var(--focused-grey);
179
+ }
180
+
46
181
  .error-occurred_at {
47
- color: var(--main-color);
182
+ color: var(--highlight-color);
48
183
  font-size: 14px;
49
184
  margin-top: 0.5em;
50
185
  margin-bottom: 2em;
51
186
  }
52
187
 
188
+ .error-occurrences__nav {
189
+ display: flex;
190
+ justify-content: flex-end;
191
+ height: 100%;
192
+ align-items: center;
193
+ }
194
+
195
+ .button.button-outline.error-occurrences__nav-link {
196
+ margin-bottom: 0;
197
+ padding: 0 1rem;
198
+ height: 2.5rem;
199
+ line-height: 2.5rem;
200
+ margin-right: 0.5rem;
201
+ border-color: #000;
202
+ color: #000;
203
+ }
204
+
205
+ .button.button-outline.error-occurrences__nav-link[disabled="disabled"],
206
+ .button.button-outline.error-occurrences__nav-link[disabled="disabled"]:focus,
207
+ .button.button-outline.error-occurrences__nav-link[disabled="disabled"]:hover {
208
+ border-color: var(--highlight-inactive-color);
209
+ color: var(--highlight-inactive-color);
210
+ }
211
+
212
+ .error-occurrences__nav-current {
213
+ margin: 0 2rem;
214
+ }
215
+
53
216
  .tab-content {
54
217
  padding: 1em 0.5em;
55
218
  }
56
219
 
57
220
  .data-title {
58
221
  font-weight: bold;
59
- color: var(--main-color);
222
+ color: var(--highlight-color);
60
223
  }
61
224
 
62
225
  .tracked-data {
63
- border-left-color: var(--main-color);
226
+ border-left-color: var(--highlight-color);
64
227
  }
65
228
 
66
229
  .backtrace {
@@ -80,7 +243,7 @@
80
243
 
81
244
  .backtrace-line__line-number {
82
245
  margin-right: 5px;
83
- color: var(--main-color);
246
+ color: var(--highlight-color);
84
247
  }
85
248
 
86
249
  .backtrace-line__file-name {