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 +4 -4
- data/lib/brainzlab/configuration.rb +20 -0
- data/lib/brainzlab/instrumentation/action_cable.rb +351 -0
- data/lib/brainzlab/instrumentation/action_controller.rb +649 -0
- data/lib/brainzlab/instrumentation/action_dispatch.rb +259 -0
- data/lib/brainzlab/instrumentation/action_mailbox.rb +197 -0
- data/lib/brainzlab/instrumentation/action_view.rb +380 -0
- data/lib/brainzlab/instrumentation/active_job.rb +569 -0
- data/lib/brainzlab/instrumentation/active_record.rb +458 -25
- data/lib/brainzlab/instrumentation/active_storage.rb +541 -0
- data/lib/brainzlab/instrumentation/active_support_cache.rb +700 -0
- data/lib/brainzlab/instrumentation/rails_deprecation.rb +139 -0
- data/lib/brainzlab/instrumentation/railties.rb +134 -0
- data/lib/brainzlab/instrumentation.rb +91 -1
- data/lib/brainzlab/version.rb +1 -1
- metadata +11 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: bf91a415760bc97688aa6d97c80fc4aa0c2677d0f299799eaf6a52ed5762b0b7
|
|
4
|
+
data.tar.gz: fc11f28cd6e93ff15430ca2a33d2068c19a0605cc27cac1232b23b730d36f1c2
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
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
|