activerabbit-ai 0.1.2 → 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/CHANGELOG.md +36 -0
- data/activerabbit-ai-0.1.2.gem +0 -0
- data/lib/active_rabbit/client/event_processor.rb +1 -0
- data/lib/active_rabbit/client/exception_tracker.rb +1 -0
- data/lib/active_rabbit/client/http_client.rb +2 -1
- data/lib/active_rabbit/client/n_plus_one_detector.rb +1 -0
- data/lib/active_rabbit/client/performance_monitor.rb +2 -0
- data/lib/active_rabbit/client/railtie.rb +124 -36
- data/lib/active_rabbit/client/version.rb +1 -1
- data/lib/active_rabbit.rb +3 -0
- metadata +3 -3
- data/activerabbit-ai-0.1.1.gem +0 -0
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: c2dcb677ef3f8500c8a55c7a26a61a8dbab9e89f89c6b24119905de6c94ee26c
|
|
4
|
+
data.tar.gz: c3af1e41fc2f0265fa9c4ba5073539a41f1e8f2ec6ac59ff921c782b78ef246a
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: aba0d27c8cb0a82718c02c24d18f4ec6ddc9311d8788fb5eaa78d0306a009c6dc04ed5fd349e1277684815705e37b64264483da0117905263b8c387237cf0f72
|
|
7
|
+
data.tar.gz: 890245267f014b27cba6d1d132fdbb491f78d7c9147ee817d1430bbeebe7e7bd37d6d784fbff4446ff03fa9c9787634ad26600f66e22213444b98e7996b5572e
|
data/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,42 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [0.2.0] - 2025-01-03
|
|
11
|
+
|
|
12
|
+
### 🚨 Major Rails Integration Improvements
|
|
13
|
+
|
|
14
|
+
This release fixes critical Rails integration issues that prevented reliable exception tracking in production environments.
|
|
15
|
+
|
|
16
|
+
### Fixed
|
|
17
|
+
- **Rails Autoload**: Fixed missing Railtie autoload in main entry point (`lib/active_rabbit.rb`)
|
|
18
|
+
- **Exception Middleware**: Properly positioned before `ActionDispatch::ShowExceptions` to catch raw exceptions
|
|
19
|
+
- **Rescued Exceptions**: Fixed `process_action.action_controller` subscriber to catch Rails-rescued exceptions
|
|
20
|
+
- **Thread Cleanup**: Improved `Thread.current` cleanup in middleware with proper nested request handling
|
|
21
|
+
- **Time Dependencies**: Added missing `require "time"` for `iso8601` method across all modules
|
|
22
|
+
|
|
23
|
+
### Added
|
|
24
|
+
- **Shutdown Hooks**: Added `at_exit` and `SIGTERM` handlers for graceful shutdown and data flushing
|
|
25
|
+
- **Enhanced Context**: Added timing data, sanitized headers, and better request context
|
|
26
|
+
- **Error Isolation**: All tracking operations now fail gracefully without breaking the application
|
|
27
|
+
- **Rails 7+ Support**: Handles both `exception_object` (Rails 7+) and legacy `exception` formats
|
|
28
|
+
- **Production Resilience**: Never blocks web requests, background sending with automatic retries
|
|
29
|
+
|
|
30
|
+
### Improved
|
|
31
|
+
- **PII Scrubbing**: Enhanced parameter and header sanitization
|
|
32
|
+
- **Exception Fingerprinting**: Better grouping of similar exceptions
|
|
33
|
+
- **Middleware Safety**: Robust error handling prevents tracking failures from affecting requests
|
|
34
|
+
- **Test Coverage**: Added comprehensive Rails integration test suite
|
|
35
|
+
|
|
36
|
+
### Technical Details
|
|
37
|
+
- Rack middleware now placed **before** Rails exception handling (critical for production)
|
|
38
|
+
- ActiveSupport::Notifications properly subscribed to catch rescued exceptions
|
|
39
|
+
- Background queue with timer-based flushing ensures data delivery
|
|
40
|
+
- Thread-safe context management with proper cleanup
|
|
41
|
+
|
|
42
|
+
This release makes ActiveRabbit production-ready for Rails applications with reliable exception tracking that works even when Rails rescues exceptions and renders error pages.
|
|
43
|
+
|
|
44
|
+
## [0.1.2] - 2024-12-20
|
|
45
|
+
|
|
10
46
|
### Added
|
|
11
47
|
- Initial release of ActiveRabbit Ruby client
|
|
12
48
|
- Error tracking with detailed context and stack traces
|
|
Binary file
|
|
@@ -4,6 +4,7 @@ require "faraday"
|
|
|
4
4
|
require "faraday/retry"
|
|
5
5
|
require "json"
|
|
6
6
|
require "concurrent"
|
|
7
|
+
require "time"
|
|
7
8
|
|
|
8
9
|
module ActiveRabbit
|
|
9
10
|
module Client
|
|
@@ -39,7 +40,7 @@ module ActiveRabbit
|
|
|
39
40
|
end
|
|
40
41
|
|
|
41
42
|
def test_connection
|
|
42
|
-
response = make_request(:post, "api/v1/test/connection", {
|
|
43
|
+
response = make_request(:post, "api/v1/test/connection", {
|
|
43
44
|
gem_version: ActiveRabbit::Client::VERSION,
|
|
44
45
|
timestamp: Time.now.iso8601
|
|
45
46
|
})
|
|
@@ -1,6 +1,14 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
begin
|
|
4
|
+
require "rails/railtie"
|
|
5
|
+
rescue LoadError
|
|
6
|
+
# Rails not available, define minimal structure for testing
|
|
7
|
+
module Rails
|
|
8
|
+
class Railtie; end
|
|
9
|
+
end
|
|
10
|
+
end
|
|
11
|
+
|
|
4
12
|
require "securerandom"
|
|
5
13
|
|
|
6
14
|
module ActiveRabbit
|
|
@@ -49,6 +57,34 @@ module ActiveRabbit
|
|
|
49
57
|
app.middleware.insert_before ActionDispatch::ShowExceptions, ExceptionMiddleware
|
|
50
58
|
end
|
|
51
59
|
|
|
60
|
+
initializer "active_rabbit.setup_shutdown_hooks" do
|
|
61
|
+
next unless ActiveRabbit::Client.configured?
|
|
62
|
+
|
|
63
|
+
# Ensure we flush pending data on shutdown
|
|
64
|
+
at_exit do
|
|
65
|
+
begin
|
|
66
|
+
ActiveRabbit::Client.flush
|
|
67
|
+
rescue => e
|
|
68
|
+
# Don't let shutdown hooks fail the process
|
|
69
|
+
Rails.logger.error "[ActiveRabbit] Error during shutdown flush: #{e.message}" if defined?(Rails)
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
# Also flush on SIGTERM (common in production deployments)
|
|
74
|
+
if Signal.list.include?('TERM')
|
|
75
|
+
Signal.trap('TERM') do
|
|
76
|
+
begin
|
|
77
|
+
ActiveRabbit::Client.flush
|
|
78
|
+
rescue => e
|
|
79
|
+
# Log but don't raise
|
|
80
|
+
Rails.logger.error "[ActiveRabbit] Error during SIGTERM flush: #{e.message}" if defined?(Rails)
|
|
81
|
+
end
|
|
82
|
+
# Continue with normal SIGTERM handling
|
|
83
|
+
exit(0)
|
|
84
|
+
end
|
|
85
|
+
end
|
|
86
|
+
end
|
|
87
|
+
|
|
52
88
|
private
|
|
53
89
|
|
|
54
90
|
def setup_exception_tracking(app)
|
|
@@ -173,25 +209,30 @@ module ActiveRabbit
|
|
|
173
209
|
end
|
|
174
210
|
end
|
|
175
211
|
|
|
176
|
-
|
|
177
|
-
# Subscribe to Rails exception notifications
|
|
212
|
+
def subscribe_to_exception_notifications
|
|
213
|
+
# Subscribe to Rails exception notifications for rescued exceptions
|
|
178
214
|
ActiveSupport::Notifications.subscribe "process_action.action_controller" do |name, started, finished, unique_id, data|
|
|
179
215
|
next unless ActiveRabbit::Client.configured?
|
|
180
|
-
next unless data[:exception]
|
|
181
|
-
|
|
182
|
-
exception_class, exception_message = data[:exception]
|
|
183
|
-
|
|
184
|
-
puts "[ActiveRabbit] Exception notification received: #{exception_class}: #{exception_message}"
|
|
185
|
-
puts "[ActiveRabbit] Available data keys: #{data.keys.inspect}"
|
|
186
|
-
|
|
187
|
-
# Create exception with proper backtrace
|
|
188
|
-
exception = exception_class.constantize.new(exception_message)
|
|
189
216
|
|
|
190
|
-
#
|
|
217
|
+
# Check for rescued exceptions in the payload
|
|
218
|
+
exception = nil
|
|
191
219
|
if data[:exception_object]
|
|
192
|
-
exception
|
|
220
|
+
# Rails 7+ provides the actual exception object
|
|
221
|
+
exception = data[:exception_object]
|
|
222
|
+
elsif data[:exception]
|
|
223
|
+
# Fallback: reconstruct exception from class name and message
|
|
224
|
+
exception_class_name, exception_message = data[:exception]
|
|
225
|
+
begin
|
|
226
|
+
exception_class = exception_class_name.constantize
|
|
227
|
+
exception = exception_class.new(exception_message)
|
|
228
|
+
rescue NameError
|
|
229
|
+
# If we can't constantize the exception class, create a generic one
|
|
230
|
+
exception = StandardError.new("#{exception_class_name}: #{exception_message}")
|
|
231
|
+
end
|
|
193
232
|
end
|
|
194
233
|
|
|
234
|
+
next unless exception
|
|
235
|
+
|
|
195
236
|
ActiveRabbit::Client.track_exception(
|
|
196
237
|
exception,
|
|
197
238
|
context: {
|
|
@@ -201,7 +242,13 @@ module ActiveRabbit
|
|
|
201
242
|
controller: data[:controller],
|
|
202
243
|
action: data[:action],
|
|
203
244
|
status: data[:status],
|
|
204
|
-
format: data[:format]
|
|
245
|
+
format: data[:format],
|
|
246
|
+
params: scrub_sensitive_params(data[:params])
|
|
247
|
+
},
|
|
248
|
+
timing: {
|
|
249
|
+
duration_ms: ((finished - started) * 1000).round(2),
|
|
250
|
+
view_runtime: data[:view_runtime],
|
|
251
|
+
db_runtime: data[:db_runtime]
|
|
205
252
|
}
|
|
206
253
|
}
|
|
207
254
|
)
|
|
@@ -225,6 +272,13 @@ module ActiveRabbit
|
|
|
225
272
|
nil
|
|
226
273
|
end
|
|
227
274
|
|
|
275
|
+
def scrub_sensitive_params(params)
|
|
276
|
+
return {} unless params
|
|
277
|
+
return params unless ActiveRabbit::Client.configuration.enable_pii_scrubbing
|
|
278
|
+
|
|
279
|
+
PiiScrubber.new(ActiveRabbit::Client.configuration).scrub(params)
|
|
280
|
+
end
|
|
281
|
+
|
|
228
282
|
def n_plus_one_detector
|
|
229
283
|
@n_plus_one_detector ||= NPlusOneDetector.new(ActiveRabbit::Client.configuration)
|
|
230
284
|
end
|
|
@@ -244,18 +298,28 @@ module ActiveRabbit
|
|
|
244
298
|
|
|
245
299
|
# Set request context
|
|
246
300
|
request_context = build_request_context(request)
|
|
301
|
+
request_id = SecureRandom.uuid
|
|
302
|
+
|
|
303
|
+
# Store previous context to restore later (in case of nested requests)
|
|
304
|
+
previous_context = Thread.current[:active_rabbit_request_context]
|
|
247
305
|
Thread.current[:active_rabbit_request_context] = request_context
|
|
248
306
|
|
|
249
307
|
# Start N+1 detection for this request
|
|
250
|
-
request_id = SecureRandom.uuid
|
|
251
308
|
n_plus_one_detector.start_request(request_id)
|
|
252
309
|
|
|
253
310
|
begin
|
|
254
311
|
@app.call(env)
|
|
255
312
|
ensure
|
|
256
|
-
#
|
|
257
|
-
|
|
258
|
-
|
|
313
|
+
# Always clean up request context, even if an exception occurred
|
|
314
|
+
begin
|
|
315
|
+
n_plus_one_detector.finish_request(request_id)
|
|
316
|
+
rescue => e
|
|
317
|
+
# Log but don't raise - we don't want cleanup to fail the request
|
|
318
|
+
Rails.logger.error "[ActiveRabbit] Error finishing N+1 detection: #{e.message}" if defined?(Rails)
|
|
319
|
+
end
|
|
320
|
+
|
|
321
|
+
# Restore previous context (handles nested requests)
|
|
322
|
+
Thread.current[:active_rabbit_request_context] = previous_context
|
|
259
323
|
end
|
|
260
324
|
end
|
|
261
325
|
|
|
@@ -299,30 +363,54 @@ module ActiveRabbit
|
|
|
299
363
|
end
|
|
300
364
|
|
|
301
365
|
def call(env)
|
|
302
|
-
puts "[ActiveRabbit] ExceptionMiddleware called for: #{env['REQUEST_METHOD']} #{env['PATH_INFO']}"
|
|
303
366
|
@app.call(env)
|
|
304
367
|
rescue Exception => exception
|
|
305
|
-
# Track the exception
|
|
306
|
-
|
|
307
|
-
|
|
368
|
+
# Track the exception, but don't let tracking errors break the request
|
|
369
|
+
begin
|
|
370
|
+
request = ActionDispatch::Request.new(env)
|
|
308
371
|
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
372
|
+
ActiveRabbit::Client.track_exception(
|
|
373
|
+
exception,
|
|
374
|
+
context: {
|
|
375
|
+
request: {
|
|
376
|
+
method: request.method,
|
|
377
|
+
path: request.path,
|
|
378
|
+
query_string: request.query_string,
|
|
379
|
+
user_agent: request.headers["User-Agent"],
|
|
380
|
+
ip_address: request.remote_ip,
|
|
381
|
+
referer: request.referer,
|
|
382
|
+
headers: sanitize_headers(request.headers)
|
|
383
|
+
},
|
|
384
|
+
middleware: {
|
|
385
|
+
caught_by: 'ExceptionMiddleware',
|
|
386
|
+
timestamp: Time.now.iso8601(3)
|
|
387
|
+
}
|
|
319
388
|
}
|
|
320
|
-
|
|
321
|
-
|
|
389
|
+
)
|
|
390
|
+
rescue => tracking_error
|
|
391
|
+
# Log tracking errors but don't let them interfere with exception handling
|
|
392
|
+
Rails.logger.error "[ActiveRabbit] Error tracking exception: #{tracking_error.message}" if defined?(Rails)
|
|
393
|
+
end
|
|
322
394
|
|
|
323
|
-
# Re-raise the exception so Rails can handle it normally
|
|
395
|
+
# Re-raise the original exception so Rails can handle it normally
|
|
324
396
|
raise exception
|
|
325
397
|
end
|
|
398
|
+
|
|
399
|
+
private
|
|
400
|
+
|
|
401
|
+
def sanitize_headers(headers)
|
|
402
|
+
# Only include safe headers to avoid PII
|
|
403
|
+
safe_headers = {}
|
|
404
|
+
headers.each do |key, value|
|
|
405
|
+
next unless key.is_a?(String)
|
|
406
|
+
|
|
407
|
+
# Include common safe headers
|
|
408
|
+
if key.match?(/^(HTTP_ACCEPT|HTTP_ACCEPT_ENCODING|HTTP_ACCEPT_LANGUAGE|HTTP_CACHE_CONTROL|HTTP_CONNECTION|HTTP_HOST|HTTP_UPGRADE_INSECURE_REQUESTS|HTTP_USER_AGENT|CONTENT_TYPE|REQUEST_METHOD|REQUEST_URI|SERVER_NAME|SERVER_PORT)$/i)
|
|
409
|
+
safe_headers[key] = value
|
|
410
|
+
end
|
|
411
|
+
end
|
|
412
|
+
safe_headers
|
|
413
|
+
end
|
|
326
414
|
end
|
|
327
415
|
end
|
|
328
416
|
end
|
data/lib/active_rabbit.rb
CHANGED
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.2.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-09-03 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: faraday
|
|
@@ -109,7 +109,7 @@ files:
|
|
|
109
109
|
- README.md
|
|
110
110
|
- Rakefile
|
|
111
111
|
- TESTING_GUIDE.md
|
|
112
|
-
- activerabbit-ai-0.1.
|
|
112
|
+
- activerabbit-ai-0.1.2.gem
|
|
113
113
|
- examples/rails_app_testing.rb
|
|
114
114
|
- examples/rails_integration.rb
|
|
115
115
|
- examples/standalone_usage.rb
|
data/activerabbit-ai-0.1.1.gem
DELETED
|
Binary file
|