dislogger 0.1.8 → 0.2.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/lib/dislogger/configuration.rb +1 -1
- data/lib/dislogger/error_handler.rb +181 -12
- data/lib/dislogger/notifiers/discord_notifier.rb +33 -12
- data/lib/dislogger/version.rb +1 -1
- metadata +1 -1
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 40772178fe1446aaa2cc9bafcb10f11815e3416ad6a7b294dfbd69e9c153c338
|
4
|
+
data.tar.gz: e6e2d4e174ae770eff2b0be41cd9364a6f81606b393061379b168a23eb60fbf8
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 8bd268340561e183ef3668cee56ea2ca964d4c2a9b70571a7d2a9b683e57199be8444191284ff1a358167002acccefd57d7abe607594c2adbcf08703c9afa484
|
7
|
+
data.tar.gz: bbfbeed87f691bfa3d1f7049a5873d16f938fbd5b019dc71f04eb4024e1be59fb3fe5b65e77de547560df35fb2adeb63f1fbe14934f0679172c0aa0b03a69497
|
@@ -13,7 +13,7 @@ module Dislogger
|
|
13
13
|
@discord_webhook_url = nil
|
14
14
|
@bot_username = 'Error Logger'
|
15
15
|
@backtrace_lines_limit = 5
|
16
|
-
@enabled_environments = %w[production staging
|
16
|
+
@enabled_environments = %w[production staging]
|
17
17
|
@environment = nil
|
18
18
|
@error_color_map = {
|
19
19
|
500 => 15158332, # Red for server errors
|
@@ -7,16 +7,116 @@ module Dislogger
|
|
7
7
|
extend ActiveSupport::Concern
|
8
8
|
|
9
9
|
included do
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
10
|
+
if ancestors.include?(ActionController::API) || ancestors.include?(ActionController::Base)
|
11
|
+
# System and Runtime Errors
|
12
|
+
rescue_from Exception, with: :handle_exception
|
13
|
+
rescue_from StandardError, with: :handle_internal_server_error
|
14
|
+
rescue_from RuntimeError, with: :handle_runtime_error
|
15
|
+
rescue_from SystemStackError, with: :handle_stack_error
|
16
|
+
rescue_from NoMemoryError, with: :handle_memory_error
|
17
|
+
rescue_from SystemCallError, with: :handle_system_error
|
18
|
+
rescue_from SignalException, with: :handle_signal_error
|
19
|
+
rescue_from ScriptError, with: :handle_script_error
|
20
|
+
|
21
|
+
# Database Errors
|
22
|
+
rescue_from ActiveRecord::RecordNotFound, with: :handle_not_found
|
23
|
+
rescue_from ActiveRecord::RecordInvalid, with: :handle_unprocessable_entity
|
24
|
+
rescue_from ActiveRecord::RecordNotUnique, with: :handle_conflict
|
25
|
+
rescue_from ActiveRecord::ConnectionTimeoutError, with: :handle_timeout
|
26
|
+
rescue_from ActiveRecord::InvalidForeignKey, with: :handle_foreign_key_error
|
27
|
+
rescue_from ActiveRecord::ReadOnlyRecord, with: :handle_forbidden
|
28
|
+
rescue_from ActiveRecord::StaleObjectError, with: :handle_conflict
|
29
|
+
|
30
|
+
# Controller and Routing Errors
|
31
|
+
rescue_from ActionController::ParameterMissing, with: :handle_unprocessable_entity
|
32
|
+
rescue_from ActionController::InvalidAuthenticityToken, with: :handle_unauthorized
|
33
|
+
rescue_from ActionController::UnknownFormat, with: :handle_not_acceptable
|
34
|
+
rescue_from ActionController::UrlGenerationError, with: :handle_bad_request
|
35
|
+
rescue_from ActionController::RoutingError, with: :handle_not_found
|
36
|
+
rescue_from AbstractController::ActionNotFound, with: :handle_not_found
|
37
|
+
|
38
|
+
# Authorization Errors
|
39
|
+
rescue_from Pundit::NotAuthorizedError, with: :handle_forbidden if defined?(Pundit)
|
40
|
+
rescue_from CanCan::AccessDenied, with: :handle_forbidden if defined?(CanCan)
|
41
|
+
|
42
|
+
# Common Ruby Errors
|
43
|
+
rescue_from NameError, with: :handle_internal_server_error
|
44
|
+
rescue_from NoMethodError, with: :handle_internal_server_error
|
45
|
+
rescue_from ArgumentError, with: :handle_bad_request
|
46
|
+
rescue_from TypeError, with: :handle_internal_server_error
|
47
|
+
rescue_from LoadError, with: :handle_internal_server_error
|
48
|
+
rescue_from SyntaxError, with: :handle_internal_server_error
|
49
|
+
rescue_from Timeout::Error, with: :handle_timeout
|
50
|
+
end
|
16
51
|
end
|
17
52
|
|
18
53
|
private
|
19
54
|
|
55
|
+
def handle_exception(exception)
|
56
|
+
# Log detailed error information
|
57
|
+
Rails.logger.error("[Dislogger] Unexpected error caught: #{exception.class.name}")
|
58
|
+
Rails.logger.error("[Dislogger] Message: #{exception.message}")
|
59
|
+
Rails.logger.error("[Dislogger] Backtrace:\n#{exception.backtrace.join("\n")}") if exception.backtrace
|
60
|
+
|
61
|
+
notify_and_render_error(
|
62
|
+
message: "An unexpected error occurred: #{exception.class.name} - #{exception.message}",
|
63
|
+
status: :internal_server_error,
|
64
|
+
backtrace: exception.backtrace,
|
65
|
+
details: {
|
66
|
+
error_class: exception.class.name,
|
67
|
+
message: exception.message
|
68
|
+
}
|
69
|
+
)
|
70
|
+
end
|
71
|
+
|
72
|
+
def handle_runtime_error(exception)
|
73
|
+
notify_and_render_error(
|
74
|
+
message: "Runtime error: #{exception.message}",
|
75
|
+
status: :internal_server_error,
|
76
|
+
backtrace: exception.backtrace
|
77
|
+
)
|
78
|
+
end
|
79
|
+
|
80
|
+
def handle_stack_error(exception)
|
81
|
+
notify_and_render_error(
|
82
|
+
message: "Stack overflow error: #{exception.message}",
|
83
|
+
status: :internal_server_error,
|
84
|
+
backtrace: exception.backtrace
|
85
|
+
)
|
86
|
+
end
|
87
|
+
|
88
|
+
def handle_memory_error(exception)
|
89
|
+
notify_and_render_error(
|
90
|
+
message: "Out of memory error: #{exception.message}",
|
91
|
+
status: :internal_server_error,
|
92
|
+
backtrace: exception.backtrace
|
93
|
+
)
|
94
|
+
end
|
95
|
+
|
96
|
+
def handle_system_error(exception)
|
97
|
+
notify_and_render_error(
|
98
|
+
message: "System error: #{exception.message}",
|
99
|
+
status: :internal_server_error,
|
100
|
+
backtrace: exception.backtrace
|
101
|
+
)
|
102
|
+
end
|
103
|
+
|
104
|
+
def handle_signal_error(exception)
|
105
|
+
notify_and_render_error(
|
106
|
+
message: "Signal error: #{exception.message}",
|
107
|
+
status: :internal_server_error,
|
108
|
+
backtrace: exception.backtrace
|
109
|
+
)
|
110
|
+
end
|
111
|
+
|
112
|
+
def handle_script_error(exception)
|
113
|
+
notify_and_render_error(
|
114
|
+
message: "Script error: #{exception.message}",
|
115
|
+
status: :internal_server_error,
|
116
|
+
backtrace: exception.backtrace
|
117
|
+
)
|
118
|
+
end
|
119
|
+
|
20
120
|
def handle_not_found(exception)
|
21
121
|
notify_and_render_error(
|
22
122
|
message: exception.message.presence || 'Resource not found',
|
@@ -25,7 +125,14 @@ module Dislogger
|
|
25
125
|
end
|
26
126
|
|
27
127
|
def handle_unprocessable_entity(exception)
|
28
|
-
errors = exception.respond_to?(:record)
|
128
|
+
errors = if exception.respond_to?(:record)
|
129
|
+
exception.record.errors.full_messages
|
130
|
+
elsif exception.respond_to?(:errors)
|
131
|
+
exception.errors
|
132
|
+
else
|
133
|
+
[exception.message]
|
134
|
+
end
|
135
|
+
|
29
136
|
notify_and_render_error(
|
30
137
|
message: 'Validation failed',
|
31
138
|
status: :unprocessable_entity,
|
@@ -33,16 +140,59 @@ module Dislogger
|
|
33
140
|
)
|
34
141
|
end
|
35
142
|
|
143
|
+
def handle_conflict(exception)
|
144
|
+
notify_and_render_error(
|
145
|
+
message: exception.message || 'Resource conflict',
|
146
|
+
status: :conflict
|
147
|
+
)
|
148
|
+
end
|
149
|
+
|
150
|
+
def handle_timeout(exception)
|
151
|
+
notify_and_render_error(
|
152
|
+
message: exception.message || 'Request timeout',
|
153
|
+
status: :request_timeout
|
154
|
+
)
|
155
|
+
end
|
156
|
+
|
157
|
+
def handle_foreign_key_error(exception)
|
158
|
+
notify_and_render_error(
|
159
|
+
message: 'Cannot delete or update due to database constraint',
|
160
|
+
status: :unprocessable_entity,
|
161
|
+
details: [exception.message]
|
162
|
+
)
|
163
|
+
end
|
164
|
+
|
165
|
+
def handle_unauthorized(exception)
|
166
|
+
notify_and_render_error(
|
167
|
+
message: exception.message || 'Unauthorized access',
|
168
|
+
status: :unauthorized
|
169
|
+
)
|
170
|
+
end
|
171
|
+
|
36
172
|
def handle_forbidden(exception)
|
37
173
|
notify_and_render_error(
|
38
|
-
message: exception.message.presence || '
|
174
|
+
message: exception.message.presence || 'Access forbidden',
|
39
175
|
status: :forbidden
|
40
176
|
)
|
41
177
|
end
|
42
178
|
|
179
|
+
def handle_not_acceptable(exception)
|
180
|
+
notify_and_render_error(
|
181
|
+
message: exception.message || 'Not acceptable',
|
182
|
+
status: :not_acceptable
|
183
|
+
)
|
184
|
+
end
|
185
|
+
|
186
|
+
def handle_bad_request(exception)
|
187
|
+
notify_and_render_error(
|
188
|
+
message: exception.message || 'Bad request',
|
189
|
+
status: :bad_request
|
190
|
+
)
|
191
|
+
end
|
192
|
+
|
43
193
|
def handle_internal_server_error(exception)
|
44
194
|
notify_and_render_error(
|
45
|
-
message: 'Internal
|
195
|
+
message: exception.message || 'Internal server error',
|
46
196
|
status: :internal_server_error,
|
47
197
|
backtrace: exception.backtrace
|
48
198
|
)
|
@@ -51,23 +201,42 @@ module Dislogger
|
|
51
201
|
def notify_and_render_error(message:, status:, details: nil, backtrace: nil)
|
52
202
|
status_code = Rack::Utils::SYMBOL_TO_STATUS_CODE[status]
|
53
203
|
|
54
|
-
Notifiers::DiscordNotifier.new
|
204
|
+
notifier = Notifiers::DiscordNotifier.new
|
205
|
+
notification_result = notifier.notify(
|
55
206
|
message: message,
|
56
207
|
status: status_code,
|
57
208
|
backtrace: backtrace
|
58
209
|
)
|
59
210
|
|
60
|
-
|
211
|
+
Rails.logger.info("[Dislogger] Notification result: #{notification_result}") if defined?(Rails)
|
212
|
+
|
213
|
+
render_error(message, status, error_details)
|
61
214
|
end
|
62
215
|
|
63
216
|
def render_error(message, status, details = nil)
|
64
217
|
error_response = {
|
65
218
|
status: status,
|
66
|
-
message: message
|
219
|
+
message: message,
|
220
|
+
timestamp: Time.current.utc.iso8601
|
67
221
|
}
|
68
222
|
error_response[:details] = details if details
|
69
223
|
|
70
224
|
render json: error_response, status: status
|
71
225
|
end
|
226
|
+
|
227
|
+
private
|
228
|
+
|
229
|
+
def filter_sensitive_params(params_hash)
|
230
|
+
# List of sensitive parameters we don't want to show
|
231
|
+
sensitive_params = %w[password password_confirmation token api_key secret]
|
232
|
+
|
233
|
+
params_hash.deep_transform_values do |value|
|
234
|
+
if sensitive_params.any? { |param| value.to_s.include?(param) }
|
235
|
+
'[FILTERED]'
|
236
|
+
else
|
237
|
+
value
|
238
|
+
end
|
239
|
+
end
|
240
|
+
end
|
72
241
|
end
|
73
242
|
end
|
@@ -4,22 +4,21 @@ module Dislogger
|
|
4
4
|
module Notifiers
|
5
5
|
class DiscordNotifier < BaseNotifier
|
6
6
|
def notify(message:, status:, backtrace: nil)
|
7
|
-
return unless enabled? && @config.discord_webhook_url.present?
|
7
|
+
return false unless enabled? && @config.discord_webhook_url.present?
|
8
8
|
|
9
|
+
log_info("Attempting to send Discord notification")
|
9
10
|
formatted_message = format_message(message, status, backtrace)
|
10
11
|
send_notification(formatted_message)
|
11
12
|
rescue StandardError => e
|
12
|
-
|
13
|
-
|
14
|
-
else
|
15
|
-
warn("Discord notification failed: #{e.message}")
|
16
|
-
end
|
13
|
+
log_error("Discord notification failed: #{e.message}")
|
14
|
+
log_error(e.backtrace.join("\n")) if e.backtrace
|
17
15
|
false
|
18
16
|
end
|
19
17
|
|
20
18
|
protected
|
21
19
|
|
22
20
|
def format_message(message, status, backtrace)
|
21
|
+
log_info("Formatting Discord message")
|
23
22
|
Formatters::DiscordFormatter.new(
|
24
23
|
message: message,
|
25
24
|
status: status,
|
@@ -29,6 +28,7 @@ module Dislogger
|
|
29
28
|
end
|
30
29
|
|
31
30
|
def send_notification(payload)
|
31
|
+
log_info("Sending notification to Discord")
|
32
32
|
response = HTTParty.post(
|
33
33
|
@config.discord_webhook_url,
|
34
34
|
body: payload.to_json,
|
@@ -37,19 +37,40 @@ module Dislogger
|
|
37
37
|
|
38
38
|
unless response.success?
|
39
39
|
error_message = "Discord API Error: #{response.code} - #{response.body}"
|
40
|
-
|
41
|
-
Rails.logger.error(error_message)
|
42
|
-
else
|
43
|
-
warn(error_message)
|
44
|
-
end
|
40
|
+
log_error(error_message)
|
45
41
|
return false
|
46
42
|
end
|
47
43
|
|
44
|
+
log_info("Discord notification sent successfully")
|
48
45
|
true
|
46
|
+
rescue StandardError => e
|
47
|
+
log_error("Failed to send Discord notification: #{e.message}")
|
48
|
+
log_error(e.backtrace.join("\n")) if e.backtrace
|
49
|
+
false
|
49
50
|
end
|
50
51
|
|
51
52
|
def enabled?
|
52
|
-
@config.enabled?
|
53
|
+
result = @config.enabled?
|
54
|
+
log_info("Dislogger enabled? #{result} (environment: #{@config.environment}, enabled_environments: #{@config.enabled_environments})")
|
55
|
+
result
|
56
|
+
end
|
57
|
+
|
58
|
+
private
|
59
|
+
|
60
|
+
def log_info(message)
|
61
|
+
if defined?(Rails) && Rails.logger
|
62
|
+
Rails.logger.info("[Dislogger] #{message}")
|
63
|
+
else
|
64
|
+
puts "[Dislogger] #{message}"
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
def log_error(message)
|
69
|
+
if defined?(Rails) && Rails.logger
|
70
|
+
Rails.logger.error("[Dislogger] #{message}")
|
71
|
+
else
|
72
|
+
warn "[Dislogger] #{message}"
|
73
|
+
end
|
53
74
|
end
|
54
75
|
end
|
55
76
|
end
|
data/lib/dislogger/version.rb
CHANGED