brainzlab 0.1.2 → 0.1.3

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 (94) hide show
  1. checksums.yaml +4 -4
  2. data/LICENSE +6 -21
  3. data/README.md +16 -2
  4. data/lib/brainzlab/beacon/client.rb +38 -40
  5. data/lib/brainzlab/beacon/provisioner.rb +1 -1
  6. data/lib/brainzlab/beacon.rb +15 -15
  7. data/lib/brainzlab/configuration.rb +92 -90
  8. data/lib/brainzlab/context.rb +2 -3
  9. data/lib/brainzlab/cortex/client.rb +29 -31
  10. data/lib/brainzlab/cortex/provisioner.rb +1 -1
  11. data/lib/brainzlab/cortex.rb +7 -11
  12. data/lib/brainzlab/dendrite/client.rb +42 -44
  13. data/lib/brainzlab/dendrite/provisioner.rb +1 -1
  14. data/lib/brainzlab/dendrite.rb +4 -4
  15. data/lib/brainzlab/devtools/data/collector.rb +22 -22
  16. data/lib/brainzlab/devtools/middleware/asset_server.rb +14 -14
  17. data/lib/brainzlab/devtools/middleware/database_handler.rb +52 -55
  18. data/lib/brainzlab/devtools/middleware/debug_panel.rb +19 -19
  19. data/lib/brainzlab/devtools/middleware/error_page.rb +45 -44
  20. data/lib/brainzlab/devtools/renderers/debug_panel_renderer.rb +39 -35
  21. data/lib/brainzlab/devtools/renderers/error_page_renderer.rb +13 -9
  22. data/lib/brainzlab/devtools.rb +11 -11
  23. data/lib/brainzlab/flux/buffer.rb +3 -3
  24. data/lib/brainzlab/flux/client.rb +14 -16
  25. data/lib/brainzlab/flux/provisioner.rb +13 -13
  26. data/lib/brainzlab/flux.rb +8 -8
  27. data/lib/brainzlab/instrumentation/action_mailer.rb +14 -13
  28. data/lib/brainzlab/instrumentation/active_record.rb +13 -15
  29. data/lib/brainzlab/instrumentation/aws.rb +43 -39
  30. data/lib/brainzlab/instrumentation/dalli.rb +20 -20
  31. data/lib/brainzlab/instrumentation/delayed_job.rb +27 -29
  32. data/lib/brainzlab/instrumentation/elasticsearch.rb +23 -24
  33. data/lib/brainzlab/instrumentation/excon.rb +27 -27
  34. data/lib/brainzlab/instrumentation/faraday.rb +3 -4
  35. data/lib/brainzlab/instrumentation/good_job.rb +28 -28
  36. data/lib/brainzlab/instrumentation/grape.rb +24 -24
  37. data/lib/brainzlab/instrumentation/graphql.rb +24 -23
  38. data/lib/brainzlab/instrumentation/httparty.rb +13 -14
  39. data/lib/brainzlab/instrumentation/mongodb.rb +7 -7
  40. data/lib/brainzlab/instrumentation/net_http.rb +6 -6
  41. data/lib/brainzlab/instrumentation/redis.rb +14 -21
  42. data/lib/brainzlab/instrumentation/resque.rb +23 -24
  43. data/lib/brainzlab/instrumentation/sidekiq.rb +29 -28
  44. data/lib/brainzlab/instrumentation/solid_queue.rb +37 -41
  45. data/lib/brainzlab/instrumentation/stripe.rb +36 -37
  46. data/lib/brainzlab/instrumentation/typhoeus.rb +19 -17
  47. data/lib/brainzlab/instrumentation.rb +20 -20
  48. data/lib/brainzlab/nerve/client.rb +38 -40
  49. data/lib/brainzlab/nerve/provisioner.rb +1 -1
  50. data/lib/brainzlab/nerve.rb +6 -6
  51. data/lib/brainzlab/pulse/client.rb +15 -11
  52. data/lib/brainzlab/pulse/instrumentation.rb +61 -57
  53. data/lib/brainzlab/pulse/propagation.rb +28 -28
  54. data/lib/brainzlab/pulse/provisioner.rb +12 -12
  55. data/lib/brainzlab/pulse/tracer.rb +3 -3
  56. data/lib/brainzlab/pulse.rb +13 -13
  57. data/lib/brainzlab/rails/log_formatter.rb +127 -121
  58. data/lib/brainzlab/rails/log_subscriber.rb +70 -76
  59. data/lib/brainzlab/rails/railtie.rb +66 -89
  60. data/lib/brainzlab/recall/buffer.rb +1 -1
  61. data/lib/brainzlab/recall/client.rb +14 -10
  62. data/lib/brainzlab/recall/logger.rb +16 -18
  63. data/lib/brainzlab/recall/provisioner.rb +16 -16
  64. data/lib/brainzlab/recall.rb +11 -13
  65. data/lib/brainzlab/reflex/breadcrumbs.rb +2 -2
  66. data/lib/brainzlab/reflex/client.rb +14 -10
  67. data/lib/brainzlab/reflex/provisioner.rb +12 -12
  68. data/lib/brainzlab/reflex.rb +29 -29
  69. data/lib/brainzlab/sentinel/client.rb +40 -42
  70. data/lib/brainzlab/sentinel/provisioner.rb +1 -1
  71. data/lib/brainzlab/sentinel.rb +5 -5
  72. data/lib/brainzlab/signal/client.rb +12 -14
  73. data/lib/brainzlab/signal/provisioner.rb +12 -12
  74. data/lib/brainzlab/signal.rb +7 -7
  75. data/lib/brainzlab/synapse/client.rb +42 -44
  76. data/lib/brainzlab/synapse/provisioner.rb +1 -1
  77. data/lib/brainzlab/synapse.rb +6 -6
  78. data/lib/brainzlab/utilities/circuit_breaker.rb +37 -41
  79. data/lib/brainzlab/utilities/health_check.rb +53 -55
  80. data/lib/brainzlab/utilities/log_formatter.rb +38 -40
  81. data/lib/brainzlab/utilities/rate_limiter.rb +5 -5
  82. data/lib/brainzlab/utilities.rb +4 -4
  83. data/lib/brainzlab/vault/cache.rb +1 -1
  84. data/lib/brainzlab/vault/client.rb +39 -41
  85. data/lib/brainzlab/vault/provisioner.rb +1 -1
  86. data/lib/brainzlab/vault.rb +19 -25
  87. data/lib/brainzlab/version.rb +1 -1
  88. data/lib/brainzlab/vision/client.rb +20 -20
  89. data/lib/brainzlab/vision/provisioner.rb +21 -21
  90. data/lib/brainzlab/vision.rb +17 -19
  91. data/lib/brainzlab-sdk.rb +1 -1
  92. data/lib/brainzlab.rb +22 -24
  93. data/lib/generators/brainzlab/install/install_generator.rb +29 -27
  94. metadata +1 -1
