activerabbit-ai 0.4.7 → 0.5.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/CHANGELOG.md +21 -0
- data/lib/active_rabbit/client/configuration.rb +3 -0
- data/lib/active_rabbit/client/exception_tracker.rb +27 -11
- data/lib/active_rabbit/client/http_client.rb +87 -51
- data/lib/active_rabbit/client/railtie.rb +140 -105
- data/lib/active_rabbit/client/version.rb +1 -3
- data/lib/active_rabbit/client.rb +28 -2
- data/lib/active_rabbit/middleware/error_capture_middleware.rb +1 -1
- data/lib/active_rabbit/reporting.rb +1 -1
- data/lib/active_rabbit/routing/not_found_app.rb +1 -1
- metadata +2 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 915dd514027b844f4959d32085c1c26dfb69924b911fe31472c9f89eeaba0fd5
|
|
4
|
+
data.tar.gz: 128a2fe0aaaf197b72ee457c1f240df1057d63c029fd65dba5a92f890ff18de5
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 382c8d0304b2048f8fa26c481530f1bfe19abac6ec8ec21c9fed57aebac84c2e40750fca4329b9864c301d6e4de7d9b0b411a97bd66d5ee9fe00cfbc58406d6e
|
|
7
|
+
data.tar.gz: d6ce4cf362160793d49d1d6d00444352c24b03eda9cb22be079d37ce65e37ee42e918be33ade5b76df761670d8bde4837dcd6c7a3893b09048774aee2a2e5fd2
|
data/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,27 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to this project will be documented in this file.
|
|
4
4
|
|
|
5
|
+
## [0.5.0] - 2025-12-12
|
|
6
|
+
|
|
7
|
+
### Changed
|
|
8
|
+
- **Minor release**: Bumped version to 0.5.0 to reflect latest stable changes and internal improvements
|
|
9
|
+
|
|
10
|
+
## [0.4.9] - 2025-11-30
|
|
11
|
+
|
|
12
|
+
### Fixed
|
|
13
|
+
- **Rails Integration**: Fixed `LocalJumpError: unexpected return` when `ActiveRabbit` is installed but not configured
|
|
14
|
+
- This error occurred when `subscribed` callbacks tried to return early if configuration was missing
|
|
15
|
+
- Changed `return` to `next` in Railtie subscription blocks
|
|
16
|
+
|
|
17
|
+
## [0.4.8] - 2025-11-27
|
|
18
|
+
|
|
19
|
+
### Added
|
|
20
|
+
- **Deploy notification API**: Added `ActiveRabbit::Client.notify_deploy` for reporting deploys to ActiveRabbit
|
|
21
|
+
- Sends deploy metadata (revision, environment, project slug, version, status, user, timestamps) to `/api/v1/deploys`
|
|
22
|
+
|
|
23
|
+
### Fixed
|
|
24
|
+
- **Test suite stability**: RSpec tests and CI config updated to keep 0.4.x series green
|
|
25
|
+
|
|
5
26
|
## [0.4.7] - 2025-11-06
|
|
6
27
|
|
|
7
28
|
### Fixed
|
|
@@ -15,6 +15,8 @@ module ActiveRabbit
|
|
|
15
15
|
attr_accessor :release, :server_name, :logger
|
|
16
16
|
attr_accessor :before_send_event, :before_send_exception
|
|
17
17
|
attr_accessor :dedupe_window # Time window in seconds for error deduplication (0 = disabled)
|
|
18
|
+
attr_accessor :revision
|
|
19
|
+
attr_accessor :disable_console_logs
|
|
18
20
|
|
|
19
21
|
def initialize
|
|
20
22
|
@api_url = ENV.fetch("active_rabbit_API_URL", "https://api.activerabbit.ai")
|
|
@@ -37,6 +39,7 @@ module ActiveRabbit
|
|
|
37
39
|
@enable_performance_monitoring = true
|
|
38
40
|
@enable_n_plus_one_detection = true
|
|
39
41
|
@enable_pii_scrubbing = true
|
|
42
|
+
@disable_console_logs = true
|
|
40
43
|
|
|
41
44
|
# PII scrubbing
|
|
42
45
|
@pii_fields = %w[
|
|
@@ -32,25 +32,25 @@ module ActiveRabbit
|
|
|
32
32
|
end
|
|
33
33
|
|
|
34
34
|
# Send exception to API and return response
|
|
35
|
-
|
|
36
|
-
|
|
35
|
+
log(:info, "[ActiveRabbit] Preparing to send exception: #{exception.class.name}")
|
|
36
|
+
log(:debug, "[ActiveRabbit] Exception data: #{exception_data.inspect}")
|
|
37
37
|
|
|
38
38
|
# Ensure we have required fields
|
|
39
39
|
unless exception_data[:exception_class] && exception_data[:message] && exception_data[:backtrace]
|
|
40
|
-
|
|
41
|
-
|
|
40
|
+
log(:error, "[ActiveRabbit] Missing required fields in exception data")
|
|
41
|
+
log(:debug, "[ActiveRabbit] Available fields: #{exception_data.keys.inspect}")
|
|
42
42
|
return nil
|
|
43
43
|
end
|
|
44
44
|
|
|
45
45
|
response = http_client.post_exception(exception_data)
|
|
46
46
|
|
|
47
47
|
if response.nil?
|
|
48
|
-
|
|
48
|
+
log(:error, "[ActiveRabbit] Failed to send exception - both primary and fallback endpoints failed")
|
|
49
49
|
return nil
|
|
50
50
|
end
|
|
51
51
|
|
|
52
|
-
|
|
53
|
-
|
|
52
|
+
log(:info, "[ActiveRabbit] Exception successfully sent to API")
|
|
53
|
+
log(:debug, "[ActiveRabbit] API Response: #{response.inspect}")
|
|
54
54
|
response
|
|
55
55
|
end
|
|
56
56
|
|
|
@@ -60,6 +60,22 @@ module ActiveRabbit
|
|
|
60
60
|
|
|
61
61
|
private
|
|
62
62
|
|
|
63
|
+
def log(level, message)
|
|
64
|
+
cfg = configuration
|
|
65
|
+
return if cfg&.disable_console_logs
|
|
66
|
+
|
|
67
|
+
logger = cfg&.logger
|
|
68
|
+
return unless logger
|
|
69
|
+
|
|
70
|
+
case level
|
|
71
|
+
when :info then logger.info(message)
|
|
72
|
+
when :debug then logger.debug(message)
|
|
73
|
+
when :error then logger.error(message)
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
rescue
|
|
77
|
+
end
|
|
78
|
+
|
|
63
79
|
def build_exception_data(exception:, context:, user_id:, tags:, handled: nil)
|
|
64
80
|
parsed_bt = parse_backtrace(exception.backtrace || [])
|
|
65
81
|
backtrace_lines = parsed_bt.map { |frame| frame[:line] }
|
|
@@ -132,10 +148,10 @@ module ActiveRabbit
|
|
|
132
148
|
end
|
|
133
149
|
|
|
134
150
|
# Log what we're sending
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
151
|
+
log(:debug, "[ActiveRabbit] Built exception data:")
|
|
152
|
+
log(:debug, "[ActiveRabbit] - Required fields: class=#{data[:exception_class]}, message=#{data[:message]}, backtrace=#{data[:backtrace]&.first}")
|
|
153
|
+
log(:debug, "[ActiveRabbit] - Error details: type=#{data[:error_type]}, source=#{data[:error_source]}, component=#{data[:error_component]}")
|
|
154
|
+
log(:debug, "[ActiveRabbit] - Request info: path=#{data[:request_path]}, method=#{data[:request_method]}, action=#{data[:controller_action]}")
|
|
139
155
|
|
|
140
156
|
data
|
|
141
157
|
end
|
|
@@ -37,33 +37,33 @@ module ActiveRabbit
|
|
|
37
37
|
# Primary endpoint path
|
|
38
38
|
path = "/api/v1/events/errors"
|
|
39
39
|
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
40
|
+
log(:info, "[ActiveRabbit] Sending exception to API...")
|
|
41
|
+
log(:debug, "[ActiveRabbit] Exception payload (pre-JSON): #{safe_preview(exception_data_with_type)}")
|
|
42
|
+
log(:debug, "[ActiveRabbit] Target path: #{path}")
|
|
43
43
|
|
|
44
44
|
begin
|
|
45
45
|
# Primary endpoint attempt
|
|
46
|
-
|
|
47
|
-
response =
|
|
48
|
-
|
|
46
|
+
log(:info, "[ActiveRabbit] Making request to primary endpoint: POST #{path}")
|
|
47
|
+
response = enqueue_request(:post, path, exception_data_with_type)
|
|
48
|
+
log(:info, "[ActiveRabbit] Exception sent successfully (errors endpoint)")
|
|
49
49
|
return response
|
|
50
50
|
rescue => e
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
51
|
+
log(:error, "[ActiveRabbit] Primary send failed: #{e.class}: #{e.message}")
|
|
52
|
+
log(:error, "[ActiveRabbit] Primary error backtrace: #{e.backtrace&.first(3)}")
|
|
53
|
+
log(:debug, "[ActiveRabbit] Falling back to /api/v1/events with type=error")
|
|
54
54
|
|
|
55
55
|
begin
|
|
56
56
|
# Fallback to generic events endpoint
|
|
57
57
|
fallback_path = "/api/v1/events"
|
|
58
58
|
fallback_body = { type: "error", data: exception_data_with_type }
|
|
59
|
-
|
|
60
|
-
response =
|
|
61
|
-
|
|
59
|
+
log(:info, "[ActiveRabbit] Making request to fallback endpoint: POST #{fallback_path}")
|
|
60
|
+
response = enqueue_request(:post, fallback_path, fallback_body)
|
|
61
|
+
log(:info, "[ActiveRabbit] Exception sent via fallback endpoint")
|
|
62
62
|
return response
|
|
63
63
|
rescue => e2
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
64
|
+
log(:error, "[ActiveRabbit] Fallback send failed: #{e2.class}: #{e2.message}")
|
|
65
|
+
log(:error, "[ActiveRabbit] Fallback error backtrace: #{e2.backtrace&.first(3)}")
|
|
66
|
+
log(:error, "[ActiveRabbit] All exception sending attempts failed")
|
|
67
67
|
nil
|
|
68
68
|
end
|
|
69
69
|
end
|
|
@@ -85,13 +85,34 @@ module ActiveRabbit
|
|
|
85
85
|
end
|
|
86
86
|
|
|
87
87
|
# Send batch to API
|
|
88
|
-
|
|
88
|
+
log(:info, "[ActiveRabbit] Sending batch of #{events.length} events...")
|
|
89
89
|
response = make_request(:post, "/api/v1/events/batch", { events: events })
|
|
90
|
-
|
|
91
|
-
|
|
90
|
+
log(:info, "[ActiveRabbit] Batch sent successfully")
|
|
91
|
+
log(:debug, "[ActiveRabbit] Batch response: #{response.inspect}")
|
|
92
92
|
response
|
|
93
93
|
end
|
|
94
94
|
|
|
95
|
+
def post(path, payload)
|
|
96
|
+
uri = URI.join(@base_uri.to_s, path)
|
|
97
|
+
req = Net::HTTP::Post.new(uri)
|
|
98
|
+
req['Content-Type'] = 'application/json'
|
|
99
|
+
req["X-Project-Token"] = configuration.api_key
|
|
100
|
+
req.body = payload.to_json
|
|
101
|
+
|
|
102
|
+
res = Net::HTTP.start(uri.hostname, uri.port, use_ssl: uri.scheme == 'https') do |http|
|
|
103
|
+
http.request(req)
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
unless res.is_a?(Net::HTTPSuccess)
|
|
107
|
+
raise APIError, "ActiveRabbit API request failed: #{res.code} #{res.body}"
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
JSON.parse(res.body)
|
|
111
|
+
rescue => e
|
|
112
|
+
log(:error, "[ActiveRabbit] HTTP POST failed: #{e.class}: #{e.message}")
|
|
113
|
+
nil
|
|
114
|
+
end
|
|
115
|
+
|
|
95
116
|
def test_connection
|
|
96
117
|
response = make_request(:post, "/api/v1/test/connection", {
|
|
97
118
|
gem_version: ActiveRabbit::Client::VERSION,
|
|
@@ -105,18 +126,18 @@ module ActiveRabbit
|
|
|
105
126
|
def flush
|
|
106
127
|
return if @request_queue.empty?
|
|
107
128
|
|
|
108
|
-
|
|
129
|
+
log(:info, "[ActiveRabbit] Starting flush with #{@request_queue.length} items")
|
|
109
130
|
batch = @request_queue.shift(@request_queue.length)
|
|
110
131
|
return if batch.empty?
|
|
111
132
|
|
|
112
133
|
begin
|
|
113
|
-
|
|
134
|
+
log(:info, "[ActiveRabbit] Sending batch of #{batch.length} items")
|
|
114
135
|
response = post_batch(batch)
|
|
115
|
-
|
|
136
|
+
log(:info, "[ActiveRabbit] Batch sent successfully: #{response.inspect}")
|
|
116
137
|
response
|
|
117
138
|
rescue => e
|
|
118
|
-
|
|
119
|
-
|
|
139
|
+
log(:error, "[ActiveRabbit] Failed to send batch: #{e.message}")
|
|
140
|
+
log(:error, "[ActiveRabbit] Backtrace: #{e.backtrace&.first(3)}")
|
|
120
141
|
raise APIError, "Failed to send batch: #{e.message}"
|
|
121
142
|
end
|
|
122
143
|
end
|
|
@@ -140,8 +161,8 @@ module ActiveRabbit
|
|
|
140
161
|
timestamp: Time.now.to_f
|
|
141
162
|
}
|
|
142
163
|
|
|
143
|
-
|
|
144
|
-
|
|
164
|
+
log(:info, "[ActiveRabbit] Enqueueing request: #{method} #{path}")
|
|
165
|
+
log(:debug, "[ActiveRabbit] Request data: #{data.inspect}")
|
|
145
166
|
|
|
146
167
|
@request_queue << formatted_data
|
|
147
168
|
|
|
@@ -169,9 +190,9 @@ module ActiveRabbit
|
|
|
169
190
|
# Ensure path starts with a single leading slash
|
|
170
191
|
normalized_path = path.start_with?("/") ? path : "/#{path}"
|
|
171
192
|
uri = URI.join(current_base, normalized_path)
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
193
|
+
log(:info, "[ActiveRabbit] Making request: #{method.upcase} #{uri}")
|
|
194
|
+
log(:debug, "[ActiveRabbit] Request headers: X-Project-Token=#{configuration.api_key}, X-Project-ID=#{configuration.project_id}")
|
|
195
|
+
log(:debug, "[ActiveRabbit] Request body: #{safe_preview(data)}")
|
|
175
196
|
|
|
176
197
|
# Retry logic with exponential backoff
|
|
177
198
|
retries = 0
|
|
@@ -179,17 +200,17 @@ module ActiveRabbit
|
|
|
179
200
|
|
|
180
201
|
begin
|
|
181
202
|
response = perform_request(uri, method, data)
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
203
|
+
log(:info, "[ActiveRabbit] Response status: #{response.code}")
|
|
204
|
+
log(:debug, "[ActiveRabbit] Response headers: #{response.to_hash.inspect}")
|
|
205
|
+
log(:debug, "[ActiveRabbit] Response body: #{response.body}")
|
|
185
206
|
|
|
186
207
|
result = handle_response(response)
|
|
187
|
-
|
|
208
|
+
log(:debug, "[ActiveRabbit] Parsed response: #{result.inspect}")
|
|
188
209
|
result
|
|
189
210
|
rescue RetryableError => e
|
|
190
211
|
if retries < max_retries
|
|
191
212
|
retries += 1
|
|
192
|
-
|
|
213
|
+
log(:info, "[ActiveRabbit] Retrying request (#{retries}/#{max_retries})")
|
|
193
214
|
sleep(configuration.retry_delay * (2 ** (retries - 1)))
|
|
194
215
|
retry
|
|
195
216
|
end
|
|
@@ -197,7 +218,7 @@ module ActiveRabbit
|
|
|
197
218
|
rescue Net::OpenTimeout, Net::ReadTimeout => e
|
|
198
219
|
if retries < max_retries && should_retry_error?(e)
|
|
199
220
|
retries += 1
|
|
200
|
-
|
|
221
|
+
log(:info, "[ActiveRabbit] Retrying request after timeout (#{retries}/#{max_retries})")
|
|
201
222
|
sleep(configuration.retry_delay * (2 ** (retries - 1))) # Exponential backoff
|
|
202
223
|
retry
|
|
203
224
|
end
|
|
@@ -205,24 +226,24 @@ module ActiveRabbit
|
|
|
205
226
|
rescue Errno::ECONNREFUSED, Errno::EHOSTUNREACH, SocketError => e
|
|
206
227
|
if retries < max_retries
|
|
207
228
|
retries += 1
|
|
208
|
-
|
|
229
|
+
log(:info, "[ActiveRabbit] Retrying request after connection error (#{retries}/#{max_retries})")
|
|
209
230
|
sleep(configuration.retry_delay * (2 ** (retries - 1)))
|
|
210
231
|
retry
|
|
211
232
|
end
|
|
212
233
|
raise APIError, "Connection failed after #{retries} retries: #{e.message}"
|
|
213
234
|
rescue APIError, RateLimitError => e
|
|
214
235
|
# Re-raise API errors as-is
|
|
215
|
-
|
|
236
|
+
log(:error, "[ActiveRabbit] API error: #{e.class}: #{e.message}")
|
|
216
237
|
raise e
|
|
217
238
|
rescue => e
|
|
218
|
-
|
|
219
|
-
|
|
239
|
+
log(:error, "[ActiveRabbit] Request failed: #{e.class}: #{e.message}")
|
|
240
|
+
log(:error, "[ActiveRabbit] Backtrace: #{e.backtrace&.first(3)}")
|
|
220
241
|
raise APIError, "Request failed: #{e.message}"
|
|
221
242
|
end
|
|
222
243
|
end
|
|
223
244
|
|
|
224
245
|
def perform_request(uri, method, data)
|
|
225
|
-
|
|
246
|
+
log(:debug, "[ActiveRabbit] Making HTTP request: #{method.upcase} #{uri}")
|
|
226
247
|
http = Net::HTTP.new(uri.host, uri.port)
|
|
227
248
|
|
|
228
249
|
# Configure SSL if HTTPS
|
|
@@ -249,7 +270,7 @@ module ActiveRabbit
|
|
|
249
270
|
raise ArgumentError, "Unsupported HTTP method: #{method}"
|
|
250
271
|
end
|
|
251
272
|
|
|
252
|
-
|
|
273
|
+
log(:debug, "[ActiveRabbit] Request path: #{uri.request_uri}")
|
|
253
274
|
|
|
254
275
|
# Set headers
|
|
255
276
|
request['Content-Type'] = 'application/json'
|
|
@@ -270,8 +291,8 @@ module ActiveRabbit
|
|
|
270
291
|
end
|
|
271
292
|
|
|
272
293
|
def handle_response(response)
|
|
273
|
-
|
|
274
|
-
|
|
294
|
+
log(:debug, "[ActiveRabbit] Response code: #{response.code}")
|
|
295
|
+
log(:debug, "[ActiveRabbit] Response body: #{response.body}")
|
|
275
296
|
|
|
276
297
|
case response.code.to_i
|
|
277
298
|
when 200..299
|
|
@@ -279,23 +300,23 @@ module ActiveRabbit
|
|
|
279
300
|
if response.body && !response.body.empty?
|
|
280
301
|
begin
|
|
281
302
|
parsed = JSON.parse(response.body)
|
|
282
|
-
|
|
303
|
+
log(:debug, "[ActiveRabbit] Parsed response: #{parsed.inspect}")
|
|
283
304
|
parsed
|
|
284
305
|
rescue JSON::ParserError => e
|
|
285
|
-
|
|
286
|
-
|
|
306
|
+
log(:error, "[ActiveRabbit] Failed to parse response: #{e.message}")
|
|
307
|
+
log(:error, "[ActiveRabbit] Raw response: #{response.body}")
|
|
287
308
|
response.body
|
|
288
309
|
end
|
|
289
310
|
else
|
|
290
|
-
|
|
311
|
+
log(:debug, "[ActiveRabbit] Empty response body")
|
|
291
312
|
{}
|
|
292
313
|
end
|
|
293
314
|
when 429
|
|
294
|
-
|
|
315
|
+
log(:error, "[ActiveRabbit] Rate limit exceeded")
|
|
295
316
|
raise RateLimitError, "Rate limit exceeded"
|
|
296
317
|
when 400..499
|
|
297
318
|
error_message = extract_error_message(response)
|
|
298
|
-
|
|
319
|
+
log(:error, "[ActiveRabbit] Client error (#{response.code}): #{error_message}")
|
|
299
320
|
raise APIError, "Client error (#{response.code}): #{error_message}"
|
|
300
321
|
when 500..599
|
|
301
322
|
error_message = extract_error_message(response)
|
|
@@ -303,11 +324,11 @@ module ActiveRabbit
|
|
|
303
324
|
configuration.logger&.warn("[ActiveRabbit] Retryable server error (#{response.code}): #{error_message}")
|
|
304
325
|
raise RetryableError, "Server error (#{response.code}): #{error_message}"
|
|
305
326
|
else
|
|
306
|
-
|
|
327
|
+
log(:error, "[ActiveRabbit] Server error (#{response.code}): #{error_message}")
|
|
307
328
|
raise APIError, "Server error (#{response.code}): #{error_message}"
|
|
308
329
|
end
|
|
309
330
|
else
|
|
310
|
-
|
|
331
|
+
log(:error, "[ActiveRabbit] Unexpected response code: #{response.code}")
|
|
311
332
|
raise APIError, "Unexpected response code: #{response.code}"
|
|
312
333
|
end
|
|
313
334
|
end
|
|
@@ -362,7 +383,22 @@ module ActiveRabbit
|
|
|
362
383
|
s = obj.inspect
|
|
363
384
|
s.length > 2000 ? s[0,2000] + "...(truncated)" : s
|
|
364
385
|
end
|
|
365
|
-
end
|
|
366
386
|
|
|
387
|
+
def log(level, message)
|
|
388
|
+
return if configuration.disable_console_logs
|
|
389
|
+
|
|
390
|
+
logger = configuration.logger
|
|
391
|
+
return unless logger
|
|
392
|
+
|
|
393
|
+
case level
|
|
394
|
+
when :info
|
|
395
|
+
logger.info(message)
|
|
396
|
+
when :debug
|
|
397
|
+
logger.debug(message)
|
|
398
|
+
when :error
|
|
399
|
+
logger.error(message)
|
|
400
|
+
end
|
|
401
|
+
end
|
|
402
|
+
end
|
|
367
403
|
end
|
|
368
404
|
end
|
|
@@ -21,40 +21,46 @@ module ActiveRabbit
|
|
|
21
21
|
config.active_rabbit = ActiveSupport::OrderedOptions.new
|
|
22
22
|
|
|
23
23
|
initializer "active_rabbit.configure", after: :initialize_logger do |app|
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
24
|
+
apply_rails_configuration(app.config.active_rabbit)
|
|
25
|
+
|
|
26
|
+
if ActiveRabbit::Client.configuration && !ActiveRabbit::Client.configuration.disable_console_logs
|
|
27
|
+
if Rails.env.development?
|
|
28
|
+
ar_puts "\n=== ActiveRabbit Configure ==="
|
|
29
|
+
ar_puts "Environment: #{Rails.env}"
|
|
30
|
+
ar_puts "Already configured? #{ActiveRabbit::Client.configured?}"
|
|
31
|
+
ar_puts "================================\n"
|
|
32
|
+
end
|
|
29
33
|
end
|
|
30
34
|
|
|
31
35
|
# Configure ActiveRabbit from Rails configuration
|
|
32
36
|
ActiveRabbit::Client.configure do |config|
|
|
33
|
-
config.environment
|
|
34
|
-
config.logger
|
|
35
|
-
config.release
|
|
37
|
+
config.environment ||= Rails.env
|
|
38
|
+
config.logger ||= Rails.logger rescue Logger.new(STDOUT)
|
|
39
|
+
config.release ||= detect_release(app)
|
|
36
40
|
end
|
|
37
41
|
|
|
38
|
-
if
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
42
|
+
# if ActiveRabbit::Client.configuration && !ActiveRabbit::Client.configuration.disable_console_logs
|
|
43
|
+
# if Rails.env.development?
|
|
44
|
+
# ar_puts "\n=== ActiveRabbit Post-Configure ==="
|
|
45
|
+
# ar_puts "Now configured? #{ActiveRabbit::Client.configured?}"
|
|
46
|
+
# ar_puts "Configuration: #{ActiveRabbit::Client.configuration.inspect}"
|
|
47
|
+
# ar_puts "================================\n"
|
|
48
|
+
# end
|
|
49
|
+
# end
|
|
44
50
|
|
|
45
51
|
# Set up exception tracking
|
|
46
52
|
setup_exception_tracking(app) if ActiveRabbit::Client.configured?
|
|
47
53
|
end
|
|
48
54
|
|
|
49
55
|
initializer "active_rabbit.subscribe_to_notifications", after: :load_config_initializers do |app|
|
|
50
|
-
|
|
56
|
+
ar_log(:info, "[ActiveRabbit] Setting up performance notifications subscriptions")
|
|
51
57
|
# Subscribe regardless; each handler guards on configured?
|
|
52
58
|
subscribe_to_controller_events
|
|
53
59
|
subscribe_to_active_record_events
|
|
54
60
|
subscribe_to_action_view_events
|
|
55
61
|
subscribe_to_action_mailer_events if defined?(ActionMailer)
|
|
56
62
|
subscribe_to_exception_notifications
|
|
57
|
-
|
|
63
|
+
ar_log(:info, "[ActiveRabbit] Subscriptions setup complete")
|
|
58
64
|
|
|
59
65
|
# Defer complex subscriptions until after initialization
|
|
60
66
|
app.config.after_initialize do
|
|
@@ -85,83 +91,83 @@ module ActiveRabbit
|
|
|
85
91
|
# Configure middleware after logger is initialized to avoid init cycles
|
|
86
92
|
initializer "active_rabbit.add_middleware", after: :initialize_logger do |app|
|
|
87
93
|
if Rails.env.development?
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
94
|
+
ar_puts "\n=== ActiveRabbit Railtie Loading ==="
|
|
95
|
+
ar_puts "Rails Environment: #{Rails.env}"
|
|
96
|
+
ar_puts "Rails Middleware Stack Phase: #{app.middleware.respond_to?(:middlewares) ? 'Ready' : 'Not Ready'}"
|
|
97
|
+
ar_puts "================================\n"
|
|
98
|
+
ar_puts "\n=== Initial Middleware Stack ==="
|
|
99
|
+
ar_puts "(not available at this boot phase)"
|
|
100
|
+
ar_puts "=======================\n"
|
|
95
101
|
end
|
|
96
102
|
|
|
97
|
-
|
|
103
|
+
ar_puts "\n=== Adding ActiveRabbit Middleware ===" if Rails.env.development?
|
|
98
104
|
# Handle both development (DebugExceptions) and production (ShowExceptions)
|
|
99
105
|
if defined?(ActionDispatch::DebugExceptions)
|
|
100
|
-
|
|
106
|
+
ar_puts "[ActiveRabbit] Found DebugExceptions, configuring middleware..." if Rails.env.development?
|
|
101
107
|
|
|
102
108
|
# First remove any existing middleware to avoid duplicates
|
|
103
109
|
begin
|
|
104
110
|
app.config.middleware.delete(ActiveRabbit::Client::ExceptionMiddleware)
|
|
105
111
|
app.config.middleware.delete(ActiveRabbit::Client::RequestContextMiddleware)
|
|
106
112
|
app.config.middleware.delete(ActiveRabbit::Client::RoutingErrorCatcher)
|
|
107
|
-
|
|
113
|
+
ar_puts "[ActiveRabbit] Cleaned up existing middleware" if Rails.env.development?
|
|
108
114
|
rescue => e
|
|
109
|
-
|
|
115
|
+
ar_puts "[ActiveRabbit] Error cleaning middleware: #{e.message}"
|
|
110
116
|
end
|
|
111
117
|
|
|
112
118
|
# Insert middleware in the correct order
|
|
113
|
-
|
|
119
|
+
ar_puts "[ActiveRabbit] Inserting middleware..." if Rails.env.development?
|
|
114
120
|
|
|
115
121
|
# Insert ErrorCaptureMiddleware after DebugExceptions to rely on rescue path
|
|
116
122
|
app.config.middleware.insert_after(ActionDispatch::DebugExceptions, ActiveRabbit::Middleware::ErrorCaptureMiddleware)
|
|
117
123
|
|
|
118
124
|
# Insert RequestContextMiddleware early in the stack
|
|
119
|
-
|
|
125
|
+
ar_puts "[ActiveRabbit] Inserting RequestContextMiddleware before RequestId" if Rails.env.development?
|
|
120
126
|
app.config.middleware.insert_before(ActionDispatch::RequestId, ActiveRabbit::Client::RequestContextMiddleware)
|
|
121
127
|
|
|
122
128
|
# Insert ExceptionMiddleware before Rails' exception handlers (kept for env-based reporting)
|
|
123
|
-
|
|
129
|
+
ar_puts "[ActiveRabbit] Inserting ExceptionMiddleware before DebugExceptions" if Rails.env.development?
|
|
124
130
|
app.config.middleware.insert_before(ActionDispatch::DebugExceptions, ActiveRabbit::Client::ExceptionMiddleware)
|
|
125
131
|
|
|
126
132
|
# Insert RoutingErrorCatcher after Rails' exception handlers
|
|
127
|
-
|
|
133
|
+
ar_puts "[ActiveRabbit] Inserting RoutingErrorCatcher after DebugExceptions" if Rails.env.development?
|
|
128
134
|
app.config.middleware.insert_after(ActionDispatch::DebugExceptions, ActiveRabbit::Client::RoutingErrorCatcher)
|
|
129
135
|
|
|
130
|
-
|
|
136
|
+
ar_puts "[ActiveRabbit] Middleware insertion complete" if Rails.env.development?
|
|
131
137
|
|
|
132
138
|
elsif defined?(ActionDispatch::ShowExceptions)
|
|
133
|
-
|
|
139
|
+
ar_puts "[ActiveRabbit] Found ShowExceptions, configuring middleware..." if Rails.env.development?
|
|
134
140
|
|
|
135
141
|
# First remove any existing middleware to avoid duplicates
|
|
136
142
|
begin
|
|
137
143
|
app.config.middleware.delete(ActiveRabbit::Client::ExceptionMiddleware)
|
|
138
144
|
app.config.middleware.delete(ActiveRabbit::Client::RequestContextMiddleware)
|
|
139
145
|
app.config.middleware.delete(ActiveRabbit::Client::RoutingErrorCatcher)
|
|
140
|
-
|
|
146
|
+
ar_puts "[ActiveRabbit] Cleaned up existing middleware" if Rails.env.development?
|
|
141
147
|
rescue => e
|
|
142
|
-
|
|
148
|
+
ar_puts "[ActiveRabbit] Error cleaning middleware: #{e.message}"
|
|
143
149
|
end
|
|
144
150
|
|
|
145
151
|
# Insert middleware in the correct order
|
|
146
|
-
|
|
152
|
+
ar_puts "[ActiveRabbit] Inserting middleware..." if Rails.env.development?
|
|
147
153
|
|
|
148
154
|
# Insert ErrorCaptureMiddleware after ShowExceptions
|
|
149
155
|
app.config.middleware.insert_after(ActionDispatch::ShowExceptions, ActiveRabbit::Middleware::ErrorCaptureMiddleware)
|
|
150
156
|
|
|
151
157
|
# Insert RequestContextMiddleware early in the stack
|
|
152
|
-
|
|
158
|
+
ar_puts "[ActiveRabbit] Inserting RequestContextMiddleware before RequestId" if Rails.env.development?
|
|
153
159
|
app.config.middleware.insert_before(ActionDispatch::RequestId, ActiveRabbit::Client::RequestContextMiddleware)
|
|
154
160
|
|
|
155
161
|
# Insert ExceptionMiddleware before Rails' exception handlers
|
|
156
|
-
|
|
162
|
+
ar_puts "[ActiveRabbit] Inserting ExceptionMiddleware before ShowExceptions" if Rails.env.development?
|
|
157
163
|
app.config.middleware.insert_before(ActionDispatch::ShowExceptions, ActiveRabbit::Client::ExceptionMiddleware)
|
|
158
164
|
|
|
159
165
|
# Insert RoutingErrorCatcher after Rails' exception handlers
|
|
160
|
-
|
|
166
|
+
ar_puts "[ActiveRabbit] Inserting RoutingErrorCatcher after ShowExceptions" if Rails.env.development?
|
|
161
167
|
app.config.middleware.insert_after(ActionDispatch::ShowExceptions, ActiveRabbit::Client::RoutingErrorCatcher)
|
|
162
168
|
|
|
163
169
|
else
|
|
164
|
-
|
|
170
|
+
ar_puts "[ActiveRabbit] No exception handlers found, using fallback configuration" if Rails.env.development?
|
|
165
171
|
app.config.middleware.use(ActiveRabbit::Middleware::ErrorCaptureMiddleware)
|
|
166
172
|
app.config.middleware.use(ActiveRabbit::Client::RequestContextMiddleware)
|
|
167
173
|
app.config.middleware.use(ActiveRabbit::Client::ExceptionMiddleware)
|
|
@@ -169,9 +175,9 @@ module ActiveRabbit
|
|
|
169
175
|
end
|
|
170
176
|
|
|
171
177
|
if Rails.env.development?
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
178
|
+
ar_puts "\n=== Final Middleware Stack ==="
|
|
179
|
+
ar_puts "(will be printed after initialize)"
|
|
180
|
+
ar_puts "=======================\n"
|
|
175
181
|
end
|
|
176
182
|
|
|
177
183
|
# Add debug wrappers in development
|
|
@@ -180,41 +186,42 @@ module ActiveRabbit
|
|
|
180
186
|
ActiveRabbit::Client::ExceptionMiddleware.class_eval do
|
|
181
187
|
alias_method :__ar_original_call, :call unless method_defined?(:__ar_original_call)
|
|
182
188
|
def call(env)
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
189
|
+
cfg = ActiveRabbit::Client.configuration
|
|
190
|
+
ar_puts "\n=== ExceptionMiddleware Enter ==="
|
|
191
|
+
ar_puts "Path: #{env['PATH_INFO']}"
|
|
192
|
+
ar_puts "Method: #{env['REQUEST_METHOD']}"
|
|
193
|
+
ar_puts "Current Exception: #{env['action_dispatch.exception']&.class} - #{env['action_dispatch.exception']&.message}"
|
|
194
|
+
ar_puts "Current Error: #{env['action_dispatch.error']&.class} - #{env['action_dispatch.error']&.message}"
|
|
195
|
+
ar_puts "Rack Exception: #{env['rack.exception']&.class} - #{env['rack.exception']&.message}"
|
|
196
|
+
ar_puts "Exception Backtrace: #{env['action_dispatch.exception']&.backtrace&.first(3)&.join("\n ")}"
|
|
197
|
+
ar_puts "Error Backtrace: #{env['action_dispatch.error']&.backtrace&.first(3)&.join("\n ")}"
|
|
198
|
+
ar_puts "Rack Backtrace: #{env['rack.exception']&.backtrace&.first(3)&.join("\n ")}"
|
|
199
|
+
ar_puts "============================\n"
|
|
193
200
|
|
|
194
201
|
begin
|
|
195
202
|
status, headers, body = __ar_original_call(env)
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
203
|
+
ar_puts "\n=== ExceptionMiddleware Exit (Success) ==="
|
|
204
|
+
ar_puts "Status: #{status}"
|
|
205
|
+
ar_puts "Headers: #{headers.inspect}"
|
|
206
|
+
ar_puts "Final Exception: #{env['action_dispatch.exception']&.class} - #{env['action_dispatch.exception']&.message}"
|
|
207
|
+
ar_puts "Final Error: #{env['action_dispatch.error']&.class} - #{env['action_dispatch.error']&.message}"
|
|
208
|
+
ar_puts "Final Rack Exception: #{env['rack.exception']&.class} - #{env['rack.exception']&.message}"
|
|
209
|
+
ar_puts "Final Exception Backtrace: #{env['action_dispatch.exception']&.backtrace&.first(3)&.join("\n ")}"
|
|
210
|
+
ar_puts "Final Error Backtrace: #{env['action_dispatch.error']&.backtrace&.first(3)&.join("\n ")}"
|
|
211
|
+
ar_puts "Final Rack Backtrace: #{env['rack.exception']&.backtrace&.first(3)&.join("\n ")}"
|
|
212
|
+
ar_puts "===========================\n"
|
|
206
213
|
[status, headers, body]
|
|
207
214
|
rescue => e
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
215
|
+
ar_puts "\n=== ExceptionMiddleware Exit (Error) ==="
|
|
216
|
+
ar_puts "Error: #{e.class} - #{e.message}"
|
|
217
|
+
ar_puts "Error Backtrace: #{e.backtrace&.first(3)&.join("\n ")}"
|
|
218
|
+
ar_puts "Original Exception: #{env['action_dispatch.exception']&.class} - #{env['action_dispatch.exception']&.message}"
|
|
219
|
+
ar_puts "Original Error: #{env['action_dispatch.error']&.class} - #{env['action_dispatch.error']&.message}"
|
|
220
|
+
ar_puts "Original Rack Exception: #{env['rack.exception']&.class} - #{env['rack.exception']&.message}"
|
|
221
|
+
ar_puts "Original Exception Backtrace: #{env['action_dispatch.exception']&.backtrace&.first(3)&.join("\n ")}"
|
|
222
|
+
ar_puts "Original Error Backtrace: #{env['action_dispatch.error']&.backtrace&.first(3)&.join("\n ")}"
|
|
223
|
+
ar_puts "Original Rack Backtrace: #{env['rack.exception']&.backtrace&.first(3)&.join("\n ")}"
|
|
224
|
+
ar_puts "===========================\n"
|
|
218
225
|
raise
|
|
219
226
|
end
|
|
220
227
|
end
|
|
@@ -224,23 +231,23 @@ module ActiveRabbit
|
|
|
224
231
|
ActiveRabbit::Client::RoutingErrorCatcher.class_eval do
|
|
225
232
|
alias_method :__ar_routing_original_call, :call unless method_defined?(:__ar_routing_original_call)
|
|
226
233
|
def call(env)
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
234
|
+
ar_puts "\n=== RoutingErrorCatcher Enter ==="
|
|
235
|
+
ar_puts "Path: #{env['PATH_INFO']}"
|
|
236
|
+
ar_puts "Method: #{env['REQUEST_METHOD']}"
|
|
237
|
+
ar_puts "Status: #{env['action_dispatch.exception']&.class}"
|
|
238
|
+
ar_puts "============================\n"
|
|
232
239
|
|
|
233
240
|
begin
|
|
234
241
|
status, headers, body = __ar_routing_original_call(env)
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
242
|
+
ar_puts "\n=== RoutingErrorCatcher Exit (Success) ==="
|
|
243
|
+
ar_puts "Status: #{status}"
|
|
244
|
+
ar_puts "===========================\n"
|
|
238
245
|
[status, headers, body]
|
|
239
246
|
rescue => e
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
247
|
+
ar_puts "\n=== RoutingErrorCatcher Exit (Error) ==="
|
|
248
|
+
ar_puts "Error: #{e.class} - #{e.message}"
|
|
249
|
+
ar_puts "Backtrace: #{e.backtrace&.first(3)&.join("\n ")}"
|
|
250
|
+
ar_puts "===========================\n"
|
|
244
251
|
raise
|
|
245
252
|
end
|
|
246
253
|
end
|
|
@@ -250,18 +257,18 @@ module ActiveRabbit
|
|
|
250
257
|
# In development, add a hook to verify middleware after initialization
|
|
251
258
|
if Rails.env.development?
|
|
252
259
|
app.config.after_initialize do
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
260
|
+
ar_log(:info, "\n=== ActiveRabbit Configuration ===")
|
|
261
|
+
ar_log(:info, "Version: #{ActiveRabbit::Client::VERSION}")
|
|
262
|
+
ar_log(:info, "Environment: #{Rails.env}")
|
|
263
|
+
ar_log(:info, "API URL: #{ActiveRabbit::Client.configuration.api_url}")
|
|
264
|
+
ar_log(:info, "================================")
|
|
258
265
|
|
|
259
|
-
|
|
266
|
+
ar_log(:info, "\n=== Middleware Stack ===")
|
|
260
267
|
(Rails.application.middleware.middlewares rescue []).each do |mw|
|
|
261
268
|
klass = (mw.respond_to?(:klass) ? mw.klass.name : mw.to_s) rescue mw.inspect
|
|
262
|
-
|
|
269
|
+
ar_log(:info, " #{klass}")
|
|
263
270
|
end
|
|
264
|
-
|
|
271
|
+
ar_log(:info, "=======================")
|
|
265
272
|
|
|
266
273
|
# Skip missing-middleware warnings in development since we may inject via alternate paths
|
|
267
274
|
unless Rails.env.development?
|
|
@@ -284,7 +291,7 @@ module ActiveRabbit
|
|
|
284
291
|
end
|
|
285
292
|
end
|
|
286
293
|
|
|
287
|
-
|
|
294
|
+
ar_log(:info, "[ActiveRabbit] Middleware configured successfully")
|
|
288
295
|
end
|
|
289
296
|
|
|
290
297
|
initializer "active_rabbit.error_reporter" do |app|
|
|
@@ -374,8 +381,8 @@ module ActiveRabbit
|
|
|
374
381
|
begin
|
|
375
382
|
reporting_file, reporting_line = ActiveRabbit::Reporting.method(:report_exception).source_location
|
|
376
383
|
http_file, http_line = ActiveRabbit::Client::HttpClient.instance_method(:post_exception).source_location
|
|
377
|
-
|
|
378
|
-
|
|
384
|
+
ar_log(:info, "[ActiveRabbit] Reporting loaded from #{reporting_file}:#{reporting_line}") if defined?(Rails)
|
|
385
|
+
ar_log(:info, "[ActiveRabbit] HttpClient#post_exception from #{http_file}:#{http_line}") if defined?(Rails)
|
|
379
386
|
rescue => e
|
|
380
387
|
Rails.logger.debug "[ActiveRabbit] boot diagnostics failed: #{e.message}" if defined?(Rails)
|
|
381
388
|
end
|
|
@@ -384,6 +391,34 @@ module ActiveRabbit
|
|
|
384
391
|
|
|
385
392
|
private
|
|
386
393
|
|
|
394
|
+
def ar_puts(msg)
|
|
395
|
+
cfg = ActiveRabbit::Client.configuration
|
|
396
|
+
return if cfg && cfg.disable_console_logs
|
|
397
|
+
puts msg
|
|
398
|
+
end
|
|
399
|
+
|
|
400
|
+
def ar_log(level, msg)
|
|
401
|
+
cfg = ActiveRabbit::Client.configuration
|
|
402
|
+
return if cfg && cfg.disable_console_logs
|
|
403
|
+
Rails.logger.public_send(level, msg) if Rails.logger
|
|
404
|
+
end
|
|
405
|
+
|
|
406
|
+
def apply_rails_configuration(rails_config)
|
|
407
|
+
return unless rails_config
|
|
408
|
+
|
|
409
|
+
options = rails_config.respond_to?(:to_h) ? rails_config.to_h : rails_config
|
|
410
|
+
return if options.nil? || options.empty?
|
|
411
|
+
|
|
412
|
+
ActiveRabbit::Client.configure do |config|
|
|
413
|
+
options.each do |key, value|
|
|
414
|
+
next if value.nil?
|
|
415
|
+
|
|
416
|
+
writer = "#{key}="
|
|
417
|
+
config.public_send(writer, value) if config.respond_to?(writer)
|
|
418
|
+
end
|
|
419
|
+
end
|
|
420
|
+
end
|
|
421
|
+
|
|
387
422
|
def setup_exception_tracking(app)
|
|
388
423
|
# Handle uncaught exceptions in development
|
|
389
424
|
if Rails.env.development? || Rails.env.test?
|
|
@@ -392,19 +427,19 @@ module ActiveRabbit
|
|
|
392
427
|
end
|
|
393
428
|
|
|
394
429
|
def subscribe_to_controller_events
|
|
395
|
-
|
|
430
|
+
ar_log(:info, "[ActiveRabbit] Subscribing to controller events (configured=#{ActiveRabbit::Client.configured?})")
|
|
396
431
|
|
|
397
432
|
ActiveSupport::Notifications.subscribe "process_action.action_controller" do |name, started, finished, unique_id, payload|
|
|
398
433
|
begin
|
|
399
434
|
unless ActiveRabbit::Client.configured?
|
|
400
435
|
Rails.logger.debug "[ActiveRabbit] Skipping performance tracking - not configured"
|
|
401
|
-
|
|
436
|
+
next
|
|
402
437
|
end
|
|
403
438
|
|
|
404
439
|
duration_ms = ((finished - started) * 1000).round(2)
|
|
405
440
|
|
|
406
|
-
|
|
407
|
-
|
|
441
|
+
ar_log(:info, "[ActiveRabbit] 📊 Controller action: #{payload[:controller]}##{payload[:action]} - #{duration_ms}ms")
|
|
442
|
+
ar_log(:info, "[ActiveRabbit] 📊 DB runtime: #{payload[:db_runtime]}, View runtime: #{payload[:view_runtime]}")
|
|
408
443
|
|
|
409
444
|
ActiveRabbit::Client.track_performance(
|
|
410
445
|
"controller.action",
|
|
@@ -672,7 +707,7 @@ module ActiveRabbit
|
|
|
672
707
|
|
|
673
708
|
def call(env)
|
|
674
709
|
# debug start - using Rails.logger to ensure it appears in development.log
|
|
675
|
-
|
|
710
|
+
ar_log(:info, "[AR] ExceptionMiddleware ENTER path=#{env['PATH_INFO']}") if defined?(Rails)
|
|
676
711
|
warn "[AR] ExceptionMiddleware ENTER path=#{env['PATH_INFO']}"
|
|
677
712
|
warn "[AR] Current exceptions in env:"
|
|
678
713
|
warn " - action_dispatch.exception: #{env['action_dispatch.exception']&.class}"
|
|
@@ -686,12 +721,12 @@ module ActiveRabbit
|
|
|
686
721
|
|
|
687
722
|
# Check for exceptions in env after app call
|
|
688
723
|
if (ex = env["action_dispatch.exception"] || env["rack.exception"] || env["action_dispatch.error"])
|
|
689
|
-
|
|
724
|
+
ar_log(:info, "[AR] env exception present: #{ex.class}: #{ex.message}") if defined?(Rails)
|
|
690
725
|
warn "[AR] env exception present: #{ex.class}: #{ex.message}"
|
|
691
726
|
warn "[AR] Exception backtrace: #{ex.backtrace&.first(3)&.join("\n ")}"
|
|
692
727
|
safe_report(ex, env, 'Rails rescued exception')
|
|
693
728
|
else
|
|
694
|
-
|
|
729
|
+
ar_log(:info, "[AR] env exception NOT present") if defined?(Rails)
|
|
695
730
|
warn "[AR] env exception NOT present"
|
|
696
731
|
warn "[AR] Final env check:"
|
|
697
732
|
warn " - action_dispatch.exception: #{env['action_dispatch.exception']&.class}"
|
|
@@ -703,7 +738,7 @@ module ActiveRabbit
|
|
|
703
738
|
[status, headers, body]
|
|
704
739
|
rescue => e
|
|
705
740
|
# Primary path: catch raw exceptions before Rails rescuers
|
|
706
|
-
|
|
741
|
+
ar_log(:info, "[AR] RESCUE caught: #{e.class}: #{e.message}") if defined?(Rails)
|
|
707
742
|
warn "[AR] RESCUE caught: #{e.class}: #{e.message}"
|
|
708
743
|
warn "[AR] Rescue backtrace: #{e.backtrace&.first(3)&.join("\n ")}"
|
|
709
744
|
|
|
@@ -748,7 +783,7 @@ module ActiveRabbit
|
|
|
748
783
|
result = ActiveRabbit::Client.track_exception(exception, context: context)
|
|
749
784
|
warn "[AR] Track result: #{result.inspect}"
|
|
750
785
|
|
|
751
|
-
|
|
786
|
+
ar_log(:info, "[ActiveRabbit] Tracked #{source}: #{exception.class.name} - #{exception.message}") if defined?(Rails)
|
|
752
787
|
rescue => tracking_error
|
|
753
788
|
# Log tracking errors but don't let them interfere with exception handling
|
|
754
789
|
warn "[AR] Error in safe_report: #{tracking_error.class} - #{tracking_error.message}"
|
data/lib/active_rabbit/client.rb
CHANGED
|
@@ -77,8 +77,8 @@ module ActiveRabbit
|
|
|
77
77
|
result = exception_tracker.track_exception(**args)
|
|
78
78
|
|
|
79
79
|
# Log the result
|
|
80
|
-
|
|
81
|
-
|
|
80
|
+
ActiveRabbit::Client.log(:info, "[ActiveRabbit] Exception tracked: #{exception.class.name}")
|
|
81
|
+
ActiveRabbit::Client.log(:debug, "[ActiveRabbit] Exception tracking result: #{result.inspect}")
|
|
82
82
|
|
|
83
83
|
result
|
|
84
84
|
end
|
|
@@ -127,6 +127,32 @@ module ActiveRabbit
|
|
|
127
127
|
track_exception(exception, context: context, user_id: user_id, tags: tags)
|
|
128
128
|
end
|
|
129
129
|
|
|
130
|
+
def notify_deploy(project_slug:, status:, user:, version:, started_at: nil, finished_at: nil)
|
|
131
|
+
payload = {
|
|
132
|
+
revision: Client.configuration.revision,
|
|
133
|
+
environment: Client.configuration.environment,
|
|
134
|
+
project_slug: project_slug,
|
|
135
|
+
version: version,
|
|
136
|
+
status: status,
|
|
137
|
+
user: user,
|
|
138
|
+
started_at: started_at,
|
|
139
|
+
finished_at: finished_at
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
http_client.post("/api/v1/deploys", payload)
|
|
143
|
+
end
|
|
144
|
+
|
|
145
|
+
def log(level, message)
|
|
146
|
+
cfg = configuration
|
|
147
|
+
return if cfg.nil? || cfg.disable_console_logs
|
|
148
|
+
|
|
149
|
+
case level
|
|
150
|
+
when :info then cfg.logger&.info(message)
|
|
151
|
+
when :debug then cfg.logger&.debug(message)
|
|
152
|
+
when :error then cfg.logger&.error(message)
|
|
153
|
+
end
|
|
154
|
+
end
|
|
155
|
+
|
|
130
156
|
private
|
|
131
157
|
|
|
132
158
|
def event_processor
|
|
@@ -13,7 +13,7 @@ module ActiveRabbit
|
|
|
13
13
|
begin
|
|
14
14
|
ActiveRabbit::Reporting.report_exception(e, env: env, handled: false, source: "middleware", force: true)
|
|
15
15
|
rescue => inner
|
|
16
|
-
|
|
16
|
+
ActiveRabbit::Client.log(:error, "[ActiveRabbit] ErrorCaptureMiddleware failed: #{inner.class}: #{inner.message}")
|
|
17
17
|
end
|
|
18
18
|
raise
|
|
19
19
|
end
|
|
@@ -40,7 +40,7 @@ module ActiveRabbit
|
|
|
40
40
|
force: force)
|
|
41
41
|
rescue => e
|
|
42
42
|
if defined?(Rails)
|
|
43
|
-
|
|
43
|
+
ActiveRabbit::Client.log(:error, "[ActiveRabbit] report_exception failed: #{e.class}: #{e.message}")
|
|
44
44
|
end
|
|
45
45
|
nil
|
|
46
46
|
end
|
|
@@ -8,7 +8,7 @@ module ActiveRabbit
|
|
|
8
8
|
begin
|
|
9
9
|
ActiveRabbit::Reporting.report_exception(error, env: env, handled: true, source: "router", force: true)
|
|
10
10
|
rescue => e
|
|
11
|
-
|
|
11
|
+
ActiveRabbit::Client.log(:error, "[ActiveRabbit] NotFoundApp failed to report: #{e.class}: #{e.message}")
|
|
12
12
|
end
|
|
13
13
|
[404, { "Content-Type" => "text/plain" }, ["Not Found"]]
|
|
14
14
|
end
|
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: activerabbit-ai
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.5.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Alex Shapalov
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: exe
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2025-
|
|
11
|
+
date: 2025-12-12 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: concurrent-ruby
|