brainzlab 0.1.3 → 0.1.4

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: a2ccfb8ed0a1aefc1a67e709438812194e6a6a1ce79f7044824ab3f2accd3aca
4
- data.tar.gz: 99df1dbd2842a09ab90165755746a8248c5b103854dcddd8c28c1f8ee7e239f9
3
+ metadata.gz: bf91a415760bc97688aa6d97c80fc4aa0c2677d0f299799eaf6a52ed5762b0b7
4
+ data.tar.gz: fc11f28cd6e93ff15430ca2a33d2068c19a0605cc27cac1232b23b730d36f1c2
5
5
  SHA512:
6
- metadata.gz: 49a8f8a4be96f4f917fef27661caa12bde990cfa88dcc19d8917c4c74c4446b63cff26ad52ee524c577d8c9bc3f6767315245e1dace168a46422858f43af2620
7
- data.tar.gz: d64004b9afc21429742c61deebbfd5cfe244b83ff477bb4c4809d9fd156917652e150f0236aca3abba6d42df53a231fd6deb54c57b7914f1267d40cc4d1f9a75
6
+ metadata.gz: '09ae587f4463bd67d19f5d059c4fd810ce337099ad7bbe4bd3eea586d01294f22c904298aa121668329a995be3df9682624fa2b01dd57e00d6e2b8243eaf6a85'
7
+ data.tar.gz: 93da1a25704e85ac769c686509d98cfd950620304f4c5d90b18fa9c14cb542c6696eab29a056eaecf125dad992607f4a3050e1009c81976650226b08f34924b7
@@ -112,7 +112,11 @@ module BrainzLab
112
112
  :instrument_graphql,
113
113
  :instrument_mongodb,
114
114
  :instrument_elasticsearch,
115
+ :instrument_action_controller,
116
+ :instrument_action_view,
115
117
  :instrument_action_mailer,
118
+ :instrument_active_job,
119
+ :instrument_active_support_cache,
116
120
  :instrument_delayed_job,
117
121
  :instrument_grape,
118
122
  :instrument_solid_queue,
@@ -123,6 +127,12 @@ module BrainzLab
123
127
  :instrument_dalli,
124
128
  :instrument_aws,
125
129
  :instrument_stripe,
130
+ :instrument_active_storage,
131
+ :instrument_action_cable,
132
+ :instrument_action_dispatch,
133
+ :instrument_rails_deprecation,
134
+ :instrument_action_mailbox,
135
+ :instrument_railties,
126
136
  :http_ignore_hosts,
127
137
  :redis_ignore_commands,
128
138
  :log_formatter_enabled,
@@ -301,7 +311,11 @@ module BrainzLab
301
311
  @instrument_graphql = true # GraphQL query instrumentation
302
312
  @instrument_mongodb = true # MongoDB/Mongoid instrumentation
303
313
  @instrument_elasticsearch = true # Elasticsearch instrumentation
314
+ @instrument_action_controller = true # ActionController instrumentation (requests, redirects, filters)
315
+ @instrument_action_view = true # ActionView instrumentation (templates, partials, collections)
304
316
  @instrument_action_mailer = true # ActionMailer instrumentation
317
+ @instrument_active_job = true # ActiveJob instrumentation (enqueue, perform, retry, discard)
318
+ @instrument_active_support_cache = true # ActiveSupport::Cache instrumentation (read, write, fetch)
305
319
  @instrument_delayed_job = true # Delayed::Job instrumentation
306
320
  @instrument_grape = true # Grape API instrumentation
307
321
  @instrument_solid_queue = true # Solid Queue job instrumentation
@@ -312,6 +326,12 @@ module BrainzLab
312
326
  @instrument_dalli = true # Dalli/Memcached instrumentation
313
327
  @instrument_aws = true # AWS SDK instrumentation
314
328
  @instrument_stripe = true # Stripe API instrumentation
329
+ @instrument_active_storage = true # ActiveStorage instrumentation (uploads, downloads, transforms)
330
+ @instrument_action_cable = true # ActionCable WebSocket instrumentation
331
+ @instrument_action_dispatch = true # ActionDispatch instrumentation (middleware, redirects, requests)
332
+ @instrument_rails_deprecation = true # Rails deprecation warnings tracking
333
+ @instrument_action_mailbox = true # ActionMailbox inbound email processing instrumentation
334
+ @instrument_railties = true # Railties config initializer loading instrumentation
315
335
  @http_ignore_hosts = %w[localhost 127.0.0.1]
