solid_errors 0.1.0 → 0.2.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/app/controllers/solid_errors/errors_controller.rb +34 -0
- data/app/models/solid_errors/error.rb +22 -0
- data/app/models/solid_errors/occurrence.rb +21 -0
- data/app/models/solid_errors/record.rb +11 -0
- data/app/views/layouts/application.html.erb +987 -0
- data/app/views/solid_errors/errors/_actions.html.erb +12 -0
- data/app/views/solid_errors/errors/_error.html.erb +24 -0
- data/app/views/solid_errors/errors/index.html.erb +18 -0
- data/app/views/solid_errors/errors/show.html.erb +101 -0
- data/app/views/solid_errors/occurrences/_collection.html.erb +25 -0
- data/app/views/solid_errors/occurrences/_occurrence.html.erb +46 -0
- data/config/routes.rb +5 -0
- data/lib/solid_errors/backtrace.rb +48 -0
- data/lib/solid_errors/backtrace_line.rb +113 -0
- data/lib/solid_errors/engine.rb +23 -0
- data/lib/solid_errors/subscriber.rb +59 -0
- data/lib/solid_errors/version.rb +1 -1
- data/lib/solid_errors.rb +5 -2
- metadata +33 -11
- data/.standard.yml +0 -3
- data/CHANGELOG.md +0 -5
- data/CODE_OF_CONDUCT.md +0 -84
- data/Gemfile +0 -12
- data/LICENSE.txt +0 -21
- data/sig/solid_errors.rbs +0 -4
- data/solid_errors.gemspec +0 -36
@@ -0,0 +1,12 @@
|
|
1
|
+
<div class="inline-flex items-center justify-start flex-wrap gap-3 whitespace-nowrap">
|
2
|
+
<% if current_controller.errors? && !current_action.errors_index? %>
|
3
|
+
<%= link_to errors_path, class: button_classes(icon: true) do %>
|
4
|
+
<%= bootstrap_svg "arrow-left" %>
|
5
|
+
<span>Back to errors</span>
|
6
|
+
<% end %>
|
7
|
+
<% end %>
|
8
|
+
|
9
|
+
<%= button_to error_path(error), method: :patch, class: button_classes(type: :primary_ghost), params: { error: { resolved_at: Time.now } } do %>
|
10
|
+
Resolve Error<span class="sr-only"> #<%= error.id %></span>
|
11
|
+
<% end %>
|
12
|
+
</div>
|
@@ -0,0 +1,24 @@
|
|
1
|
+
<tr class="even:bg-gray-50 align-top">
|
2
|
+
<td scope="col" class="whitespace-wrap py-4 pl-4 pr-3 font-medium text-gray-900 sm:pl-3">
|
3
|
+
<div>
|
4
|
+
<%= error.emoji %>
|
5
|
+
<%= link_to error_path(error), class: "text-blue-400 underline inline-flex items-baseline gap-1" do %>
|
6
|
+
<strong><code><%= error.exception_class %></code></strong>
|
7
|
+
<% end %>
|
8
|
+
from
|
9
|
+
<em><code><%= error.source %></code></em>
|
10
|
+
</div>
|
11
|
+
<pre class="ml-6 mt-4"><%= error.message %></pre>
|
12
|
+
</td>
|
13
|
+
<td scope="col" class="whitespace-nowrap px-3 py-4 pt-7 text-gray-500 text-right">
|
14
|
+
<%= error.occurrences.size %>
|
15
|
+
</td>
|
16
|
+
<td scope="col" class="whitespace-nowrap px-3 py-4 pt-7 text-gray-500 text-right">
|
17
|
+
<%= time_ago_in_words error.occurrences.maximum(:created_at), scope: 'datetime.distance_in_words.short' %>
|
18
|
+
</td>
|
19
|
+
<td class="relative whitespace-nowrap py-4 pl-3 pr-4 text-right text-sm font-medium sm:pr-3">
|
20
|
+
<%= button_to error_path(error), method: :patch, class: button_classes(type: :primary_ghost), params: { error: { resolved_at: Time.now } } do %>
|
21
|
+
Resolve<span class="sr-only">, Error #<%= error.id %></span>
|
22
|
+
<% end %>
|
23
|
+
</td>
|
24
|
+
</tr>
|
@@ -0,0 +1,18 @@
|
|
1
|
+
<table class="min-w-full divide-y divide-gray-300">
|
2
|
+
<thead>
|
3
|
+
<tr>
|
4
|
+
<th scope="col" class="py-3.5 pl-4 pr-3 text-left text-sm font-semibold text-gray-900 sm:pl-3">Error</th>
|
5
|
+
<th scope="col" class="px-3 py-3.5 text-right text-sm font-semibold text-gray-900">Count</th>
|
6
|
+
<th scope="col" class="px-3 py-3.5 text-right text-sm font-semibold text-gray-900">Last</th>
|
7
|
+
<th scope="col" class="relative py-3.5 pl-3 pr-4 sm:pr-3">
|
8
|
+
<span class="sr-only">Resolve</span>
|
9
|
+
</th>
|
10
|
+
</tr>
|
11
|
+
</thead>
|
12
|
+
|
13
|
+
<tbody class="bg-white">
|
14
|
+
<%= render partial: "solid_errors/errors/error", collection: @errors %>
|
15
|
+
</tbody>
|
16
|
+
</table>
|
17
|
+
|
18
|
+
<%#= @errors.count %>
|
@@ -0,0 +1,101 @@
|
|
1
|
+
<%= tag.section id: dom_id(@error), class: "space-y-6" do %>
|
2
|
+
<div class="flex justify-between items-center">
|
3
|
+
<%= tag.h1 class: "font-bold flex items-center text-2xl gap-2" do %>
|
4
|
+
<%= @error.emoji %>
|
5
|
+
<code><%= @error.exception_class %></code>
|
6
|
+
from
|
7
|
+
<em><code><%= @error.source %></code></em>
|
8
|
+
<% end %>
|
9
|
+
|
10
|
+
<small class="text-base">
|
11
|
+
#<code><%= @error.id %></code>
|
12
|
+
</small>
|
13
|
+
</div>
|
14
|
+
|
15
|
+
<pre class=""><%= @error.message %></pre>
|
16
|
+
|
17
|
+
<dl class="flex-1 grid grid-cols-2 gap-x-4">
|
18
|
+
<div class="flex items-center justify-between flex-wrap gap-x-2">
|
19
|
+
<dt class="font-bold">
|
20
|
+
<%= SolidErrors::Error.human_attribute_name(:first_seen) %>
|
21
|
+
</dt>
|
22
|
+
<dd class="inline-flex items-center gap-1">
|
23
|
+
<% first_seen_at = @error.occurrences.minimum(:created_at) %>
|
24
|
+
|
25
|
+
<abbr title="<%= first_seen_at.iso8601 %>" class="cursor-help">
|
26
|
+
<%= time_tag first_seen_at, "#{time_ago_in_words(first_seen_at)} ago" %>
|
27
|
+
</abbr>
|
28
|
+
</dd>
|
29
|
+
</div>
|
30
|
+
<div class="flex items-center justify-between flex-wrap gap-x-2">
|
31
|
+
<dt class="font-bold">
|
32
|
+
<%= SolidErrors::Error.human_attribute_name(:last_seen) %>
|
33
|
+
</dt>
|
34
|
+
<dd class="inline-flex items-center gap-1">
|
35
|
+
<% last_seen_at = @error.occurrences.maximum(:created_at) %>
|
36
|
+
|
37
|
+
<abbr title="<%= last_seen_at.iso8601 %>" class="cursor-help">
|
38
|
+
<%= time_tag last_seen_at, "#{time_ago_in_words(last_seen_at)} ago" %>
|
39
|
+
</abbr>
|
40
|
+
</dd>
|
41
|
+
</div>
|
42
|
+
<div class="flex items-center justify-between flex-wrap gap-x-2">
|
43
|
+
<dt class="font-bold">
|
44
|
+
<%= SolidErrors::Error.human_attribute_name(:occurrences) %>
|
45
|
+
</dt>
|
46
|
+
<dd class="inline-flex items-center gap-1">
|
47
|
+
<%= @error.occurrences.size %>
|
48
|
+
</dd>
|
49
|
+
</div>
|
50
|
+
<div class="flex items-center justify-between flex-wrap gap-x-2">
|
51
|
+
<dt class="font-bold">
|
52
|
+
<%= SolidErrors::Error.human_attribute_name(:exception_class) %>
|
53
|
+
</dt>
|
54
|
+
<dd class="inline-flex items-center gap-1">
|
55
|
+
<code><%= @error.exception_class %></code>
|
56
|
+
</dd>
|
57
|
+
</div>
|
58
|
+
<div class="flex items-center justify-between flex-wrap gap-x-2">
|
59
|
+
<dt class="font-bold">
|
60
|
+
<%= SolidErrors::Error.human_attribute_name(:severity) %>
|
61
|
+
</dt>
|
62
|
+
<dd class="inline-flex items-center gap-1">
|
63
|
+
<span class="<%= badge_classes %>">
|
64
|
+
<%= @error.severity %>
|
65
|
+
</span>
|
66
|
+
</dd>
|
67
|
+
</div>
|
68
|
+
<div class="flex items-center justify-between flex-wrap gap-x-2">
|
69
|
+
<dt class="font-bold">
|
70
|
+
<%= SolidErrors::Error.human_attribute_name(:source) %>
|
71
|
+
</dt>
|
72
|
+
<dd class="inline-flex items-center gap-1">
|
73
|
+
<em><%= @error.source %></em>
|
74
|
+
</dd>
|
75
|
+
</div>
|
76
|
+
<div class="flex items-center justify-between flex-wrap gap-x-2">
|
77
|
+
<dt class="font-bold">
|
78
|
+
<%= SolidErrors::Error.human_attribute_name(:project_root) %>
|
79
|
+
</dt>
|
80
|
+
<dd class="inline-flex items-center gap-1">
|
81
|
+
<span><%= SolidErrors::BacktraceLine::RAILS_ROOT %></span>
|
82
|
+
</dd>
|
83
|
+
</div>
|
84
|
+
<div class="flex items-center justify-between flex-wrap gap-x-2">
|
85
|
+
<dt class="font-bold">
|
86
|
+
<%= SolidErrors::Error.human_attribute_name(:gem_root) %>
|
87
|
+
</dt>
|
88
|
+
<dd class="inline-flex items-center gap-1">
|
89
|
+
<span><%= Gem.path.join("\n") %></span>
|
90
|
+
</dd>
|
91
|
+
</div>
|
92
|
+
</dl>
|
93
|
+
|
94
|
+
<%= render "solid_errors/errors/actions", error: @error %>
|
95
|
+
<% end %>
|
96
|
+
|
97
|
+
<hr class="mt-4 pb-4">
|
98
|
+
|
99
|
+
<%= render "solid_errors/occurrences/collection",
|
100
|
+
occurrences: @error.occurrences,
|
101
|
+
titled: true %>
|
@@ -0,0 +1,25 @@
|
|
1
|
+
<% titled ||= false %>
|
2
|
+
|
3
|
+
<section id="occurrences" class="sm:rounded-md mt-4 -mx-2 sm:mx-0">
|
4
|
+
<% if titled %>
|
5
|
+
<div class="flex justify-between items-center mb-3 border-b">
|
6
|
+
<h2 class="font-bold text-2xl">
|
7
|
+
Occurrences
|
8
|
+
</h2>
|
9
|
+
<p class="text-right">
|
10
|
+
Total: <strong><%= occurrences.size %></strong>
|
11
|
+
</p>
|
12
|
+
</div>
|
13
|
+
<% end %>
|
14
|
+
|
15
|
+
<div class="min-w-full">
|
16
|
+
<% if occurrences.any? %>
|
17
|
+
<%= render occurrences %>
|
18
|
+
<% else %>
|
19
|
+
<div class="flex items-center justify-center gap-2">
|
20
|
+
<%= bootstrap_svg "list-ul" %>
|
21
|
+
<span class="text-2xl">No occurrences yet…</span>
|
22
|
+
</div>
|
23
|
+
<% end %>
|
24
|
+
</div>
|
25
|
+
</section>
|
@@ -0,0 +1,46 @@
|
|
1
|
+
<% seen_at = occurrence.created_at %>
|
2
|
+
<% backtrace = occurrence.parsed_backtrace %>
|
3
|
+
|
4
|
+
<%= tag.section id: dom_id(occurrence), class: "" do %>
|
5
|
+
<%= tag.details open: occurrence_counter.zero? do %>
|
6
|
+
<summary class="hover:bg-gray-50 p-2 rounded cursor-pointer">
|
7
|
+
<%= time_tag seen_at, seen_at %>
|
8
|
+
(<em><%= time_ago_in_words(seen_at) %> ago</em>)
|
9
|
+
</summary>
|
10
|
+
<div class="">
|
11
|
+
<dl class="ml-6">
|
12
|
+
<% occurrence.context&.each do |key, value| %>
|
13
|
+
<div class="flex items-center justify-between flex-wrap gap-x-2">
|
14
|
+
<dt class="font-bold">
|
15
|
+
<%= SolidErrors::Occurrence.human_attribute_name(key) %>
|
16
|
+
</dt>
|
17
|
+
<dd class="">
|
18
|
+
<%= value %>
|
19
|
+
</dd>
|
20
|
+
</div>
|
21
|
+
<% end %>
|
22
|
+
<div class="">
|
23
|
+
<dt class="font-bold">
|
24
|
+
<%= SolidErrors::Occurrence.human_attribute_name(:backtrace) %>
|
25
|
+
</dt>
|
26
|
+
<dd class="">
|
27
|
+
<% backtrace.lines.each_with_index do |line, i| %>
|
28
|
+
<%= tag.details open: line.application? || i.zero? do %>
|
29
|
+
<summary class="hover:bg-gray-50 px-2 py-1 rounded cursor-pointer">
|
30
|
+
<span class="text-gray-500"><%= File.dirname(line.filtered_file) %>/</span><span class="text-blue-500"><%= File.basename(line.filtered_file) %></span>:<span class="text-gray-900"><%= line.filtered_number %></span>
|
31
|
+
<span class="text-gray-500">in</span>
|
32
|
+
<code class="text-green-500"><%= line.filtered_method %></code>
|
33
|
+
</summary>
|
34
|
+
<div><pre class="debug_dump"><code class="torchlight"><% line.source.each do |n, code| %>
|
35
|
+
<div class="line"><span class="mr-2 text-right select-none text-gray-600"><%= n %></span><span><%= code %></span></div>
|
36
|
+
<% end %></code></pre></div>
|
37
|
+
<% end %>
|
38
|
+
<% end %>
|
39
|
+
</dd>
|
40
|
+
</div>
|
41
|
+
</dl>
|
42
|
+
</div>
|
43
|
+
<% end %>
|
44
|
+
<% end %>
|
45
|
+
|
46
|
+
<hr class="mt-4 pb-4">
|
data/config/routes.rb
ADDED
@@ -0,0 +1,48 @@
|
|
1
|
+
module SolidErrors
|
2
|
+
class Backtrace
|
3
|
+
# Holder for an Array of Backtrace::Line instances.
|
4
|
+
attr_reader :lines, :application_lines
|
5
|
+
|
6
|
+
def self.parse(ruby_backtrace, opts = {})
|
7
|
+
ruby_lines = ruby_backtrace.to_a
|
8
|
+
|
9
|
+
lines = ruby_lines.collect do |unparsed_line|
|
10
|
+
BacktraceLine.parse(unparsed_line.to_s, opts)
|
11
|
+
end.compact
|
12
|
+
|
13
|
+
instance = new(lines)
|
14
|
+
end
|
15
|
+
|
16
|
+
def initialize(lines)
|
17
|
+
self.lines = lines
|
18
|
+
self.application_lines = lines.select(&:application?)
|
19
|
+
end
|
20
|
+
|
21
|
+
# Convert Backtrace to array.
|
22
|
+
#
|
23
|
+
# Returns array containing backtrace lines.
|
24
|
+
def to_ary
|
25
|
+
lines.take(1000).map { |l| { :number => l.filtered_number, :file => l.filtered_file, :method => l.filtered_method, :source => l.source } }
|
26
|
+
end
|
27
|
+
alias :to_a :to_ary
|
28
|
+
|
29
|
+
# JSON support.
|
30
|
+
#
|
31
|
+
# Returns JSON representation of backtrace.
|
32
|
+
def as_json(options = {})
|
33
|
+
to_ary
|
34
|
+
end
|
35
|
+
|
36
|
+
def to_s
|
37
|
+
lines.map(&:to_s).join("\n")
|
38
|
+
end
|
39
|
+
|
40
|
+
def inspect
|
41
|
+
"<Backtrace: " + lines.collect { |line| line.inspect }.join(", ") + ">"
|
42
|
+
end
|
43
|
+
|
44
|
+
private
|
45
|
+
|
46
|
+
attr_writer :lines, :application_lines
|
47
|
+
end
|
48
|
+
end
|
@@ -0,0 +1,113 @@
|
|
1
|
+
module SolidErrors
|
2
|
+
class BacktraceLine
|
3
|
+
# Backtrace line regexp (optionally allowing leading X: for windows support).
|
4
|
+
INPUT_FORMAT = %r{^((?:[a-zA-Z]:)?[^:]+):(\d+)(?::in `([^']+)')?$}.freeze
|
5
|
+
STRING_EMPTY = ''.freeze
|
6
|
+
GEM_ROOT = '[GEM_ROOT]'.freeze
|
7
|
+
PROJECT_ROOT = '[PROJECT_ROOT]'.freeze
|
8
|
+
PROJECT_ROOT_CACHE = {}
|
9
|
+
GEM_ROOT_CACHE = {}
|
10
|
+
RELATIVE_ROOT = Regexp.new('^\.\/').freeze
|
11
|
+
RAILS_ROOT = ::Rails.root.to_s.dup.freeze
|
12
|
+
ROOT_REGEXP = Regexp.new("^#{ Regexp.escape(RAILS_ROOT) }").freeze
|
13
|
+
BACKTRACE_FILTERS = [
|
14
|
+
lambda { |line|
|
15
|
+
return line unless defined?(Gem)
|
16
|
+
GEM_ROOT_CACHE[line] ||= Gem.path.reduce(line) do |line, path|
|
17
|
+
line.sub(path, GEM_ROOT)
|
18
|
+
end
|
19
|
+
},
|
20
|
+
lambda { |line|
|
21
|
+
c = (PROJECT_ROOT_CACHE[RAILS_ROOT] ||= {})
|
22
|
+
return c[line] if c.has_key?(line)
|
23
|
+
c[line] ||= line.sub(ROOT_REGEXP, PROJECT_ROOT)
|
24
|
+
},
|
25
|
+
lambda { |line| line.sub(RELATIVE_ROOT, STRING_EMPTY) }
|
26
|
+
].freeze
|
27
|
+
|
28
|
+
attr_reader :file
|
29
|
+
attr_reader :number
|
30
|
+
attr_reader :method
|
31
|
+
attr_reader :filtered_file, :filtered_number, :filtered_method
|
32
|
+
|
33
|
+
# Parses a single line of a given backtrace
|
34
|
+
#
|
35
|
+
# @param [String] unparsed_line The raw line from +caller+ or some backtrace.
|
36
|
+
#
|
37
|
+
# @return The parsed backtrace line.
|
38
|
+
def self.parse(unparsed_line, opts = {})
|
39
|
+
filtered_line = BACKTRACE_FILTERS.reduce(unparsed_line) do |line, proc|
|
40
|
+
proc.call(line)
|
41
|
+
end
|
42
|
+
|
43
|
+
if filtered_line
|
44
|
+
match = unparsed_line.match(INPUT_FORMAT) || [].freeze
|
45
|
+
fmatch = filtered_line.match(INPUT_FORMAT) || [].freeze
|
46
|
+
|
47
|
+
file, number, method = match[1], match[2], match[3]
|
48
|
+
filtered_args = [fmatch[1], fmatch[2], fmatch[3]]
|
49
|
+
new(file, number, method, *filtered_args, opts.fetch(:source_radius, 2))
|
50
|
+
else
|
51
|
+
nil
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
def initialize(file, number, method, filtered_file = file,
|
56
|
+
filtered_number = number, filtered_method = method,
|
57
|
+
source_radius = 2)
|
58
|
+
self.filtered_file = filtered_file
|
59
|
+
self.filtered_number = filtered_number
|
60
|
+
self.filtered_method = filtered_method
|
61
|
+
self.file = file
|
62
|
+
self.number = number
|
63
|
+
self.method = method
|
64
|
+
self.source_radius = source_radius
|
65
|
+
end
|
66
|
+
|
67
|
+
# Reconstructs the line in a readable fashion.
|
68
|
+
def to_s
|
69
|
+
"#{filtered_file}:#{filtered_number}:in `#{filtered_method}'"
|
70
|
+
end
|
71
|
+
|
72
|
+
def ==(other)
|
73
|
+
to_s == other.to_s
|
74
|
+
end
|
75
|
+
|
76
|
+
def inspect
|
77
|
+
"<Line:#{to_s}>"
|
78
|
+
end
|
79
|
+
|
80
|
+
# Determines if this line is part of the application trace or not.
|
81
|
+
def application?
|
82
|
+
(filtered_file =~ /^\[PROJECT_ROOT\]/i) && !(filtered_file =~ /^\[PROJECT_ROOT\]\/vendor/i)
|
83
|
+
end
|
84
|
+
|
85
|
+
def source
|
86
|
+
@source ||= get_source(file, number, source_radius)
|
87
|
+
end
|
88
|
+
|
89
|
+
private
|
90
|
+
|
91
|
+
attr_writer :file, :number, :method, :filtered_file, :filtered_number, :filtered_method
|
92
|
+
|
93
|
+
attr_accessor :source_radius
|
94
|
+
|
95
|
+
# Open source file and read line(s).
|
96
|
+
#
|
97
|
+
# Returns an array of line(s) from source file.
|
98
|
+
def get_source(file, number, radius = 2)
|
99
|
+
return {} unless file && File.exist?(file)
|
100
|
+
|
101
|
+
before = after = radius
|
102
|
+
start = (number.to_i - 1) - before
|
103
|
+
start = 0 and before = 1 if start <= 0
|
104
|
+
duration = before + 1 + after
|
105
|
+
|
106
|
+
l = 0
|
107
|
+
File.open(file) do |f|
|
108
|
+
start.times { f.gets ; l += 1 }
|
109
|
+
return Hash[duration.times.map { (line = f.gets) ? [(l += 1), line] : nil }.compact]
|
110
|
+
end
|
111
|
+
end
|
112
|
+
end
|
113
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module SolidErrors
|
4
|
+
class Engine < ::Rails::Engine
|
5
|
+
isolate_namespace SolidErrors
|
6
|
+
|
7
|
+
rake_tasks do
|
8
|
+
load "solid_errors/tasks.rb"
|
9
|
+
end
|
10
|
+
|
11
|
+
config.solid_errors = ActiveSupport::OrderedOptions.new
|
12
|
+
|
13
|
+
initializer "solid_errors.config" do
|
14
|
+
config.solid_errors.each do |name, value|
|
15
|
+
SolidErrors.public_send("#{name}=", value)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
initializer "solid_errors.active_record.error_subscriber" do
|
20
|
+
Rails.error.subscribe(SolidErrors::Subscriber.new)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,59 @@
|
|
1
|
+
module SolidErrors
|
2
|
+
class Subscriber
|
3
|
+
IGNORED_ERRORS = ['ActionController::RoutingError',
|
4
|
+
'AbstractController::ActionNotFound',
|
5
|
+
'ActionController::MethodNotAllowed',
|
6
|
+
'ActionController::UnknownHttpMethod',
|
7
|
+
'ActionController::NotImplemented',
|
8
|
+
'ActionController::UnknownFormat',
|
9
|
+
'ActionController::InvalidAuthenticityToken',
|
10
|
+
'ActionController::InvalidCrossOriginRequest',
|
11
|
+
'ActionDispatch::Http::Parameters::ParseError',
|
12
|
+
'ActionController::BadRequest',
|
13
|
+
'ActionController::ParameterMissing',
|
14
|
+
'ActiveRecord::RecordNotFound',
|
15
|
+
'ActionController::UnknownAction',
|
16
|
+
'ActionDispatch::Http::MimeNegotiation::InvalidType',
|
17
|
+
'Rack::QueryParser::ParameterTypeError',
|
18
|
+
'Rack::QueryParser::InvalidParameterError',
|
19
|
+
'CGI::Session::CookieStore::TamperedWithCookie',
|
20
|
+
'Mongoid::Errors::DocumentNotFound',
|
21
|
+
'Sinatra::NotFound',
|
22
|
+
'Sidekiq::JobRetry::Skip'].map(&:freeze).freeze
|
23
|
+
|
24
|
+
|
25
|
+
def report(error, handled:, severity:, context:, source: nil)
|
26
|
+
return if ignore_by_class?(error.class.name)
|
27
|
+
|
28
|
+
error_attributes = {
|
29
|
+
exception_class: error.class.name,
|
30
|
+
message: s(error.message),
|
31
|
+
severity: severity,
|
32
|
+
source: source
|
33
|
+
}
|
34
|
+
if (record = SolidErrors::Error.find_by(error_attributes))
|
35
|
+
record.update!(resolved_at: nil, updated_at: Time.now)
|
36
|
+
else
|
37
|
+
record = SolidErrors::Error.create!(error_attributes)
|
38
|
+
end
|
39
|
+
|
40
|
+
SolidErrors::Occurrence.create(
|
41
|
+
error_id: record.id,
|
42
|
+
backtrace: error.backtrace.join("\n"),
|
43
|
+
context: s(context)
|
44
|
+
)
|
45
|
+
end
|
46
|
+
|
47
|
+
def s(data)
|
48
|
+
Sanitizer.sanitize(data)
|
49
|
+
end
|
50
|
+
|
51
|
+
def ignore_by_class?(error_class_name)
|
52
|
+
IGNORED_ERRORS.any? do |ignored_class|
|
53
|
+
ignored_class_name = ignored_class.respond_to?(:name) ? ignored_class.name : ignored_class
|
54
|
+
|
55
|
+
ignored_class_name == error_class_name
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
data/lib/solid_errors/version.rb
CHANGED
data/lib/solid_errors.rb
CHANGED
@@ -1,8 +1,11 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require_relative "solid_errors/version"
|
4
|
+
require_relative "solid_errors/subscriber"
|
5
|
+
require_relative "solid_errors/backtrace"
|
6
|
+
require_relative "solid_errors/backtrace_line"
|
7
|
+
require_relative "solid_errors/engine"
|
4
8
|
|
5
9
|
module SolidErrors
|
6
|
-
|
7
|
-
# Your code goes here...
|
10
|
+
mattr_accessor :connects_to
|
8
11
|
end
|
metadata
CHANGED
@@ -1,15 +1,29 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: solid_errors
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1
|
4
|
+
version: 0.2.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Stephen Margheim
|
8
8
|
autorequire:
|
9
|
-
bindir:
|
9
|
+
bindir: bin
|
10
10
|
cert_chain: []
|
11
11
|
date: 2024-01-14 00:00:00.000000000 Z
|
12
|
-
dependencies:
|
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.0'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '7.0'
|
13
27
|
description:
|
14
28
|
email:
|
15
29
|
- stephen.margheim@gmail.com
|
@@ -17,24 +31,32 @@ executables: []
|
|
17
31
|
extensions: []
|
18
32
|
extra_rdoc_files: []
|
19
33
|
files:
|
20
|
-
- ".standard.yml"
|
21
|
-
- CHANGELOG.md
|
22
|
-
- CODE_OF_CONDUCT.md
|
23
|
-
- Gemfile
|
24
|
-
- LICENSE.txt
|
25
34
|
- README.md
|
26
35
|
- Rakefile
|
36
|
+
- app/controllers/solid_errors/errors_controller.rb
|
37
|
+
- app/models/solid_errors/error.rb
|
38
|
+
- app/models/solid_errors/occurrence.rb
|
39
|
+
- app/models/solid_errors/record.rb
|
40
|
+
- app/views/layouts/application.html.erb
|
41
|
+
- app/views/solid_errors/errors/_actions.html.erb
|
42
|
+
- app/views/solid_errors/errors/_error.html.erb
|
43
|
+
- app/views/solid_errors/errors/index.html.erb
|
44
|
+
- app/views/solid_errors/errors/show.html.erb
|
45
|
+
- app/views/solid_errors/occurrences/_collection.html.erb
|
46
|
+
- app/views/solid_errors/occurrences/_occurrence.html.erb
|
47
|
+
- config/routes.rb
|
27
48
|
- lib/solid_errors.rb
|
49
|
+
- lib/solid_errors/backtrace.rb
|
50
|
+
- lib/solid_errors/backtrace_line.rb
|
51
|
+
- lib/solid_errors/engine.rb
|
52
|
+
- lib/solid_errors/subscriber.rb
|
28
53
|
- lib/solid_errors/version.rb
|
29
|
-
- sig/solid_errors.rbs
|
30
|
-
- solid_errors.gemspec
|
31
54
|
homepage: https://github.com/fractaledmind/solid_errors
|
32
55
|
licenses:
|
33
56
|
- MIT
|
34
57
|
metadata:
|
35
58
|
homepage_uri: https://github.com/fractaledmind/solid_errors
|
36
59
|
source_code_uri: https://github.com/fractaledmind/solid_errors
|
37
|
-
changelog_uri: https://github.com/fractaledmind/solid_errors/blob/main/CHANGELOG.md
|
38
60
|
post_install_message:
|
39
61
|
rdoc_options: []
|
40
62
|
require_paths:
|
data/.standard.yml
DELETED