exception-track 0.6.2 → 1.3.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/README.md +8 -3
- data/app/assets/config/exception_track_manifest.js +0 -0
- data/app/assets/stylesheets/exception-track/application.css +126 -44
- data/app/controllers/exception_track/logs_controller.rb +2 -3
- data/app/views/exception_track/logs/index.html.erb +2 -1
- data/config/initializers/exception-track.rb +11 -6
- data/db/migrate/20170217023900_create_exception_track_logs.rb +2 -2
- data/lib/exception-track/configuration.rb +1 -0
- data/lib/exception-track/engine.rb +3 -1
- data/lib/exception-track/log_subscriber.rb +12 -0
- data/lib/exception-track/version.rb +1 -1
- data/lib/exception-track.rb +4 -3
- data/lib/exception_notification/rack.rb +66 -0
- data/lib/exception_notification/rails.rb +11 -0
- data/lib/exception_notification/resque.rb +24 -0
- data/lib/exception_notification/sidekiq.rb +29 -0
- data/lib/exception_notification/version.rb +5 -0
- data/lib/exception_notification.rb +14 -0
- data/lib/exception_notifier/base_notifier.rb +30 -0
- data/lib/exception_notifier/{exception_track_notifier.rb → db_notifier.rb} +30 -26
- data/lib/exception_notifier/modules/backtrace_cleaner.rb +13 -0
- data/lib/exception_notifier/modules/error_grouping.rb +90 -0
- data/lib/exception_notifier/modules/formatter.rb +125 -0
- data/lib/exception_notifier/notifier.rb +191 -0
- metadata +91 -22
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 71a2bc45d225661a466bdd4fe3ae8227253cf0e4e48430a3f3db33605c04aed7
|
4
|
+
data.tar.gz: fbf989949bd59b5ac8ea1b44c159cae09ded01832c21c42193729a95b20f3015
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: a7b776ccbda802c32ca77f60ec4257e198d2e9e96c611ccc03c14fb00efc1fb5f127a60e9df627b985ddef347d1e1ceb2f04c8919894ab73da3ac796fe8cc0c5
|
7
|
+
data.tar.gz: 14a4f70638b02742170ff3138d553624a8c379b2c0480624f178354fd0324dbcb58d5d75627e78f24026338fbeb750478431034ac76eb3b7cb3f8cd42df4dc9e
|
data/README.md
CHANGED
@@ -1,5 +1,4 @@
|
|
1
|
-
ExceptionTrack
|
2
|
-
--------------
|
1
|
+
## ExceptionTrack
|
3
2
|
|
4
3
|
Tracking exceptions for Rails application store them in database.
|
5
4
|
|
@@ -7,7 +6,7 @@ This gem is base on [exception_notification](https://github.com/smartinez87/exce
|
|
7
6
|
|
8
7
|
[中文介绍和使用说明](https://ruby-china.org/topics/32325)
|
9
8
|
|
10
|
-
[](https://badge.fury.io/rb/exception-track) [](https://badge.fury.io/rb/exception-track) [](https://github.com/rails-engine/exception-track/actions?query=workflow%3Abuild) [](https://codecov.io/github/rails-engine/exception-track?branch=master)
|
11
10
|
|
12
11
|

|
13
12
|
|
@@ -41,6 +40,12 @@ Rails.application.routes.draw do
|
|
41
40
|
end
|
42
41
|
```
|
43
42
|
|
43
|
+
Migrate database:
|
44
|
+
|
45
|
+
```shell
|
46
|
+
bundle exec rails db:migrate
|
47
|
+
```
|
48
|
+
|
44
49
|
Now you can open: http://localhost:3000/exception-track
|
45
50
|
|
46
51
|
## Configuration
|
File without changes
|
@@ -1,27 +1,78 @@
|
|
1
|
+
:root {
|
2
|
+
--body-background-color: #eee;
|
3
|
+
--card-background-color: #fff;
|
4
|
+
--card-border-color: #fff;
|
5
|
+
--card-shadow-color: rgba(200, 200, 200, 0.26);
|
6
|
+
--text-color: #333;
|
7
|
+
--text-color1: #666;
|
8
|
+
--text-color2: #999;
|
9
|
+
--link-color: #364cc9;
|
10
|
+
--table-background-color1: #f5f5f5;
|
11
|
+
--table-border-color: #f0f0f0;
|
12
|
+
--input-border-color: #ddd;
|
13
|
+
--button-background-color: #fff;
|
14
|
+
--button-border-color: #eee;
|
15
|
+
--button-text-color: #555;
|
16
|
+
--button-hover-background-color: #f7f7f7;
|
17
|
+
--button-hover-border-color: #eee;
|
18
|
+
--button-danger-color: #f85149;
|
19
|
+
--button-danger-hover-background-color: #f85149;
|
20
|
+
--button-danger-hover-color: #fff;
|
21
|
+
--pre-background-color: #f7f7f7;
|
22
|
+
--notice-color: #fff;
|
23
|
+
--notice-background-color: #3CBD46;
|
24
|
+
}
|
25
|
+
|
26
|
+
@media (prefers-color-scheme: dark) {
|
27
|
+
:root {
|
28
|
+
--body-background-color: #0d1117;
|
29
|
+
--card-background-color: rgba(200, 200, 200, 0.02);
|
30
|
+
--card-border-color: #30363d;
|
31
|
+
--card-shadow-color: rgba(0, 0, 0, 0.16);
|
32
|
+
--text-color: #c9d1d9;
|
33
|
+
--text-color1: #999;
|
34
|
+
--text-color2: #888;
|
35
|
+
--link-color: #58a6ff;
|
36
|
+
--table-background-color1: #161b22;
|
37
|
+
--table-border-color: #30363d;
|
38
|
+
--input-border-color: #333;
|
39
|
+
--button-background-color: #21262d;
|
40
|
+
--button-border-color: #30363d;
|
41
|
+
--button-text-color: #c9d1d9;
|
42
|
+
--button-hover-background-color: #30363d;
|
43
|
+
--button-hover-border-color: #8b949e;
|
44
|
+
--pre-background-color: #161b22;
|
45
|
+
--notice-color: #3CBD46;
|
46
|
+
--notice-background-color: #3cbd4621;
|
47
|
+
}
|
48
|
+
}
|
1
49
|
|
2
50
|
body {
|
3
51
|
margin: 0;
|
4
52
|
padding: 20px 0;
|
5
|
-
background-color:
|
53
|
+
background-color: var(--body-background-color);
|
6
54
|
}
|
7
55
|
|
8
56
|
body, textarea {
|
9
57
|
font-family: -apple-system, BlinkMacSystemFont, "Helvetica Neue", Arial, Helvetica, sans-serif;
|
10
58
|
font-size: 14px;
|
11
59
|
line-height: 1.4;
|
12
|
-
color:
|
60
|
+
color: var(--text-color);
|
13
61
|
}
|
14
62
|
|
15
63
|
.footer {
|
16
64
|
padding: 15px;
|
17
65
|
text-align: center;
|
18
|
-
color:
|
66
|
+
color: var(--text-color2);
|
67
|
+
}
|
68
|
+
|
69
|
+
.footer a:link, .footer a:visited {
|
70
|
+
color: var(--text-color1);
|
71
|
+
text-decoration: underline;
|
19
72
|
}
|
20
|
-
.footer a:link,
|
21
|
-
.footer a:visited { color: #666; text-decoration: underline;}
|
22
73
|
|
23
74
|
a, a:visited, a:active {
|
24
|
-
color:
|
75
|
+
color: var(--link-color);
|
25
76
|
text-decoration: none;
|
26
77
|
}
|
27
78
|
|
@@ -34,15 +85,15 @@ table {
|
|
34
85
|
border-collapse: collapse;
|
35
86
|
border-spacing: 0;
|
36
87
|
margin-bottom: 20px;
|
88
|
+
border: 1px solid var(--table-border-color);
|
89
|
+
border-radius: 3px;
|
37
90
|
}
|
38
91
|
|
39
|
-
|
40
|
-
|
41
|
-
|
92
|
+
td.date {
|
93
|
+
width: 170px;
|
94
|
+
font-size: 12px;
|
42
95
|
}
|
43
96
|
|
44
|
-
td.date { width: 150px; font-size: 12px; }
|
45
|
-
|
46
97
|
h1 {
|
47
98
|
margin-top: 0;
|
48
99
|
font-size: 20px;
|
@@ -66,29 +117,28 @@ ul {
|
|
66
117
|
table td, table th {
|
67
118
|
padding: 10px 15px;
|
68
119
|
}
|
69
|
-
|
70
|
-
|
71
|
-
|
120
|
+
|
121
|
+
th {
|
122
|
+
text-align: left;
|
123
|
+
background: var(--table-background-color1);
|
124
|
+
border-bottom: 1px solid var(--table-border-color);
|
72
125
|
}
|
73
126
|
|
74
|
-
|
75
|
-
|
76
|
-
padding: 10px;
|
77
|
-
white-space: pre-wrap;
|
78
|
-
word-break: break-word;
|
127
|
+
td {
|
128
|
+
border-top: solid 1px var(--table-border-color);
|
79
129
|
}
|
80
130
|
|
81
131
|
textarea {
|
82
132
|
width: 100%;
|
83
133
|
height: 100px;
|
84
|
-
border: solid 1px
|
134
|
+
border: solid 1px var(--input-border-color);
|
85
135
|
padding: 10px;
|
86
136
|
}
|
87
137
|
|
88
138
|
hr {
|
89
139
|
border: none;
|
90
140
|
height: 0;
|
91
|
-
border-top: solid 1px
|
141
|
+
border-top: solid 1px var(--input-border-color);
|
92
142
|
margin-bottom: 15px;
|
93
143
|
}
|
94
144
|
|
@@ -107,16 +157,29 @@ hr {
|
|
107
157
|
-ms-user-select: none;
|
108
158
|
user-select: none;
|
109
159
|
border-radius: 3px;
|
110
|
-
|
160
|
+
transition: .2s cubic-bezier(.3, 0, .5, 1);
|
161
|
+
transition-property: color, background-color, border-color;
|
162
|
+
border: 1px solid var(--button-border-color);
|
111
163
|
padding: 6px 16px;
|
112
|
-
color:
|
164
|
+
color: var(--button-text-color) !important;
|
113
165
|
outline: 0 !important;
|
114
|
-
background:
|
166
|
+
background: var(--button-background-color);
|
115
167
|
}
|
116
|
-
|
117
|
-
.btn
|
168
|
+
|
169
|
+
.btn:hover {
|
170
|
+
text-decoration: none !important;
|
171
|
+
background: var(--button-hover-background-color);
|
172
|
+
}
|
173
|
+
|
174
|
+
.btn-danger {
|
175
|
+
background: var(--button-background-color);
|
176
|
+
color: var(--button-danger-color) !important;
|
177
|
+
border-color: var(--button-border-color);
|
178
|
+
}
|
179
|
+
|
118
180
|
.btn-danger:hover {
|
119
|
-
background:
|
181
|
+
background: var(--button-danger-hover-background-color);
|
182
|
+
color: var(--button-danger-hover-color) !important;
|
120
183
|
}
|
121
184
|
|
122
185
|
.container {
|
@@ -124,8 +187,9 @@ hr {
|
|
124
187
|
margin-left: auto;
|
125
188
|
margin-right: auto;
|
126
189
|
padding: 20px;
|
127
|
-
background-color:
|
128
|
-
box-shadow: 0 1px 8px
|
190
|
+
background-color: var(--card-background-color);
|
191
|
+
box-shadow: 0 1px 8px var(--card-shadow-color);
|
192
|
+
border: 1px solid var(--card-border-color);
|
129
193
|
border-radius: 3px;
|
130
194
|
}
|
131
195
|
|
@@ -140,49 +204,67 @@ hr {
|
|
140
204
|
height: 34px;
|
141
205
|
line-height: 34px;
|
142
206
|
}
|
143
|
-
|
144
|
-
.toolbar
|
207
|
+
|
208
|
+
.toolbar form {
|
209
|
+
display: inline;
|
210
|
+
}
|
211
|
+
|
212
|
+
.toolbar .pull-right {
|
213
|
+
float: right;
|
214
|
+
}
|
145
215
|
|
146
216
|
#notice {
|
147
217
|
padding: 8px 15px;
|
148
|
-
background:
|
149
|
-
color:
|
218
|
+
background: var(--notice-background-color);
|
219
|
+
color: var(--notice-color);
|
150
220
|
margin-bottom: 15px;
|
151
221
|
border-radius: 3px;
|
152
222
|
}
|
153
223
|
|
154
224
|
pre {
|
155
|
-
|
225
|
+
white-space: pre-wrap;
|
226
|
+
word-break: break-word;
|
227
|
+
background: var(--pre-background-color);
|
156
228
|
padding: 25px;
|
157
229
|
border-radius: 3px;
|
158
230
|
font-size: 12px;
|
159
231
|
font-family: Menlo, Monaco, Consolas, monospace;
|
160
232
|
}
|
161
233
|
|
162
|
-
h1 {
|
234
|
+
h1 {
|
235
|
+
font-size: 16px;
|
236
|
+
}
|
163
237
|
|
164
238
|
.pagination {
|
165
239
|
padding-bottom: 15px;
|
166
240
|
font-size: 14px;
|
167
241
|
}
|
168
242
|
|
169
|
-
.pagination li {
|
243
|
+
.pagination li {
|
244
|
+
display: inline;
|
245
|
+
}
|
170
246
|
|
171
247
|
.pagination a {
|
172
248
|
display: inline-block;
|
173
249
|
padding: 5px 10px;
|
174
|
-
border:
|
175
|
-
|
250
|
+
border-radius: 3px;
|
251
|
+
border: 1px solid var(--button-border-color);
|
252
|
+
color: var(--button-text-color);
|
253
|
+
background: var(--button-background-color);
|
176
254
|
text-decoration: none;
|
177
255
|
}
|
256
|
+
|
178
257
|
.pagination a:hover {
|
179
|
-
background:
|
258
|
+
background: var(--button-hover-background-color);
|
259
|
+
color: var(--button-hover-color);
|
260
|
+
border-color: var(--button-hover-border-color);
|
180
261
|
}
|
181
|
-
|
182
|
-
.pagination .current {
|
262
|
+
|
263
|
+
.pagination em, .pagination .current {
|
264
|
+
border-radius: 3px;
|
183
265
|
display: inline-block;
|
184
266
|
padding: 5px 10px;
|
185
|
-
border: 1px solid
|
186
|
-
background:
|
267
|
+
border: 1px solid var(--button-hover-border-color);
|
268
|
+
background: var(--button-hover-background-color);
|
187
269
|
font-style: normal;
|
188
|
-
}
|
270
|
+
}
|
@@ -3,7 +3,7 @@
|
|
3
3
|
module ExceptionTrack
|
4
4
|
class LogsController < ActionController::Base
|
5
5
|
layout "exception-track/application"
|
6
|
-
before_action :set_log, only:
|
6
|
+
before_action :set_log, only: :show
|
7
7
|
|
8
8
|
# GET /exception_logs
|
9
9
|
def index
|
@@ -12,7 +12,7 @@ module ExceptionTrack
|
|
12
12
|
|
13
13
|
def export
|
14
14
|
@logs = Log.order("id desc").where("created_at >= ?", 3.months.ago)
|
15
|
-
send_data JSON.pretty_generate(@logs.as_json(only: [
|
15
|
+
send_data JSON.pretty_generate(@logs.as_json(only: %i[title body created_at updated_at])), filename: "#{Date.current}.json", disposition: "attachment"
|
16
16
|
end
|
17
17
|
|
18
18
|
# GET /exception_logs/1
|
@@ -25,7 +25,6 @@ module ExceptionTrack
|
|
25
25
|
end
|
26
26
|
|
27
27
|
private
|
28
|
-
|
29
28
|
# Use callbacks to share common setup or constraints between actions.
|
30
29
|
def set_log
|
31
30
|
@log = Log.find(params[:id])
|
@@ -6,7 +6,7 @@
|
|
6
6
|
}
|
7
7
|
</script>
|
8
8
|
<div class="toolbar">
|
9
|
-
|
9
|
+
<a class="btn" href="/">Back to App</a>
|
10
10
|
|
11
11
|
<div class="pull-right">
|
12
12
|
<%= form_tag(export_logs_path, method: 'post') do %>
|
@@ -21,6 +21,7 @@
|
|
21
21
|
<% if @logs.blank? %>
|
22
22
|
<div class="no-record">No exceptions.</div>
|
23
23
|
<% else %>
|
24
|
+
<h1><%= pluralize(@logs.total_count, 'exception') %></h1>
|
24
25
|
<%= paginate @logs %>
|
25
26
|
|
26
27
|
<table class="table table-borded tabl">
|
@@ -9,10 +9,15 @@ ExceptionTrack.configure do
|
|
9
9
|
end
|
10
10
|
|
11
11
|
# ExceptionNotification.configure do |config|
|
12
|
-
# config.ignored_exceptions += %w
|
13
|
-
#
|
14
|
-
#
|
15
|
-
#
|
16
|
-
#
|
17
|
-
#
|
12
|
+
# config.ignored_exceptions += %w[
|
13
|
+
# ActionView::TemplateError
|
14
|
+
# ActionController::InvalidAuthenticityToken
|
15
|
+
# ActionController::BadRequest
|
16
|
+
# ActionView::MissingTemplate
|
17
|
+
# ActionController::UrlGenerationError
|
18
|
+
# ActionController::UnknownFormat
|
19
|
+
# ActionController::InvalidCrossOriginRequest
|
20
|
+
# ActionController::ParameterMissing
|
21
|
+
# Mime::Type::InvalidMimeType
|
22
|
+
# ]
|
18
23
|
# end
|
@@ -1,10 +1,10 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
class CreateExceptionTrackLogs < ActiveRecord::Migration[5.
|
3
|
+
class CreateExceptionTrackLogs < ActiveRecord::Migration[5.2]
|
4
4
|
def change
|
5
5
|
create_table :exception_tracks do |t|
|
6
6
|
t.string :title
|
7
|
-
t.text :body, limit:
|
7
|
+
t.text :body, limit: 16_777_215
|
8
8
|
|
9
9
|
t.timestamps
|
10
10
|
end
|
@@ -5,7 +5,9 @@ module ExceptionTrack
|
|
5
5
|
isolate_namespace ExceptionTrack
|
6
6
|
|
7
7
|
initializer "exception-track.assets.precompile", group: :all do |app|
|
8
|
-
app.config.assets.precompile += %w
|
8
|
+
app.config.assets.precompile += %w[exception-track/application.css]
|
9
9
|
end
|
10
|
+
|
11
|
+
ExceptionTrack::LogSubscriber.attach_to :exception_track
|
10
12
|
end
|
11
13
|
end
|
@@ -0,0 +1,12 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ExceptionTrack
|
4
|
+
class LogSubscriber < ActiveSupport::LogSubscriber
|
5
|
+
# ActiveSupport::Notifications.instrument('track.exception_track', action: action)
|
6
|
+
def track(event)
|
7
|
+
prefix = color("ExceptionTrack", CYAN)
|
8
|
+
title = color(event.payload[:title], RED)
|
9
|
+
debug " #{prefix} track db (#{event.duration.round(1)}ms)"
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
data/lib/exception-track.rb
CHANGED
@@ -2,11 +2,11 @@
|
|
2
2
|
|
3
3
|
require "exception-track/version"
|
4
4
|
require "exception-track/configuration"
|
5
|
+
require "exception-track/log_subscriber"
|
5
6
|
require "exception-track/engine"
|
6
7
|
|
7
8
|
require "exception_notification"
|
8
9
|
require "exception_notification/rails"
|
9
|
-
require "exception_notifier/exception_track_notifier"
|
10
10
|
|
11
11
|
require "kaminari"
|
12
12
|
|
@@ -14,17 +14,18 @@ module ExceptionTrack
|
|
14
14
|
class << self
|
15
15
|
def config
|
16
16
|
return @config if defined?(@config)
|
17
|
+
|
17
18
|
@config = Configuration.new
|
18
19
|
@config.environments = %i[development production]
|
19
20
|
@config
|
20
21
|
end
|
21
22
|
|
22
23
|
def configure(&block)
|
23
|
-
config.
|
24
|
+
config.instance_eval(&block)
|
24
25
|
end
|
25
26
|
end
|
26
27
|
end
|
27
28
|
|
28
29
|
ExceptionNotification.configure do |config|
|
29
|
-
config.add_notifier :
|
30
|
+
config.add_notifier :db, {}
|
30
31
|
end
|
@@ -0,0 +1,66 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ExceptionNotification
|
4
|
+
class Rack
|
5
|
+
class CascadePassException < RuntimeError; end
|
6
|
+
|
7
|
+
def initialize(app, options = {})
|
8
|
+
@app = app
|
9
|
+
|
10
|
+
ExceptionNotifier.tap do |en|
|
11
|
+
en.ignored_exceptions = options.delete(:ignore_exceptions) if options.key?(:ignore_exceptions)
|
12
|
+
en.error_grouping = options.delete(:error_grouping) if options.key?(:error_grouping)
|
13
|
+
en.error_grouping_period = options.delete(:error_grouping_period) if options.key?(:error_grouping_period)
|
14
|
+
en.notification_trigger = options.delete(:notification_trigger) if options.key?(:notification_trigger)
|
15
|
+
|
16
|
+
if options.key?(:error_grouping_cache)
|
17
|
+
en.error_grouping_cache = options.delete(:error_grouping_cache)
|
18
|
+
elsif defined?(Rails) && Rails.respond_to?(:cache)
|
19
|
+
en.error_grouping_cache = Rails.cache
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
if options.key?(:ignore_if)
|
24
|
+
rack_ignore = options.delete(:ignore_if)
|
25
|
+
ExceptionNotifier.ignore_if do |exception, opts|
|
26
|
+
opts.key?(:env) && rack_ignore.call(opts[:env], exception)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
if options.key?(:ignore_notifier_if)
|
31
|
+
rack_ignore_by_notifier = options.delete(:ignore_notifier_if)
|
32
|
+
rack_ignore_by_notifier.each do |notifier, proc|
|
33
|
+
ExceptionNotifier.ignore_notifier_if(notifier) do |exception, opts|
|
34
|
+
opts.key?(:env) && proc.call(opts[:env], exception)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
ExceptionNotifier.ignore_crawlers(options.delete(:ignore_crawlers)) if options.key?(:ignore_crawlers)
|
40
|
+
|
41
|
+
@ignore_cascade_pass = options.delete(:ignore_cascade_pass) { true }
|
42
|
+
|
43
|
+
options.each do |notifier_name, opts|
|
44
|
+
ExceptionNotifier.register_exception_notifier(notifier_name, opts)
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
def call(env)
|
49
|
+
_, headers, = response = @app.call(env)
|
50
|
+
|
51
|
+
if !@ignore_cascade_pass && headers["X-Cascade"] == "pass"
|
52
|
+
msg = "This exception means that the preceding Rack middleware set the 'X-Cascade' header to 'pass' -- in " \
|
53
|
+
"Rails, this often means that the route was not found (404 error)."
|
54
|
+
raise CascadePassException, msg
|
55
|
+
end
|
56
|
+
|
57
|
+
response
|
58
|
+
rescue Exception => e
|
59
|
+
env["exception_notifier.delivered"] = true if ExceptionNotifier.notify_exception(e, env: env)
|
60
|
+
|
61
|
+
raise e unless e.is_a?(CascadePassException)
|
62
|
+
|
63
|
+
response
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
@@ -0,0 +1,11 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ExceptionNotification
|
4
|
+
class Engine < ::Rails::Engine
|
5
|
+
config.exception_notification = ExceptionNotifier
|
6
|
+
config.exception_notification.logger = Rails.logger
|
7
|
+
config.exception_notification.error_grouping_cache = Rails.cache
|
8
|
+
|
9
|
+
config.app_middleware.use ExceptionNotification::Rack
|
10
|
+
end
|
11
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "resque/failure/base"
|
4
|
+
|
5
|
+
module ExceptionNotification
|
6
|
+
class Resque < Resque::Failure::Base
|
7
|
+
def self.count
|
8
|
+
::Resque::Stat[:failed]
|
9
|
+
end
|
10
|
+
|
11
|
+
def save
|
12
|
+
data = {
|
13
|
+
error_class: exception.class.name,
|
14
|
+
error_message: exception.message,
|
15
|
+
failed_at: Time.now.to_s,
|
16
|
+
payload: payload,
|
17
|
+
queue: queue,
|
18
|
+
worker: worker.to_s
|
19
|
+
}
|
20
|
+
|
21
|
+
ExceptionNotifier.notify_exception(exception, data: {resque: data})
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "sidekiq"
|
4
|
+
|
5
|
+
# Note: this class is only needed for Sidekiq version < 3.
|
6
|
+
module ExceptionNotification
|
7
|
+
class Sidekiq
|
8
|
+
def call(_worker, msg, _queue)
|
9
|
+
yield
|
10
|
+
rescue Exception => e
|
11
|
+
ExceptionNotifier.notify_exception(e, data: {sidekiq: msg})
|
12
|
+
raise e
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
if ::Sidekiq::VERSION < "3"
|
18
|
+
::Sidekiq.configure_server do |config|
|
19
|
+
config.server_middleware do |chain|
|
20
|
+
chain.add ::ExceptionNotification::Sidekiq
|
21
|
+
end
|
22
|
+
end
|
23
|
+
else
|
24
|
+
::Sidekiq.configure_server do |config|
|
25
|
+
config.error_handlers << proc do |ex, context|
|
26
|
+
ExceptionNotifier.notify_exception(ex, data: {sidekiq: context})
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "exception_notifier/notifier"
|
4
|
+
require "exception_notification/rack"
|
5
|
+
require "exception_notification/version"
|
6
|
+
|
7
|
+
module ExceptionNotification
|
8
|
+
# Alternative way to setup ExceptionNotification.
|
9
|
+
# Run 'rails generate exception_notification:install' to create
|
10
|
+
# a fresh initializer with all configuration values.
|
11
|
+
def self.configure
|
12
|
+
yield ExceptionNotifier
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ExceptionNotifier
|
4
|
+
class BaseNotifier
|
5
|
+
attr_accessor :base_options
|
6
|
+
|
7
|
+
def initialize(options = {})
|
8
|
+
@base_options = options
|
9
|
+
end
|
10
|
+
|
11
|
+
def send_notice(exception, options, message, message_opts = nil)
|
12
|
+
_pre_callback(exception, options, message, message_opts)
|
13
|
+
result = yield(message, message_opts)
|
14
|
+
_post_callback(exception, options, message, message_opts)
|
15
|
+
result
|
16
|
+
end
|
17
|
+
|
18
|
+
def _pre_callback(exception, options, message, message_opts)
|
19
|
+
return unless @base_options[:pre_callback].respond_to?(:call)
|
20
|
+
|
21
|
+
@base_options[:pre_callback].call(options, self, exception.backtrace, message, message_opts)
|
22
|
+
end
|
23
|
+
|
24
|
+
def _post_callback(exception, options, message, message_opts)
|
25
|
+
return unless @base_options[:post_callback].respond_to?(:call)
|
26
|
+
|
27
|
+
@base_options[:post_callback].call(options, self, exception.backtrace, message, message_opts)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|