activerabbit-ai 0.4.4 → 0.4.7
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 +29 -0
- data/activerabbit-ai-0.4.5.gem +0 -0
- data/lib/active_rabbit/client/error_reporter.rb +21 -7
- data/lib/active_rabbit/client/performance_monitor.rb +13 -1
- data/lib/active_rabbit/client/railtie.rb +46 -31
- data/lib/active_rabbit/client/version.rb +1 -1
- data/lib/active_rabbit/reporting.rb +8 -0
- data/lib/active_rabbit.rb +1 -8
- data/lib/activerabbit-ai.rb +7 -0
- metadata +4 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 06e58258544f159566a756467253a1a2a6a0a696479b37cd1f265902928a3f8c
|
|
4
|
+
data.tar.gz: e12b3dcd16a43ca524027e2b0ecd608d8bbe0441f2f89586d7bb5d1f57dbda1a
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: eb2e853ad6f3791c567a9498af2a6684756042d0b7fa626e94ae057910422a594f0d705f5fbc923334af52c110062701db37c86766d14be17ef41d495e758fae
|
|
7
|
+
data.tar.gz: d498e0a7c16cb8abeb8c96724caf88ad32ac598e0d84994c90712de3e8f182a763aaeda73f39c8a3f94b00c31ff1b5b3629535946313a8adec952ccb0bf0466b
|
data/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,35 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to this project will be documented in this file.
|
|
4
4
|
|
|
5
|
+
## [0.4.7] - 2025-11-06
|
|
6
|
+
|
|
7
|
+
### Fixed
|
|
8
|
+
- **Multiple duplicate events**: Disabled Rails Error Reporter and rack.exception fallback subscriptions
|
|
9
|
+
- These mechanisms were creating 3-4 duplicate events for each error
|
|
10
|
+
- Middleware is now the single source of truth for error capture, providing the best context
|
|
11
|
+
- Each error now creates exactly ONE event instead of 3-4
|
|
12
|
+
|
|
13
|
+
### Changed
|
|
14
|
+
- Rails Error Reporter subscription: DISABLED (middleware captures all controller errors)
|
|
15
|
+
- rack.exception subscription: DISABLED (middleware captures all rack-level errors)
|
|
16
|
+
- Middleware remains as the primary and only error capture mechanism
|
|
17
|
+
|
|
18
|
+
## [0.4.6] - 2025-11-05
|
|
19
|
+
|
|
20
|
+
### Fixed
|
|
21
|
+
- **Duplicate issues bug**: Fixed error reporting creating duplicate issues with "unknown" controller_action
|
|
22
|
+
- Error Reporter now properly extracts controller/action from Rails routing context
|
|
23
|
+
- `rack.exception` fallback now also extracts controller_action correctly
|
|
24
|
+
- All error capture mechanisms now produce consistent controller_action values, preventing duplicate issue grouping
|
|
25
|
+
|
|
26
|
+
### Details
|
|
27
|
+
Previously, the same error could create multiple issues in the dashboard:
|
|
28
|
+
- Middleware capture: `TestController#action` ✓
|
|
29
|
+
- Rails Error Reporter: `unknown` ✗ (created duplicate)
|
|
30
|
+
- Rack.exception fallback: `unknown` ✗ (created duplicate)
|
|
31
|
+
|
|
32
|
+
Now all mechanisms extract and use the same controller_action format, ensuring errors are properly grouped into a single issue.
|
|
33
|
+
|
|
5
34
|
## [0.4.4] - 2025-10-22
|
|
6
35
|
|
|
7
36
|
### Improved
|
|
Binary file
|
|
@@ -12,23 +12,23 @@ module ActiveRabbit
|
|
|
12
12
|
|
|
13
13
|
# Time-based deduplication: track errors with timestamps
|
|
14
14
|
$reported_errors ||= {}
|
|
15
|
-
|
|
15
|
+
|
|
16
16
|
# Generate a unique key for this error
|
|
17
17
|
error_key = "#{exception.class.name}:#{exception.message}:#{exception.backtrace&.first}"
|
|
18
|
-
|
|
18
|
+
|
|
19
19
|
# Get dedupe window from config (default 5 minutes, 0 = disabled)
|
|
20
|
-
dedupe_window = defined?(ActiveRabbit::Client.configuration.dedupe_window) ?
|
|
20
|
+
dedupe_window = defined?(ActiveRabbit::Client.configuration.dedupe_window) ?
|
|
21
21
|
ActiveRabbit::Client.configuration.dedupe_window : 300
|
|
22
|
-
|
|
22
|
+
|
|
23
23
|
current_time = Time.now.to_i
|
|
24
24
|
last_seen = $reported_errors[error_key]
|
|
25
|
-
|
|
25
|
+
|
|
26
26
|
# Report if: never seen before, OR dedupe disabled (0), OR outside dedupe window
|
|
27
27
|
should_report = last_seen.nil? || dedupe_window == 0 || (current_time - last_seen) > dedupe_window
|
|
28
|
-
|
|
28
|
+
|
|
29
29
|
if should_report
|
|
30
30
|
$reported_errors[error_key] = current_time
|
|
31
|
-
|
|
31
|
+
|
|
32
32
|
# Clean old entries to prevent memory leak (keep last hour)
|
|
33
33
|
$reported_errors.delete_if { |_, timestamp| current_time - timestamp > 3600 }
|
|
34
34
|
|
|
@@ -56,6 +56,20 @@ module ActiveRabbit
|
|
|
56
56
|
# Top-level convenience for UI
|
|
57
57
|
ctx[:request_path] = ctx[:request][:path]
|
|
58
58
|
ctx[:request_method] = ctx[:request][:method]
|
|
59
|
+
|
|
60
|
+
# Extract controller_action from routing info to prevent duplicate issues
|
|
61
|
+
routing = req_info[:routing]
|
|
62
|
+
if routing && routing[:controller] && routing[:action]
|
|
63
|
+
controller_name = routing[:controller].to_s
|
|
64
|
+
action_name = routing[:action].to_s
|
|
65
|
+
ctx[:controller_action] = "#{controller_name}##{action_name}"
|
|
66
|
+
end
|
|
67
|
+
else
|
|
68
|
+
# Fallback: Try to extract from controller instance in context
|
|
69
|
+
controller = context && (context[:controller] || context['controller'])
|
|
70
|
+
if controller && controller.respond_to?(:controller_name) && controller.respond_to?(:action_name)
|
|
71
|
+
ctx[:controller_action] = "#{controller.controller_name.camelize}Controller##{controller.action_name}"
|
|
72
|
+
end
|
|
59
73
|
end
|
|
60
74
|
|
|
61
75
|
if defined?(ActionController::RoutingError) && exception.is_a?(ActionController::RoutingError)
|
|
@@ -77,10 +77,22 @@ module ActiveRabbit
|
|
|
77
77
|
private
|
|
78
78
|
|
|
79
79
|
def build_performance_data(name:, duration_ms:, metadata:)
|
|
80
|
+
metadata = metadata || {}
|
|
81
|
+
|
|
82
|
+
# Extract important fields from metadata for top-level inclusion
|
|
83
|
+
controller = metadata[:controller] || metadata["controller"]
|
|
84
|
+
action = metadata[:action] || metadata["action"]
|
|
85
|
+
controller_action = "#{controller}##{action}" if controller && action
|
|
86
|
+
|
|
80
87
|
data = {
|
|
81
88
|
name: name.to_s,
|
|
82
89
|
duration_ms: duration_ms.to_f,
|
|
83
|
-
|
|
90
|
+
db_duration_ms: metadata[:db_runtime] || metadata["db_runtime"],
|
|
91
|
+
view_duration_ms: metadata[:view_runtime] || metadata["view_runtime"],
|
|
92
|
+
controller_action: controller_action,
|
|
93
|
+
request_path: metadata[:path] || metadata["path"],
|
|
94
|
+
request_method: metadata[:method] || metadata["method"],
|
|
95
|
+
metadata: scrub_pii(metadata),
|
|
84
96
|
timestamp: Time.now.iso8601(3),
|
|
85
97
|
environment: configuration.environment,
|
|
86
98
|
release: configuration.release,
|
|
@@ -46,35 +46,39 @@ module ActiveRabbit
|
|
|
46
46
|
setup_exception_tracking(app) if ActiveRabbit::Client.configured?
|
|
47
47
|
end
|
|
48
48
|
|
|
49
|
-
initializer "active_rabbit.subscribe_to_notifications" do |app|
|
|
50
|
-
|
|
49
|
+
initializer "active_rabbit.subscribe_to_notifications", after: :load_config_initializers do |app|
|
|
50
|
+
Rails.logger.info "[ActiveRabbit] Setting up performance notifications subscriptions"
|
|
51
|
+
# Subscribe regardless; each handler guards on configured?
|
|
52
|
+
subscribe_to_controller_events
|
|
53
|
+
subscribe_to_active_record_events
|
|
54
|
+
subscribe_to_action_view_events
|
|
55
|
+
subscribe_to_action_mailer_events if defined?(ActionMailer)
|
|
56
|
+
subscribe_to_exception_notifications
|
|
57
|
+
Rails.logger.info "[ActiveRabbit] Subscriptions setup complete"
|
|
58
|
+
|
|
59
|
+
# Defer complex subscriptions until after initialization
|
|
51
60
|
app.config.after_initialize do
|
|
52
|
-
# Subscribe regardless; each handler guards on configured?
|
|
53
|
-
subscribe_to_controller_events
|
|
54
|
-
subscribe_to_active_record_events
|
|
55
|
-
subscribe_to_action_view_events
|
|
56
|
-
subscribe_to_action_mailer_events if defined?(ActionMailer)
|
|
57
|
-
subscribe_to_exception_notifications
|
|
58
|
-
|
|
59
|
-
# Fallback: low-level rack.exception subscription (older Rails and deep middleware errors)
|
|
60
|
-
ActiveSupport::Notifications.subscribe("rack.exception") do |*args|
|
|
61
|
-
begin
|
|
62
|
-
payload = args.last
|
|
63
|
-
exception = payload[:exception_object]
|
|
64
|
-
env = payload[:env]
|
|
65
|
-
next unless exception
|
|
66
61
|
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
62
|
+
# DISABLED: rack.exception creates duplicate events because middleware already catches all errors
|
|
63
|
+
# The middleware is the primary error capture mechanism and catches errors at the optimal level
|
|
64
|
+
# ActiveSupport::Notifications.subscribe("rack.exception") do |*args|
|
|
65
|
+
# begin
|
|
66
|
+
# payload = args.last
|
|
67
|
+
# exception = payload[:exception_object]
|
|
68
|
+
# env = payload[:env]
|
|
69
|
+
# next unless exception
|
|
70
|
+
#
|
|
71
|
+
# ActiveRabbit::Reporting.report_exception(
|
|
72
|
+
# exception,
|
|
73
|
+
# env: env,
|
|
74
|
+
# handled: false,
|
|
75
|
+
# source: "rack.exception",
|
|
76
|
+
# force: true
|
|
77
|
+
# )
|
|
78
|
+
# rescue => e
|
|
79
|
+
# Rails.logger.error "[ActiveRabbit] Error handling rack.exception: #{e.class}: #{e.message}" if defined?(Rails)
|
|
80
|
+
# end
|
|
81
|
+
# end
|
|
78
82
|
end
|
|
79
83
|
end
|
|
80
84
|
|
|
@@ -284,10 +288,11 @@ module ActiveRabbit
|
|
|
284
288
|
end
|
|
285
289
|
|
|
286
290
|
initializer "active_rabbit.error_reporter" do |app|
|
|
287
|
-
#
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
+
# DISABLED: Rails error reporter creates duplicate events because middleware already catches all errors
|
|
292
|
+
# The middleware provides better context and catches errors at the right level
|
|
293
|
+
# app.config.after_initialize do
|
|
294
|
+
# ActiveRabbit::Client::ErrorReporter.attach!
|
|
295
|
+
# end
|
|
291
296
|
end
|
|
292
297
|
|
|
293
298
|
initializer "active_rabbit.sidekiq" do
|
|
@@ -387,10 +392,20 @@ module ActiveRabbit
|
|
|
387
392
|
end
|
|
388
393
|
|
|
389
394
|
def subscribe_to_controller_events
|
|
395
|
+
Rails.logger.info "[ActiveRabbit] Subscribing to controller events (configured=#{ActiveRabbit::Client.configured?})"
|
|
396
|
+
|
|
390
397
|
ActiveSupport::Notifications.subscribe "process_action.action_controller" do |name, started, finished, unique_id, payload|
|
|
391
398
|
begin
|
|
399
|
+
unless ActiveRabbit::Client.configured?
|
|
400
|
+
Rails.logger.debug "[ActiveRabbit] Skipping performance tracking - not configured"
|
|
401
|
+
return
|
|
402
|
+
end
|
|
403
|
+
|
|
392
404
|
duration_ms = ((finished - started) * 1000).round(2)
|
|
393
405
|
|
|
406
|
+
Rails.logger.info "[ActiveRabbit] 📊 Controller action: #{payload[:controller]}##{payload[:action]} - #{duration_ms}ms"
|
|
407
|
+
Rails.logger.info "[ActiveRabbit] 📊 DB runtime: #{payload[:db_runtime]}, View runtime: #{payload[:view_runtime]}"
|
|
408
|
+
|
|
394
409
|
ActiveRabbit::Client.track_performance(
|
|
395
410
|
"controller.action",
|
|
396
411
|
duration_ms,
|
|
@@ -16,6 +16,14 @@ module ActiveRabbit
|
|
|
16
16
|
enriched_context[:source] ||= source
|
|
17
17
|
enriched_context[:handled] = handled if !enriched_context.key?(:handled)
|
|
18
18
|
|
|
19
|
+
# Extract controller_action from routing info to prevent duplicate issues
|
|
20
|
+
routing = req_info[:routing]
|
|
21
|
+
if routing && routing[:controller] && routing[:action] && !enriched_context[:controller_action]
|
|
22
|
+
controller_name = routing[:controller].to_s
|
|
23
|
+
action_name = routing[:action].to_s
|
|
24
|
+
enriched_context[:controller_action] = "#{controller_name}##{action_name}"
|
|
25
|
+
end
|
|
26
|
+
|
|
19
27
|
# Enrich for routing errors so UI shows controller action and 404 specifics
|
|
20
28
|
if defined?(ActionController::RoutingError) && exception.is_a?(ActionController::RoutingError)
|
|
21
29
|
enriched_context[:controller_action] ||= 'Routing#not_found'
|
data/lib/active_rabbit.rb
CHANGED
|
@@ -4,11 +4,4 @@ require_relative "active_rabbit/client"
|
|
|
4
4
|
require_relative "active_rabbit/routing/not_found_app"
|
|
5
5
|
|
|
6
6
|
# Load Rails integration
|
|
7
|
-
if defined?(Rails)
|
|
8
|
-
begin
|
|
9
|
-
require_relative "active_rabbit/client/railtie"
|
|
10
|
-
rescue => e
|
|
11
|
-
warn "[ActiveRabbit] Rails integration failed: #{e.message}" if Rails.env&.development?
|
|
12
|
-
warn "[ActiveRabbit] Backtrace: #{e.backtrace.first(5).join(', ')}" if Rails.env&.development?
|
|
13
|
-
end
|
|
14
|
-
end
|
|
7
|
+
require_relative "active_rabbit/client/railtie" if defined?(Rails)
|
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.
|
|
4
|
+
version: 0.4.7
|
|
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-11-12 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: concurrent-ruby
|
|
@@ -81,6 +81,7 @@ files:
|
|
|
81
81
|
- README.md
|
|
82
82
|
- Rakefile
|
|
83
83
|
- TESTING_GUIDE.md
|
|
84
|
+
- activerabbit-ai-0.4.5.gem
|
|
84
85
|
- check_api_data.rb
|
|
85
86
|
- examples/rails_app_testing.rb
|
|
86
87
|
- examples/rails_integration.rb
|
|
@@ -105,6 +106,7 @@ files:
|
|
|
105
106
|
- lib/active_rabbit/middleware/error_capture_middleware.rb
|
|
106
107
|
- lib/active_rabbit/reporting.rb
|
|
107
108
|
- lib/active_rabbit/routing/not_found_app.rb
|
|
109
|
+
- lib/activerabbit-ai.rb
|
|
108
110
|
- script/test_production_readiness.rb
|
|
109
111
|
- script/test_rails_integration.rb
|
|
110
112
|
- setup_local_gem_testing.sh
|