brainzlab-rails 0.1.1

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.
@@ -0,0 +1,164 @@
1
+ # frozen_string_literal: true
2
+
3
+ module BrainzLab
4
+ module Rails
5
+ # Central subscriber that hooks into ALL Rails ActiveSupport::Notifications
6
+ # Uses monotonic_subscribe for accurate timing measurements
7
+ class Subscriber
8
+ RAILS_EVENT_PATTERNS = [
9
+ # Action Controller
10
+ 'start_processing.action_controller',
11
+ 'process_action.action_controller',
12
+ 'redirect_to.action_controller',
13
+ 'halted_callback.action_controller',
14
+ 'send_file.action_controller',
15
+ 'send_data.action_controller',
16
+ 'send_stream.action_controller',
17
+ 'unpermitted_parameters.action_controller',
18
+ 'rate_limit.action_controller',
19
+
20
+ # Action Controller Caching
21
+ 'write_fragment.action_controller',
22
+ 'read_fragment.action_controller',
23
+ 'expire_fragment.action_controller',
24
+ 'exist_fragment?.action_controller',
25
+
26
+ # Action View
27
+ 'render_template.action_view',
28
+ 'render_partial.action_view',
29
+ 'render_collection.action_view',
30
+ 'render_layout.action_view',
31
+
32
+ # Action Dispatch
33
+ 'process_middleware.action_dispatch',
34
+ 'redirect.action_dispatch',
35
+ 'request.action_dispatch',
36
+
37
+ # Active Record
38
+ 'sql.active_record',
39
+ 'instantiation.active_record',
40
+ 'start_transaction.active_record',
41
+ 'transaction.active_record',
42
+ 'strict_loading_violation.active_record',
43
+
44
+ # Active Job
45
+ 'enqueue.active_job',
46
+ 'enqueue_at.active_job',
47
+ 'enqueue_all.active_job',
48
+ 'enqueue_retry.active_job',
49
+ 'perform_start.active_job',
50
+ 'perform.active_job',
51
+ 'retry_stopped.active_job',
52
+ 'discard.active_job',
53
+
54
+ # Action Cable
55
+ 'perform_action.action_cable',
56
+ 'transmit.action_cable',
57
+ 'transmit_subscription_confirmation.action_cable',
58
+ 'transmit_subscription_rejection.action_cable',
59
+ 'broadcast.action_cable',
60
+
61
+ # Action Mailer
62
+ 'deliver.action_mailer',
63
+ 'process.action_mailer',
64
+
65
+ # Action Mailbox
66
+ 'process.action_mailbox',
67
+
68
+ # Active Storage
69
+ 'service_upload.active_storage',
70
+ 'service_streaming_download.active_storage',
71
+ 'service_download.active_storage',
72
+ 'service_download_chunk.active_storage',
73
+ 'service_delete.active_storage',
74
+ 'service_delete_prefixed.active_storage',
75
+ 'service_exist.active_storage',
76
+ 'service_url.active_storage',
77
+ 'service_update_metadata.active_storage',
78
+ 'preview.active_storage',
79
+ 'transform.active_storage',
80
+ 'analyze.active_storage',
81
+
82
+ # Active Support Cache
83
+ 'cache_read.active_support',
84
+ 'cache_read_multi.active_support',
85
+ 'cache_generate.active_support',
86
+ 'cache_fetch_hit.active_support',
87
+ 'cache_write.active_support',
88
+ 'cache_write_multi.active_support',
89
+ 'cache_increment.active_support',
90
+ 'cache_decrement.active_support',
91
+ 'cache_delete.active_support',
92
+ 'cache_delete_multi.active_support',
93
+ 'cache_delete_matched.active_support',
94
+ 'cache_cleanup.active_support',
95
+ 'cache_prune.active_support',
96
+ 'cache_exist?.active_support',
97
+ 'message_serializer_fallback.active_support',
98
+
99
+ # Rails
100
+ 'deprecation.rails',
101
+
102
+ # Railties
103
+ 'load_config_initializer.railties'
104
+ ].freeze
105
+
106
+ attr_reader :configuration, :event_router, :subscriptions
107
+
108
+ def initialize(configuration)
109
+ @configuration = configuration
110
+ @event_router = EventRouter.new(configuration)
111
+ @subscriptions = []
112
+ end
113
+
114
+ def subscribe_all!
115
+ RAILS_EVENT_PATTERNS.each do |event_name|
116
+ subscribe_to(event_name)
117
+ end
118
+
119
+ BrainzLab.debug_log("[BrainzLab::Rails] Subscribed to #{@subscriptions.size} events")
120
+ end
121
+
122
+ def unsubscribe_all!
123
+ @subscriptions.each do |subscription|
124
+ ActiveSupport::Notifications.unsubscribe(subscription)
125
+ end
126
+ @subscriptions.clear
127
+ end
128
+
129
+ private
130
+
131
+ def subscribe_to(event_name)
132
+ # Use monotonic_subscribe for accurate timing
133
+ subscription = ActiveSupport::Notifications.monotonic_subscribe(event_name) do |name, started, finished, unique_id, payload|
134
+ handle_event(name, started, finished, unique_id, payload)
135
+ end
136
+
137
+ @subscriptions << subscription
138
+ end
139
+
140
+ def handle_event(name, started, finished, unique_id, payload)
141
+ return unless @configuration.should_sample?
142
+
143
+ # Calculate precise duration using monotonic time
144
+ duration_ms = ((finished - started) * 1000).round(3)
145
+
146
+ # Build event data structure
147
+ event_data = {
148
+ name: name,
149
+ started_at: started,
150
+ finished_at: finished,
151
+ duration_ms: duration_ms,
152
+ unique_id: unique_id,
153
+ payload: payload,
154
+ timestamp: Time.now.utc.iso8601(3)
155
+ }
156
+
157
+ # Route to appropriate products
158
+ @event_router.route(event_data)
159
+ rescue StandardError => e
160
+ BrainzLab.debug_log("[BrainzLab::Rails] Error handling event #{name}: #{e.message}")
161
+ end
162
+ end
163
+ end
164
+ end
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ module BrainzLab
4
+ module Rails
5
+ VERSION = '0.1.1'
6
+ end
7
+ end
@@ -0,0 +1,72 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'brainzlab'
4
+ require 'active_support'
5
+ require 'active_support/notifications'
6
+
7
+ # Tell SDK that we're handling Rails instrumentation via ActiveSupport::Notifications
8
+ # This prevents double-tracking - SDK will only install non-Rails instrumentation
9
+ # (HTTP clients, Redis, GraphQL, etc.)
10
+ BrainzLab.configuration.rails_instrumentation_handled_externally = true
11
+
12
+ require_relative 'brainzlab/rails/version'
13
+ require_relative 'brainzlab/rails/configuration'
14
+ require_relative 'brainzlab/rails/subscriber'
15
+ require_relative 'brainzlab/rails/event_router'
16
+
17
+ # Collectors
18
+ require_relative 'brainzlab/rails/collectors/base'
19
+ require_relative 'brainzlab/rails/collectors/action_controller'
20
+ require_relative 'brainzlab/rails/collectors/action_view'
21
+ require_relative 'brainzlab/rails/collectors/active_record'
22
+ require_relative 'brainzlab/rails/collectors/active_job'
23
+ require_relative 'brainzlab/rails/collectors/action_cable'
24
+ require_relative 'brainzlab/rails/collectors/action_mailer'
25
+ require_relative 'brainzlab/rails/collectors/active_storage'
26
+ require_relative 'brainzlab/rails/collectors/cache'
27
+
28
+ # Analyzers
29
+ require_relative 'brainzlab/rails/analyzers/n_plus_one_detector'
30
+ require_relative 'brainzlab/rails/analyzers/slow_query_analyzer'
31
+ require_relative 'brainzlab/rails/analyzers/cache_efficiency'
32
+
33
+ # Railtie for auto-initialization
34
+ require_relative 'brainzlab/rails/railtie' if defined?(::Rails::Railtie)
35
+
36
+ module BrainzLab
37
+ module Rails
38
+ class << self
39
+ attr_accessor :configuration
40
+
41
+ def configure
42
+ self.configuration ||= Configuration.new
43
+ yield(configuration) if block_given?
44
+ configuration
45
+ end
46
+
47
+ def subscriber
48
+ @subscriber ||= Subscriber.new(configuration)
49
+ end
50
+
51
+ def start!
52
+ return if @started
53
+
54
+ subscriber.subscribe_all!
55
+ @started = true
56
+ BrainzLab.debug_log('[BrainzLab::Rails] Started instrumentation')
57
+ end
58
+
59
+ def stop!
60
+ return unless @started
61
+
62
+ subscriber.unsubscribe_all!
63
+ @started = false
64
+ BrainzLab.debug_log('[BrainzLab::Rails] Stopped instrumentation')
65
+ end
66
+
67
+ def started?
68
+ @started == true
69
+ end
70
+ end
71
+ end
72
+ end
metadata ADDED
@@ -0,0 +1,178 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: brainzlab-rails
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.1
5
+ platform: ruby
6
+ authors:
7
+ - Brainz Lab
8
+ bindir: exe
9
+ cert_chain: []
10
+ date: 1980-01-02 00:00:00.000000000 Z
11
+ dependencies:
12
+ - !ruby/object:Gem::Dependency
13
+ name: brainzlab
14
+ requirement: !ruby/object:Gem::Requirement
15
+ requirements:
16
+ - - ">="
17
+ - !ruby/object:Gem::Version
18
+ version: 0.1.6
19
+ type: :runtime
20
+ prerelease: false
21
+ version_requirements: !ruby/object:Gem::Requirement
22
+ requirements:
23
+ - - ">="
24
+ - !ruby/object:Gem::Version
25
+ version: 0.1.6
26
+ - !ruby/object:Gem::Dependency
27
+ name: rails
28
+ requirement: !ruby/object:Gem::Requirement
29
+ requirements:
30
+ - - ">="
31
+ - !ruby/object:Gem::Version
32
+ version: '7.0'
33
+ type: :runtime
34
+ prerelease: false
35
+ version_requirements: !ruby/object:Gem::Requirement
36
+ requirements:
37
+ - - ">="
38
+ - !ruby/object:Gem::Version
39
+ version: '7.0'
40
+ - !ruby/object:Gem::Dependency
41
+ name: activesupport
42
+ requirement: !ruby/object:Gem::Requirement
43
+ requirements:
44
+ - - ">="
45
+ - !ruby/object:Gem::Version
46
+ version: '7.0'
47
+ type: :runtime
48
+ prerelease: false
49
+ version_requirements: !ruby/object:Gem::Requirement
50
+ requirements:
51
+ - - ">="
52
+ - !ruby/object:Gem::Version
53
+ version: '7.0'
54
+ - !ruby/object:Gem::Dependency
55
+ name: bundler
56
+ requirement: !ruby/object:Gem::Requirement
57
+ requirements:
58
+ - - "~>"
59
+ - !ruby/object:Gem::Version
60
+ version: '2.0'
61
+ type: :development
62
+ prerelease: false
63
+ version_requirements: !ruby/object:Gem::Requirement
64
+ requirements:
65
+ - - "~>"
66
+ - !ruby/object:Gem::Version
67
+ version: '2.0'
68
+ - !ruby/object:Gem::Dependency
69
+ name: rake
70
+ requirement: !ruby/object:Gem::Requirement
71
+ requirements:
72
+ - - "~>"
73
+ - !ruby/object:Gem::Version
74
+ version: '13.0'
75
+ type: :development
76
+ prerelease: false
77
+ version_requirements: !ruby/object:Gem::Requirement
78
+ requirements:
79
+ - - "~>"
80
+ - !ruby/object:Gem::Version
81
+ version: '13.0'
82
+ - !ruby/object:Gem::Dependency
83
+ name: rspec
84
+ requirement: !ruby/object:Gem::Requirement
85
+ requirements:
86
+ - - "~>"
87
+ - !ruby/object:Gem::Version
88
+ version: '3.0'
89
+ type: :development
90
+ prerelease: false
91
+ version_requirements: !ruby/object:Gem::Requirement
92
+ requirements:
93
+ - - "~>"
94
+ - !ruby/object:Gem::Version
95
+ version: '3.0'
96
+ - !ruby/object:Gem::Dependency
97
+ name: rspec-rails
98
+ requirement: !ruby/object:Gem::Requirement
99
+ requirements:
100
+ - - "~>"
101
+ - !ruby/object:Gem::Version
102
+ version: '6.0'
103
+ type: :development
104
+ prerelease: false
105
+ version_requirements: !ruby/object:Gem::Requirement
106
+ requirements:
107
+ - - "~>"
108
+ - !ruby/object:Gem::Version
109
+ version: '6.0'
110
+ - !ruby/object:Gem::Dependency
111
+ name: rubocop
112
+ requirement: !ruby/object:Gem::Requirement
113
+ requirements:
114
+ - - "~>"
115
+ - !ruby/object:Gem::Version
116
+ version: '1.21'
117
+ type: :development
118
+ prerelease: false
119
+ version_requirements: !ruby/object:Gem::Requirement
120
+ requirements:
121
+ - - "~>"
122
+ - !ruby/object:Gem::Version
123
+ version: '1.21'
124
+ description: Deep Rails instrumentation that routes events to Pulse (APM), Recall
125
+ (Logs), Reflex (Errors), Flux (Metrics), and Nerve (Jobs). One gem, full Rails observability.
126
+ email:
127
+ - support@brainzlab.ai
128
+ executables: []
129
+ extensions: []
130
+ extra_rdoc_files: []
131
+ files:
132
+ - CLAUDE.md
133
+ - IMPLEMENTATION_PLAN.md
134
+ - Rakefile
135
+ - brainzlab-rails.gemspec
136
+ - lib/brainzlab-rails.rb
137
+ - lib/brainzlab/rails/analyzers/cache_efficiency.rb
138
+ - lib/brainzlab/rails/analyzers/n_plus_one_detector.rb
139
+ - lib/brainzlab/rails/analyzers/slow_query_analyzer.rb
140
+ - lib/brainzlab/rails/collectors/action_cable.rb
141
+ - lib/brainzlab/rails/collectors/action_controller.rb
142
+ - lib/brainzlab/rails/collectors/action_mailer.rb
143
+ - lib/brainzlab/rails/collectors/action_view.rb
144
+ - lib/brainzlab/rails/collectors/active_job.rb
145
+ - lib/brainzlab/rails/collectors/active_record.rb
146
+ - lib/brainzlab/rails/collectors/active_storage.rb
147
+ - lib/brainzlab/rails/collectors/base.rb
148
+ - lib/brainzlab/rails/collectors/cache.rb
149
+ - lib/brainzlab/rails/configuration.rb
150
+ - lib/brainzlab/rails/event_router.rb
151
+ - lib/brainzlab/rails/railtie.rb
152
+ - lib/brainzlab/rails/subscriber.rb
153
+ - lib/brainzlab/rails/version.rb
154
+ homepage: https://brainzlab.ai
155
+ licenses:
156
+ - MIT
157
+ metadata:
158
+ homepage_uri: https://brainzlab.ai
159
+ source_code_uri: https://github.com/brainz-lab/brainzlab-rails
160
+ changelog_uri: https://github.com/brainz-lab/brainzlab-rails/blob/main/CHANGELOG.md
161
+ rdoc_options: []
162
+ require_paths:
163
+ - lib
164
+ required_ruby_version: !ruby/object:Gem::Requirement
165
+ requirements:
166
+ - - ">="
167
+ - !ruby/object:Gem::Version
168
+ version: 3.1.0
169
+ required_rubygems_version: !ruby/object:Gem::Requirement
170
+ requirements:
171
+ - - ">="
172
+ - !ruby/object:Gem::Version
173
+ version: '0'
174
+ requirements: []
175
+ rubygems_version: 3.6.9
176
+ specification_version: 4
177
+ summary: Rails-native observability powered by ActiveSupport::Notifications
178
+ test_files: []