fluyenta-ruby 0.1.14

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.
Files changed (121) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +68 -0
  3. data/LICENSE +11 -0
  4. data/README.md +571 -0
  5. data/lib/brainzlab/beacon/client.rb +227 -0
  6. data/lib/brainzlab/beacon/provisioner.rb +44 -0
  7. data/lib/brainzlab/beacon.rb +215 -0
  8. data/lib/brainzlab/configuration.rb +676 -0
  9. data/lib/brainzlab/context.rb +90 -0
  10. data/lib/brainzlab/cortex/cache.rb +59 -0
  11. data/lib/brainzlab/cortex/client.rb +159 -0
  12. data/lib/brainzlab/cortex/provisioner.rb +49 -0
  13. data/lib/brainzlab/cortex.rb +223 -0
  14. data/lib/brainzlab/debug.rb +305 -0
  15. data/lib/brainzlab/dendrite/client.rb +250 -0
  16. data/lib/brainzlab/dendrite/provisioner.rb +44 -0
  17. data/lib/brainzlab/dendrite.rb +195 -0
  18. data/lib/brainzlab/development/logger.rb +150 -0
  19. data/lib/brainzlab/development/store.rb +121 -0
  20. data/lib/brainzlab/development.rb +72 -0
  21. data/lib/brainzlab/devtools/assets/devtools.css +1329 -0
  22. data/lib/brainzlab/devtools/assets/devtools.js +396 -0
  23. data/lib/brainzlab/devtools/assets/logo.svg +6 -0
  24. data/lib/brainzlab/devtools/assets/templates/debug_panel.html.erb +511 -0
  25. data/lib/brainzlab/devtools/assets/templates/error_page.html.erb +1086 -0
  26. data/lib/brainzlab/devtools/data/collector.rb +248 -0
  27. data/lib/brainzlab/devtools/middleware/asset_server.rb +63 -0
  28. data/lib/brainzlab/devtools/middleware/database_handler.rb +177 -0
  29. data/lib/brainzlab/devtools/middleware/debug_panel.rb +126 -0
  30. data/lib/brainzlab/devtools/middleware/error_page.rb +377 -0
  31. data/lib/brainzlab/devtools/renderers/debug_panel_renderer.rb +159 -0
  32. data/lib/brainzlab/devtools/renderers/error_page_renderer.rb +98 -0
  33. data/lib/brainzlab/devtools.rb +75 -0
  34. data/lib/brainzlab/errors.rb +490 -0
  35. data/lib/brainzlab/flux/buffer.rb +96 -0
  36. data/lib/brainzlab/flux/client.rb +68 -0
  37. data/lib/brainzlab/flux/provisioner.rb +124 -0
  38. data/lib/brainzlab/flux.rb +184 -0
  39. data/lib/brainzlab/instrumentation/action_cable.rb +351 -0
  40. data/lib/brainzlab/instrumentation/action_controller.rb +649 -0
  41. data/lib/brainzlab/instrumentation/action_dispatch.rb +259 -0
  42. data/lib/brainzlab/instrumentation/action_mailbox.rb +197 -0
  43. data/lib/brainzlab/instrumentation/action_mailer.rb +182 -0
  44. data/lib/brainzlab/instrumentation/action_view.rb +380 -0
  45. data/lib/brainzlab/instrumentation/active_job.rb +569 -0
  46. data/lib/brainzlab/instrumentation/active_record.rb +559 -0
  47. data/lib/brainzlab/instrumentation/active_storage.rb +541 -0
  48. data/lib/brainzlab/instrumentation/active_support_cache.rb +730 -0
  49. data/lib/brainzlab/instrumentation/aws.rb +183 -0
  50. data/lib/brainzlab/instrumentation/dalli.rb +108 -0
  51. data/lib/brainzlab/instrumentation/delayed_job.rb +234 -0
  52. data/lib/brainzlab/instrumentation/elasticsearch.rb +209 -0
  53. data/lib/brainzlab/instrumentation/excon.rb +152 -0
  54. data/lib/brainzlab/instrumentation/faraday.rb +181 -0
  55. data/lib/brainzlab/instrumentation/good_job.rb +102 -0
  56. data/lib/brainzlab/instrumentation/grape.rb +293 -0
  57. data/lib/brainzlab/instrumentation/graphql.rb +252 -0
  58. data/lib/brainzlab/instrumentation/httparty.rb +193 -0
  59. data/lib/brainzlab/instrumentation/mongodb.rb +187 -0
  60. data/lib/brainzlab/instrumentation/net_http.rb +114 -0
  61. data/lib/brainzlab/instrumentation/rails_deprecation.rb +139 -0
  62. data/lib/brainzlab/instrumentation/railties.rb +134 -0
  63. data/lib/brainzlab/instrumentation/redis.rb +324 -0
  64. data/lib/brainzlab/instrumentation/resque.rb +114 -0
  65. data/lib/brainzlab/instrumentation/sidekiq.rb +265 -0
  66. data/lib/brainzlab/instrumentation/solid_queue.rb +194 -0
  67. data/lib/brainzlab/instrumentation/stripe.rb +163 -0
  68. data/lib/brainzlab/instrumentation/typhoeus.rb +106 -0
  69. data/lib/brainzlab/instrumentation.rb +360 -0
  70. data/lib/brainzlab/nerve/client.rb +235 -0
  71. data/lib/brainzlab/nerve/provisioner.rb +44 -0
  72. data/lib/brainzlab/nerve.rb +219 -0
  73. data/lib/brainzlab/pulse/client.rb +203 -0
  74. data/lib/brainzlab/pulse/instrumentation.rb +401 -0
  75. data/lib/brainzlab/pulse/propagation.rb +241 -0
  76. data/lib/brainzlab/pulse/provisioner.rb +114 -0
  77. data/lib/brainzlab/pulse/tracer.rb +111 -0
  78. data/lib/brainzlab/pulse.rb +294 -0
  79. data/lib/brainzlab/rails/log_formatter.rb +807 -0
  80. data/lib/brainzlab/rails/log_subscriber.rb +334 -0
  81. data/lib/brainzlab/rails/railtie.rb +606 -0
  82. data/lib/brainzlab/recall/buffer.rb +66 -0
  83. data/lib/brainzlab/recall/client.rb +158 -0
  84. data/lib/brainzlab/recall/logger.rb +116 -0
  85. data/lib/brainzlab/recall/provisioner.rb +130 -0
  86. data/lib/brainzlab/recall.rb +175 -0
  87. data/lib/brainzlab/reflex/breadcrumbs.rb +55 -0
  88. data/lib/brainzlab/reflex/client.rb +150 -0
  89. data/lib/brainzlab/reflex/provisioner.rb +116 -0
  90. data/lib/brainzlab/reflex.rb +421 -0
  91. data/lib/brainzlab/sentinel/client.rb +236 -0
  92. data/lib/brainzlab/sentinel/provisioner.rb +44 -0
  93. data/lib/brainzlab/sentinel.rb +165 -0
  94. data/lib/brainzlab/signal/client.rb +60 -0
  95. data/lib/brainzlab/signal/provisioner.rb +115 -0
  96. data/lib/brainzlab/signal.rb +136 -0
  97. data/lib/brainzlab/synapse/client.rb +308 -0
  98. data/lib/brainzlab/synapse/provisioner.rb +44 -0
  99. data/lib/brainzlab/synapse.rb +270 -0
  100. data/lib/brainzlab/testing/event_store.rb +377 -0
  101. data/lib/brainzlab/testing/helpers.rb +650 -0
  102. data/lib/brainzlab/testing/matchers.rb +391 -0
  103. data/lib/brainzlab/testing.rb +327 -0
  104. data/lib/brainzlab/utilities/circuit_breaker.rb +290 -0
  105. data/lib/brainzlab/utilities/health_check.rb +294 -0
  106. data/lib/brainzlab/utilities/log_formatter.rb +254 -0
  107. data/lib/brainzlab/utilities/rate_limiter.rb +230 -0
  108. data/lib/brainzlab/utilities.rb +17 -0
  109. data/lib/brainzlab/vault/cache.rb +80 -0
  110. data/lib/brainzlab/vault/client.rb +216 -0
  111. data/lib/brainzlab/vault/provisioner.rb +49 -0
  112. data/lib/brainzlab/vault.rb +262 -0
  113. data/lib/brainzlab/version.rb +5 -0
  114. data/lib/brainzlab/vision/client.rb +175 -0
  115. data/lib/brainzlab/vision/provisioner.rb +136 -0
  116. data/lib/brainzlab/vision.rb +155 -0
  117. data/lib/brainzlab-sdk.rb +3 -0
  118. data/lib/brainzlab.rb +306 -0
  119. data/lib/generators/brainzlab/install/install_generator.rb +63 -0
  120. data/lib/generators/brainzlab/install/templates/brainzlab.rb.tt +77 -0
  121. metadata +251 -0
