activeerror 1.1.2 → 2.1.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/LICENSE.md +1 -1
- data/README.md +12 -11
- data/Rakefile +3 -21
- data/app/assets/stylesheets/activeerror/application.css +175 -0
- data/app/assets/stylesheets/activeerror/calibre-bold.woff2 +0 -0
- data/app/assets/stylesheets/activeerror/calibre-regular-italic.woff2 +0 -0
- data/app/assets/stylesheets/activeerror/calibre-regular.woff2 +0 -0
- data/app/assets/stylesheets/activeerror/calibre-semibold-italic.woff2 +0 -0
- data/app/assets/stylesheets/activeerror/calibre-semibold.woff2 +0 -0
- data/app/controllers/active_error/faults_controller.rb +5 -1
- data/app/models/active_error/fault.rb +8 -2
- data/app/models/active_error/instance.rb +4 -1
- data/app/views/active_error/faults/index.html.erb +36 -13
- data/app/views/layouts/active_error/application.html.erb +1 -90
- data/app/views/layouts/active_error/rescue.html.erb +278 -0
- data/config/routes.rb +1 -1
- data/db/migrate/20241218160110_add_session_to_instances.rb +7 -0
- data/lib/active_error/captor.rb +2 -2
- data/lib/active_error/engine.rb +12 -2
- data/lib/active_error/error_subscriber.rb +12 -0
- data/lib/active_error/renderer.rb +23 -2
- data/lib/active_error/version.rb +1 -1
- data/lib/active_error.rb +33 -3
- data/lib/tasks/active_error_tasks.rake +8 -0
- metadata +14 -7
- data/app/views/active_error/faults/show.html.erb +0 -17
- data/lib/active_error/middleware.rb +0 -19
- data/lib/active_error/railtie.rb +0 -18
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 8863be8933a7fabe7cf1a290850f48945386d423bcf4e9af26b586d79a91a4a2
|
4
|
+
data.tar.gz: 4f74875e89add4919da890e32991c3487a4bec173d444c562f68fbb66c42adb8
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 7d5b80abc31ed68518b39378a2d1c51887ece3a2cae2d4d3b93faaafc07bbdbc1910807ef80c3b3ad8f4d107c9868408153e0f5ff7e4212c5f5de3dded0fcf3b
|
7
|
+
data.tar.gz: 48d4a99d59716a84e24074b9eda55aa5a84b113398989a9b8f32bd64692a5752dad21ebc6eda2d477c89edd17ee1a905222c9ac189c42255f39c0581ab512837
|
data/LICENSE.md
CHANGED
data/README.md
CHANGED
@@ -1,12 +1,14 @@
|
|
1
1
|
# ActiveError
|
2
2
|
|
3
3
|
One of the fundemental tools needed to take your Rails app to production is a
|
4
|
-
way to track errors that are triggered. Unfortunately,
|
5
|
-
open source
|
4
|
+
way to track errors that are triggered. Unfortunately, there aren't many free,
|
5
|
+
easy, open source ways to track them for small or medium apps. Honeybadger, Sentry,
|
6
6
|
and AppSignal are great, but they are are closed source. With Sentry
|
7
7
|
looking at using your data as training
|
8
8
|
data([link](https://blog.sentry.io/ai-privacy-and-terms-of-service-updates/?original_referrer=https%3A%2F%2Fsentry.io%2F))
|
9
9
|
there should be an easy open source alternative where you control the data.
|
10
|
+
With Rails 8's ethos of No PAAS, there should be a way for new apps to start out
|
11
|
+
with a basic error reporter and not be forced to pay a third party for one.
|
10
12
|
|
11
13
|
ActiveError hooks into the [error reporting
|
12
14
|
api](https://guides.rubyonrails.org/error_reporting.html) baked directly into
|
@@ -16,8 +18,8 @@ with is the one baked into Rails that we use everyday in development. So with
|
|
16
18
|
ActiveError, when an error gets raised it's captured and stored in the
|
17
19
|
database (we attempt to group the same error together as Instances to reduce
|
18
20
|
noise) and then we recreate the error and display it using the built in debug
|
19
|
-
view from Rails. Once you've resolved the
|
20
|
-
will destroy the record.
|
21
|
+
view from Rails (with a few font and styling tweaks). Once you've resolved the
|
22
|
+
error you can click "Resolve" which will destroy the record.
|
21
23
|
|
22
24
|

|
23
25
|

|
@@ -39,22 +41,21 @@ Or install it yourself as:
|
|
39
41
|
$ gem install activeerror
|
40
42
|
```
|
41
43
|
|
42
|
-
|
44
|
+
Run the installer and migrate:
|
43
45
|
```bash
|
44
|
-
bin/rails
|
46
|
+
bin/rails active_error:install
|
45
47
|
bin/rails db:migrate
|
46
48
|
```
|
47
49
|
|
48
|
-
This
|
49
|
-
|
50
|
+
This will mount a route in your routes file to view the errors at `/errors`.
|
50
51
|
|
51
52
|
##### Config
|
52
53
|
|
53
|
-
You can supply the string version of an error class to `ignored` to
|
54
|
-
classes of errors.
|
54
|
+
You can supply a list of the string version of an error class to `ignored` to
|
55
|
+
ignore classes of errors.
|
55
56
|
|
56
57
|
```ruby
|
57
|
-
|
58
|
+
config.active_error.ignored = ["NoMethodError"]
|
58
59
|
```
|
59
60
|
|
60
61
|
## Development
|
data/Rakefile
CHANGED
@@ -1,20 +1,6 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
|
4
|
-
require "bundler/setup"
|
5
|
-
rescue LoadError
|
6
|
-
puts "You must `gem install bundler` and `bundle install` to run rake tasks"
|
7
|
-
end
|
8
|
-
|
9
|
-
require "rdoc/task"
|
10
|
-
|
11
|
-
RDoc::Task.new(:rdoc) do |rdoc|
|
12
|
-
rdoc.rdoc_dir = "rdoc"
|
13
|
-
rdoc.title = "ActiveError"
|
14
|
-
rdoc.options << "--line-numbers"
|
15
|
-
rdoc.rdoc_files.include("README.md")
|
16
|
-
rdoc.rdoc_files.include("lib/**/*.rb")
|
17
|
-
end
|
3
|
+
require "bundler/setup"
|
18
4
|
|
19
5
|
APP_RAKEFILE = File.expand_path("test/dummy/Rakefile", __dir__)
|
20
6
|
load "rails/tasks/engine.rake"
|
@@ -23,12 +9,8 @@ load "rails/tasks/statistics.rake"
|
|
23
9
|
|
24
10
|
require "bundler/gem_tasks"
|
25
11
|
|
26
|
-
|
27
|
-
|
28
|
-
Rake::TestTask.new(:test) do |t|
|
29
|
-
t.libs << "test"
|
30
|
-
t.pattern = "test/**/*_test.rb"
|
31
|
-
t.verbose = false
|
12
|
+
task test: :environment do
|
13
|
+
sh("bin/rails test")
|
32
14
|
end
|
33
15
|
|
34
16
|
task default: :test
|
@@ -0,0 +1,175 @@
|
|
1
|
+
@font-face {
|
2
|
+
font-family: 'Calibre';
|
3
|
+
font-style: normal;
|
4
|
+
font-weight: 400;
|
5
|
+
src: url('./calibre-regular.woff2') format('woff2');
|
6
|
+
font-display: swap;
|
7
|
+
}
|
8
|
+
|
9
|
+
@font-face {
|
10
|
+
font-family: 'Calibre';
|
11
|
+
font-style: italic;
|
12
|
+
font-weight: 400;
|
13
|
+
src: url('./calibre-regular-italic.woff2') format('woff2');
|
14
|
+
font-display: swap;
|
15
|
+
}
|
16
|
+
|
17
|
+
@font-face {
|
18
|
+
font-family: 'Calibre';
|
19
|
+
font-style: normal;
|
20
|
+
font-weight: 600;
|
21
|
+
src: url('./calibre-semibold.woff2') format('woff2');
|
22
|
+
font-display: swap;
|
23
|
+
}
|
24
|
+
|
25
|
+
@font-face {
|
26
|
+
font-family: 'Calibre';
|
27
|
+
font-style: italic;
|
28
|
+
font-weight: 600;
|
29
|
+
src: url('./calibre-semibold-italic.woff2') format('woff2');
|
30
|
+
font-display: swap;
|
31
|
+
}
|
32
|
+
|
33
|
+
@font-face {
|
34
|
+
font-family: 'Calibre';
|
35
|
+
font-style: normal;
|
36
|
+
font-weight: 700;
|
37
|
+
src: url('./calibre-bold.woff2') format('woff2');
|
38
|
+
font-display: swap;
|
39
|
+
}
|
40
|
+
|
41
|
+
.active_error {
|
42
|
+
body {
|
43
|
+
background-color: #fff;
|
44
|
+
color: #333;
|
45
|
+
margin: 0px;
|
46
|
+
}
|
47
|
+
|
48
|
+
body, p, ol, ul, td, input {
|
49
|
+
font-family: helvetica, verdana, arial, sans-serif;
|
50
|
+
font-size: 15px;
|
51
|
+
line-height: 18px;
|
52
|
+
font-family: 'Calibre';
|
53
|
+
}
|
54
|
+
|
55
|
+
header {
|
56
|
+
color: #D30001;
|
57
|
+
background: rgb(238, 231, 233);
|
58
|
+
padding: 7.5px 22.5px;
|
59
|
+
display: flex;
|
60
|
+
flex-direction: row;
|
61
|
+
justify-content: space-between;
|
62
|
+
align-items: center;
|
63
|
+
}
|
64
|
+
|
65
|
+
h1 {
|
66
|
+
flex: 1;
|
67
|
+
overflow-wrap: break-word;
|
68
|
+
margin: 0.2em 0;
|
69
|
+
line-height: 1.1em;
|
70
|
+
font-size: 30px;
|
71
|
+
}
|
72
|
+
|
73
|
+
#notice {
|
74
|
+
font-family: 'Calibre';
|
75
|
+
color: #000;
|
76
|
+
margin: 0px;
|
77
|
+
font-size: 20px;
|
78
|
+
}
|
79
|
+
|
80
|
+
table {
|
81
|
+
margin: 0;
|
82
|
+
border-collapse: collapse;
|
83
|
+
word-wrap:break-word;
|
84
|
+
table-layout: auto;
|
85
|
+
width: 100%;
|
86
|
+
}
|
87
|
+
|
88
|
+
.tr {
|
89
|
+
border-bottom: 3px solid rgba(38,27,35,0.1);
|
90
|
+
display: grid;
|
91
|
+
grid-template-columns: 10px 70px 1fr 215px 150px 180px;
|
92
|
+
align-items: center;
|
93
|
+
|
94
|
+
.all-cols {
|
95
|
+
grid-column: span 6 / span 6;
|
96
|
+
margin-bottom: 10px;
|
97
|
+
|
98
|
+
&> div {
|
99
|
+
display: grid;
|
100
|
+
grid-template-columns: 1fr 215px 300px;
|
101
|
+
margin-left: 110px;
|
102
|
+
flex-direction: row;
|
103
|
+
|
104
|
+
&> div {
|
105
|
+
padding-right: 5px;
|
106
|
+
}
|
107
|
+
div {
|
108
|
+
margin: 3px 0px;
|
109
|
+
|
110
|
+
a {
|
111
|
+
word-break: break-all;
|
112
|
+
}
|
113
|
+
}
|
114
|
+
}
|
115
|
+
}
|
116
|
+
|
117
|
+
a {
|
118
|
+
color: #261B23;
|
119
|
+
&:hover {
|
120
|
+
color: #D30001;
|
121
|
+
}
|
122
|
+
}
|
123
|
+
|
124
|
+
.td {
|
125
|
+
font-family: 'Calibre';
|
126
|
+
font-size: 20px;
|
127
|
+
|
128
|
+
&.chevron {
|
129
|
+
padding: 0px;
|
130
|
+
padding-left: 3px;
|
131
|
+
cursor: pointer;
|
132
|
+
font-size: 14px;
|
133
|
+
&.rotate {
|
134
|
+
transform: rotate(90deg);
|
135
|
+
}
|
136
|
+
}
|
137
|
+
|
138
|
+
&.location {
|
139
|
+
display: flex;
|
140
|
+
flex-direction: column;
|
141
|
+
|
142
|
+
span {
|
143
|
+
font-size: 16px;
|
144
|
+
margin-top: 10px;
|
145
|
+
}
|
146
|
+
}
|
147
|
+
}
|
148
|
+
}
|
149
|
+
|
150
|
+
.tr:nth-child(even) {
|
151
|
+
background: rgb(238, 231, 233);
|
152
|
+
}
|
153
|
+
|
154
|
+
.tr .td {
|
155
|
+
padding: 10px 30px;
|
156
|
+
}
|
157
|
+
|
158
|
+
.button-as-link {
|
159
|
+
border: none;
|
160
|
+
text-decoration: none;
|
161
|
+
cursor: pointer;
|
162
|
+
background: none;
|
163
|
+
font-size: 20px;
|
164
|
+
margin: 0;
|
165
|
+
padding: 7px 25px;
|
166
|
+
line-height: 18px;
|
167
|
+
color: #fff;
|
168
|
+
border-radius: 9999px;
|
169
|
+
background-color: #D30001;
|
170
|
+
}
|
171
|
+
}
|
172
|
+
|
173
|
+
.hidden {
|
174
|
+
display: none;
|
175
|
+
}
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
@@ -5,10 +5,14 @@ module ActiveError
|
|
5
5
|
before_action :set_fault, only: %i(show destroy)
|
6
6
|
|
7
7
|
def index
|
8
|
-
@faults = Fault.top_level
|
8
|
+
@faults = Fault.top_level.includes(:instances)
|
9
9
|
end
|
10
10
|
|
11
11
|
def show
|
12
|
+
instance = @fault.instances.last ||
|
13
|
+
@fault.instances.new(headers: {}, parameters: {})
|
14
|
+
|
15
|
+
render html: Renderer.new(instance:).body
|
12
16
|
end
|
13
17
|
|
14
18
|
def destroy
|
@@ -2,12 +2,14 @@
|
|
2
2
|
|
3
3
|
module ActiveError
|
4
4
|
class Fault < ApplicationRecord
|
5
|
+
self.filter_attributes = [:backtrace]
|
6
|
+
|
5
7
|
has_one :parent_cause, class_name: "ActiveError::Fault",
|
6
8
|
foreign_key: :cause_id, inverse_of: :cause,
|
7
9
|
dependent: :restrict_with_error
|
8
10
|
belongs_to :cause, optional: true, class_name: "ActiveError::Fault",
|
9
11
|
dependent: :destroy
|
10
|
-
has_many :instances, dependent: :
|
12
|
+
has_many :instances, dependent: :delete_all
|
11
13
|
|
12
14
|
store :options, accessors: [:template], coder: YAML,
|
13
15
|
yaml: { unsafe_load: true }
|
@@ -39,12 +41,16 @@ module ActiveError
|
|
39
41
|
super(Marshal.dump(new_template))
|
40
42
|
end
|
41
43
|
|
44
|
+
def location
|
45
|
+
Rails.backtrace_cleaner.clean(backtrace).first
|
46
|
+
end
|
47
|
+
|
42
48
|
private
|
43
49
|
|
44
50
|
def controller_display
|
45
51
|
return unless controller?
|
46
52
|
|
47
|
-
" in #{controller.camelize}
|
53
|
+
" in #{controller.camelize}"
|
48
54
|
end
|
49
55
|
|
50
56
|
def action_display
|
@@ -8,6 +8,7 @@ module ActiveError
|
|
8
8
|
|
9
9
|
serialize :parameters, coder: JSON
|
10
10
|
serialize :headers, coder: JSON
|
11
|
+
serialize :session, coder: JSON
|
11
12
|
|
12
13
|
delegate :browser, :platform, :version, to: :user_agent
|
13
14
|
|
@@ -16,7 +17,9 @@ module ActiveError
|
|
16
17
|
end
|
17
18
|
|
18
19
|
def request
|
19
|
-
@request ||= ActionDispatch::Request.new(request_env)
|
20
|
+
@request ||= ActionDispatch::Request.new(request_env).tap do |req|
|
21
|
+
req.session = session
|
22
|
+
end
|
20
23
|
end
|
21
24
|
|
22
25
|
private
|
@@ -3,16 +3,39 @@
|
|
3
3
|
<p id="notice"><%= notice %><p>
|
4
4
|
</header>
|
5
5
|
|
6
|
-
<table>
|
7
|
-
|
8
|
-
|
9
|
-
<
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
</
|
6
|
+
<div class="table">
|
7
|
+
<% @faults.each do |fault| %>
|
8
|
+
<div class="tr">
|
9
|
+
<div class="td chevron">▶</div>
|
10
|
+
<div class="td">#<%= fault.id %></div>
|
11
|
+
<div class="td location">
|
12
|
+
<%= link_to fault.title, fault_path(fault) %>
|
13
|
+
|
14
|
+
<% if fault.location.present? %>
|
15
|
+
<span><%= fault.location %></span>
|
16
|
+
<% end %>
|
17
|
+
</div>
|
18
|
+
<div class="td"><%= time_ago_in_words(fault.created_at).capitalize %> ago</div>
|
19
|
+
<div class="td"><%= fault.occurrences %></div>
|
20
|
+
<div class="td"><%= button_to 'Resolve', fault, method: :delete, form: { onSubmit: "return confirm('Are you sure?')" }, class: "button-as-link" %></div>
|
21
|
+
<div class="all-cols hidden">
|
22
|
+
<% fault.instances.each do |instance| %>
|
23
|
+
<div>
|
24
|
+
<div><%= link_to instance.url, fault_instance_path(fault, instance) %></div>
|
25
|
+
<div><%= time_ago_in_words(instance.created_at).capitalize %> ago</div>
|
26
|
+
<div><%= instance.user_agent_display %></div>
|
27
|
+
</div>
|
28
|
+
<% end %>
|
29
|
+
</div>
|
30
|
+
</div>
|
31
|
+
<% end %>
|
32
|
+
</div>
|
33
|
+
|
34
|
+
<script>
|
35
|
+
document.querySelectorAll(".chevron").forEach((el) => {
|
36
|
+
el.addEventListener("click", (event) => {
|
37
|
+
event.target.parentElement.querySelector(".all-cols").classList.toggle("hidden")
|
38
|
+
event.target.classList.toggle("rotate")
|
39
|
+
})
|
40
|
+
})
|
41
|
+
</script>
|
@@ -5,96 +5,7 @@
|
|
5
5
|
<%= csrf_meta_tags %>
|
6
6
|
<%= csp_meta_tag %>
|
7
7
|
|
8
|
-
|
9
|
-
.active_error body {
|
10
|
-
background-color: #FAFAFA;
|
11
|
-
color: #333;
|
12
|
-
color-scheme: light dark;
|
13
|
-
supported-color-schemes: light dark;
|
14
|
-
margin: 0px;
|
15
|
-
}
|
16
|
-
|
17
|
-
@media (prefers-color-scheme: dark) {
|
18
|
-
.active_error body {
|
19
|
-
background-color: #222;
|
20
|
-
color: #ECECEC;
|
21
|
-
}
|
22
|
-
}
|
23
|
-
|
24
|
-
.active_error body, .active_error p, .active_error ol, .active_error ul, .active_error td {
|
25
|
-
font-family: helvetica, verdana, arial, sans-serif;
|
26
|
-
font-size: 13px;
|
27
|
-
line-height: 18px;
|
28
|
-
}
|
29
|
-
|
30
|
-
.active_error .button-as-link {
|
31
|
-
border: none;
|
32
|
-
text-decoration: underline;
|
33
|
-
cursor: pointer;
|
34
|
-
background: none;
|
35
|
-
font-size: 15px;
|
36
|
-
margin: 0;
|
37
|
-
padding: 0;
|
38
|
-
line-height: 18px;
|
39
|
-
}
|
40
|
-
|
41
|
-
.active_error a, .active_error .button-as-link { color: #980905; }
|
42
|
-
.active_error a:visited, .active_error .button-as-link:visited { color: #666; }
|
43
|
-
.active_error a.trace-frames {
|
44
|
-
color: #666;
|
45
|
-
overflow-wrap: break-word;
|
46
|
-
}
|
47
|
-
.active_error a:hover, .active_error .button-as-link:hover, .active_error a.trace-frames.selected { color: #C00; }
|
48
|
-
.active_error a.summary:hover { color: #FFF; }
|
49
|
-
|
50
|
-
.active_error th {
|
51
|
-
padding-bottom: 5px;
|
52
|
-
}
|
53
|
-
|
54
|
-
.active_error td {
|
55
|
-
padding: 0 5px 7px;
|
56
|
-
}
|
57
|
-
|
58
|
-
.active_error #notice {
|
59
|
-
color: green;
|
60
|
-
margin: 0px;
|
61
|
-
}
|
62
|
-
|
63
|
-
.active_error header {
|
64
|
-
color: #f0f0f0;
|
65
|
-
background: #C00;
|
66
|
-
padding: 0.5em 1.5em;
|
67
|
-
display: flex;
|
68
|
-
flex-direction: row;
|
69
|
-
align-items: center;
|
70
|
-
margin-bottom: 0.83em;
|
71
|
-
}
|
72
|
-
|
73
|
-
.active_error h1 {
|
74
|
-
flex: 1;
|
75
|
-
overflow-wrap: break-word;
|
76
|
-
margin: 0.2em 0;
|
77
|
-
line-height: 1.1em;
|
78
|
-
font-size: 2em;
|
79
|
-
}
|
80
|
-
|
81
|
-
.active_error table {
|
82
|
-
padding: 0 1.5em;
|
83
|
-
width: 100%;
|
84
|
-
}
|
85
|
-
|
86
|
-
.active_error table tr td:first-of-type {
|
87
|
-
padding-left: 0;
|
88
|
-
}
|
89
|
-
|
90
|
-
.active_error .w-5 {
|
91
|
-
width: 5%;
|
92
|
-
}
|
93
|
-
|
94
|
-
.active_error .w-15 {
|
95
|
-
width: 15%;
|
96
|
-
}
|
97
|
-
</style>
|
8
|
+
<%= stylesheet_link_tag "activeerror/application" %>
|
98
9
|
</head>
|
99
10
|
<body><%= yield %></body>
|
100
11
|
</html>
|
@@ -0,0 +1,278 @@
|
|
1
|
+
<!DOCTYPE html>
|
2
|
+
<html lang="en">
|
3
|
+
<head>
|
4
|
+
<meta charset="utf-8" />
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1">
|
6
|
+
<meta name="turbo-visit-control" content="reload">
|
7
|
+
<title>Action Controller: Exception caught</title>
|
8
|
+
<%= stylesheet_link_tag "activeerror/application" %>
|
9
|
+
<link rel="preconnect" href="https://fonts.googleapis.com">
|
10
|
+
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
11
|
+
<link href="https://fonts.googleapis.com/css2?family=Fira+Code:wght@400&display=swap" rel="stylesheet">
|
12
|
+
<style>
|
13
|
+
body {
|
14
|
+
background-color: #FAFAFA;
|
15
|
+
color: #333;
|
16
|
+
color-scheme: light dark;
|
17
|
+
supported-color-schemes: light dark;
|
18
|
+
margin: 0px;
|
19
|
+
}
|
20
|
+
|
21
|
+
pre, code {
|
22
|
+
font-family: "JetBrains Mono", monospace;
|
23
|
+
font-optical-sizing: auto;
|
24
|
+
font-weight: 400;
|
25
|
+
font-style: normal;
|
26
|
+
}
|
27
|
+
|
28
|
+
body, p, ol, ul, td {
|
29
|
+
font-family: "Calibre", helvetica, verdana, arial, sans-serif;
|
30
|
+
font-size: 15px;
|
31
|
+
line-height: 18px;
|
32
|
+
}
|
33
|
+
|
34
|
+
pre {
|
35
|
+
font-size: 11px;
|
36
|
+
white-space: pre-wrap;
|
37
|
+
}
|
38
|
+
|
39
|
+
pre.box {
|
40
|
+
border: 1px solid #EEE;
|
41
|
+
padding: 10px;
|
42
|
+
margin: 0px;
|
43
|
+
width: 958px;
|
44
|
+
}
|
45
|
+
|
46
|
+
header {
|
47
|
+
color: #F0F0F0;
|
48
|
+
background: #C00;
|
49
|
+
padding: 0.5em 1.5em;
|
50
|
+
}
|
51
|
+
|
52
|
+
h1 {
|
53
|
+
overflow-wrap: break-word;
|
54
|
+
margin: 0.2em 0;
|
55
|
+
line-height: 1.1em;
|
56
|
+
font-size: 2em;
|
57
|
+
}
|
58
|
+
|
59
|
+
header {
|
60
|
+
color: #D30001;
|
61
|
+
background: rgb(238, 231, 233);
|
62
|
+
padding: 7.5px 22.5px;
|
63
|
+
display: flex;
|
64
|
+
flex-direction: row;
|
65
|
+
justify-content: space-between;
|
66
|
+
align-items: center;
|
67
|
+
}
|
68
|
+
|
69
|
+
h1 {
|
70
|
+
flex: 1;
|
71
|
+
overflow-wrap: break-word;
|
72
|
+
margin: 0.2em 0;
|
73
|
+
line-height: 1.1em;
|
74
|
+
font-size: 30px;
|
75
|
+
font-family: "Calibre";
|
76
|
+
}
|
77
|
+
|
78
|
+
h2 {
|
79
|
+
color: #C00;
|
80
|
+
line-height: 25px;
|
81
|
+
}
|
82
|
+
|
83
|
+
code.traces {
|
84
|
+
font-size: 11px;
|
85
|
+
}
|
86
|
+
|
87
|
+
.response-heading, .request-heading {
|
88
|
+
margin-top: 30px;
|
89
|
+
font-family: "Calibre";
|
90
|
+
font-size: 22px;
|
91
|
+
}
|
92
|
+
|
93
|
+
.exception-message {
|
94
|
+
padding: 8px 0;
|
95
|
+
}
|
96
|
+
|
97
|
+
.exception-message .message {
|
98
|
+
margin-bottom: 8px;
|
99
|
+
line-height: 25px;
|
100
|
+
font-size: 24px;
|
101
|
+
font-weight: 600;
|
102
|
+
color: #C00;
|
103
|
+
font-family: "Calibre";
|
104
|
+
}
|
105
|
+
|
106
|
+
.details {
|
107
|
+
border: 1px solid #D0D0D0;
|
108
|
+
border-radius: 4px;
|
109
|
+
margin: 1em 0px;
|
110
|
+
display: block;
|
111
|
+
max-width: 978px;
|
112
|
+
}
|
113
|
+
|
114
|
+
.summary {
|
115
|
+
padding: 8px 15px;
|
116
|
+
border-bottom: 1px solid #D0D0D0;
|
117
|
+
display: block;
|
118
|
+
}
|
119
|
+
|
120
|
+
a.summary {
|
121
|
+
color: #F0F0F0;
|
122
|
+
text-decoration: none;
|
123
|
+
background: #C52F24;
|
124
|
+
border-bottom: none;
|
125
|
+
}
|
126
|
+
|
127
|
+
.details pre {
|
128
|
+
margin: 5px;
|
129
|
+
border: none;
|
130
|
+
}
|
131
|
+
|
132
|
+
#container {
|
133
|
+
box-sizing: border-box;
|
134
|
+
width: 100%;
|
135
|
+
padding: 0 1.5em;
|
136
|
+
}
|
137
|
+
|
138
|
+
.source * {
|
139
|
+
margin: 0px;
|
140
|
+
padding: 0px;
|
141
|
+
}
|
142
|
+
|
143
|
+
.source {
|
144
|
+
border: 1px solid #D9D9D9;
|
145
|
+
background: #ECECEC;
|
146
|
+
max-width: 978px;
|
147
|
+
}
|
148
|
+
|
149
|
+
.source pre {
|
150
|
+
padding: 10px 0px;
|
151
|
+
border: none;
|
152
|
+
}
|
153
|
+
|
154
|
+
.source .data {
|
155
|
+
font-size: 80%;
|
156
|
+
overflow: auto;
|
157
|
+
background-color: #FFF;
|
158
|
+
}
|
159
|
+
|
160
|
+
.info {
|
161
|
+
padding: 0.5em;
|
162
|
+
}
|
163
|
+
|
164
|
+
.source .data .line_numbers {
|
165
|
+
background-color: #ECECEC;
|
166
|
+
color: #555;
|
167
|
+
padding: 1em .5em;
|
168
|
+
border-right: 1px solid #DDD;
|
169
|
+
text-align: right;
|
170
|
+
}
|
171
|
+
|
172
|
+
.line {
|
173
|
+
padding-left: 10px;
|
174
|
+
white-space: pre;
|
175
|
+
}
|
176
|
+
|
177
|
+
.line:hover {
|
178
|
+
background-color: #F6F6F6;
|
179
|
+
}
|
180
|
+
|
181
|
+
.line.active {
|
182
|
+
background-color: #FCC;
|
183
|
+
}
|
184
|
+
|
185
|
+
.error_highlight {
|
186
|
+
display: inline-block;
|
187
|
+
background-color: #FF9;
|
188
|
+
text-decoration: #F00 wavy underline;
|
189
|
+
}
|
190
|
+
|
191
|
+
.error_highlight_tip {
|
192
|
+
color: #666;
|
193
|
+
padding: 2px 2px;
|
194
|
+
font-size: 10px;
|
195
|
+
}
|
196
|
+
|
197
|
+
.button_to {
|
198
|
+
display: inline-block;
|
199
|
+
margin-top: 0.75em;
|
200
|
+
margin-bottom: 0.75em;
|
201
|
+
}
|
202
|
+
|
203
|
+
.hidden {
|
204
|
+
display: none;
|
205
|
+
}
|
206
|
+
|
207
|
+
.correction {
|
208
|
+
list-style-type: none;
|
209
|
+
}
|
210
|
+
|
211
|
+
input[type="submit"] {
|
212
|
+
color: white;
|
213
|
+
background-color: #C00;
|
214
|
+
border: none;
|
215
|
+
border-radius: 12px;
|
216
|
+
box-shadow: 0 3px #F99;
|
217
|
+
font-size: 13px;
|
218
|
+
font-weight: bold;
|
219
|
+
margin: 0;
|
220
|
+
padding: 10px 18px;
|
221
|
+
cursor: pointer;
|
222
|
+
-webkit-appearance: none;
|
223
|
+
}
|
224
|
+
input[type="submit"]:focus,
|
225
|
+
input[type="submit"]:hover {
|
226
|
+
opacity: 0.8;
|
227
|
+
}
|
228
|
+
input[type="submit"]:active {
|
229
|
+
box-shadow: 0 2px #F99;
|
230
|
+
transform: translateY(1px)
|
231
|
+
}
|
232
|
+
|
233
|
+
a { color: #980905; }
|
234
|
+
a:visited { color: #666; }
|
235
|
+
a.trace-frames {
|
236
|
+
color: #666;
|
237
|
+
overflow-wrap: break-word;
|
238
|
+
}
|
239
|
+
a:hover, a.trace-frames.selected { color: #C00; }
|
240
|
+
a.summary:hover { color: #FFF; }
|
241
|
+
|
242
|
+
h2.response-heading {
|
243
|
+
display: none;
|
244
|
+
&+p {
|
245
|
+
display: none;
|
246
|
+
&+pre {
|
247
|
+
display: none;
|
248
|
+
}
|
249
|
+
}
|
250
|
+
}
|
251
|
+
<%= yield :style %>
|
252
|
+
</style>
|
253
|
+
|
254
|
+
<script>
|
255
|
+
var toggle = function(id) {
|
256
|
+
document.getElementById(id).classList.toggle('hidden');
|
257
|
+
return false;
|
258
|
+
}
|
259
|
+
var show = function(id) {
|
260
|
+
document.getElementById(id).style.display = 'block';
|
261
|
+
}
|
262
|
+
var hide = function(id) {
|
263
|
+
document.getElementById(id).style.display = 'none';
|
264
|
+
}
|
265
|
+
var toggleSessionDump = function() {
|
266
|
+
return toggle('session_dump');
|
267
|
+
}
|
268
|
+
var toggleEnvDump = function() {
|
269
|
+
return toggle('env_dump');
|
270
|
+
}
|
271
|
+
</script>
|
272
|
+
</head>
|
273
|
+
<body>
|
274
|
+
|
275
|
+
<%= yield %>
|
276
|
+
|
277
|
+
</body>
|
278
|
+
</html>
|
data/config/routes.rb
CHANGED
data/lib/active_error/captor.rb
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
module ActiveError
|
4
|
-
BacktraceLocation = Struct.new(:absolute_path, :base_label, :to_s, :label,
|
4
|
+
BacktraceLocation = Struct.new(:absolute_path, :base_label, :to_s, :label,
|
5
5
|
:lineno, :path) do
|
6
6
|
def inspect = to_s
|
7
7
|
|
@@ -74,7 +74,7 @@ module ActiveError
|
|
74
74
|
slice(*ActionDispatch::Request::ENV_METHODS, "HTTP_USER_AGENT")
|
75
75
|
|
76
76
|
fault.instances.create(headers:, parameters: request&.filtered_parameters,
|
77
|
-
url: request&.url)
|
77
|
+
url: request&.url, session: request.session.to_h)
|
78
78
|
end
|
79
79
|
|
80
80
|
def controller
|
data/lib/active_error/engine.rb
CHANGED
@@ -7,9 +7,19 @@ module ActiveError
|
|
7
7
|
class Engine < ::Rails::Engine
|
8
8
|
isolate_namespace ActiveError
|
9
9
|
|
10
|
-
|
11
|
-
app.config.middleware.use ActiveError::Middleware
|
10
|
+
config.active_error = ActiveSupport::OrderedOptions.new
|
12
11
|
|
12
|
+
initializer "active_error.error_reporter" do |_app|
|
13
|
+
Rails.error.subscribe(::ActiveError::ErrorSubscriber.new)
|
14
|
+
end
|
15
|
+
|
16
|
+
initializer "active_error.config" do
|
17
|
+
config.active_error.each do |name, value|
|
18
|
+
ActiveError.public_send(:"#{name}=", value)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
initializer "active_error.context" do
|
13
23
|
ActionController::Base.before_action do
|
14
24
|
Rails.error.set_context(active_error_request: request)
|
15
25
|
end
|
@@ -0,0 +1,12 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ActiveError
|
4
|
+
class ErrorSubscriber
|
5
|
+
def report(exception, context:, handled:, **_opts)
|
6
|
+
return if !ActiveError.enabled? || handled ||
|
7
|
+
ActiveError.ignored_classes.include?(exception.class.to_s)
|
8
|
+
|
9
|
+
Captor.new(exception:, request: context[:active_error_request]).capture
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
@@ -8,7 +8,11 @@ module ActiveError
|
|
8
8
|
end
|
9
9
|
|
10
10
|
def body
|
11
|
-
@body ||=
|
11
|
+
@body ||= begin
|
12
|
+
set_lookup_context
|
13
|
+
|
14
|
+
template.render(template: file, layout: "active_error/rescue")
|
15
|
+
end
|
12
16
|
end
|
13
17
|
|
14
18
|
private
|
@@ -28,10 +32,27 @@ module ActiveError
|
|
28
32
|
end
|
29
33
|
|
30
34
|
def template
|
31
|
-
|
35
|
+
@template ||=
|
36
|
+
ActionDispatch::DebugView.
|
32
37
|
new(request: instance.request, exception_wrapper:, traces:,
|
33
38
|
exception: exception_wrapper.exception, trace_to_show:,
|
34
39
|
source_extracts:, show_source_idx: source_to_show_id,)
|
35
40
|
end
|
41
|
+
|
42
|
+
def set_lookup_context
|
43
|
+
template.define_singleton_method :lookup_context= do |new_context|
|
44
|
+
@lookup_context = new_context
|
45
|
+
@view_renderer = ActionView::Renderer.new @lookup_context
|
46
|
+
end
|
47
|
+
|
48
|
+
template.lookup_context = new_lookup_context
|
49
|
+
end
|
50
|
+
|
51
|
+
def new_lookup_context
|
52
|
+
ActionView::LookupContext.new([
|
53
|
+
*ActionDispatch::DebugView::RESCUES_TEMPLATE_PATHS.dup,
|
54
|
+
ActiveError::Engine.root.join("app/views/layouts").to_s
|
55
|
+
])
|
56
|
+
end
|
36
57
|
end
|
37
58
|
end
|
data/lib/active_error/version.rb
CHANGED
data/lib/active_error.rb
CHANGED
@@ -1,15 +1,45 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require "active_error/error_subscriber"
|
3
4
|
require "active_error/exception_mock/default"
|
4
5
|
require "active_error/exception_mock/template_error"
|
5
6
|
require "active_error/exception_mock"
|
6
7
|
require "active_error/captor"
|
7
|
-
require "active_error/middleware"
|
8
8
|
require "active_error/renderer"
|
9
9
|
require "active_error/engine"
|
10
|
-
require "active_error/railtie"
|
11
10
|
require "active_error/version"
|
12
11
|
|
13
12
|
module ActiveError
|
14
|
-
mattr_accessor :ignored
|
13
|
+
mattr_accessor :ignored, :enabled
|
14
|
+
|
15
|
+
IGNORE_DEFAULT = [
|
16
|
+
"AbstractController::ActionNotFound",
|
17
|
+
"ActionController::BadRequest",
|
18
|
+
"ActionController::InvalidAuthenticityToken",
|
19
|
+
"ActionController::InvalidCrossOriginRequest",
|
20
|
+
"ActionController::MethodNotAllowed",
|
21
|
+
"ActionController::NotImplemented",
|
22
|
+
"ActionController::ParameterMissing",
|
23
|
+
"ActionController::RoutingError",
|
24
|
+
"ActionController::UnknownAction",
|
25
|
+
"ActionController::UnknownFormat",
|
26
|
+
"ActionDispatch::Http::MimeNegotiation::InvalidType",
|
27
|
+
"ActionController::UnknownHttpMethod",
|
28
|
+
"ActionDispatch::Http::Parameters::ParseError",
|
29
|
+
"ActiveRecord::RecordNotFound"
|
30
|
+
].freeze
|
31
|
+
|
32
|
+
class << self
|
33
|
+
def ignored_classes
|
34
|
+
ignored.to_a + IGNORE_DEFAULT
|
35
|
+
end
|
36
|
+
|
37
|
+
def enabled?
|
38
|
+
if enabled.nil?
|
39
|
+
!Rails.env.local?
|
40
|
+
else
|
41
|
+
enabled.present?
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
15
45
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: activeerror
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version:
|
4
|
+
version: 2.1.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Nick Pezza
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2024-
|
11
|
+
date: 2024-12-18 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rails
|
@@ -48,6 +48,12 @@ files:
|
|
48
48
|
- LICENSE.md
|
49
49
|
- README.md
|
50
50
|
- Rakefile
|
51
|
+
- app/assets/stylesheets/activeerror/application.css
|
52
|
+
- app/assets/stylesheets/activeerror/calibre-bold.woff2
|
53
|
+
- app/assets/stylesheets/activeerror/calibre-regular-italic.woff2
|
54
|
+
- app/assets/stylesheets/activeerror/calibre-regular.woff2
|
55
|
+
- app/assets/stylesheets/activeerror/calibre-semibold-italic.woff2
|
56
|
+
- app/assets/stylesheets/activeerror/calibre-semibold.woff2
|
51
57
|
- app/controllers/active_error/application_controller.rb
|
52
58
|
- app/controllers/active_error/faults/instances_controller.rb
|
53
59
|
- app/controllers/active_error/faults_controller.rb
|
@@ -55,23 +61,24 @@ files:
|
|
55
61
|
- app/models/active_error/fault.rb
|
56
62
|
- app/models/active_error/instance.rb
|
57
63
|
- app/views/active_error/faults/index.html.erb
|
58
|
-
- app/views/active_error/faults/show.html.erb
|
59
64
|
- app/views/layouts/active_error/application.html.erb
|
65
|
+
- app/views/layouts/active_error/rescue.html.erb
|
60
66
|
- config/routes.rb
|
61
67
|
- db/migrate/20200727220359_create_active_error_faults.rb
|
62
68
|
- db/migrate/20200727225318_create_active_error_instances.rb
|
69
|
+
- db/migrate/20241218160110_add_session_to_instances.rb
|
63
70
|
- lib/active_error.rb
|
64
71
|
- lib/active_error/captor.rb
|
65
72
|
- lib/active_error/engine.rb
|
73
|
+
- lib/active_error/error_subscriber.rb
|
66
74
|
- lib/active_error/exception_mock.rb
|
67
75
|
- lib/active_error/exception_mock/default.rb
|
68
76
|
- lib/active_error/exception_mock/template_error.rb
|
69
|
-
- lib/active_error/middleware.rb
|
70
|
-
- lib/active_error/railtie.rb
|
71
77
|
- lib/active_error/renderer.rb
|
72
78
|
- lib/active_error/version.rb
|
73
79
|
- lib/activeerror.rb
|
74
80
|
- lib/generators/active_error/install/install_generator.rb
|
81
|
+
- lib/tasks/active_error_tasks.rake
|
75
82
|
homepage: https://github.com/npezza93/activeerror
|
76
83
|
licenses:
|
77
84
|
- MIT
|
@@ -85,14 +92,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
85
92
|
requirements:
|
86
93
|
- - ">="
|
87
94
|
- !ruby/object:Gem::Version
|
88
|
-
version: 3.
|
95
|
+
version: 3.2.0
|
89
96
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
90
97
|
requirements:
|
91
98
|
- - ">="
|
92
99
|
- !ruby/object:Gem::Version
|
93
100
|
version: '0'
|
94
101
|
requirements: []
|
95
|
-
rubygems_version: 3.5.
|
102
|
+
rubygems_version: 3.5.18
|
96
103
|
signing_key:
|
97
104
|
specification_version: 4
|
98
105
|
summary: Rails exception logger
|
@@ -1,17 +0,0 @@
|
|
1
|
-
<header>
|
2
|
-
<h1>Instances of <%= @fault.title %> (Fault #<%= @fault.id %>)</h1>
|
3
|
-
<p id="notice"><%= notice %><p>
|
4
|
-
</header>
|
5
|
-
|
6
|
-
<table>
|
7
|
-
<tbody>
|
8
|
-
<% @fault.instances.each do |instance| %>
|
9
|
-
<tr>
|
10
|
-
<td>#<%= instance.id %></td>
|
11
|
-
<td><%= link_to instance.url, fault_instance_path(@fault, instance) %></td>
|
12
|
-
<td class="w-15"><%= instance.user_agent_display %></td>
|
13
|
-
<td class="w-15"><%= time_ago_in_words(instance.created_at).capitalize %> ago</td>
|
14
|
-
</tr>
|
15
|
-
<% end %>
|
16
|
-
</tbody>
|
17
|
-
</table>
|
@@ -1,19 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module ActiveError
|
4
|
-
class Middleware
|
5
|
-
def initialize(app)
|
6
|
-
@app = app
|
7
|
-
end
|
8
|
-
|
9
|
-
def call(env)
|
10
|
-
@app.call(env)
|
11
|
-
rescue StandardError => e
|
12
|
-
unless Rails.application.config.reloading_enabled?
|
13
|
-
Rails.error.report(e, handled: false,
|
14
|
-
source: "application.action_dispatch")
|
15
|
-
end
|
16
|
-
raise
|
17
|
-
end
|
18
|
-
end
|
19
|
-
end
|
data/lib/active_error/railtie.rb
DELETED
@@ -1,18 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module ActiveError
|
4
|
-
class ErrorSubscriber
|
5
|
-
def report(exception, context:, handled:, **_opts)
|
6
|
-
return if Rails.env.development? || handled ||
|
7
|
-
ActiveError.ignored.to_a.include?(exception.class.to_s)
|
8
|
-
|
9
|
-
Captor.new(exception:, request: context[:active_error_request]).capture
|
10
|
-
end
|
11
|
-
end
|
12
|
-
|
13
|
-
class Railtie < ::Rails::Railtie
|
14
|
-
initializer "active_error.error_reporter" do |_app|
|
15
|
-
Rails.error.subscribe(::ActiveError::ErrorSubscriber.new)
|
16
|
-
end
|
17
|
-
end
|
18
|
-
end
|