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,134 @@
1
+ # frozen_string_literal: true
2
+
3
+ module BrainzLab
4
+ module Instrumentation
5
+ class Railties
6
+ # Thresholds for slow initializers (in milliseconds)
7
+ SLOW_INITIALIZER_THRESHOLD = 100 # 100ms
8
+ VERY_SLOW_INITIALIZER_THRESHOLD = 500 # 500ms
9
+
10
+ class << self
11
+ def install!
12
+ return unless defined?(::Rails)
13
+ return if @installed
14
+
15
+ install_load_config_initializer_subscriber!
16
+
17
+ @installed = true
18
+ BrainzLab.debug_log('Railties instrumentation installed')
19
+ end
20
+
21
+ def installed?
22
+ @installed == true
23
+ end
24
+
25
+ private
26
+
27
+ # ============================================
28
+ # load_config_initializer.railties
29
+ # Fired when each config initializer is loaded
30
+ # ============================================
31
+ def install_load_config_initializer_subscriber!
32
+ ActiveSupport::Notifications.subscribe('load_config_initializer.railties') do |*args|
33
+ event = ActiveSupport::Notifications::Event.new(*args)
34
+ handle_load_config_initializer(event)
35
+ end
36
+ end
37
+
38
+ def handle_load_config_initializer(event)
39
+ payload = event.payload
40
+ duration = event.duration.round(2)
41
+
42
+ initializer = payload[:initializer]
43
+
44
+ # Extract just the filename for cleaner display
45
+ initializer_name = extract_initializer_name(initializer)
46
+
47
+ # Determine level based on duration
48
+ level = case duration
49
+ when 0...SLOW_INITIALIZER_THRESHOLD then :info
50
+ when SLOW_INITIALIZER_THRESHOLD...VERY_SLOW_INITIALIZER_THRESHOLD then :warning
51
+ else :error
52
+ end
53
+
54
+ # Record breadcrumb (only for slow initializers to reduce noise during boot)
55
+ if duration >= 10 && BrainzLab.configuration.reflex_effectively_enabled?
56
+ BrainzLab::Reflex.add_breadcrumb(
57
+ "Initializer loaded: #{initializer_name} (#{duration}ms)",
58
+ category: 'rails.initializer',
59
+ level: level,
60
+ data: {
61
+ initializer: initializer_name,
62
+ path: initializer,
63
+ duration_ms: duration
64
+ }.compact
65
+ )
66
+ end
67
+
68
+ # Add Pulse span for tracking boot time
69
+ record_initializer_span(event, initializer_name, initializer, duration)
70
+
71
+ # Log slow initializers
72
+ log_slow_initializer(initializer_name, initializer, duration) if duration >= SLOW_INITIALIZER_THRESHOLD
73
+ rescue StandardError => e
74
+ BrainzLab.debug_log("Railties load_config_initializer instrumentation failed: #{e.message}")
75
+ end
76
+
77
+ # ============================================
78
+ # Span Recording
79
+ # ============================================
80
+ def record_initializer_span(event, name, path, duration)
81
+ return unless BrainzLab.configuration.pulse_effectively_enabled?
82
+
83
+ tracer = BrainzLab::Pulse.tracer
84
+ return unless tracer.current_trace
85
+
86
+ span_data = {
87
+ span_id: SecureRandom.uuid,
88
+ name: "rails.initializer.#{name}",
89
+ kind: 'internal',
90
+ started_at: event.time,
91
+ ended_at: event.end,
92
+ duration_ms: duration,
93
+ error: false,
94
+ data: {
95
+ 'rails.initializer.name' => name,
96
+ 'rails.initializer.path' => path
97
+ }.compact
98
+ }
99
+
100
+ tracer.current_spans << span_data
101
+ end
102
+
103
+ # ============================================
104
+ # Logging
105
+ # ============================================
106
+ def log_slow_initializer(name, path, duration)
107
+ return unless BrainzLab.configuration.recall_effectively_enabled?
108
+
109
+ level = duration >= VERY_SLOW_INITIALIZER_THRESHOLD ? :error : :warn
110
+
111
+ BrainzLab::Recall.send(
112
+ level,
113
+ "Slow initializer: #{name} (#{duration}ms)",
114
+ initializer: name,
115
+ path: path,
116
+ duration_ms: duration,
117
+ threshold_exceeded: duration >= VERY_SLOW_INITIALIZER_THRESHOLD ? 'critical' : 'warning'
118
+ )
119
+ end
120
+
121
+ # ============================================
122
+ # Helper Methods
123
+ # ============================================
124
+ def extract_initializer_name(path)
125
+ return 'unknown' unless path
126
+
127
+ # Extract filename without extension from path
128
+ # e.g., "/app/config/initializers/devise.rb" -> "devise"
129
+ File.basename(path.to_s, '.*')
130
+ end
131
+ end
132
+ end
133
+ end
134
+ end
@@ -0,0 +1,324 @@
1
+ # frozen_string_literal: true
2
+
3
+ module BrainzLab
4
+ module Instrumentation
5
+ module RedisInstrumentation
6
+ @installed = false
7
+
8
+ class << self
9
+ def install!
10
+ return unless defined?(::Redis)
11
+ return if @installed
12
+
13
+ # Redis 5+ uses middleware, older versions need patching
14
+ if redis_5_or_newer?
15
+ install_middleware!
16
+ else
17
+ install_patch!
18
+ end
19
+
20
+ @installed = true
21
+ BrainzLab.debug_log('Redis instrumentation installed')
22
+ end
23
+
24
+ def installed?
25
+ @installed
26
+ end
27
+
28
+ def reset!
29
+ @installed = false
30
+ end
31
+
32
+ private
33
+
34
+ def redis_5_or_newer?
35
+ defined?(::Redis::VERSION) && Gem::Version.new(::Redis::VERSION) >= Gem::Version.new('5.0')
36
+ end
37
+
38
+ def install_middleware!
39
+ # Redis 5+ uses RedisClient with middleware support
40
+ return unless defined?(::RedisClient)
41
+
42
+ ::RedisClient.register(Middleware)
43
+ end
44
+
45
+ def install_patch!
46
+ # Redis < 5 - patch the client
47
+ ::Redis::Client.prepend(LegacyPatch)
48
+ end
49
+ end
50
+
51
+ # Middleware for Redis 5+ (RedisClient)
52
+ module Middleware
53
+ def call(command, redis_config)
54
+ return super unless should_track?
55
+
56
+ track_command(command) { super }
57
+ end
58
+
59
+ def call_pipelined(commands, redis_config)
60
+ return super unless should_track?
61
+
62
+ track_pipeline(commands) { super }
63
+ end
64
+
65
+ private
66
+
67
+ def should_track?
68
+ BrainzLab.configuration.instrument_redis
69
+ end
70
+
71
+ def should_skip_command?(command)
72
+ cmd_name = command.first.to_s.downcase
73
+ ignore = BrainzLab.configuration.redis_ignore_commands || []
74
+ ignore.map(&:downcase).include?(cmd_name)
75
+ end
76
+
77
+ def track_command(command)
78
+ return yield if should_skip_command?(command)
79
+
80
+ started_at = Process.clock_gettime(Process::CLOCK_MONOTONIC)
81
+
82
+ begin
83
+ result = yield
84
+ record_command(command, started_at)
85
+ result
86
+ rescue StandardError => e
87
+ error_info = e.class.name
88
+ record_command(command, started_at, error_info)
89
+ raise
90
+ end
91
+ end
92
+
93
+ def track_pipeline(commands)
94
+ started_at = Process.clock_gettime(Process::CLOCK_MONOTONIC)
95
+
96
+ begin
97
+ result = yield
98
+ record_pipeline(commands, started_at)
99
+ result
100
+ rescue StandardError => e
101
+ error_info = e.class.name
102
+ record_pipeline(commands, started_at, error_info)
103
+ raise
104
+ end
105
+ end
106
+
107
+ def record_command(command, started_at, error = nil)
108
+ duration_ms = ((Process.clock_gettime(Process::CLOCK_MONOTONIC) - started_at) * 1000).round(2)
109
+ cmd_name = command.first.to_s.upcase
110
+ key = extract_key(command)
111
+ level = error ? :error : :info
112
+
113
+ # Add breadcrumb for Reflex
114
+ if BrainzLab.configuration.reflex_enabled
115
+ BrainzLab::Reflex.add_breadcrumb(
116
+ "Redis #{cmd_name}",
117
+ category: 'redis',
118
+ level: level,
119
+ data: {
120
+ command: cmd_name,
121
+ key: truncate_key(key),
122
+ duration_ms: duration_ms,
123
+ error: error
124
+ }.compact
125
+ )
126
+ end
127
+
128
+ # Record span for Pulse APM
129
+ record_pulse_span(cmd_name, key, duration_ms, error)
130
+ rescue StandardError => e
131
+ BrainzLab.debug_log("Redis instrumentation error: #{e.message}")
132
+ end
133
+
134
+ def record_pipeline(commands, started_at, error = nil)
135
+ duration_ms = ((Process.clock_gettime(Process::CLOCK_MONOTONIC) - started_at) * 1000).round(2)
136
+ cmd_names = commands.map { |c| c.first.to_s.upcase }.uniq.join(', ')
137
+ level = error ? :error : :info
138
+
139
+ # Add breadcrumb for Reflex
140
+ if BrainzLab.configuration.reflex_enabled
141
+ BrainzLab::Reflex.add_breadcrumb(
142
+ "Redis PIPELINE (#{commands.size} commands)",
143
+ category: 'redis',
144
+ level: level,
145
+ data: {
146
+ commands: cmd_names,
147
+ count: commands.size,
148
+ duration_ms: duration_ms,
149
+ error: error
150
+ }.compact
151
+ )
152
+ end
153
+
154
+ # Record span for Pulse APM
155
+ record_pulse_span('PIPELINE', nil, duration_ms, error, commands.size)
156
+ rescue StandardError => e
157
+ BrainzLab.debug_log("Redis instrumentation error: #{e.message}")
158
+ end
159
+
160
+ def record_pulse_span(command, key, duration_ms, error, pipeline_count = nil)
161
+ spans = Thread.current[:brainzlab_pulse_spans]
162
+ return unless spans
163
+
164
+ span = {
165
+ span_id: SecureRandom.uuid,
166
+ name: "Redis #{command}",
167
+ kind: 'redis',
168
+ started_at: Time.now.utc - (duration_ms / 1000.0),
169
+ ended_at: Time.now.utc,
170
+ duration_ms: duration_ms,
171
+ data: {
172
+ command: command,
173
+ key: truncate_key(key),
174
+ pipeline_count: pipeline_count
175
+ }.compact
176
+ }
177
+
178
+ if error
179
+ span[:error] = true
180
+ span[:error_class] = error
181
+ end
182
+
183
+ spans << span
184
+ end
185
+
186
+ def extract_key(command)
187
+ return nil if command.size < 2
188
+
189
+ # Most Redis commands have the key as the second argument
190
+ key = command[1]
191
+ key.is_a?(String) ? key : key.to_s
192
+ end
193
+
194
+ def truncate_key(key)
195
+ return nil unless key
196
+
197
+ key.to_s[0, 100]
198
+ end
199
+ end
200
+
201
+ # Patch for Redis < 5
202
+ module LegacyPatch
203
+ def call(command)
204
+ return super unless should_track?
205
+ return super if should_skip_command?(command)
206
+
207
+ started_at = Process.clock_gettime(Process::CLOCK_MONOTONIC)
208
+
209
+ begin
210
+ result = super
211
+ record_command(command, started_at)
212
+ result
213
+ rescue StandardError => e
214
+ error_info = e.class.name
215
+ record_command(command, started_at, error_info)
216
+ raise
217
+ end
218
+ end
219
+
220
+ def call_pipeline(pipeline)
221
+ return super unless should_track?
222
+
223
+ started_at = Process.clock_gettime(Process::CLOCK_MONOTONIC)
224
+ commands = pipeline.commands
225
+
226
+ begin
227
+ result = super
228
+ record_pipeline(commands, started_at)
229
+ result
230
+ rescue StandardError => e
231
+ error_info = e.class.name
232
+ record_pipeline(commands, started_at, error_info)
233
+ raise
234
+ end
235
+ end
236
+
237
+ private
238
+
239
+ def should_track?
240
+ BrainzLab.configuration.instrument_redis
241
+ end
242
+
243
+ def should_skip_command?(command)
244
+ cmd_name = command.first.to_s.downcase
245
+ ignore = BrainzLab.configuration.redis_ignore_commands || []
246
+ ignore.map(&:downcase).include?(cmd_name)
247
+ end
248
+
249
+ def record_command(command, started_at, error = nil)
250
+ duration_ms = ((Process.clock_gettime(Process::CLOCK_MONOTONIC) - started_at) * 1000).round(2)
251
+ cmd_name = command.first.to_s.upcase
252
+ key = command[1]&.to_s
253
+ level = error ? :error : :info
254
+
255
+ if BrainzLab.configuration.reflex_enabled
256
+ BrainzLab::Reflex.add_breadcrumb(
257
+ "Redis #{cmd_name}",
258
+ category: 'redis',
259
+ level: level,
260
+ data: {
261
+ command: cmd_name,
262
+ key: key&.slice(0, 100),
263
+ duration_ms: duration_ms,
264
+ error: error
265
+ }.compact
266
+ )
267
+ end
268
+
269
+ record_pulse_span(cmd_name, key, duration_ms, error)
270
+ rescue StandardError => e
271
+ BrainzLab.debug_log("Redis instrumentation error: #{e.message}")
272
+ end
273
+
274
+ def record_pipeline(commands, started_at, error = nil)
275
+ duration_ms = ((Process.clock_gettime(Process::CLOCK_MONOTONIC) - started_at) * 1000).round(2)
276
+ level = error ? :error : :info
277
+
278
+ if BrainzLab.configuration.reflex_enabled
279
+ BrainzLab::Reflex.add_breadcrumb(
280
+ "Redis PIPELINE (#{commands.size} commands)",
281
+ category: 'redis',
282
+ level: level,
283
+ data: {
284
+ count: commands.size,
285
+ duration_ms: duration_ms,
286
+ error: error
287
+ }.compact
288
+ )
289
+ end
290
+
291
+ record_pulse_span('PIPELINE', nil, duration_ms, error, commands.size)
292
+ rescue StandardError => e
293
+ BrainzLab.debug_log("Redis instrumentation error: #{e.message}")
294
+ end
295
+
296
+ def record_pulse_span(command, key, duration_ms, error, pipeline_count = nil)
297
+ spans = Thread.current[:brainzlab_pulse_spans]
298
+ return unless spans
299
+
300
+ span = {
301
+ span_id: SecureRandom.uuid,
302
+ name: "Redis #{command}",
303
+ kind: 'redis',
304
+ started_at: Time.now.utc - (duration_ms / 1000.0),
305
+ ended_at: Time.now.utc,
306
+ duration_ms: duration_ms,
307
+ data: {
308
+ command: command,
309
+ key: key&.slice(0, 100),
310
+ pipeline_count: pipeline_count
311
+ }.compact
312
+ }
313
+
314
+ if error
315
+ span[:error] = true
316
+ span[:error_class] = error
317
+ end
318
+
319
+ spans << span
320
+ end
321
+ end
322
+ end
323
+ end
324
+ end
@@ -0,0 +1,114 @@
1
+ # frozen_string_literal: true
2
+
3
+ module BrainzLab
4
+ module Instrumentation
5
+ module ResqueInstrumentation
6
+ class << self
7
+ def install!
8
+ return unless defined?(::Resque)
9
+
10
+ install_hooks!
11
+ install_failure_backend!
12
+
13
+ BrainzLab.debug_log('[Instrumentation] Resque instrumentation installed')
14
+ end
15
+
16
+ private
17
+
18
+ def install_hooks!
19
+ ::Resque.before_fork do |_job|
20
+ # Clear any stale connections before forking
21
+ BrainzLab::Recall.reset! if defined?(BrainzLab::Recall)
22
+ BrainzLab::Pulse.reset! if defined?(BrainzLab::Pulse)
23
+ end
24
+
25
+ ::Resque.after_fork do |job|
26
+ # Re-establish connections after forking
27
+ end
28
+ end
29
+
30
+ def install_failure_backend!
31
+ # Create a custom failure backend
32
+ failure_backend = Class.new do
33
+ def initialize(exception, worker, queue, payload)
34
+ @exception = exception
35
+ @worker = worker
36
+ @queue = queue
37
+ @payload = payload
38
+ end
39
+
40
+ def save
41
+ job_class = @payload['class'] || 'Unknown'
42
+
43
+ if BrainzLab.configuration.reflex_effectively_enabled?
44
+ BrainzLab::Reflex.capture(@exception,
45
+ tags: { job_class: job_class, queue: @queue, source: 'resque' },
46
+ extra: {
47
+ worker: @worker.to_s,
48
+ args: @payload['args']
49
+ })
50
+ end
51
+
52
+ return unless BrainzLab.configuration.flux_effectively_enabled?
53
+
54
+ BrainzLab::Flux.increment('resque.job.failed', tags: { job_class: job_class, queue: @queue })
55
+ end
56
+ end
57
+
58
+ # Add our failure backend to the chain
59
+ return unless defined?(::Resque::Failure)
60
+
61
+ ::Resque::Failure.backend = ::Resque::Failure::Multiple.new(
62
+ ::Resque::Failure.backend,
63
+ failure_backend
64
+ )
65
+ end
66
+ end
67
+
68
+ # Middleware module to include in Resque jobs
69
+ module Middleware
70
+ def self.included(base)
71
+ base.extend(ClassMethods)
72
+ end
73
+
74
+ module ClassMethods
75
+ def around_perform_brainzlab(*_args)
76
+ job_class = name
77
+ queue = Resque.queue_from_class(self) || 'default'
78
+ started_at = Time.now
79
+
80
+ BrainzLab::Context.current.set_context(
81
+ job_class: job_class,
82
+ queue: queue
83
+ )
84
+
85
+ begin
86
+ yield
87
+ ensure
88
+ duration_ms = ((Time.now - started_at) * 1000).round(2)
89
+
90
+ if BrainzLab.configuration.pulse_effectively_enabled?
91
+ BrainzLab::Pulse.record_trace(
92
+ "job.#{job_class}",
93
+ kind: 'job',
94
+ started_at: started_at,
95
+ ended_at: Time.now,
96
+ job_class: job_class,
97
+ queue: queue
98
+ )
99
+ end
100
+
101
+ if BrainzLab.configuration.flux_effectively_enabled?
102
+ tags = { job_class: job_class, queue: queue }
103
+ BrainzLab::Flux.distribution('resque.job.duration_ms', duration_ms, tags: tags)
104
+ BrainzLab::Flux.increment('resque.job.processed', tags: tags)
105
+ end
106
+
107
+ BrainzLab.clear_context!
108
+ end
109
+ end
110
+ end
111
+ end
112
+ end
113
+ end
114
+ end