@@ -11,17 +11,13 @@ module BrainzLab
11
11
  return if @installed
12
12
 
13
13
  # Install lifecycle hooks
14
- if defined?(::Delayed::Worker)
15
- install_lifecycle_hooks!
16
- end
14
+ install_lifecycle_hooks! if defined?(::Delayed::Worker)
17
15
 
18
16
  # Install plugin if Delayed::Plugin is available
19
- if defined?(::Delayed::Plugin)
20
- ::Delayed::Worker.plugins << Plugin
21
- end
17
+ ::Delayed::Worker.plugins << Plugin if defined?(::Delayed::Plugin)
22
18
 
23
19
  @installed = true
24
- BrainzLab.debug_log("Delayed::Job instrumentation installed")
20
+ BrainzLab.debug_log('Delayed::Job instrumentation installed')
25
21
  end
26
22
 
27
23
  def installed?
@@ -35,15 +31,15 @@ module BrainzLab
35
31
  private
36
32
 
37
33
  def install_lifecycle_hooks!
38
- ::Delayed::Worker.lifecycle.around(:invoke_job) do |job, *args, &block|
34
+ ::Delayed::Worker.lifecycle.around(:invoke_job) do |job, *_args, &block|
39
35
  around_invoke(job, &block)
40
36
  end
41
37
 
42
- ::Delayed::Worker.lifecycle.after(:error) do |worker, job|
38
+ ::Delayed::Worker.lifecycle.after(:error) do |_worker, job|
43
39
  record_error(job)
44
40
  end
45
41
 
46
- ::Delayed::Worker.lifecycle.after(:failure) do |worker, job|
42
+ ::Delayed::Worker.lifecycle.after(:failure) do |_worker, job|
47
43
  record_failure(job)
48
44
  end
49
45
  rescue StandardError => e
@@ -53,7 +49,7 @@ module BrainzLab
53
49
  def around_invoke(job, &block)
54
50
  started_at = Time.now.utc
55
51
  job_name = extract_job_name(job)
