solid_errors 0.3.4 → 0.4.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 +113 -11
- data/app/controllers/solid_errors/application_controller.rb +20 -0
- data/app/controllers/solid_errors/errors_controller.rb +11 -1
- data/app/mailers/solid_errors/error_mailer.rb +15 -0
- data/app/models/solid_errors/backtrace_line.rb +6 -5
- data/app/models/solid_errors/occurrence.rb +6 -0
- data/app/views/layouts/solid_errors/_style.html +1114 -0
- data/app/views/layouts/solid_errors/application.html.erb +11 -1085
- data/app/views/solid_errors/error_mailer/error_occurred.html.erb +17 -0
- data/app/views/solid_errors/error_mailer/error_occurred.text.erb +0 -0
- data/app/views/solid_errors/errors/_error.html.erb +98 -27
- data/app/views/solid_errors/errors/_row.html.erb +31 -0
- data/app/views/solid_errors/errors/index.html.erb +1 -1
- data/app/views/solid_errors/errors/show.html.erb +5 -101
- data/app/views/solid_errors/occurrences/_collection.html.erb +38 -3
- data/app/views/solid_errors/occurrences/_occurrence.html.erb +9 -4
- data/lib/generators/solid_errors/install/templates/create_solid_errors_tables.rb.erb +5 -6
- data/lib/solid_errors/engine.rb +1 -1
- data/lib/solid_errors/sanitizer.rb +1 -1
- data/lib/solid_errors/subscriber.rb +3 -2
- data/lib/solid_errors/version.rb +1 -1
- data/lib/solid_errors.rb +17 -2
- metadata +78 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: b0f7fa69e2efa8949c085b3ba7b72b407dd3d670643c76260a6f8addf836305f
|
4
|
+
data.tar.gz: f931d1de445d5d3fe124dc352e2a81513d174d37ed6ae923122e95e2468ccc7e
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 3a2eb10327f7a11569d1cc90f2418bd28252d177b23c329c8a4105b2cb22a4fa17e36b44dbe5f1f341c222de2be9ba149fb4ce6e308b5c6aa99b1160de678b83
|
7
|
+
data.tar.gz: 52f3d7fd1669173da1ce9155370d92bae1678c4ae4326c2637e026c93cc9eeb84e091c587f8322fd6b9f9228c79f31262c0cd9b42be98d65837004754efc2ec3
|
data/README.md
CHANGED
@@ -30,22 +30,28 @@ Solid Errors is a DB-based, app-internal exception tracker for Rails application
|
|
30
30
|
## Installation
|
31
31
|
|
32
32
|
Install the gem and add to the application's Gemfile by executing:
|
33
|
-
|
34
|
-
|
33
|
+
```bash
|
34
|
+
$ bundle add solid_errors
|
35
|
+
```
|
35
36
|
|
36
37
|
If bundler is not being used to manage dependencies, install the gem by executing:
|
37
|
-
|
38
|
-
|
38
|
+
```bash
|
39
|
+
$ gem install solid_errors
|
40
|
+
```
|
39
41
|
|
40
42
|
After installing the gem, run the installer:
|
41
|
-
|
42
|
-
|
43
|
+
```bash
|
44
|
+
$ rails generate solid_errors:install
|
45
|
+
```
|
43
46
|
|
44
47
|
This will copy the required migration over to your app.
|
45
48
|
|
46
|
-
Then mount the engine in your config/routes.rb file
|
47
|
-
|
48
|
-
|
49
|
+
Then mount the engine in your `config/routes.rb` file:
|
50
|
+
```ruby
|
51
|
+
authenticate :user, -> (user) { user.admin? } do
|
52
|
+
mount SolidErrors::Engine, at: "/solid_errors"
|
53
|
+
end
|
54
|
+
```
|
49
55
|
|
50
56
|
> [!NOTE]
|
51
57
|
> Be sure to [secure the dashboard](#authentication) in production.
|
@@ -60,11 +66,14 @@ There are intentionally few features; you can view and resolve errors. That’s
|
|
60
66
|
|
61
67
|
### Configuration
|
62
68
|
|
63
|
-
You can configure Solid Errors via the Rails configuration object, under the `solid_errors` key. Currently,
|
69
|
+
You can configure Solid Errors via the Rails configuration object, under the `solid_errors` key. Currently, 6 configuration options are available:
|
64
70
|
|
65
71
|
* `connects_to` - The database configuration to use for the Solid Errors database. See [Database Configuration](#database-configuration) for more information.
|
66
72
|
* `username` - The username to use for HTTP authentication. See [Authentication](#authentication) for more information.
|
67
73
|
* `password` - The password to use for HTTP authentication. See [Authentication](#authentication) for more information.
|
74
|
+
* `sends_email` - Whether or not to send emails when an error occurs. See [Email notifications](#email-notifications) for more information.
|
75
|
+
* `email_from` - The email address to send a notification from. See [Email notifications](#email-notifications) for more information.
|
76
|
+
* `email_to` - The email address(es) to send a notification to. See [Email notifications](#email-notifications) for more information.
|
68
77
|
|
69
78
|
#### Database Configuration
|
70
79
|
|
@@ -103,7 +112,7 @@ config.solid_errors.password = Rails.application.credentials.solid_errors.passwo
|
|
103
112
|
|
104
113
|
Either way, if you have set a username and password, Solid Errors will use basic HTTP authentication. If you have not set a username and password, Solid Errors will not require any authentication to view the dashboard.
|
105
114
|
|
106
|
-
If you use Devise for
|
115
|
+
If you use Devise for authentication in your app, you can also restrict access to the dashboard by using their `authenticate` constraint in your routes file:
|
107
116
|
|
108
117
|
```ruby
|
109
118
|
authenticate :user, -> (user) { user.admin? } do
|
@@ -111,6 +120,29 @@ authenticate :user, -> (user) { user.admin? } do
|
|
111
120
|
end
|
112
121
|
```
|
113
122
|
|
123
|
+
#### Email notifications
|
124
|
+
|
125
|
+
Solid Errors _can_ send email notifications whenever an error occurs, if your application has ActionMailer already properly setup to send emails. However, in order to activate this feature you must define the email address(es) to send the notifications to. Optionally, you can also define the email address to send the notifications from (useful if your email provider only allows emails to be sent from a predefined list of addresses) or simply turn off this feature altogether.
|
126
|
+
|
127
|
+
There are two ways to configure email notifications. First, you can use environment variables:
|
128
|
+
|
129
|
+
```ruby
|
130
|
+
ENV["SOLIDERRORS_SEND_EMAILS"] = true # defaults to true
|
131
|
+
ENV["SOLIDERRORS_EMAIL_FROM"] = "errors@myapp.com" # defaults to "solid_errors@noreply.com"
|
132
|
+
ENV["SOLIDERRORS_EMAIL_TO"] = "devs@myapp.com" # no default, must be set
|
133
|
+
```
|
134
|
+
|
135
|
+
Second, you can set the values via the configuration object:
|
136
|
+
|
137
|
+
```ruby
|
138
|
+
# Set authentication credentials for Solid Errors
|
139
|
+
config.solid_errors.send_emails = true
|
140
|
+
config.solid_errors.email_from = "errors@myapp.com"
|
141
|
+
config.solid_errors.email_to = "devs@myapp.com"
|
142
|
+
```
|
143
|
+
|
144
|
+
If you have set `send_emails` to `true` and have set an `email_to` address, Solid Errors will send an email notification whenever an error occurs. If you have not set `send_emails` to `true` or have not set an `email_to` address, Solid Errors will not send any email notifications.
|
145
|
+
|
114
146
|
### Examples
|
115
147
|
|
116
148
|
There are only two screens in the dashboard.
|
@@ -123,6 +155,76 @@ There are only two screens in the dashboard.
|
|
123
155
|
|
124
156
|
![image description](images/show-screenshot.png)
|
125
157
|
|
158
|
+
### Usage with API-only Applications
|
159
|
+
|
160
|
+
If your Rails application is an API-only application (generated with the `rails new --api` command), you will need to add the following middleware to your `config/application.rb` file in order to use the dashboard UI provided by Solid Errors:
|
161
|
+
|
162
|
+
```ruby
|
163
|
+
# /config/application.rb
|
164
|
+
config.middleware.use ActionDispatch::Cookies
|
165
|
+
config.middleware.use ActionDispatch::Session::CookieStore
|
166
|
+
config.middleware.use ActionDispatch::Flash
|
167
|
+
```
|
168
|
+
|
169
|
+
### Overwriting the views
|
170
|
+
|
171
|
+
You can find the views in [`app/views`](https://github.com/fractaledmind/solid_errors/tree/main/app/views).
|
172
|
+
|
173
|
+
```bash
|
174
|
+
app/views/
|
175
|
+
├── layouts
|
176
|
+
│ └── solid_errors
|
177
|
+
│ ├── _style.html
|
178
|
+
│ └── application.html.erb
|
179
|
+
└── solid_errors
|
180
|
+
├── error_mailer
|
181
|
+
│ ├── error_occurred.html.erb
|
182
|
+
│ └── error_occurred.text.erb
|
183
|
+
├── errors
|
184
|
+
│ ├── _actions.html.erb
|
185
|
+
│ ├── _error.html.erb
|
186
|
+
│ ├── _row.html.erb
|
187
|
+
│ ├── index.html.erb
|
188
|
+
│ └── show.html.erb
|
189
|
+
└── occurrences
|
190
|
+
├── _collection.html.erb
|
191
|
+
└── _occurrence.html.erb
|
192
|
+
```
|
193
|
+
|
194
|
+
You can always take control of the views by creating your own views and/or partials at these paths in your application. For example, if you wanted to overwrite the application layout, you could create a file at `app/views/layouts/solid_errors/application.html.erb`. If you wanted to remove the footer and the automatically disappearing flash messages, as one concrete example, you could define that file as:
|
195
|
+
|
196
|
+
```erb
|
197
|
+
<!DOCTYPE html>
|
198
|
+
<html>
|
199
|
+
<head>
|
200
|
+
<title>Solid Errors</title>
|
201
|
+
<%= csrf_meta_tags %>
|
202
|
+
<%= csp_meta_tag %>
|
203
|
+
|
204
|
+
<%= render "layouts/solid_errors/style" %>
|
205
|
+
</head>
|
206
|
+
<body class="pb-4">
|
207
|
+
<main class="container mx-auto mt-4">
|
208
|
+
<%= content_for?(:content) ? yield(:content) : yield %>
|
209
|
+
</main>
|
210
|
+
|
211
|
+
<div class="fixed top-0 left-0 right-0 text-center py-2">
|
212
|
+
<% if notice.present? %>
|
213
|
+
<p class="py-2 px-3 bg-green-50 text-green-500 font-medium rounded-lg inline-block">
|
214
|
+
<%= notice %>
|
215
|
+
</p>
|
216
|
+
<% end %>
|
217
|
+
|
218
|
+
<% if alert.present? %>
|
219
|
+
<p class="py-2 px-3 bg-red-50 text-red-500 font-medium rounded-lg inline-block">
|
220
|
+
<%= alert %>
|
221
|
+
</p>
|
222
|
+
<% end %>
|
223
|
+
</div>
|
224
|
+
</body>
|
225
|
+
</html>
|
226
|
+
```
|
227
|
+
|
126
228
|
## Development
|
127
229
|
|
128
230
|
After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake test` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
|
@@ -3,5 +3,25 @@ module SolidErrors
|
|
3
3
|
protect_from_forgery with: :exception
|
4
4
|
|
5
5
|
http_basic_authenticate_with name: SolidErrors.username, password: SolidErrors.password if SolidErrors.password
|
6
|
+
|
7
|
+
# adapted from: https://github.com/ddnexus/pagy/blob/master/gem/lib/pagy.rb
|
8
|
+
OverflowError = Class.new(StandardError)
|
9
|
+
class Page
|
10
|
+
attr_reader :count, :items, :pages, :first, :last, :prev, :next, :offset, :from, :to
|
11
|
+
|
12
|
+
def initialize(collection, params)
|
13
|
+
@count = collection.count
|
14
|
+
@items = (params[:items] || 20).to_i
|
15
|
+
@pages = [(@count.to_f / @items).ceil, 1].max
|
16
|
+
@page = ((page = (params[:page] || 1).to_i) > @pages) ? @pages : page
|
17
|
+
@first = (1 unless @page == 1)
|
18
|
+
@last = (@pages unless @page == @pages)
|
19
|
+
@prev = (@page - 1 unless @page == 1)
|
20
|
+
@next = (@page == @pages) ? nil : @page + 1
|
21
|
+
@offset = (@items * (@page - 1))
|
22
|
+
@from = [@offset + 1, @count].min
|
23
|
+
@to = [@offset + @items, @count].min
|
24
|
+
end
|
25
|
+
end
|
6
26
|
end
|
7
27
|
end
|
@@ -1,5 +1,7 @@
|
|
1
1
|
module SolidErrors
|
2
2
|
class ErrorsController < ApplicationController
|
3
|
+
around_action :force_english_locale!
|
4
|
+
|
3
5
|
before_action :set_error, only: %i[show update]
|
4
6
|
|
5
7
|
# GET /errors
|
@@ -9,13 +11,17 @@ module SolidErrors
|
|
9
11
|
|
10
12
|
@errors = Error.unresolved
|
11
13
|
.joins(:occurrences)
|
12
|
-
.select(errors_table[Arel.star],
|
14
|
+
.select(errors_table[Arel.star],
|
15
|
+
occurrences_table[:created_at].maximum.as("recent_occurrence"),
|
16
|
+
occurrences_table[:id].count.as("occurrences_count"))
|
13
17
|
.group(errors_table[:id])
|
14
18
|
.order(recent_occurrence: :desc)
|
15
19
|
end
|
16
20
|
|
17
21
|
# GET /errors/1
|
18
22
|
def show
|
23
|
+
@page = Page.new(@error.occurrences, params)
|
24
|
+
@occurrences = @error.occurrences.offset(@page.offset).limit(@page.items)
|
19
25
|
end
|
20
26
|
|
21
27
|
# PATCH/PUT /errors/1
|
@@ -34,5 +40,9 @@ module SolidErrors
|
|
34
40
|
def set_error
|
35
41
|
@error = Error.find(params[:id])
|
36
42
|
end
|
43
|
+
|
44
|
+
def force_english_locale!(&action)
|
45
|
+
I18n.with_locale(:en, &action)
|
46
|
+
end
|
37
47
|
end
|
38
48
|
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
module SolidErrors
|
2
|
+
# adapted from: https://github.com/codergeek121/email_error_reporter/blob/main/lib/email_error_reporter/error_mailer.rb
|
3
|
+
class ErrorMailer < ActionMailer::Base
|
4
|
+
def error_occurred(occurrence)
|
5
|
+
@occurrence = occurrence
|
6
|
+
@error = occurrence.error
|
7
|
+
|
8
|
+
mail(
|
9
|
+
subject: "#{@error.emoji} #{@error.exception_class}",
|
10
|
+
from: SolidErrors.email_from,
|
11
|
+
to: SolidErrors.email_to
|
12
|
+
)
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -2,7 +2,7 @@ module SolidErrors
|
|
2
2
|
# adapted from: https://github.com/honeybadger-io/honeybadger-ruby/blob/master/lib/honeybadger/backtrace.rb
|
3
3
|
class BacktraceLine
|
4
4
|
# Backtrace line regexp (optionally allowing leading X: for windows support).
|
5
|
-
INPUT_FORMAT = %r{^((?:[a-zA-Z]:)?[^:]+):(\d+)(?::in `([^']+)')?$}
|
5
|
+
INPUT_FORMAT = %r{^((?:[a-zA-Z]:)?[^:]+):(\d+)(?::in `([^']+)')?$}
|
6
6
|
STRING_EMPTY = "".freeze
|
7
7
|
GEM_ROOT = "[GEM_ROOT]".freeze
|
8
8
|
PROJECT_ROOT = "[PROJECT_ROOT]".freeze
|
@@ -29,7 +29,7 @@ module SolidErrors
|
|
29
29
|
attr_reader :file
|
30
30
|
attr_reader :number
|
31
31
|
attr_reader :method
|
32
|
-
attr_reader :filtered_file, :filtered_number, :filtered_method
|
32
|
+
attr_reader :filtered_file, :filtered_number, :filtered_method, :unparsed_line
|
33
33
|
|
34
34
|
# Parses a single line of a given backtrace
|
35
35
|
#
|
@@ -47,13 +47,14 @@ module SolidErrors
|
|
47
47
|
|
48
48
|
file, number, method = match[1], match[2], match[3]
|
49
49
|
filtered_args = [fmatch[1], fmatch[2], fmatch[3]]
|
50
|
-
new(file, number, method, *filtered_args, opts.fetch(:source_radius, 2))
|
50
|
+
new(unparsed_line, file, number, method, *filtered_args, opts.fetch(:source_radius, 2))
|
51
51
|
end
|
52
52
|
end
|
53
53
|
|
54
|
-
def initialize(file, number, method, filtered_file = file,
|
54
|
+
def initialize(unparsed_line, file, number, method, filtered_file = file,
|
55
55
|
filtered_number = number, filtered_method = method,
|
56
56
|
source_radius = 2)
|
57
|
+
self.unparsed_line = unparsed_line
|
57
58
|
self.filtered_file = filtered_file
|
58
59
|
self.filtered_number = filtered_number
|
59
60
|
self.filtered_method = filtered_method
|
@@ -87,7 +88,7 @@ module SolidErrors
|
|
87
88
|
|
88
89
|
private
|
89
90
|
|
90
|
-
attr_writer :file, :number, :method, :filtered_file, :filtered_number, :filtered_method
|
91
|
+
attr_writer :file, :number, :method, :filtered_file, :filtered_number, :filtered_method, :unparsed_line
|
91
92
|
|
92
93
|
attr_accessor :source_radius
|
93
94
|
|
@@ -2,6 +2,8 @@ module SolidErrors
|
|
2
2
|
class Occurrence < Record
|
3
3
|
belongs_to :error, class_name: "SolidErrors::Error"
|
4
4
|
|
5
|
+
after_create_commit :send_email, if: -> { SolidErrors.send_emails? && SolidErrors.email_to.present? }
|
6
|
+
|
5
7
|
# The parsed exception backtrace. Lines in this backtrace that are from installed gems
|
6
8
|
# have the base path for gem installs replaced by "[GEM_ROOT]", while those in the project
|
7
9
|
# have "[PROJECT_ROOT]".
|
@@ -17,5 +19,9 @@ module SolidErrors
|
|
17
19
|
def parse_backtrace(backtrace)
|
18
20
|
Backtrace.parse(backtrace)
|
19
21
|
end
|
22
|
+
|
23
|
+
def send_email
|
24
|
+
ErrorMailer.error_occurred(self).deliver_later
|
25
|
+
end
|
20
26
|
end
|
21
27
|
end
|