@@ -0,0 +1,730 @@
1
+ # frozen_string_literal: true
2
+
3
+ module BrainzLab
4
+ module Instrumentation
5
+ class ActiveSupportCache
6
+ # Thresholds for slow cache operations (in milliseconds)
7
+ SLOW_CACHE_THRESHOLD = 10
8
+ VERY_SLOW_CACHE_THRESHOLD = 50
9
+
10
+ class << self
11
+ def install!
12
+ return unless defined?(::ActiveSupport::Cache)
13
+ return if @installed
14
+
15
+ install_cache_read_subscriber!
16
+ install_cache_read_multi_subscriber!
17
+ install_cache_write_subscriber!
18
+ install_cache_write_multi_subscriber!
19
+ install_cache_delete_subscriber!
20
+ install_cache_exist_subscriber!
21
+ install_cache_fetch_hit_subscriber!
22
+ install_cache_generate_subscriber!
23
+ install_cache_increment_subscriber!
24
+ install_cache_decrement_subscriber!
25
+ install_cache_delete_multi_subscriber!
26
+ install_cache_delete_matched_subscriber!
27
+ install_cache_cleanup_subscriber!
28
+ install_cache_prune_subscriber!
29
+ install_message_serializer_fallback_subscriber!
30
+
31
+ @installed = true
32
+ BrainzLab.debug_log('ActiveSupport::Cache instrumentation installed')
33
+ end
34
+
35
+ def installed?
36
+ @installed == true
37
+ end
38
+
39
+ private
40
+
41
+ # ============================================
42
+ # Cache Read
43
+ # ============================================
44
+ def install_cache_read_subscriber!
45
+ ActiveSupport::Notifications.subscribe('cache_read.active_support') do |*args|
46
+ BrainzLab.with_instrumentation_guard do
47
+ event = ActiveSupport::Notifications::Event.new(*args)
48
+ handle_cache_read(event)
49
+ end
50
+ end
51
+ end
52
+
53
+ def handle_cache_read(event)
54
+ payload = event.payload
55
+ duration = event.duration.round(2)
56
+
57
+ key = payload[:key]
58
+ hit = payload[:hit]
59
+ super_operation = payload[:super_operation]
60
+
61
+ # Skip if this is part of a fetch operation (will be tracked separately)
62
+ return if super_operation == :fetch
63
+
64
+ # Record breadcrumb
65
+ record_cache_breadcrumb('read', key, duration, hit: hit)
66
+
67
+ # Add Pulse span
68
+ record_cache_span(event, 'read', key, duration, hit: hit)
69
+
70
+ # Track cache metrics
71
+ track_cache_metrics('read', hit, duration)
72
+ rescue StandardError => e
73
+ BrainzLab.debug_log("ActiveSupport::Cache read instrumentation failed: #{e.message}")
74
+ end
75
+
76
+ # ============================================
77
+ # Cache Read Multi
78
+ # ============================================
79
+ def install_cache_read_multi_subscriber!
80
+ ActiveSupport::Notifications.subscribe('cache_read_multi.active_support') do |*args|
81
+ BrainzLab.with_instrumentation_guard do
82
+ event = ActiveSupport::Notifications::Event.new(*args)
83
+ handle_cache_read_multi(event)
84
+ end
85
+ end
86
+ end
87
+
88
+ def handle_cache_read_multi(event)
89
+ payload = event.payload
90
+ duration = event.duration.round(2)
91
+
92
+ key = payload[:key] # Array of keys
93
+ hits = payload[:hits] # Keys that were found
94
+ super_operation = payload[:super_operation]
95
+
96
+ return if super_operation == :fetch_multi
97
+
98
+ key_count = Array(key).size
99
+ hit_count = Array(hits).size
100
+
101
+ # Record breadcrumb
102
+ if BrainzLab.configuration.reflex_effectively_enabled?
103
+ BrainzLab::Reflex.add_breadcrumb(
104
+ "Cache read_multi: #{hit_count}/#{key_count} hits (#{duration}ms)",
105
+ category: 'cache.read_multi',
106
+ level: duration >= SLOW_CACHE_THRESHOLD ? :warning : :info,
107
+ data: {
108
+ key_count: key_count,
109
+ hit_count: hit_count,
110
+ miss_count: key_count - hit_count,
111
+ duration_ms: duration,
112
+ hit_rate: key_count > 0 ? (hit_count.to_f / key_count * 100).round(1) : 0
113
+ }
114
+ )
115
+ end
116
+
117
+ # Add Pulse span
118
+ record_cache_span(event, 'read_multi', "#{key_count} keys", duration,
119
+ key_count: key_count, hit_count: hit_count)
120
+ rescue StandardError => e
121
+ BrainzLab.debug_log("ActiveSupport::Cache read_multi instrumentation failed: #{e.message}")
122
+ end
123
+
124
+ # ============================================
125
+ # Cache Write
126
+ # ============================================
127
+ def install_cache_write_subscriber!
128
+ ActiveSupport::Notifications.subscribe('cache_write.active_support') do |*args|
129
+ BrainzLab.with_instrumentation_guard do
130
+ event = ActiveSupport::Notifications::Event.new(*args)
131
+ handle_cache_write(event)
132
+ end
133
+ end
134
+ end
135
+
136
+ def handle_cache_write(event)
137
+ payload = event.payload
138
+ duration = event.duration.round(2)
139
+
140
+ key = payload[:key]
141
+
142
+ # Record breadcrumb
143
+ record_cache_breadcrumb('write', key, duration)
144
+
145
+ # Add Pulse span
146
+ record_cache_span(event, 'write', key, duration)
147
+
148
+ # Log slow writes
149
+ log_slow_cache_operation('write', key, duration) if duration >= SLOW_CACHE_THRESHOLD
150
+ rescue StandardError => e
151
+ BrainzLab.debug_log("ActiveSupport::Cache write instrumentation failed: #{e.message}")
152
+ end
153
+
154
+ # ============================================
155
+ # Cache Write Multi
156
+ # ============================================
157
+ def install_cache_write_multi_subscriber!
158
+ ActiveSupport::Notifications.subscribe('cache_write_multi.active_support') do |*args|
159
+ BrainzLab.with_instrumentation_guard do
160
+ event = ActiveSupport::Notifications::Event.new(*args)
161
+ handle_cache_write_multi(event)
162
+ end
163
+ end
164
+ end
165
+
166
+ def handle_cache_write_multi(event)
167
+ payload = event.payload
168
+ duration = event.duration.round(2)
169
+
170
+ key = payload[:key] # Hash of key => value pairs
171
+ key_count = key.is_a?(Hash) ? key.size : 1
172
+
173
+ # Record breadcrumb
174
+ if BrainzLab.configuration.reflex_effectively_enabled?
175
+ BrainzLab::Reflex.add_breadcrumb(
176
+ "Cache write_multi: #{key_count} keys (#{duration}ms)",
177
+ category: 'cache.write_multi',
178
+ level: duration >= SLOW_CACHE_THRESHOLD ? :warning : :info,
179
+ data: {
180
+ key_count: key_count,
181
+ duration_ms: duration
182
+ }
183
+ )
184
+ end
185
+
186
+ # Add Pulse span
187
+ record_cache_span(event, 'write_multi', "#{key_count} keys", duration, key_count: key_count)
188
+ rescue StandardError => e
189
+ BrainzLab.debug_log("ActiveSupport::Cache write_multi instrumentation failed: #{e.message}")
190
+ end
191
+
192
+ # ============================================
193
+ # Cache Delete
194
+ # ============================================
195
+ def install_cache_delete_subscriber!
196
+ ActiveSupport::Notifications.subscribe('cache_delete.active_support') do |*args|
197
+ BrainzLab.with_instrumentation_guard do
198
+ event = ActiveSupport::Notifications::Event.new(*args)
199
+ handle_cache_delete(event)
200
+ end
201
+ end
202
+ end
203
+
204
+ def handle_cache_delete(event)
205
+ payload = event.payload
206
+ duration = event.duration.round(2)
207
+
208
+ key = payload[:key]
209
+
210
+ # Record breadcrumb
211
+ record_cache_breadcrumb('delete', key, duration)
212
+
213
+ # Add Pulse span
214
+ record_cache_span(event, 'delete', key, duration)
215
+ rescue StandardError => e
216
+ BrainzLab.debug_log("ActiveSupport::Cache delete instrumentation failed: #{e.message}")
217
+ end
218
+
219
+ # ============================================
220
+ # Cache Exist?
221
+ # ============================================
222
+ def install_cache_exist_subscriber!
223
+ ActiveSupport::Notifications.subscribe('cache_exist?.active_support') do |*args|
224
+ BrainzLab.with_instrumentation_guard do
225
+ event = ActiveSupport::Notifications::Event.new(*args)
226
+ handle_cache_exist(event)
227
+ end
228
+ end
229
+ end
230
+
231
+ def handle_cache_exist(event)
232
+ payload = event.payload
233
+ duration = event.duration.round(2)
234
+
235
+ key = payload[:key]
236
+
237
+ # Only track if slow or significant
238
+ return if duration < 1
239
+
240
+ # Add Pulse span (skip breadcrumb for exist? as it's noisy)
241
+ record_cache_span(event, 'exist', key, duration)
242
+ rescue StandardError => e
243
+ BrainzLab.debug_log("ActiveSupport::Cache exist? instrumentation failed: #{e.message}")
244
+ end
245
+
246
+ # ============================================
247
+ # Cache Fetch Hit (successful fetch from cache)
248
+ # ============================================
249
+ def install_cache_fetch_hit_subscriber!
250
+ ActiveSupport::Notifications.subscribe('cache_fetch_hit.active_support') do |*args|
251
+ BrainzLab.with_instrumentation_guard do
252
+ event = ActiveSupport::Notifications::Event.new(*args)
253
+ handle_cache_fetch_hit(event)
254
+ end
255
+ end
256
+ end
257
+
258
+ def handle_cache_fetch_hit(event)
259
+ payload = event.payload
260
+ duration = event.duration.round(2)
261
+
262
+ key = payload[:key]
263
+
264
+ # Record breadcrumb
265
+ record_cache_breadcrumb('fetch', key, duration, hit: true)
266
+
267
+ # Add Pulse span
268
+ record_cache_span(event, 'fetch', key, duration, hit: true)
269
+
270
+ # Track cache metrics
271
+ track_cache_metrics('fetch', true, duration)
272
+ rescue StandardError => e
273
+ BrainzLab.debug_log("ActiveSupport::Cache fetch_hit instrumentation failed: #{e.message}")
274
+ end
275
+
276
+ # ============================================
277
+ # Cache Generate (cache miss, value computed)
278
+ # ============================================
279
+ def install_cache_generate_subscriber!
280
+ ActiveSupport::Notifications.subscribe('cache_generate.active_support') do |*args|
281
+ BrainzLab.with_instrumentation_guard do
282
+ event = ActiveSupport::Notifications::Event.new(*args)
283
+ handle_cache_generate(event)
284
+ end
285
+ end
286
+ end
287
+
288
+ def handle_cache_generate(event)
289
+ payload = event.payload
290
+ duration = event.duration.round(2)
291
+
292
+ key = payload[:key]
293
+
294
+ # Record breadcrumb - this is a cache miss that triggered computation
295
+ if BrainzLab.configuration.reflex_effectively_enabled?
296
+ level = case duration
297
+ when 0...SLOW_CACHE_THRESHOLD then :info
298
+ when SLOW_CACHE_THRESHOLD...VERY_SLOW_CACHE_THRESHOLD then :warning
299
+ else :error
300
+ end
301
+
302
+ BrainzLab::Reflex.add_breadcrumb(
303
+ "Cache miss + generate: #{truncate_key(key)} (#{duration}ms)",
304
+ category: 'cache.generate',
305
+ level: level,
306
+ data: {
307
+ key: truncate_key(key),
308
+ duration_ms: duration,
309
+ hit: false
310
+ }
311
+ )
312
+ end
313
+
314
+ # Add Pulse span
315
+ record_cache_span(event, 'generate', key, duration, hit: false)
316
+
317
+ # Track cache metrics
318
+ track_cache_metrics('fetch', false, duration)
319
+
320
+ # Log slow cache generations
321
+ log_slow_cache_operation('generate', key, duration) if duration >= SLOW_CACHE_THRESHOLD
322
+ rescue StandardError => e
323
+ BrainzLab.debug_log("ActiveSupport::Cache generate instrumentation failed: #{e.message}")
324
+ end
325
+
326
+ # ============================================
327
+ # Cache Increment
328
+ # ============================================
329
+ def install_cache_increment_subscriber!
330
+ ActiveSupport::Notifications.subscribe('cache_increment.active_support') do |*args|
331
+ BrainzLab.with_instrumentation_guard do
332
+ event = ActiveSupport::Notifications::Event.new(*args)
333
+ handle_cache_increment(event)
334
+ end
335
+ end
336
+ end
337
+
338
+ def handle_cache_increment(event)
339
+ payload = event.payload
340
+ duration = event.duration.round(2)
341
+
342
+ key = payload[:key]
343
+ amount = payload[:amount]
344
+
345
+ # Record breadcrumb
346
+ if BrainzLab.configuration.reflex_effectively_enabled?
347
+ BrainzLab::Reflex.add_breadcrumb(
348
+ "Cache increment: #{truncate_key(key)} by #{amount} (#{duration}ms)",
349
+ category: 'cache.increment',
350
+ level: :info,
351
+ data: {
352
+ key: truncate_key(key),
353
+ amount: amount,
354
+ duration_ms: duration
355
+ }.compact
356
+ )
357
+ end
358
+
359
+ # Add Pulse span
360
+ record_cache_span(event, 'increment', key, duration, amount: amount)
361
+ rescue StandardError => e
362
+ BrainzLab.debug_log("ActiveSupport::Cache increment instrumentation failed: #{e.message}")
363
+ end
364
+
365
+ # ============================================
366
+ # Cache Decrement
367
+ # ============================================
368
+ def install_cache_decrement_subscriber!
369
+ ActiveSupport::Notifications.subscribe('cache_decrement.active_support') do |*args|
370
+ BrainzLab.with_instrumentation_guard do
371
+ event = ActiveSupport::Notifications::Event.new(*args)
372
+ handle_cache_decrement(event)
373
+ end
374
+ end
375
+ end
376
+
377
+ def handle_cache_decrement(event)
378
+ payload = event.payload
379
+ duration = event.duration.round(2)
380
+
381
+ key = payload[:key]
382
+ amount = payload[:amount]
383
+
384
+ # Record breadcrumb
385
+ if BrainzLab.configuration.reflex_effectively_enabled?
386
+ BrainzLab::Reflex.add_breadcrumb(
387
+ "Cache decrement: #{truncate_key(key)} by #{amount} (#{duration}ms)",
388
+ category: 'cache.decrement',
389
+ level: :info,
390
+ data: {
391
+ key: truncate_key(key),
392
+ amount: amount,
393
+ duration_ms: duration
394
+ }.compact
395
+ )
396
+ end
397
+
398
+ # Add Pulse span
399
+ record_cache_span(event, 'decrement', key, duration, amount: amount)
400
+ rescue StandardError => e
401
+ BrainzLab.debug_log("ActiveSupport::Cache decrement instrumentation failed: #{e.message}")
402
+ end
403
+
404
+ # ============================================
405
+ # Cache Delete Multi
406
+ # ============================================
407
+ def install_cache_delete_multi_subscriber!
408
+ ActiveSupport::Notifications.subscribe('cache_delete_multi.active_support') do |*args|
409
+ BrainzLab.with_instrumentation_guard do
410
+ event = ActiveSupport::Notifications::Event.new(*args)
411
+ handle_cache_delete_multi(event)
412
+ end
413
+ end
414
+ end
415
+
416
+ def handle_cache_delete_multi(event)
417
+ payload = event.payload
418
+ duration = event.duration.round(2)
419
+
420
+ key = payload[:key] # Array of keys
421
+ key_count = Array(key).size
422
+
423
+ # Record breadcrumb
424
+ if BrainzLab.configuration.reflex_effectively_enabled?
425
+ BrainzLab::Reflex.add_breadcrumb(
426
+ "Cache delete_multi: #{key_count} keys (#{duration}ms)",
427
+ category: 'cache.delete_multi',
428
+ level: :info,
429
+ data: {
430
+ key_count: key_count,
431
+ duration_ms: duration
432
+ }
433
+ )
434
+ end
435
+
436
+ # Add Pulse span
437
+ record_cache_span(event, 'delete_multi', "#{key_count} keys", duration, key_count: key_count)
438
+ rescue StandardError => e
439
+ BrainzLab.debug_log("ActiveSupport::Cache delete_multi instrumentation failed: #{e.message}")
440
+ end
441
+
442
+ # ============================================
443
+ # Cache Delete Matched (pattern-based delete)
444
+ # ============================================
445
+ def install_cache_delete_matched_subscriber!
446
+ ActiveSupport::Notifications.subscribe('cache_delete_matched.active_support') do |*args|
447
+ BrainzLab.with_instrumentation_guard do
448
+ event = ActiveSupport::Notifications::Event.new(*args)
449
+ handle_cache_delete_matched(event)
450
+ end
451
+ end
452
+ end
453
+
454
+ def handle_cache_delete_matched(event)
455
+ payload = event.payload
456
+ duration = event.duration.round(2)
457
+
458
+ key = payload[:key] # Pattern
459
+
460
+ # Record breadcrumb - pattern deletes are significant
461
+ if BrainzLab.configuration.reflex_effectively_enabled?
462
+ BrainzLab::Reflex.add_breadcrumb(
463
+ "Cache delete_matched: #{truncate_key(key)} (#{duration}ms)",
464
+ category: 'cache.delete_matched',
465
+ level: :warning,
466
+ data: {
467
+ pattern: truncate_key(key),
468
+ duration_ms: duration
469
+ }
470
+ )
471
+ end
472
+
473
+ # Add Pulse span
474
+ record_cache_span(event, 'delete_matched', key, duration)
475
+
476
+ # Log pattern deletes
477
+ if BrainzLab.configuration.recall_effectively_enabled?
478
+ BrainzLab::Recall.info(
479
+ "Cache pattern delete",
480
+ pattern: truncate_key(key),
481
+ duration_ms: duration
482
+ )
483
+ end
484
+ rescue StandardError => e
485
+ BrainzLab.debug_log("ActiveSupport::Cache delete_matched instrumentation failed: #{e.message}")
486
+ end
487
+
488
+ # ============================================
489
+ # Cache Cleanup (remove expired entries)
490
+ # ============================================
491
+ def install_cache_cleanup_subscriber!
492
+ ActiveSupport::Notifications.subscribe('cache_cleanup.active_support') do |*args|
493
+ BrainzLab.with_instrumentation_guard do
494
+ event = ActiveSupport::Notifications::Event.new(*args)
495
+ handle_cache_cleanup(event)
496
+ end
497
+ end
498
+ end
499
+
500
+ def handle_cache_cleanup(event)
501
+ payload = event.payload
502
+ duration = event.duration.round(2)
503
+
504
+ size = payload[:size]
505
+ key = payload[:key]
506
+
507
+ # Record breadcrumb
508
+ if BrainzLab.configuration.reflex_effectively_enabled?
509
+ BrainzLab::Reflex.add_breadcrumb(
510
+ "Cache cleanup: size=#{size} (#{duration}ms)",
511
+ category: 'cache.cleanup',
512
+ level: :info,
513
+ data: {
514
+ size: size,
515
+ duration_ms: duration
516
+ }.compact
517
+ )
518
+ end
519
+
520
+ # Add Pulse span
521
+ record_cache_span(event, 'cleanup', 'cleanup', duration, size: size)
522
+ rescue StandardError => e
523
+ BrainzLab.debug_log("ActiveSupport::Cache cleanup instrumentation failed: #{e.message}")
524
+ end
525
+
526
+ # ============================================
527
+ # Cache Prune (reduce cache size)
528
+ # ============================================
529
+ def install_cache_prune_subscriber!
530
+ ActiveSupport::Notifications.subscribe('cache_prune.active_support') do |*args|
531
+ BrainzLab.with_instrumentation_guard do
532
+ event = ActiveSupport::Notifications::Event.new(*args)
533
+ handle_cache_prune(event)
534
+ end
535
+ end
536
+ end
537
+
538
+ def handle_cache_prune(event)
539
+ payload = event.payload
540
+ duration = event.duration.round(2)
541
+
542
+ key = payload[:key]
543
+ from = payload[:from]
544
+ to = payload[:to]
545
+
546
+ # Record breadcrumb
547
+ if BrainzLab.configuration.reflex_effectively_enabled?
548
+ BrainzLab::Reflex.add_breadcrumb(
549
+ "Cache prune: #{from} -> #{to} (#{duration}ms)",
550
+ category: 'cache.prune',
551
+ level: :info,
552
+ data: {
553
+ from: from,
554
+ to: to,
555
+ duration_ms: duration
556
+ }.compact
557
+ )
558
+ end
559
+
560
+ # Add Pulse span
561
+ record_cache_span(event, 'prune', 'prune', duration, from: from, to: to)
562
+
563
+ # Log prune operations
564
+ if BrainzLab.configuration.recall_effectively_enabled?
565
+ BrainzLab::Recall.info(
566
+ "Cache pruned",
567
+ from: from,
568
+ to: to,
569
+ duration_ms: duration
570
+ )
571
+ end
572
+ rescue StandardError => e
573
+ BrainzLab.debug_log("ActiveSupport::Cache prune instrumentation failed: #{e.message}")
574
+ end
575
+
576
+ # ============================================
577
+ # Message Serializer Fallback
578
+ # Fired when a message is deserialized using a fallback serializer
579
+ # This typically indicates a migration between serialization formats
580
+ # ============================================
581
+ def install_message_serializer_fallback_subscriber!
582
+ ActiveSupport::Notifications.subscribe('message_serializer_fallback.active_support') do |*args|
583
+ BrainzLab.with_instrumentation_guard do
584
+ event = ActiveSupport::Notifications::Event.new(*args)
585
+ handle_message_serializer_fallback(event)
586
+ end
587
+ end
588
+ end
589
+
590
+ def handle_message_serializer_fallback(event)
591
+ payload = event.payload
592
+ duration = event.duration.round(2)
593
+
594
+ serializer = payload[:serializer]
595
+ fallback = payload[:fallback]
596
+ serialized = payload[:serialized]
597
+ deserialized = payload[:deserialized]
598
+
599
+ # Record breadcrumb - this is a warning as it indicates a format mismatch
600
+ if BrainzLab.configuration.reflex_effectively_enabled?
601
+ BrainzLab::Reflex.add_breadcrumb(
602
+ "Message serializer fallback: #{serializer} -> #{fallback}",
603
+ category: 'serializer.fallback',
604
+ level: :warning,
605
+ data: {
606
+ serializer: serializer.to_s,
607
+ fallback: fallback.to_s,
608
+ duration_ms: duration
609
+ }.compact
610
+ )
611
+ end
612
+
613
+ # Log to Recall - this is significant for debugging serialization issues
614
+ if BrainzLab.configuration.recall_effectively_enabled?
615
+ BrainzLab::Recall.warn(
616
+ "Message serializer fallback used",
617
+ serializer: serializer.to_s,
618
+ fallback: fallback.to_s,
619
+ duration_ms: duration
620
+ )
621
+ end
622
+ rescue StandardError => e
623
+ BrainzLab.debug_log("ActiveSupport message_serializer_fallback instrumentation failed: #{e.message}")
624
+ end
625
+
626
+ # ============================================
627
+ # Recording Helpers
628
+ # ============================================
629
+ def record_cache_breadcrumb(operation, key, duration, hit: nil)
630
+ return unless BrainzLab.configuration.reflex_effectively_enabled?
631
+
632
+ level = duration >= SLOW_CACHE_THRESHOLD ? :warning : :info
633
+
634
+ message = if hit.nil?
635
+ "Cache #{operation}: #{truncate_key(key)} (#{duration}ms)"
636
+ elsif hit
637
+ "Cache #{operation} hit: #{truncate_key(key)} (#{duration}ms)"
638
+ else
639
+ "Cache #{operation} miss: #{truncate_key(key)} (#{duration}ms)"
640
+ end
641
+
642
+ data = {
643
+ key: truncate_key(key),
644
+ operation: operation,
645
+ duration_ms: duration
646
+ }
647
+ data[:hit] = hit unless hit.nil?
648
+
649
+ BrainzLab::Reflex.add_breadcrumb(
650
+ message,
651
+ category: "cache.#{operation}",
652
+ level: level,
653
+ data: data.compact
654
+ )
655
+ end
656
+
657
+ def record_cache_span(event, operation, key, duration, hit: nil, **extra)
658
+ return unless BrainzLab.configuration.pulse_effectively_enabled?
659
+
660
+ tracer = BrainzLab::Pulse.tracer
661
+ return unless tracer.current_trace
662
+
663
+ span_data = {
664
+ span_id: SecureRandom.uuid,
665
+ name: "cache.#{operation}",
666
+ kind: 'cache',
667
+ started_at: event.time,
668
+ ended_at: event.end,
669
+ duration_ms: duration,
670
+ error: false,
671
+ data: {
672
+ 'cache.operation' => operation,
673
+ 'cache.key' => truncate_key(key),
674
+ 'cache.hit' => hit
675
+ }.merge(extra.transform_keys { |k| "cache.#{k}" }).compact
676
+ }
677
+
678
+ tracer.current_spans << span_data
679
+ end
680
+
681
+ def track_cache_metrics(operation, hit, duration)
682
+ return unless BrainzLab.configuration.pulse_effectively_enabled?
683
+
684
+ # Increment cache operation counter
685
+ BrainzLab::Pulse.counter(
686
+ "cache.#{operation}.total",
687
+ 1,
688
+ tags: { hit: hit.to_s }
689
+ )
690
+
691
+ # Record cache operation duration
692
+ BrainzLab::Pulse.histogram(
693
+ "cache.#{operation}.duration_ms",
694
+ duration,
695
+ tags: { hit: hit.to_s }
696
+ )
697
+ end
698
+
699
+ def log_slow_cache_operation(operation, key, duration)
700
+ return unless BrainzLab.configuration.recall_effectively_enabled?
701
+
702
+ level = duration >= VERY_SLOW_CACHE_THRESHOLD ? :error : :warn
703
+
704
+ BrainzLab::Recall.send(
705
+ level,
706
+ "Slow cache #{operation}: #{truncate_key(key)} (#{duration}ms)",
707
+ operation: operation,
708
+ key: truncate_key(key),
709
+ duration_ms: duration,
710
+ threshold_exceeded: duration >= VERY_SLOW_CACHE_THRESHOLD ? 'critical' : 'warning'
711
+ )
712
+ end
713
+
714
+ # ============================================
715
+ # Helper Methods
716
+ # ============================================
717
+ def truncate_key(key, max_length = 100)
718
+ return 'unknown' unless key
719
+
720
+ key_str = key.to_s
721
+ if key_str.length > max_length
722
+ "#{key_str[0, max_length - 3]}..."
723
+ else
724
+ key_str
725
+ end
726
+ end
727
+ end
728
+ end
729
+ end
730
+ end