activeerror 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.
- checksums.yaml +7 -0
- data/LICENSE.md +21 -0
- data/README.md +71 -0
- data/Rakefile +34 -0
- data/app/controllers/active_error/application_controller.rb +7 -0
- data/app/controllers/active_error/faults/instances_controller.rb +17 -0
- data/app/controllers/active_error/faults_controller.rb +26 -0
- data/app/models/active_error/application_record.rb +7 -0
- data/app/models/active_error/fault.rb +60 -0
- data/app/models/active_error/instance.rb +34 -0
- data/app/views/active_error/faults/index.html.erb +18 -0
- data/app/views/active_error/faults/show.html.erb +17 -0
- data/app/views/layouts/active_error/application.html.erb +98 -0
- data/config/routes.rb +11 -0
- data/db/migrate/20200727220359_create_active_error_faults.rb +21 -0
- data/db/migrate/20200727225318_create_active_error_instances.rb +14 -0
- data/lib/active_error/captor.rb +79 -0
- data/lib/active_error/engine.rb +22 -0
- data/lib/active_error/exception_mock/default.rb +31 -0
- data/lib/active_error/exception_mock/template_error.rb +28 -0
- data/lib/active_error/exception_mock.rb +14 -0
- data/lib/active_error/renderer.rb +42 -0
- data/lib/active_error/version.rb +5 -0
- data/lib/active_error.rb +12 -0
- data/lib/activeerror.rb +3 -0
- data/lib/generators/active_error/install/install_generator.rb +11 -0
- metadata +97 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 7152e2ef6d0ce997ab2c1ac6ad20301641bc3888da16d14d8ba447f0f25bbe42
|
4
|
+
data.tar.gz: c75638bb627a0bc6250e3eb7fd2c0041dda4eb7b1ac6849de0d2bda07cd7663d
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 2576054be5ff1b83af55777b9135a991e6fe05a845c1197d55aad43b79676fb830a09712be37c0d7591f1eb7f5e36b09af7ad8c71cf3a129f32a9d985dd3fb5d
|
7
|
+
data.tar.gz: 81ff00bc1f0e4e0fbe9230ec37e0fb72b1bc960375f9c64c9c6348fff2ec6ca2d8f98b237dec00b9e02fadbd4a7eee9b3427d2aea486f5c747ed06b479df0f9c
|
data/LICENSE.md
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
The MIT License (MIT)
|
2
|
+
|
3
|
+
Copyright (c) 2019 Nick Pezza
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in
|
13
|
+
all copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
21
|
+
THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,71 @@
|
|
1
|
+
# ActiveError
|
2
|
+
|
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, theres no free, easy,
|
5
|
+
open source way to track them for small or medium apps. Honeybadger and Sentry are great,
|
6
|
+
but they are only free for a single user and are closed source. With Sentry
|
7
|
+
looking at using your data as training
|
8
|
+
data([link](https://blog.sentry.io/ai-privacy-and-terms-of-service-updates/?original_referrer=https%3A%2F%2Fsentry.io%2F))
|
9
|
+
there should be an easy open source alternative where you control the data.
|
10
|
+
|
11
|
+
ActiveError hooks into the [error reporting
|
12
|
+
api](https://guides.rubyonrails.org/error_reporting.html) baked directly into
|
13
|
+
Rails. These third party error loggers also try to make their own fancy
|
14
|
+
backtrace and debugging view. But the one Rails developers are most comfortable
|
15
|
+
with is the one baked into Rails that we use everyday in development. So with
|
16
|
+
ActiveError, when an error gets raised it's captured and stored in the
|
17
|
+
database (we attempt to group the same error together as Instances to reduce
|
18
|
+
noise) and then we recreate the error and display it using the built in debug
|
19
|
+
view from Rails. Once you've resolved the error you can click "Resolve" with
|
20
|
+
will destroy the record.
|
21
|
+
|
22
|
+

|
23
|
+

|
24
|
+
|
25
|
+
## Installation
|
26
|
+
Add this line to your application's Gemfile:
|
27
|
+
|
28
|
+
```ruby
|
29
|
+
gem "activeerror"
|
30
|
+
```
|
31
|
+
|
32
|
+
And then execute:
|
33
|
+
```bash
|
34
|
+
$ bundle
|
35
|
+
```
|
36
|
+
|
37
|
+
Or install it yourself as:
|
38
|
+
```bash
|
39
|
+
$ gem install activeerror
|
40
|
+
```
|
41
|
+
|
42
|
+
And then install migrations:
|
43
|
+
```bash
|
44
|
+
bin/rails active_error:install
|
45
|
+
bin/rails rails db:migrate
|
46
|
+
```
|
47
|
+
|
48
|
+
This also mounts a route n your routes file to view the errors at `/errors`.
|
49
|
+
|
50
|
+
## Development
|
51
|
+
|
52
|
+
After checking out the repo, run `bin/setup` to install dependencies. Then, run
|
53
|
+
`rails test` to run the unit tests.
|
54
|
+
|
55
|
+
To install this gem onto your local machine, run `bundle exec rake install`. To
|
56
|
+
release a new version, execute `bin/publish (major|minor|patch)` which will
|
57
|
+
update the version number in `version.rb`, create a git tag for the version,
|
58
|
+
push git commits and tags, and push the `.gem` file to GitHub.
|
59
|
+
|
60
|
+
## Contributing
|
61
|
+
|
62
|
+
Bug reports and pull requests are welcome on
|
63
|
+
[GitHub](https://github.com/npezza93/active_error). This project is intended to
|
64
|
+
be a safe, welcoming space for collaboration, and contributors are expected to
|
65
|
+
adhere to the [Contributor Covenant](http://contributor-covenant.org) code of
|
66
|
+
conduct.
|
67
|
+
|
68
|
+
## License
|
69
|
+
|
70
|
+
The gem is available as open source under the terms of the
|
71
|
+
[MIT License](https://opensource.org/licenses/MIT).
|
data/Rakefile
ADDED
@@ -0,0 +1,34 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
begin
|
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
|
18
|
+
|
19
|
+
APP_RAKEFILE = File.expand_path("test/dummy/Rakefile", __dir__)
|
20
|
+
load "rails/tasks/engine.rake"
|
21
|
+
|
22
|
+
load "rails/tasks/statistics.rake"
|
23
|
+
|
24
|
+
require "bundler/gem_tasks"
|
25
|
+
|
26
|
+
require "rake/testtask"
|
27
|
+
|
28
|
+
Rake::TestTask.new(:test) do |t|
|
29
|
+
t.libs << "test"
|
30
|
+
t.pattern = "test/**/*_test.rb"
|
31
|
+
t.verbose = false
|
32
|
+
end
|
33
|
+
|
34
|
+
task default: :test
|
@@ -0,0 +1,17 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ActiveError
|
4
|
+
module Faults
|
5
|
+
class InstancesController < ApplicationController
|
6
|
+
def show
|
7
|
+
render html: Renderer.new(instance:).body
|
8
|
+
end
|
9
|
+
|
10
|
+
private
|
11
|
+
|
12
|
+
def instance
|
13
|
+
Instance.find_by(fault_id: params[:fault_id], id: params[:id])
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ActiveError
|
4
|
+
class FaultsController < ApplicationController
|
5
|
+
before_action :set_fault, only: %i(show destroy)
|
6
|
+
|
7
|
+
def index
|
8
|
+
@faults = Fault.top_level
|
9
|
+
end
|
10
|
+
|
11
|
+
def show
|
12
|
+
end
|
13
|
+
|
14
|
+
def destroy
|
15
|
+
@fault.destroy
|
16
|
+
|
17
|
+
redirect_to faults_url, notice: "Exception was resolved"
|
18
|
+
end
|
19
|
+
|
20
|
+
private
|
21
|
+
|
22
|
+
def set_fault
|
23
|
+
@fault = Fault.find(params[:id])
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,60 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ActiveError
|
4
|
+
class Fault < ApplicationRecord
|
5
|
+
has_one :parent_cause, class_name: "ActiveError::Fault",
|
6
|
+
foreign_key: :cause_id, inverse_of: :cause,
|
7
|
+
dependent: :restrict_with_error
|
8
|
+
belongs_to :cause, optional: true, class_name: "ActiveError::Fault",
|
9
|
+
dependent: :destroy
|
10
|
+
has_many :instances, dependent: :destroy
|
11
|
+
|
12
|
+
store :options, accessors: [:template], coder: YAML,
|
13
|
+
yaml: { unsafe_load: true }
|
14
|
+
|
15
|
+
serialize :backtrace, type: Array, coder: YAML
|
16
|
+
serialize :backtrace_locations, type: Array, coder: YAML,
|
17
|
+
yaml: { unsafe_load: true }
|
18
|
+
serialize :blamed_files, type: Array, coder: YAML
|
19
|
+
|
20
|
+
scope :top_level, lambda {
|
21
|
+
left_joins(:parent_cause).where(
|
22
|
+
parent_causes_active_error_faults: { id: nil }
|
23
|
+
)
|
24
|
+
}
|
25
|
+
|
26
|
+
def exception
|
27
|
+
ExceptionMock.make(fault: self)
|
28
|
+
end
|
29
|
+
|
30
|
+
def title
|
31
|
+
"#{klass}#{controller_display}#{action_display}"
|
32
|
+
end
|
33
|
+
|
34
|
+
def occurrences
|
35
|
+
"#{instances_count} #{'instance'.pluralize(instances_count)}"
|
36
|
+
end
|
37
|
+
|
38
|
+
def template
|
39
|
+
Marshal.load(super) unless super.nil? # rubocop:disable Security/MarshalLoad
|
40
|
+
end
|
41
|
+
|
42
|
+
def template=(new_template)
|
43
|
+
super(Marshal.dump(new_template))
|
44
|
+
end
|
45
|
+
|
46
|
+
private
|
47
|
+
|
48
|
+
def controller_display
|
49
|
+
return unless controller?
|
50
|
+
|
51
|
+
" in #{controller.camelize}Controller"
|
52
|
+
end
|
53
|
+
|
54
|
+
def action_display
|
55
|
+
return unless action? && controller?
|
56
|
+
|
57
|
+
"##{action}"
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "useragent"
|
4
|
+
|
5
|
+
module ActiveError
|
6
|
+
class Instance < ApplicationRecord
|
7
|
+
belongs_to :fault, counter_cache: true
|
8
|
+
|
9
|
+
serialize :parameters, coder: JSON
|
10
|
+
serialize :headers, coder: JSON
|
11
|
+
|
12
|
+
delegate :browser, :platform, :version, to: :user_agent
|
13
|
+
|
14
|
+
def user_agent_display
|
15
|
+
"#{platform} #{browser} #{version}"
|
16
|
+
end
|
17
|
+
|
18
|
+
def request
|
19
|
+
@request ||= ActionDispatch::Request.new(request_env)
|
20
|
+
end
|
21
|
+
|
22
|
+
private
|
23
|
+
|
24
|
+
def user_agent
|
25
|
+
@user_agent ||= UserAgent.parse(headers["HTTP_USER_AGENT"])
|
26
|
+
end
|
27
|
+
|
28
|
+
def request_env
|
29
|
+
ENV.to_h.merge(headers).merge(
|
30
|
+
"action_dispatch.request.parameters" => parameters,
|
31
|
+
)
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
<header>
|
2
|
+
<h1>Errors</h1>
|
3
|
+
<p id="notice"><%= notice %><p>
|
4
|
+
</header>
|
5
|
+
|
6
|
+
<table>
|
7
|
+
<tbody>
|
8
|
+
<% @faults.each do |fault| %>
|
9
|
+
<tr>
|
10
|
+
<td>#<%= fault.id %></td>
|
11
|
+
<td><%= link_to fault.title, fault %></td>
|
12
|
+
<td class="w-15"><%= time_ago_in_words(fault.created_at).capitalize %> ago</td>
|
13
|
+
<td class="w-15"><%= fault.occurrences %></td>
|
14
|
+
<td class="w-5"><%= button_to 'Resolve', fault, method: :delete, form: { onSubmit: "return confirm('Are you sure?')" }, class: "button-as-link" %></td>
|
15
|
+
</tr>
|
16
|
+
<% end %>
|
17
|
+
</tbody>
|
18
|
+
</table>
|
@@ -0,0 +1,17 @@
|
|
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>
|
@@ -0,0 +1,98 @@
|
|
1
|
+
<!DOCTYPE html>
|
2
|
+
<html class="active_error">
|
3
|
+
<head>
|
4
|
+
<title>Active error</title>
|
5
|
+
<%= csrf_meta_tags %>
|
6
|
+
<%= csp_meta_tag %>
|
7
|
+
|
8
|
+
<style>
|
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: #d3f0b7;
|
60
|
+
}
|
61
|
+
|
62
|
+
.active_error header {
|
63
|
+
color: #f0f0f0;
|
64
|
+
background: #C00;
|
65
|
+
padding: 0.5em 1.5em;
|
66
|
+
display: flex;
|
67
|
+
flex-direction: row;
|
68
|
+
margin-bottom: 0.83em;
|
69
|
+
}
|
70
|
+
|
71
|
+
.active_error h1 {
|
72
|
+
flex: 1;
|
73
|
+
overflow-wrap: break-word;
|
74
|
+
margin: 0.2em 0;
|
75
|
+
line-height: 1.1em;
|
76
|
+
font-size: 2em;
|
77
|
+
}
|
78
|
+
|
79
|
+
.active_error table {
|
80
|
+
padding: 0 1.5em;
|
81
|
+
width: 100%;
|
82
|
+
}
|
83
|
+
|
84
|
+
.active_error table tr td:first-of-type {
|
85
|
+
padding-left: 0;
|
86
|
+
}
|
87
|
+
|
88
|
+
.active_error .w-5 {
|
89
|
+
width: 5%;
|
90
|
+
}
|
91
|
+
|
92
|
+
.active_error .w-15 {
|
93
|
+
width: 15%;
|
94
|
+
}
|
95
|
+
</style>
|
96
|
+
</head>
|
97
|
+
<body><%= yield %></body>
|
98
|
+
</html>
|
data/config/routes.rb
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class CreateActiveErrorFaults < ActiveRecord::Migration[7.1]
|
4
|
+
def change
|
5
|
+
create_table :active_error_faults do |t|
|
6
|
+
t.belongs_to :cause
|
7
|
+
t.text :backtrace
|
8
|
+
t.text :backtrace_locations
|
9
|
+
t.string :klass
|
10
|
+
t.text :message
|
11
|
+
t.string :controller
|
12
|
+
t.string :action
|
13
|
+
t.integer :instances_count
|
14
|
+
t.text :blamed_files
|
15
|
+
t.text :options
|
16
|
+
t.index %i(klass backtrace message)
|
17
|
+
|
18
|
+
t.timestamps
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class CreateActiveErrorInstances < ActiveRecord::Migration[7.1]
|
4
|
+
def change
|
5
|
+
create_table :active_error_instances do |t|
|
6
|
+
t.belongs_to :fault
|
7
|
+
t.string :url
|
8
|
+
t.text :headers
|
9
|
+
t.text :parameters
|
10
|
+
|
11
|
+
t.timestamps
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,79 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ActiveError
|
4
|
+
BacktraceLocation = Struct.new(:absolute_path, :base_label, :to_s, :label, # rubocop:disable Lint/StructNewOverride
|
5
|
+
:lineno, :path) do
|
6
|
+
def inspect = to_s
|
7
|
+
|
8
|
+
def spot(exception)
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
class Captor
|
13
|
+
def initialize(exception:, request:, capture_instance: true)
|
14
|
+
@exception = exception
|
15
|
+
@request = request
|
16
|
+
@capture_instance = capture_instance
|
17
|
+
end
|
18
|
+
|
19
|
+
def capture
|
20
|
+
create_instance if capture_instance?
|
21
|
+
fault
|
22
|
+
end
|
23
|
+
|
24
|
+
private
|
25
|
+
|
26
|
+
attr_reader :exception, :request, :capture_instance
|
27
|
+
|
28
|
+
delegate :parameters, to: :request
|
29
|
+
|
30
|
+
def fault
|
31
|
+
@fault ||= Fault.default_scoped.find_or_create_by(fault_attrs) do |fault|
|
32
|
+
fault.message = exception.message
|
33
|
+
fault.cause = cause
|
34
|
+
fault.backtrace_locations = backtrace_locations
|
35
|
+
fault.template = template if template?
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
def fault_attrs
|
40
|
+
{ backtrace: exception.backtrace, klass: exception.class.to_s,
|
41
|
+
controller: parameters[:controller], action: parameters[:action] }
|
42
|
+
end
|
43
|
+
|
44
|
+
def template?
|
45
|
+
exception.class.is_a?(ActionView::Template::Error)
|
46
|
+
end
|
47
|
+
|
48
|
+
def template
|
49
|
+
exception.instance_variable_get(:@template)
|
50
|
+
end
|
51
|
+
|
52
|
+
def cause
|
53
|
+
return if exception.cause.blank?
|
54
|
+
|
55
|
+
self.class.new(exception: exception.cause, request:,
|
56
|
+
capture_instance: false).capture
|
57
|
+
end
|
58
|
+
|
59
|
+
def backtrace_locations
|
60
|
+
exception.backtrace_locations&.map do |location|
|
61
|
+
BacktraceLocation.new(location.absolute_path, location.base_label,
|
62
|
+
location.to_s, location.label, location.lineno,
|
63
|
+
location.path)
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
def capture_instance?
|
68
|
+
capture_instance
|
69
|
+
end
|
70
|
+
|
71
|
+
def create_instance
|
72
|
+
headers = request.env.
|
73
|
+
slice(*ActionDispatch::Request::ENV_METHODS, "HTTP_USER_AGENT")
|
74
|
+
|
75
|
+
fault.instances.create(headers:, parameters: request.filtered_parameters,
|
76
|
+
url: request.url)
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ActiveError
|
4
|
+
class ErrorSubscriber
|
5
|
+
def report(exception, context:, **_opts)
|
6
|
+
return if Rails.env.local?
|
7
|
+
|
8
|
+
Captor.new(exception:, request: context[:active_error_request]).capture
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
class Engine < ::Rails::Engine
|
13
|
+
isolate_namespace ActiveError
|
14
|
+
|
15
|
+
initializer "active_error.middleware" do |_app|
|
16
|
+
ActionController::Base.before_action do
|
17
|
+
Rails.error.set_context(active_error_request: request)
|
18
|
+
end
|
19
|
+
Rails.error.subscribe(::ActiveError::ErrorSubscriber.new)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# require "active_support/dependencies"
|
4
|
+
|
5
|
+
module ActiveError
|
6
|
+
module ExceptionMock
|
7
|
+
class Default < StandardError
|
8
|
+
# include ActiveSupport::Dependencies::Blamable
|
9
|
+
|
10
|
+
def initialize(fault:)
|
11
|
+
@klass = fault.klass
|
12
|
+
@backtrace = fault.backtrace
|
13
|
+
@backtrace_locations = fault.backtrace_locations
|
14
|
+
@message = fault.message
|
15
|
+
# @blamed_files = fault.blamed_files
|
16
|
+
@cause = ExceptionMock.make(fault: fault.cause)
|
17
|
+
end
|
18
|
+
|
19
|
+
def class
|
20
|
+
klass.constantize
|
21
|
+
end
|
22
|
+
|
23
|
+
attr_reader :backtrace, :backtrace_locations, :message, :cause,
|
24
|
+
:line_number
|
25
|
+
|
26
|
+
private
|
27
|
+
|
28
|
+
attr_reader :klass
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "action_view/template/error"
|
4
|
+
|
5
|
+
module ActiveError
|
6
|
+
module ExceptionMock
|
7
|
+
class TemplateError < ActionView::Template::Error
|
8
|
+
def initialize(fault:)
|
9
|
+
@klass = fault.klass
|
10
|
+
set_backtrace(fault.backtrace)
|
11
|
+
@message = fault.message
|
12
|
+
@blamed_files = fault.blamed_files
|
13
|
+
@template = fault.template
|
14
|
+
@cause = ExceptionMock.make(fault: fault.cause)
|
15
|
+
end
|
16
|
+
|
17
|
+
def class
|
18
|
+
klass.constantize
|
19
|
+
end
|
20
|
+
|
21
|
+
attr_reader :message
|
22
|
+
|
23
|
+
private
|
24
|
+
|
25
|
+
attr_reader :klass
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ActiveError
|
4
|
+
module ExceptionMock
|
5
|
+
def self.make(fault:)
|
6
|
+
case fault&.klass
|
7
|
+
when "ActionView::Template::Error" then TemplateError.new(fault:)
|
8
|
+
when nil then nil
|
9
|
+
else
|
10
|
+
Default.new(fault:)
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ActiveError
|
4
|
+
class Renderer
|
5
|
+
def initialize(instance:)
|
6
|
+
@instance = instance
|
7
|
+
@exception = instance.fault.exception
|
8
|
+
end
|
9
|
+
|
10
|
+
def body
|
11
|
+
@body ||= template.render(template: file, layout: "rescues/layout")
|
12
|
+
end
|
13
|
+
|
14
|
+
private
|
15
|
+
|
16
|
+
attr_reader :exception, :instance
|
17
|
+
|
18
|
+
delegate :trace_to_show, :source_to_show_id, :source_extracts,
|
19
|
+
:traces, to: :wrapper
|
20
|
+
|
21
|
+
def backtrace_cleaner
|
22
|
+
Rails.backtrace_cleaner
|
23
|
+
end
|
24
|
+
|
25
|
+
def wrapper
|
26
|
+
@wrapper ||=
|
27
|
+
ActionDispatch::ExceptionWrapper.new(backtrace_cleaner, exception)
|
28
|
+
end
|
29
|
+
|
30
|
+
def file
|
31
|
+
"rescues/#{wrapper.rescue_template}"
|
32
|
+
end
|
33
|
+
|
34
|
+
def template
|
35
|
+
ActionDispatch::DebugView.new(
|
36
|
+
request: instance.request, exception_wrapper: wrapper,
|
37
|
+
exception: wrapper.exception, traces:,
|
38
|
+
show_source_idx: source_to_show_id, trace_to_show:, source_extracts:
|
39
|
+
)
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
data/lib/active_error.rb
ADDED
@@ -0,0 +1,12 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "active_error/exception_mock/default"
|
4
|
+
require "active_error/exception_mock/template_error"
|
5
|
+
require "active_error/exception_mock"
|
6
|
+
require "active_error/captor"
|
7
|
+
require "active_error/renderer"
|
8
|
+
require "active_error/engine"
|
9
|
+
require "active_error/version"
|
10
|
+
|
11
|
+
module ActiveError
|
12
|
+
end
|
data/lib/activeerror.rb
ADDED
@@ -0,0 +1,11 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class ActiveError::InstallGenerator < Rails::Generators::Base
|
4
|
+
def add_route
|
5
|
+
route "mount ActiveError::Engine => \"/errors\""
|
6
|
+
end
|
7
|
+
|
8
|
+
def create_migrations
|
9
|
+
rails_command "active_error:install:migrations", inline: true
|
10
|
+
end
|
11
|
+
end
|
metadata
ADDED
@@ -0,0 +1,97 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: activeerror
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 1.0.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Nick Pezza
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2024-01-09 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: rails
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ">="
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '7.1'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ">="
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '7.1'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: useragent
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ">="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0'
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ">="
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0'
|
41
|
+
description:
|
42
|
+
email:
|
43
|
+
- pezza@hey.com
|
44
|
+
executables: []
|
45
|
+
extensions: []
|
46
|
+
extra_rdoc_files: []
|
47
|
+
files:
|
48
|
+
- LICENSE.md
|
49
|
+
- README.md
|
50
|
+
- Rakefile
|
51
|
+
- app/controllers/active_error/application_controller.rb
|
52
|
+
- app/controllers/active_error/faults/instances_controller.rb
|
53
|
+
- app/controllers/active_error/faults_controller.rb
|
54
|
+
- app/models/active_error/application_record.rb
|
55
|
+
- app/models/active_error/fault.rb
|
56
|
+
- app/models/active_error/instance.rb
|
57
|
+
- app/views/active_error/faults/index.html.erb
|
58
|
+
- app/views/active_error/faults/show.html.erb
|
59
|
+
- app/views/layouts/active_error/application.html.erb
|
60
|
+
- config/routes.rb
|
61
|
+
- db/migrate/20200727220359_create_active_error_faults.rb
|
62
|
+
- db/migrate/20200727225318_create_active_error_instances.rb
|
63
|
+
- lib/active_error.rb
|
64
|
+
- lib/active_error/captor.rb
|
65
|
+
- lib/active_error/engine.rb
|
66
|
+
- lib/active_error/exception_mock.rb
|
67
|
+
- lib/active_error/exception_mock/default.rb
|
68
|
+
- lib/active_error/exception_mock/template_error.rb
|
69
|
+
- lib/active_error/renderer.rb
|
70
|
+
- lib/active_error/version.rb
|
71
|
+
- lib/activeerror.rb
|
72
|
+
- lib/generators/active_error/install/install_generator.rb
|
73
|
+
homepage: https://github.com/npezza93/active_error
|
74
|
+
licenses:
|
75
|
+
- MIT
|
76
|
+
metadata:
|
77
|
+
rubygems_mfa_required: 'true'
|
78
|
+
post_install_message:
|
79
|
+
rdoc_options: []
|
80
|
+
require_paths:
|
81
|
+
- lib
|
82
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
83
|
+
requirements:
|
84
|
+
- - ">="
|
85
|
+
- !ruby/object:Gem::Version
|
86
|
+
version: 3.1.0
|
87
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
88
|
+
requirements:
|
89
|
+
- - ">="
|
90
|
+
- !ruby/object:Gem::Version
|
91
|
+
version: '0'
|
92
|
+
requirements: []
|
93
|
+
rubygems_version: 3.5.3
|
94
|
+
signing_key:
|
95
|
+
specification_version: 4
|
96
|
+
summary: Rails exception logger
|
97
|
+
test_files: []
|