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.
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 {