56
- queue = job.queue || "default"
52
+ queue = job.queue || 'default'
57
53
 
58
54
  # Calculate queue wait time
59
55
  queue_wait_ms = job.created_at ? ((started_at - job.created_at) * 1000).round(2) : nil
@@ -64,7 +60,7 @@ module BrainzLab
64
60
  # Add breadcrumb
65
61
  BrainzLab::Reflex.add_breadcrumb(
66
62
  "DelayedJob #{job_name}",
67
- category: "job.delayed_job",
63
+ category: 'job.delayed_job',
68
64
  level: :info,
69
65
  data: { job_id: job.id, queue: queue, attempts: job.attempts }
70
66
  )
@@ -133,7 +129,7 @@ module BrainzLab
133
129
  payload = {
134
130
  trace_id: SecureRandom.uuid,
135
131
  name: job_name,
136
- kind: "job",
132
+ kind: 'job',
137
133
  started_at: started_at.utc.iso8601(3),
138
134
  ended_at: ended_at.utc.iso8601(3),
139
135
  duration_ms: duration_ms,
@@ -162,7 +158,7 @@ module BrainzLab
162
158
 
163
159
  BrainzLab::Reflex.add_breadcrumb(
164
160
  "DelayedJob error: #{extract_job_name(job)}",
165
- category: "job.delayed_job.error",
161
+ category: 'job.delayed_job.error',
166
162
  level: :error,
167
163
  data: {
168
164
  job_id: job.id,
@@ -177,7 +173,7 @@ module BrainzLab
177
173
  def record_failure(job)
178
174
  BrainzLab::Reflex.add_breadcrumb(
179
175
  "DelayedJob failed permanently: #{extract_job_name(job)}",
180
- category: "job.delayed_job.failure",
176
+ category: 'job.delayed_job.failure',
181
177
  level: :error,
182
178
  data: {
183
179
  job_id: job.id,
@@ -200,7 +196,7 @@ module BrainzLab
200
196
  payload.class.name
201
197
  end
202
198
  rescue StandardError
203
- job.name || "Unknown"
199
+ job.name || 'Unknown'
204
200
  end
205
201
 
206
202
  def format_timestamp(ts)
@@ -216,21 +212,23 @@ module BrainzLab
216
212
  end
217
213
 
218
214
  # Delayed::Job Plugin (alternative installation method)
219
- class Plugin < ::Delayed::Plugin
220
- callbacks do |lifecycle|
221
- lifecycle.around(:invoke_job) do |job, *args, &block|
222
- DelayedJobInstrumentation.send(:around_invoke, job, &block)
223
- end
224
-
225
- lifecycle.after(:error) do |worker, job|
226
- DelayedJobInstrumentation.send(:record_error, job)
227
- end
228
-
229
- lifecycle.after(:failure) do |worker, job|
230
- DelayedJobInstrumentation.send(:record_failure, job)
215
+ if defined?(::Delayed::Plugin)
216
+ class Plugin < ::Delayed::Plugin
217
+ callbacks do |lifecycle|
218
+ lifecycle.around(:invoke_job) do |job, *_args, &block|
219
+ DelayedJobInstrumentation.send(:around_invoke, job, &block)
220
+ end
221
+
222
+ lifecycle.after(:error) do |_worker, job|
223
+ DelayedJobInstrumentation.send(:record_error, job)
224
+ end
225
+
226
+ lifecycle.after(:failure) do |_worker, job|
227
+ DelayedJobInstrumentation.send(:record_failure, job)
228
+ end
231
229
  end
232
230
  end
233
- end if defined?(::Delayed::Plugin)
231
+ end
234
232
  end
235
233
  end
236
234
  end
@@ -32,7 +32,7 @@ module BrainzLab
32
32
  return unless installed_any
33
33
 
34
34
  @installed = true
35
- BrainzLab.debug_log("Elasticsearch instrumentation installed")
35
+ BrainzLab.debug_log('Elasticsearch instrumentation installed')
36
36
  end
37
37
 
38
38
  def installed?
@@ -64,14 +64,12 @@ module BrainzLab
64
64
  return super unless should_track?
65
65
 
66
66
  started_at = Time.now.utc
67
- error_info = nil
68
67
 
69
68
  begin
70
69
  response = super
71
70
  record_request(method, path, params, started_at, response.status)
72
71
  response
73
72
  rescue StandardError => e
74
- error_info = e
75
73
  record_request(method, path, params, started_at, nil, e)
76
74
  raise
77
75
  end
@@ -83,7 +81,7 @@ module BrainzLab
83
81
  BrainzLab.configuration.instrument_elasticsearch
84
82
  end
85
83
 
86
- def record_request(method, path, params, started_at, status, error = nil)
84
+ def record_request(method, path, _params, started_at, status, error = nil)
87
85
  duration_ms = ((Time.now.utc - started_at) * 1000).round(2)
88
86
  operation = extract_operation(method, path)
89
87
  index = extract_index(path)
@@ -93,7 +91,7 @@ module BrainzLab
93
91
  if BrainzLab.configuration.reflex_enabled
94
92
  BrainzLab::Reflex.add_breadcrumb(
95
93
  "ES #{operation}",
96
- category: "elasticsearch",
94
+ category: 'elasticsearch',
97
95
  level: level,
98
96
  data: {
99
97
  method: method.to_s.upcase,
@@ -143,7 +141,7 @@ module BrainzLab
143
141
  span = {
144
142
  span_id: SecureRandom.uuid,
145
143
  name: "ES #{operation}",
146
- kind: "elasticsearch",
144
+ kind: 'elasticsearch',
147
145
  started_at: started_at,
148
146
  ended_at: Time.now.utc,
149
147
  duration_ms: duration_ms,
@@ -168,25 +166,25 @@ module BrainzLab
168
166
  method_str = method.to_s.upcase
169
167
 
170
168
  case path
171
- when %r{/_search} then "search"
172
- when %r{/_bulk} then "bulk"
173
- when %r{/_count} then "count"
174
- when %r{/_mget} then "mget"
175
- when %r{/_msearch} then "msearch"
176
- when %r{/_update_by_query} then "update_by_query"
177
- when %r{/_delete_by_query} then "delete_by_query"
178
- when %r{/_refresh} then "refresh"
179
- when %r{/_mapping} then "mapping"
180
- when %r{/_settings} then "settings"
181
- when %r{/_alias} then "alias"
182
- when %r{/_analyze} then "analyze"
169
+ when %r{/_search} then 'search'
170
+ when %r{/_bulk} then 'bulk'
171
+ when %r{/_count} then 'count'
172
+ when %r{/_mget} then 'mget'
173
+ when %r{/_msearch} then 'msearch'
174
+ when %r{/_update_by_query} then 'update_by_query'
175
+ when %r{/_delete_by_query} then 'delete_by_query'
176
+ when %r{/_refresh} then 'refresh'
177
+ when %r{/_mapping} then 'mapping'
178
+ when %r{/_settings} then 'settings'
179
+ when %r{/_alias} then 'alias'
180
+ when %r{/_analyze} then 'analyze'
183
181
  else
184
182
  case method_str
185
- when "GET" then "get"
186
- when "POST" then "index"
187
- when "PUT" then "update"
188
- when "DELETE" then "delete"
189
- when "HEAD" then "exists"
183
+ when 'GET' then 'get'
184
+ when 'POST' then 'index'
185
+ when 'PUT' then 'update'
186
+ when 'DELETE' then 'delete'
187
+ when 'HEAD' then 'exists'
190
188
  else method_str.downcase
191
189
  end
192
190
  end
@@ -195,13 +193,14 @@ module BrainzLab
195
193
  def extract_index(path)
196
194
  # Extract index name from path like /my-index/_search
197
195
  match = path.match(%r{^/([^/_]+)})
198
- match[1] if match && !match[1].start_with?("_")
196
+ match[1] if match && !match[1].start_with?('_')
199
197
  rescue StandardError
200
198
  nil
201
199
  end
202
200
 
203
201
  def truncate_path(path)
204
202
  return nil unless path
203
+
205
204
  path.to_s[0, 200]
206
205
  end
207
206
  end
@@ -9,7 +9,7 @@ module BrainzLab
9
9
 
10
10
  install_middleware!
11
11
 
12
- BrainzLab.debug_log("[Instrumentation] Excon instrumentation installed")
12
+ BrainzLab.debug_log('[Instrumentation] Excon instrumentation installed')
13
13
  end
14
14
 
15
15
  private
@@ -19,15 +19,15 @@ module BrainzLab
19
19
  ::Excon.defaults[:instrumentor] = BrainzLabInstrumentor
20
20
 
21
21
  # Also set up middleware
22
- if ::Excon.defaults[:middlewares]
23
- ::Excon.defaults[:middlewares] = [Middleware] + ::Excon.defaults[:middlewares]
24
- end
22
+ return unless ::Excon.defaults[:middlewares]
23
+
24
+ ::Excon.defaults[:middlewares] = [Middleware] + ::Excon.defaults[:middlewares]
25
25
  end
26
26
  end
27
27
 
28
28
  # Excon Instrumentor for ActiveSupport-style notifications
29
29
  module BrainzLabInstrumentor
30
- def self.instrument(name, params = {}, &block)
30
+ def self.instrument(name, params = {})
31
31
  started_at = Time.now
32
32
 
33
33
  begin
@@ -40,19 +40,19 @@ module BrainzLab
40
40
  end
41
41
  end
42
42
 
43
- def self.track_request(name, params, started_at, error)
43
+ def self.track_request(_name, params, started_at, error)
44
44
  return if skip_tracking?(params)
45
45
 
46
46
  duration_ms = ((Time.now - started_at) * 1000).round(2)
47
- host = params[:host] || "unknown"
48
- method = (params[:method] || "GET").to_s.upcase
49
- path = params[:path] || "/"
47
+ host = params[:host] || 'unknown'
48
+ method = (params[:method] || 'GET').to_s.upcase
49
+ path = params[:path] || '/'
50
50
  status = params[:status]
51
51
 
52
52
  # Add breadcrumb
53
53
  BrainzLab::Reflex.add_breadcrumb(
54
54
  "HTTP #{method} #{host}#{path}",
55
- category: "http",
55
+ category: 'http',
56
56
  level: error ? :error : :info,
57
57
  data: {
58
58
  method: method,
@@ -65,21 +65,21 @@ module BrainzLab
65
65
 
66
66
  # Track with Pulse
67
67
  if BrainzLab.configuration.pulse_effectively_enabled?
68
- BrainzLab::Pulse.span("http.excon", kind: "http") do
68
+ BrainzLab::Pulse.span('http.excon', kind: 'http') do
69
69
  # Already completed, just recording
70
70
  end
71
71
  end
72
72
 
73
73
  # Track with Flux
74
- if BrainzLab.configuration.flux_effectively_enabled?
75
- tags = { host: host, method: method, status: status.to_s }
76
- BrainzLab::Flux.distribution("http.excon.duration_ms", duration_ms, tags: tags)
77
- BrainzLab::Flux.increment("http.excon.requests", tags: tags)
74
+ return unless BrainzLab.configuration.flux_effectively_enabled?
78
75
 
79
- if error || (status && status >= 400)
80
- BrainzLab::Flux.increment("http.excon.errors", tags: tags)
81
- end
82
- end
76
+ tags = { host: host, method: method, status: status.to_s }
77
+ BrainzLab::Flux.distribution('http.excon.duration_ms', duration_ms, tags: tags)
78
+ BrainzLab::Flux.increment('http.excon.requests', tags: tags)
79
+
80
+ return unless error || (status && status >= 400)
81
+
82
+ BrainzLab::Flux.increment('http.excon.errors', tags: tags)
83
83
  end
84
84
 
85
85
  def self.skip_tracking?(params)
@@ -122,22 +122,22 @@ module BrainzLab
122
122
  return if skip_host?(host)
123
123
 
124
124
  duration_ms = ((Time.now - started_at) * 1000).round(2)
125
- method = (datum[:method] || "GET").to_s.upcase
126
- path = datum[:path] || "/"
125
+ method = (datum[:method] || 'GET').to_s.upcase
126
+ path = datum[:path] || '/'
127
127
  status = datum[:response]&.dig(:status)
128
128
 
129
129
  BrainzLab::Reflex.add_breadcrumb(
130
130
  "HTTP #{method} #{host}#{path} -> #{status || 'error'}",
131
- category: "http",
131
+ category: 'http',
132
132
  level: error ? :error : :info,
133
133
  data: { method: method, host: host, status: status, duration_ms: duration_ms }
134
134
  )
135
135
 
136
- if BrainzLab.configuration.flux_effectively_enabled?
137
- tags = { host: host, method: method }
138
- tags[:status] = status.to_s if status
139
- BrainzLab::Flux.distribution("http.excon.duration_ms", duration_ms, tags: tags)
140
- end
136
+ return unless BrainzLab.configuration.flux_effectively_enabled?
137
+
138
+ tags = { host: host, method: method }
139
+ tags[:status] = status.to_s if status
140
+ BrainzLab::Flux.distribution('http.excon.duration_ms', duration_ms, tags: tags)
141
141
  end
142
142
 
143
143
  def skip_host?(host)
@@ -14,7 +14,7 @@ module BrainzLab
14
14
  ::Faraday::Middleware.register_middleware(brainzlab: Middleware)
15
15
 
16
16
  @installed = true
17
- BrainzLab.debug_log("Faraday instrumentation installed")
17
+ BrainzLab.debug_log('Faraday instrumentation installed')
18
18
  end
19
19
 
20
20
  def installed?
@@ -46,7 +46,6 @@ module BrainzLab
46
46
  inject_trace_context(env)
47
47
 
48
48
  started_at = Process.clock_gettime(Process::CLOCK_MONOTONIC)
49
- error_info = nil
50
49
 
51
50
  begin
52
51
  response = @app.call(env)
@@ -98,7 +97,7 @@ module BrainzLab
98
97
  if BrainzLab.configuration.reflex_enabled
99
98
  BrainzLab::Reflex.add_breadcrumb(
100
99
  "#{method} #{url}",
101
- category: "http.faraday",
100
+ category: 'http.faraday',
102
101
  level: level,
103
102
  data: {
104
103
  method: method,
@@ -138,7 +137,7 @@ module BrainzLab
138
137
  span = {
139
138
  span_id: SecureRandom.uuid,
140
139
  name: "HTTP #{method} #{host}",
141
- kind: "http",
140
+ kind: 'http',
142
141
  started_at: Time.now.utc - (duration_ms / 1000.0),
143
142
  ended_at: Time.now.utc,
144
143
  duration_ms: duration_ms,
@@ -10,7 +10,7 @@ module BrainzLab
10
10
  install_notifier!
11
11
  install_middleware!
12
12
 
13
- BrainzLab.debug_log("[Instrumentation] GoodJob instrumentation installed")
13
+ BrainzLab.debug_log('[Instrumentation] GoodJob instrumentation installed')
14
14
  end
15
15
 
16
16
  private
@@ -19,12 +19,12 @@ module BrainzLab
19
19
  return unless defined?(::ActiveSupport::Notifications)
20
20
 
21
21
  # GoodJob emits ActiveSupport notifications
22
- ::ActiveSupport::Notifications.subscribe("perform_job.good_job") do |*args|
22
+ ::ActiveSupport::Notifications.subscribe('perform_job.good_job') do |*args|
23
23
  event = ::ActiveSupport::Notifications::Event.new(*args)
24
24
  handle_perform(event)
25
25
  end
26
26
 
27
- ::ActiveSupport::Notifications.subscribe("finished_job_task.good_job") do |*args|
27
+ ::ActiveSupport::Notifications.subscribe('finished_job_task.good_job') do |*args|
28
28
  event = ::ActiveSupport::Notifications::Event.new(*args)
29
29
  handle_finished(event)
30
30
  end
@@ -34,25 +34,28 @@ module BrainzLab
34
34
  return unless defined?(::GoodJob::Adapter)
35
35
 
36
36
  # Add our callback to GoodJob
37
- if ::GoodJob.respond_to?(:on_thread_error)
38
- ::GoodJob.on_thread_error = ->(error) do
39
- BrainzLab::Reflex.capture(error, tags: { source: "good_job" }) if BrainzLab.configuration.reflex_effectively_enabled?
37
+ return unless ::GoodJob.respond_to?(:on_thread_error)
38
+
39
+ ::GoodJob.on_thread_error = lambda { |error|
40
+ if BrainzLab.configuration.reflex_effectively_enabled?
41
+ BrainzLab::Reflex.capture(error,
42
+ tags: { source: 'good_job' })
40
43
  end
41
- end
44
+ }
42
45
  end
43
46
 
44
47
  def handle_perform(event)
45
48
  payload = event.payload
46
49
  job = payload[:job]
47
- job_class = job&.class&.name || payload[:job_class] || "Unknown"
48
- queue = job&.queue_name || payload[:queue_name] || "default"
50
+ job_class = job&.class&.name || payload[:job_class] || 'Unknown'
51
+ queue = job&.queue_name || payload[:queue_name] || 'default'
49
52
  duration_ms = event.duration.round(2)
50
53
 
51
54
  # Track with Pulse
52
55
  if BrainzLab.configuration.pulse_effectively_enabled?
53
56
  BrainzLab::Pulse.record_trace(
54
57
  "job.#{job_class}",
55
- kind: "job",
58
+ kind: 'job',
56
59
  started_at: event.time,
57
60
  ended_at: event.end,
58
61
  job_class: job_class,
@@ -67,33 +70,30 @@ module BrainzLab
67
70
  # Track with Flux
68
71
  if BrainzLab.configuration.flux_effectively_enabled?
69
72
  tags = { job_class: job_class, queue: queue }
70
- BrainzLab::Flux.distribution("good_job.job.duration_ms", duration_ms, tags: tags)
71
- BrainzLab::Flux.increment("good_job.job.processed", tags: tags)
73
+ BrainzLab::Flux.distribution('good_job.job.duration_ms', duration_ms, tags: tags)
74
+ BrainzLab::Flux.increment('good_job.job.processed', tags: tags)
72
75
 
73
- if payload[:error]
74
- BrainzLab::Flux.increment("good_job.job.failed", tags: tags)
75
- end
76
+ BrainzLab::Flux.increment('good_job.job.failed', tags: tags) if payload[:error]
76
77
  end
77
78
 
78
79
  # Capture error with Reflex
79
- if payload[:error] && BrainzLab.configuration.reflex_effectively_enabled?
80
- BrainzLab::Reflex.capture(payload[:error],
81
- tags: { job_class: job_class, queue: queue, source: "good_job" },
82
- extra: { job_id: job&.job_id, duration_ms: duration_ms }
83
- )
84
- end
80
+ return unless payload[:error] && BrainzLab.configuration.reflex_effectively_enabled?
81
+
82
+ BrainzLab::Reflex.capture(payload[:error],
83
+ tags: { job_class: job_class, queue: queue, source: 'good_job' },
84
+ extra: { job_id: job&.job_id, duration_ms: duration_ms })
85
85
  end
86
86
 
87
87
  def handle_finished(event)
88
88
  payload = event.payload
89
89
 
90
- if BrainzLab.configuration.flux_effectively_enabled?
91
- result = payload[:result]
92
- if result == :discarded
93
- BrainzLab::Flux.increment("good_job.job.discarded")
94
- elsif result == :retried
95
- BrainzLab::Flux.increment("good_job.job.retried")
96
- end
90
+ return unless BrainzLab.configuration.flux_effectively_enabled?
91
+
92
+ result = payload[:result]
93
+ if result == :discarded
94
+ BrainzLab::Flux.increment('good_job.job.discarded')
95
+ elsif result == :retried
96
+ BrainzLab::Flux.increment('good_job.job.retried')
97
97
  end
98
98
  end
99
99
  end
@@ -14,7 +14,7 @@ module BrainzLab
14
14
  install_notifications!
15
15
 
16
16
  @installed = true
17
- BrainzLab.debug_log("Grape instrumentation installed")
17
+ BrainzLab.debug_log('Grape instrumentation installed')
18
18
  end
19
19
 
20
20
  def installed?
@@ -29,23 +29,23 @@ module BrainzLab
29
29
 
30
30
  def install_notifications!
31
31
  # Grape emits these notifications
32
- ActiveSupport::Notifications.subscribe("endpoint_run.grape") do |*args|
32
+ ActiveSupport::Notifications.subscribe('endpoint_run.grape') do |*args|
33
33
  event = ActiveSupport::Notifications::Event.new(*args)
34
34
  record_endpoint(event)
35
35
  end
36
36
 
37
- ActiveSupport::Notifications.subscribe("endpoint_render.grape") do |*args|
37
+ ActiveSupport::Notifications.subscribe('endpoint_render.grape') do |*args|
38
38
  event = ActiveSupport::Notifications::Event.new(*args)
39
39
  record_render(event)
40
40
  end
41
41
 
42
- ActiveSupport::Notifications.subscribe("endpoint_run_filters.grape") do |*args|
42
+ ActiveSupport::Notifications.subscribe('endpoint_run_filters.grape') do |*args|
43
43
  event = ActiveSupport::Notifications::Event.new(*args)
44
44
  record_filters(event)
45
45
  end
46
46
 
47
47
  # Format validation
48
- ActiveSupport::Notifications.subscribe("format_response.grape") do |*args|
48
+ ActiveSupport::Notifications.subscribe('format_response.grape') do |*args|
49
49
  event = ActiveSupport::Notifications::Event.new(*args)
50
50
  record_format(event)
51
51
  end
@@ -58,19 +58,19 @@ module BrainzLab
58
58
  endpoint = payload[:endpoint]
59
59
  env = payload[:env] || {}
60
60
 
61
- method = env["REQUEST_METHOD"] || "GET"
62
- path = endpoint&.options&.dig(:path)&.first || env["PATH_INFO"] || "/"
61
+ method = env['REQUEST_METHOD'] || 'GET'
62
+ path = endpoint&.options&.dig(:path)&.first || env['PATH_INFO'] || '/'
63
63
  route_pattern = extract_route_pattern(endpoint)
64
64
  duration_ms = event.duration.round(2)
65
65
 
66
- status = env["api.endpoint"]&.status || 200
66
+ status = env['api.endpoint']&.status || 200
67
67
  level = status >= 400 ? :error : :info
68
68
 
69
69
  # Add breadcrumb for Reflex
70
70
  if BrainzLab.configuration.reflex_enabled
71
71
  BrainzLab::Reflex.add_breadcrumb(
72
72
  "Grape #{method} #{route_pattern}",
73
- category: "grape.endpoint",
73
+ category: 'grape.endpoint',
74
74
  level: level,
75
75
  data: {
76
76
  method: method,
@@ -85,7 +85,7 @@ module BrainzLab
85
85
  # Record span for Pulse
86
86
  record_span(
87
87
  name: "Grape #{method} #{route_pattern}",
88
- kind: "grape",
88
+ kind: 'grape',
89
89
  started_at: event.time,
90
90
  ended_at: event.end,
91
91
  duration_ms: duration_ms,
@@ -117,12 +117,12 @@ module BrainzLab
117
117
  duration_ms = event.duration.round(2)
118
118
 
119
119
  record_span(
120
- name: "Grape render",
121
- kind: "grape.render",
120
+ name: 'Grape render',
121
+ kind: 'grape.render',
122
122
  started_at: event.time,
123
123
  ended_at: event.end,
124
124
  duration_ms: duration_ms,
125
- data: { phase: "render" }
125
+ data: { phase: 'render' }
126
126
  )
127
127
  rescue StandardError => e
128
128
  BrainzLab.debug_log("Grape render recording failed: #{e.message}")
@@ -131,11 +131,11 @@ module BrainzLab
131
131
  def record_filters(event)
132
132
  payload = event.payload
133
133
  duration_ms = event.duration.round(2)
134
- filter_type = payload[:type] || "filter"
134
+ filter_type = payload[:type] || 'filter'
135
135
 
136
136
  record_span(
137
137
  name: "Grape #{filter_type} filters",
138
- kind: "grape.filter",
138
+ kind: 'grape.filter',
139
139
  started_at: event.time,
140
140
  ended_at: event.end,
141
141
  duration_ms: duration_ms,
@@ -149,12 +149,12 @@ module BrainzLab
149
149
  duration_ms = event.duration.round(2)
150
150
 
151
151
  record_span(
152
- name: "Grape format response",
153
- kind: "grape.format",
152
+ name: 'Grape format response',
153
+ kind: 'grape.format',
154
154
  started_at: event.time,
155
155
  ended_at: event.end,
156
156
  duration_ms: duration_ms,
157
- data: { phase: "format" }
157
+ data: { phase: 'format' }
158
158
  )
159
159
  rescue StandardError => e
160
160
  BrainzLab.debug_log("Grape format recording failed: #{e.message}")
@@ -177,14 +177,14 @@ module BrainzLab
177
177
  end
178
178
 
179
179
  def extract_route_pattern(endpoint)
180
- return "/" unless endpoint
180
+ return '/' unless endpoint
181
181
 
182
182
  route = endpoint.route
183
- return "/" unless route
183
+ return '/' unless route
184
184
 
185
- route.pattern&.path || route.path || "/"
185
+ route.pattern&.path || route.path || '/'
186
186
  rescue StandardError
187
- "/"
187
+ '/'
188
188
  end
189
189
  end
190
190
 
@@ -243,14 +243,14 @@ module BrainzLab
243
243
  path = request.path
244
244
 
245
245
  # Get route pattern from Grape if available
246
- route_pattern = env["grape.routing_args"]&.dig(:route_info)&.pattern&.path || path
246
+ route_pattern = env['grape.routing_args']&.dig(:route_info)&.pattern&.path || path
247
247
 
248
248
  spans = Thread.current[:brainzlab_pulse_spans] || []
249
249
 
250
250
  payload = {
251
251
  trace_id: SecureRandom.uuid,
252
252
  name: "#{method} #{route_pattern}",
253
- kind: "request",
253
+ kind: 'request',
254
254
  started_at: started_at.utc.iso8601(3),
255
255
  ended_at: ended_at.utc.iso8601(3),
256
256
  duration_ms: duration_ms,