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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 86853d97a830e8d411ba9052ff231b29a2cddb526799e6b5c89e848ce026c7b2
4
- data.tar.gz: 1faeb9a59503089d03b0ecda80fab72cd8028b3bfd49f4da5c5d25ff9cfb3139
3
+ metadata.gz: 06e58258544f159566a756467253a1a2a6a0a696479b37cd1f265902928a3f8c
4
+ data.tar.gz: e12b3dcd16a43ca524027e2b0ecd608d8bbe0441f2f89586d7bb5d1f57dbda1a
5
5
  SHA512:
6
- metadata.gz: 760d010f75113d98d6e2f83fd3768d37d7cf67e307c24418717caa8c07ea489337ed7923ef876ed7bb95e4556e6780466efaec374d2ffc62d00b7b16a37f6fda
7
- data.tar.gz: 441b28d1cc42c02a13c3efe4d387674932fceafca7c8571c6b24792ac69134338050ad030ebe65253ee48387e108b8b7d85014866bd42fed1e56f1c9fa356647
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
- metadata: scrub_pii(metadata || {}),
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
- # Defer subscription until after application initializers (configuration complete)
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
- ActiveRabbit::Reporting.report_exception(
68
- exception,
69
- env: env,
70
- handled: false,
71
- source: "rack.exception",
72
- force: true
73
- )
74
- rescue => e
75
- Rails.logger.error "[ActiveRabbit] Error handling rack.exception: #{e.class}: #{e.message}" if defined?(Rails)
76
- end
77
- end
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
- # Defer attaching so application config has been applied
288
- app.config.after_initialize do
289
- ActiveRabbit::Client::ErrorReporter.attach!
290
- end
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,
@@ -2,6 +2,6 @@
2
2
 
3
3
  module ActiveRabbit
4
4
  module Client
5
- VERSION = "0.4.4"
5
+ VERSION = "0.4.7"
6
6
  end
7
7
  end
@@ -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)
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Compatibility loader for Bundler and Rails autoloading
4
+ # because gem names cannot map "-" to "_" automatically.
5
+ # This ensures `require "activerabbit-ai"` loads the real code.
6
+
7
+ require_relative "active_rabbit"
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
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-10-23 00:00:00.000000000 Z
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