316
336
  @redis_ignore_commands = %w[ping info] # Commands to skip tracking
317
337
 
@@ -0,0 +1,351 @@
1
+ # frozen_string_literal: true
2
+
3
+ module BrainzLab
4
+ module Instrumentation
5
+ class ActionCable
6
+ # Thresholds for slow operations (in milliseconds)
7
+ SLOW_ACTION_THRESHOLD = 100
8
+ VERY_SLOW_ACTION_THRESHOLD = 500
9
+
10
+ class << self
11
+ def install!
12
+ return unless defined?(::ActionCable)
13
+ return if @installed
14
+
15
+ install_perform_action_subscriber!
16
+ install_transmit_subscriber!
17
+ install_transmit_subscription_confirmation_subscriber!
18
+ install_transmit_subscription_rejection_subscriber!
19
+ install_broadcast_subscriber!
20
+
21
+ @installed = true
22
+ BrainzLab.debug_log('ActionCable instrumentation installed')
23
+ end
24
+
25
+ def installed?
26
+ @installed == true
27
+ end
28
+
29
+ private
30
+
31
+ # ============================================
32
+ # perform_action.action_cable
33
+ # ============================================
34
+ def install_perform_action_subscriber!
35
+ ActiveSupport::Notifications.subscribe('perform_action.action_cable') do |*args|
36
+ event = ActiveSupport::Notifications::Event.new(*args)
37
+ handle_perform_action(event)
38
+ end
39
+ end
40
+
41
+ def handle_perform_action(event)
42
+ payload = event.payload
43
+ duration = event.duration.round(2)
44
+
45
+ channel_class = payload[:channel_class]
46
+ action = payload[:action]
47
+ data = payload[:data]
48
+
49
+ # Determine level based on duration
50
+ level = case duration
51
+ when 0...SLOW_ACTION_THRESHOLD then :info
52
+ when SLOW_ACTION_THRESHOLD...VERY_SLOW_ACTION_THRESHOLD then :warning
53
+ else :error
54
+ end
55
+
56
+ # Record breadcrumb
57
+ if BrainzLab.configuration.reflex_effectively_enabled?
58
+ BrainzLab::Reflex.add_breadcrumb(
59
+ "Cable action: #{channel_class}##{action} (#{duration}ms)",
60
+ category: 'cable.action',
61
+ level: level,
62
+ data: {
63
+ channel: channel_class,
64
+ action: action,
65
+ duration_ms: duration
66
+ }.compact
67
+ )
68
+ end
69
+
70
+ # Add Pulse span
71
+ record_action_span(event, channel_class, action, duration, data)
72
+
73
+ # Log slow actions
74
+ log_slow_action(channel_class, action, duration) if duration >= SLOW_ACTION_THRESHOLD
75
+ rescue StandardError => e
76
+ BrainzLab.debug_log("ActionCable perform_action instrumentation failed: #{e.message}")
77
+ end
78
+
79
+ # ============================================
80
+ # transmit.action_cable
81
+ # ============================================
82
+ def install_transmit_subscriber!
83
+ ActiveSupport::Notifications.subscribe('transmit.action_cable') do |*args|
84
+ event = ActiveSupport::Notifications::Event.new(*args)
85
+ handle_transmit(event)
86
+ end
87
+ end
88
+
89
+ def handle_transmit(event)
90
+ payload = event.payload
91
+ duration = event.duration.round(2)
92
+
93
+ channel_class = payload[:channel_class]
94
+ data = payload[:data]
95
+ via = payload[:via]
96
+
97
+ # Record breadcrumb
98
+ if BrainzLab.configuration.reflex_effectively_enabled?
99
+ message = via ? "Cable transmit via #{via}" : 'Cable transmit'
100
+ BrainzLab::Reflex.add_breadcrumb(
101
+ "#{message}: #{channel_class} (#{duration}ms)",
102
+ category: 'cable.transmit',
103
+ level: :info,
104
+ data: {
105
+ channel: channel_class,
106
+ via: via,
107
+ duration_ms: duration
108
+ }.compact
109
+ )
110
+ end
111
+
112
+ # Add Pulse span
113
+ record_transmit_span(event, channel_class, duration, via)
114
+ rescue StandardError => e
115
+ BrainzLab.debug_log("ActionCable transmit instrumentation failed: #{e.message}")
116
+ end
117
+
118
+ # ============================================
119
+ # transmit_subscription_confirmation.action_cable
120
+ # ============================================
121
+ def install_transmit_subscription_confirmation_subscriber!
122
+ ActiveSupport::Notifications.subscribe('transmit_subscription_confirmation.action_cable') do |*args|
123
+ event = ActiveSupport::Notifications::Event.new(*args)
124
+ handle_subscription_confirmation(event)
125
+ end
126
+ end
127
+
128
+ def handle_subscription_confirmation(event)
129
+ payload = event.payload
130
+ duration = event.duration.round(2)
131
+
132
+ channel_class = payload[:channel_class]
133
+
134
+ # Record breadcrumb
135
+ if BrainzLab.configuration.reflex_effectively_enabled?
136
+ BrainzLab::Reflex.add_breadcrumb(
137
+ "Cable subscribed: #{channel_class}",
138
+ category: 'cable.subscribe',
139
+ level: :info,
140
+ data: {
141
+ channel: channel_class,
142
+ status: 'confirmed',
143
+ duration_ms: duration
144
+ }.compact
145
+ )
146
+ end
147
+
148
+ # Add Pulse span
149
+ record_subscription_span(event, channel_class, 'confirmed', duration)
150
+ rescue StandardError => e
151
+ BrainzLab.debug_log("ActionCable subscription confirmation instrumentation failed: #{e.message}")
152
+ end
153
+
154
+ # ============================================
155
+ # transmit_subscription_rejection.action_cable
156
+ # ============================================
157
+ def install_transmit_subscription_rejection_subscriber!
158
+ ActiveSupport::Notifications.subscribe('transmit_subscription_rejection.action_cable') do |*args|
159
+ event = ActiveSupport::Notifications::Event.new(*args)
160
+ handle_subscription_rejection(event)
161
+ end
162
+ end
163
+
164
+ def handle_subscription_rejection(event)
165
+ payload = event.payload
166
+ duration = event.duration.round(2)
167
+
168
+ channel_class = payload[:channel_class]
169
+
170
+ # Record breadcrumb - rejection is a warning
171
+ if BrainzLab.configuration.reflex_effectively_enabled?
172
+ BrainzLab::Reflex.add_breadcrumb(
173
+ "Cable subscription rejected: #{channel_class}",
174
+ category: 'cable.subscribe',
175
+ level: :warning,
176
+ data: {
177
+ channel: channel_class,
178
+ status: 'rejected',
179
+ duration_ms: duration
180
+ }.compact
181
+ )
182
+ end
183
+
184
+ # Add Pulse span
185
+ record_subscription_span(event, channel_class, 'rejected', duration)
186
+
187
+ # Log rejection to Recall
188
+ if BrainzLab.configuration.recall_effectively_enabled?
189
+ BrainzLab::Recall.warn(
190
+ "ActionCable subscription rejected",
191
+ channel: channel_class
192
+ )
193
+ end
194
+ rescue StandardError => e
195
+ BrainzLab.debug_log("ActionCable subscription rejection instrumentation failed: #{e.message}")
196
+ end
197
+
198
+ # ============================================
199
+ # broadcast.action_cable
200
+ # ============================================
201
+ def install_broadcast_subscriber!
202
+ ActiveSupport::Notifications.subscribe('broadcast.action_cable') do |*args|
203
+ event = ActiveSupport::Notifications::Event.new(*args)
204
+ handle_broadcast(event)
205
+ end
206
+ end
207
+
208
+ def handle_broadcast(event)
209
+ payload = event.payload
210
+ duration = event.duration.round(2)
211
+
212
+ broadcasting = payload[:broadcasting]
213
+ message = payload[:message]
214
+ coder = payload[:coder]
215
+
216
+ # Record breadcrumb
217
+ if BrainzLab.configuration.reflex_effectively_enabled?
218
+ BrainzLab::Reflex.add_breadcrumb(
219
+ "Cable broadcast: #{broadcasting} (#{duration}ms)",
220
+ category: 'cable.broadcast',
221
+ level: :info,
222
+ data: {
223
+ broadcasting: broadcasting,
224
+ coder: coder&.to_s,
225
+ duration_ms: duration
226
+ }.compact
227
+ )
228
+ end
229
+
230
+ # Add Pulse span
231
+ record_broadcast_span(event, broadcasting, duration, coder)
232
+ rescue StandardError => e
233
+ BrainzLab.debug_log("ActionCable broadcast instrumentation failed: #{e.message}")
234
+ end
235
+
236
+ # ============================================
237
+ # Span Recording Helpers
238
+ # ============================================
239
+ def record_action_span(event, channel_class, action, duration, data)
240
+ return unless BrainzLab.configuration.pulse_effectively_enabled?
241
+
242
+ tracer = BrainzLab::Pulse.tracer
243
+ return unless tracer.current_trace
244
+
245
+ span_data = {
246
+ span_id: SecureRandom.uuid,
247
+ name: "cable.action.#{action}",
248
+ kind: 'websocket',
249
+ started_at: event.time,
250
+ ended_at: event.end,
251
+ duration_ms: duration,
252
+ error: false,
253
+ data: {
254
+ 'cable.channel' => channel_class,
255
+ 'cable.action' => action
256
+ }.compact
257
+ }
258
+
259
+ tracer.current_spans << span_data
260
+ end
261
+
262
+ def record_transmit_span(event, channel_class, duration, via)
263
+ return unless BrainzLab.configuration.pulse_effectively_enabled?
264
+
265
+ tracer = BrainzLab::Pulse.tracer
266
+ return unless tracer.current_trace
267
+
268
+ span_data = {
269
+ span_id: SecureRandom.uuid,
270
+ name: 'cable.transmit',
271
+ kind: 'websocket',
272
+ started_at: event.time,
273
+ ended_at: event.end,
274
+ duration_ms: duration,
275
+ error: false,
276
+ data: {
277
+ 'cable.channel' => channel_class,
278
+ 'cable.via' => via
279
+ }.compact
280
+ }
281
+
282
+ tracer.current_spans << span_data
283
+ end
284
+
285
+ def record_subscription_span(event, channel_class, status, duration)
286
+ return unless BrainzLab.configuration.pulse_effectively_enabled?
287
+
288
+ tracer = BrainzLab::Pulse.tracer
289
+ return unless tracer.current_trace
290
+
291
+ span_data = {
292
+ span_id: SecureRandom.uuid,
293
+ name: 'cable.subscribe',
294
+ kind: 'websocket',
295
+ started_at: event.time,
296
+ ended_at: event.end,
297
+ duration_ms: duration,
298
+ error: status == 'rejected',
299
+ data: {
300
+ 'cable.channel' => channel_class,
301
+ 'cable.subscription_status' => status
302
+ }.compact
303
+ }
304
+
305
+ tracer.current_spans << span_data
306
+ end
307
+
308
+ def record_broadcast_span(event, broadcasting, duration, coder)
309
+ return unless BrainzLab.configuration.pulse_effectively_enabled?
310
+
311
+ tracer = BrainzLab::Pulse.tracer
312
+ return unless tracer.current_trace
313
+
314
+ span_data = {
315
+ span_id: SecureRandom.uuid,
316
+ name: 'cable.broadcast',
317
+ kind: 'websocket',
318
+ started_at: event.time,
319
+ ended_at: event.end,
320
+ duration_ms: duration,
321
+ error: false,
322
+ data: {
323
+ 'cable.broadcasting' => broadcasting,
324
+ 'cable.coder' => coder&.to_s
325
+ }.compact
326
+ }
327
+
328
+ tracer.current_spans << span_data
329
+ end
330
+
331
+ # ============================================
332
+ # Logging Helpers
333
+ # ============================================
334
+ def log_slow_action(channel_class, action, duration)
335
+ return unless BrainzLab.configuration.recall_effectively_enabled?
336
+
337
+ level = duration >= VERY_SLOW_ACTION_THRESHOLD ? :error : :warn
338
+
339
+ BrainzLab::Recall.send(
340
+ level,
341
+ "Slow ActionCable action: #{channel_class}##{action} (#{duration}ms)",
342
+ channel: channel_class,
343
+ action: action,
344
+ duration_ms: duration,
345
+ threshold_exceeded: duration >= VERY_SLOW_ACTION_THRESHOLD ? 'critical' : 'warning'
346
+ )
347
+ end
348
+ end
349
+ end
350
+ end
351
+ end