brainzlab 0.1.1 → 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.
- checksums.yaml +4 -4
- data/LICENSE +6 -21
- data/README.md +24 -2
- data/lib/brainzlab/beacon/client.rb +207 -0
- data/lib/brainzlab/beacon/provisioner.rb +44 -0
- data/lib/brainzlab/beacon.rb +215 -0
- data/lib/brainzlab/configuration.rb +372 -32
- data/lib/brainzlab/context.rb +2 -3
- data/lib/brainzlab/cortex/cache.rb +59 -0
- data/lib/brainzlab/cortex/client.rb +139 -0
- data/lib/brainzlab/cortex/provisioner.rb +49 -0
- data/lib/brainzlab/cortex.rb +223 -0
- data/lib/brainzlab/dendrite/client.rb +230 -0
- data/lib/brainzlab/dendrite/provisioner.rb +44 -0
- data/lib/brainzlab/dendrite.rb +195 -0
- data/lib/brainzlab/devtools/assets/devtools.css +1106 -0
- data/lib/brainzlab/devtools/assets/devtools.js +322 -0
- data/lib/brainzlab/devtools/assets/logo.svg +6 -0
- data/lib/brainzlab/devtools/assets/templates/debug_panel.html.erb +500 -0
- data/lib/brainzlab/devtools/assets/templates/error_page.html.erb +1086 -0
- data/lib/brainzlab/devtools/data/collector.rb +248 -0
- data/lib/brainzlab/devtools/middleware/asset_server.rb +63 -0
- data/lib/brainzlab/devtools/middleware/database_handler.rb +177 -0
- data/lib/brainzlab/devtools/middleware/debug_panel.rb +126 -0
- data/lib/brainzlab/devtools/middleware/error_page.rb +377 -0
- data/lib/brainzlab/devtools/renderers/debug_panel_renderer.rb +159 -0
- data/lib/brainzlab/devtools/renderers/error_page_renderer.rb +98 -0
- data/lib/brainzlab/devtools.rb +75 -0
- data/lib/brainzlab/flux/buffer.rb +96 -0
- data/lib/brainzlab/flux/client.rb +68 -0
- data/lib/brainzlab/flux/provisioner.rb +57 -0
- data/lib/brainzlab/flux.rb +174 -0
- data/lib/brainzlab/instrumentation/action_mailer.rb +14 -13
- data/lib/brainzlab/instrumentation/active_record.rb +28 -13
- data/lib/brainzlab/instrumentation/aws.rb +183 -0
- data/lib/brainzlab/instrumentation/dalli.rb +108 -0
- data/lib/brainzlab/instrumentation/delayed_job.rb +27 -29
- data/lib/brainzlab/instrumentation/elasticsearch.rb +23 -24
- data/lib/brainzlab/instrumentation/excon.rb +152 -0
- data/lib/brainzlab/instrumentation/faraday.rb +3 -4
- data/lib/brainzlab/instrumentation/good_job.rb +102 -0
- data/lib/brainzlab/instrumentation/grape.rb +24 -24
- data/lib/brainzlab/instrumentation/graphql.rb +24 -23
- data/lib/brainzlab/instrumentation/httparty.rb +13 -14
- data/lib/brainzlab/instrumentation/mongodb.rb +7 -7
- data/lib/brainzlab/instrumentation/net_http.rb +6 -6
- data/lib/brainzlab/instrumentation/redis.rb +14 -21
- data/lib/brainzlab/instrumentation/resque.rb +114 -0
- data/lib/brainzlab/instrumentation/sidekiq.rb +29 -28
- data/lib/brainzlab/instrumentation/solid_queue.rb +194 -0
- data/lib/brainzlab/instrumentation/stripe.rb +163 -0
- data/lib/brainzlab/instrumentation/typhoeus.rb +106 -0
- data/lib/brainzlab/instrumentation.rb +84 -12
- data/lib/brainzlab/nerve/client.rb +215 -0
- data/lib/brainzlab/nerve/provisioner.rb +44 -0
- data/lib/brainzlab/nerve.rb +219 -0
- data/lib/brainzlab/pulse/client.rb +15 -11
- data/lib/brainzlab/pulse/instrumentation.rb +90 -53
- data/lib/brainzlab/pulse/propagation.rb +29 -29
- data/lib/brainzlab/pulse/provisioner.rb +12 -12
- data/lib/brainzlab/pulse/tracer.rb +4 -4
- data/lib/brainzlab/pulse.rb +14 -14
- data/lib/brainzlab/rails/log_formatter.rb +127 -121
- data/lib/brainzlab/rails/log_subscriber.rb +70 -77
- data/lib/brainzlab/rails/railtie.rb +96 -86
- data/lib/brainzlab/recall/buffer.rb +1 -1
- data/lib/brainzlab/recall/client.rb +14 -10
- data/lib/brainzlab/recall/logger.rb +16 -18
- data/lib/brainzlab/recall/provisioner.rb +29 -12
- data/lib/brainzlab/recall.rb +14 -11
- data/lib/brainzlab/reflex/breadcrumbs.rb +2 -2
- data/lib/brainzlab/reflex/client.rb +14 -10
- data/lib/brainzlab/reflex/provisioner.rb +12 -12
- data/lib/brainzlab/reflex.rb +31 -31
- data/lib/brainzlab/sentinel/client.rb +216 -0
- data/lib/brainzlab/sentinel/provisioner.rb +44 -0
- data/lib/brainzlab/sentinel.rb +165 -0
- data/lib/brainzlab/signal/client.rb +60 -0
- data/lib/brainzlab/signal/provisioner.rb +55 -0
- data/lib/brainzlab/signal.rb +136 -0
- data/lib/brainzlab/synapse/client.rb +288 -0
- data/lib/brainzlab/synapse/provisioner.rb +44 -0
- data/lib/brainzlab/synapse.rb +270 -0
- data/lib/brainzlab/utilities/circuit_breaker.rb +261 -0
- data/lib/brainzlab/utilities/health_check.rb +294 -0
- data/lib/brainzlab/utilities/log_formatter.rb +254 -0
- data/lib/brainzlab/utilities/rate_limiter.rb +230 -0
- data/lib/brainzlab/utilities.rb +17 -0
- data/lib/brainzlab/vault/cache.rb +80 -0
- data/lib/brainzlab/vault/client.rb +196 -0
- data/lib/brainzlab/vault/provisioner.rb +49 -0
- data/lib/brainzlab/vault.rb +262 -0
- data/lib/brainzlab/version.rb +1 -1
- data/lib/brainzlab/vision/client.rb +128 -0
- data/lib/brainzlab/vision/provisioner.rb +136 -0
- data/lib/brainzlab/vision.rb +155 -0
- data/lib/brainzlab-sdk.rb +1 -1
- data/lib/brainzlab.rb +112 -13
- data/lib/generators/brainzlab/install/install_generator.rb +29 -27
- metadata +60 -1
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
require
|
|
3
|
+
require 'active_support/log_subscriber'
|
|
4
4
|
|
|
5
5
|
module BrainzLab
|
|
6
6
|
module Rails
|
|
@@ -21,12 +21,11 @@ module BrainzLab
|
|
|
21
21
|
params = payload[:params]&.except(*INTERNAL_PARAMS) || {}
|
|
22
22
|
|
|
23
23
|
formatter.start_request(request_id,
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
)
|
|
24
|
+
method: payload[:method],
|
|
25
|
+
path: payload[:path],
|
|
26
|
+
params: filter_params(params),
|
|
27
|
+
controller: payload[:controller],
|
|
28
|
+
action: payload[:action])
|
|
30
29
|
end
|
|
31
30
|
|
|
32
31
|
def process_action(event)
|
|
@@ -38,18 +37,15 @@ module BrainzLab
|
|
|
38
37
|
payload = event.payload
|
|
39
38
|
|
|
40
39
|
formatter.process_action(request_id,
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
)
|
|
40
|
+
controller: payload[:controller],
|
|
41
|
+
action: payload[:action],
|
|
42
|
+
status: payload[:status],
|
|
43
|
+
duration: event.duration,
|
|
44
|
+
view_runtime: payload[:view_runtime],
|
|
45
|
+
db_runtime: payload[:db_runtime])
|
|
48
46
|
|
|
49
47
|
# Handle exception if present
|
|
50
|
-
if payload[:exception_object]
|
|
51
|
-
formatter.error(request_id, payload[:exception_object])
|
|
52
|
-
end
|
|
48
|
+
formatter.error(request_id, payload[:exception_object]) if payload[:exception_object]
|
|
53
49
|
|
|
54
50
|
# Output the formatted log
|
|
55
51
|
output = formatter.end_request(request_id)
|
|
@@ -81,11 +77,11 @@ module BrainzLab
|
|
|
81
77
|
case obj
|
|
82
78
|
when Hash
|
|
83
79
|
obj.each_with_object({}) do |(k, v), h|
|
|
84
|
-
if filter_keys.include?(k.to_s.downcase)
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
80
|
+
h[k] = if filter_keys.include?(k.to_s.downcase)
|
|
81
|
+
'[FILTERED]'
|
|
82
|
+
else
|
|
83
|
+
deep_filter(v, filter_keys)
|
|
84
|
+
end
|
|
89
85
|
end
|
|
90
86
|
when Array
|
|
91
87
|
obj.map { |v| deep_filter(v, filter_keys) }
|
|
@@ -121,13 +117,12 @@ module BrainzLab
|
|
|
121
117
|
sql_pattern = normalize_sql(payload[:sql])
|
|
122
118
|
|
|
123
119
|
LogSubscriber.formatter.sql_query(request_id,
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
)
|
|
120
|
+
name: payload[:name],
|
|
121
|
+
duration: event.duration,
|
|
122
|
+
sql: payload[:sql],
|
|
123
|
+
sql_pattern: sql_pattern,
|
|
124
|
+
cached: payload[:cached] || payload[:name] == 'CACHE',
|
|
125
|
+
source: source)
|
|
131
126
|
end
|
|
132
127
|
|
|
133
128
|
private
|
|
@@ -135,11 +130,11 @@ module BrainzLab
|
|
|
135
130
|
def extract_source_location(backtrace)
|
|
136
131
|
# Find the first line that's in app/ directory
|
|
137
132
|
backtrace.each do |line|
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
133
|
+
next unless line.include?('/app/') && !line.include?('/brainzlab')
|
|
134
|
+
|
|
135
|
+
# Extract just the relevant part: app/models/user.rb:42
|
|
136
|
+
match = line.match(%r{(app/[^:]+:\d+)})
|
|
137
|
+
return match[1] if match
|
|
143
138
|
end
|
|
144
139
|
nil
|
|
145
140
|
end
|
|
@@ -148,12 +143,11 @@ module BrainzLab
|
|
|
148
143
|
return nil unless sql
|
|
149
144
|
|
|
150
145
|
sql
|
|
151
|
-
.gsub(/\b\d+\b/,
|
|
152
|
-
.gsub(/'[^']*'/,
|
|
153
|
-
.gsub(
|
|
154
|
-
.gsub(
|
|
155
|
-
.gsub(
|
|
156
|
-
.gsub(/\s+/, " ") # Normalize whitespace
|
|
146
|
+
.gsub(/\b\d+\b/, '?') # Replace numbers
|
|
147
|
+
.gsub(/'[^']*'/, '?') # Replace single-quoted strings
|
|
148
|
+
.gsub(/\$\d+/, '?') # Replace positional params
|
|
149
|
+
.gsub(%r{/\*.*?\*/}, '') # Remove comments
|
|
150
|
+
.gsub(/\s+/, ' ') # Normalize whitespace
|
|
157
151
|
.strip
|
|
158
152
|
end
|
|
159
153
|
end
|
|
@@ -183,18 +177,18 @@ module BrainzLab
|
|
|
183
177
|
return broadcasting unless broadcasting
|
|
184
178
|
|
|
185
179
|
# Extract channel name from gid format: logs:Z2lkOi8vcmVjYWxsL1Byb2plY3QvNDhi...
|
|
186
|
-
if broadcasting.start_with?(
|
|
187
|
-
|
|
188
|
-
elsif broadcasting.include?(
|
|
180
|
+
if broadcasting.start_with?('logs:')
|
|
181
|
+
'LogsChannel'
|
|
182
|
+
elsif broadcasting.include?(':')
|
|
189
183
|
# Generic channel:id format
|
|
190
|
-
broadcasting.split(
|
|
184
|
+
"#{broadcasting.split(':').first.capitalize}Channel"
|
|
191
185
|
else
|
|
192
186
|
broadcasting
|
|
193
187
|
end
|
|
194
188
|
end
|
|
195
189
|
|
|
196
190
|
def format_message(message)
|
|
197
|
-
return
|
|
191
|
+
return '{}' unless message
|
|
198
192
|
|
|
199
193
|
case message
|
|
200
194
|
when Hash
|
|
@@ -215,58 +209,60 @@ module BrainzLab
|
|
|
215
209
|
parts = []
|
|
216
210
|
|
|
217
211
|
# Show key fields for log entries
|
|
218
|
-
if hash[
|
|
219
|
-
level = hash[
|
|
212
|
+
if hash['level'] || hash[:level]
|
|
213
|
+
level = hash['level'] || hash[:level]
|
|
220
214
|
parts << colorize_level(level)
|
|
221
215
|
end
|
|
222
216
|
|
|
223
|
-
if hash[
|
|
224
|
-
msg = hash[
|
|
217
|
+
if hash['message'] || hash[:message]
|
|
218
|
+
msg = hash['message'] || hash[:message]
|
|
225
219
|
parts << truncate(msg.to_s, 50)
|
|
226
220
|
end
|
|
227
221
|
|
|
228
|
-
if hash[
|
|
229
|
-
id = hash[
|
|
222
|
+
if hash['id'] || hash[:id]
|
|
223
|
+
id = hash['id'] || hash[:id]
|
|
230
224
|
parts << colorize(id.to_s[0..7], :gray)
|
|
231
225
|
end
|
|
232
226
|
|
|
233
|
-
parts.any? ? parts.join(
|
|
227
|
+
parts.any? ? parts.join(' ') : hash.keys.first(3).join(', ')
|
|
234
228
|
end
|
|
235
229
|
|
|
236
230
|
def build_output(channel, message, duration)
|
|
237
|
-
time = Time.current.strftime(
|
|
238
|
-
duration_str = duration ? "#{duration.round(1)}ms" :
|
|
231
|
+
time = Time.current.strftime('%H:%M:%S')
|
|
232
|
+
duration_str = duration ? "#{duration.round(1)}ms" : ''
|
|
239
233
|
|
|
240
234
|
parts = [
|
|
241
235
|
colorize(time, :gray),
|
|
242
|
-
colorize(
|
|
236
|
+
colorize('⚡', :magenta),
|
|
243
237
|
colorize(channel, :magenta),
|
|
244
|
-
colorize(
|
|
238
|
+
colorize('→', :gray),
|
|
245
239
|
message,
|
|
246
240
|
colorize(duration_str, :gray)
|
|
247
241
|
]
|
|
248
242
|
|
|
249
|
-
"
|
|
243
|
+
" #{parts.join(' ')}\n"
|
|
250
244
|
end
|
|
251
245
|
|
|
252
246
|
def truncate(text, length)
|
|
253
247
|
return text if text.nil? || text.length <= length
|
|
248
|
+
|
|
254
249
|
"#{text[0..(length - 4)]}..."
|
|
255
250
|
end
|
|
256
251
|
|
|
257
252
|
def colorize(text, color)
|
|
258
253
|
return text unless $stdout.tty?
|
|
259
254
|
return text unless COLORS[color]
|
|
255
|
+
|
|
260
256
|
"#{COLORS[color]}#{text}#{COLORS[:reset]}"
|
|
261
257
|
end
|
|
262
258
|
|
|
263
259
|
def colorize_level(level)
|
|
264
260
|
color = case level.to_s.downcase
|
|
265
|
-
when
|
|
266
|
-
when
|
|
267
|
-
when
|
|
268
|
-
when
|
|
269
|
-
when
|
|
261
|
+
when 'debug' then :gray
|
|
262
|
+
when 'info' then :green
|
|
263
|
+
when 'warn', 'warning' then :yellow
|
|
264
|
+
when 'error' then :red
|
|
265
|
+
when 'fatal' then :red
|
|
270
266
|
else :white
|
|
271
267
|
end
|
|
272
268
|
colorize(level.to_s.upcase.ljust(5), color)
|
|
@@ -285,10 +281,9 @@ module BrainzLab
|
|
|
285
281
|
template = template_name(payload[:identifier])
|
|
286
282
|
|
|
287
283
|
LogSubscriber.formatter.render_template(request_id,
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
)
|
|
284
|
+
template: template,
|
|
285
|
+
duration: event.duration,
|
|
286
|
+
layout: payload[:layout])
|
|
292
287
|
end
|
|
293
288
|
|
|
294
289
|
def render_partial(event)
|
|
@@ -301,10 +296,9 @@ module BrainzLab
|
|
|
301
296
|
template = template_name(payload[:identifier])
|
|
302
297
|
|
|
303
298
|
LogSubscriber.formatter.render_partial(request_id,
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
)
|
|
299
|
+
template: template,
|
|
300
|
+
duration: event.duration,
|
|
301
|
+
count: payload[:count])
|
|
308
302
|
end
|
|
309
303
|
|
|
310
304
|
def render_layout(event)
|
|
@@ -317,9 +311,8 @@ module BrainzLab
|
|
|
317
311
|
layout = template_name(payload[:identifier])
|
|
318
312
|
|
|
319
313
|
LogSubscriber.formatter.render_layout(request_id,
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
)
|
|
314
|
+
layout: layout,
|
|
315
|
+
duration: event.duration)
|
|
323
316
|
end
|
|
324
317
|
|
|
325
318
|
private
|
|
@@ -328,10 +321,10 @@ module BrainzLab
|
|
|
328
321
|
return nil unless identifier
|
|
329
322
|
|
|
330
323
|
# Extract relative path from full identifier
|
|
331
|
-
if identifier.include?(
|
|
332
|
-
identifier.split(
|
|
333
|
-
elsif identifier.include?(
|
|
334
|
-
identifier.split(
|
|
324
|
+
if identifier.include?('/app/views/')
|
|
325
|
+
identifier.split('/app/views/').last
|
|
326
|
+
elsif identifier.include?('/views/')
|
|
327
|
+
identifier.split('/views/').last
|
|
335
328
|
else
|
|
336
329
|
File.basename(identifier)
|
|
337
330
|
end
|
|
@@ -1,13 +1,25 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
+
require 'English'
|
|
3
4
|
module BrainzLab
|
|
4
5
|
module Rails
|
|
5
6
|
class Railtie < ::Rails::Railtie
|
|
6
7
|
generators do
|
|
7
|
-
require
|
|
8
|
+
require 'generators/brainzlab/install/install_generator'
|
|
8
9
|
end
|
|
9
10
|
|
|
10
|
-
|
|
11
|
+
# Load Vault secrets early, before configuration
|
|
12
|
+
# This allows secrets to be used in config files
|
|
13
|
+
initializer 'brainzlab.load_vault_secrets', before: :load_environment_config do
|
|
14
|
+
if BrainzLab.configuration.vault_enabled && BrainzLab.configuration.vault_auto_load
|
|
15
|
+
BrainzLab.debug_log('[Vault] Auto-loading secrets into ENV...')
|
|
16
|
+
BrainzLab::Vault.load!(
|
|
17
|
+
provider_keys: BrainzLab.configuration.vault_load_provider_keys
|
|
18
|
+
)
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
initializer 'brainzlab.configure_rails_initialization' do |app|
|
|
11
23
|
# Set defaults from Rails
|
|
12
24
|
BrainzLab.configure do |config|
|
|
13
25
|
config.environment ||= ::Rails.env.to_s
|
|
@@ -20,11 +32,31 @@ module BrainzLab
|
|
|
20
32
|
|
|
21
33
|
# Add request context middleware (runs early)
|
|
22
34
|
app.middleware.insert_after ActionDispatch::RequestId, BrainzLab::Rails::Middleware
|
|
35
|
+
|
|
36
|
+
# Add DevTools middlewares if enabled
|
|
37
|
+
if BrainzLab.configuration.devtools_enabled
|
|
38
|
+
require_relative '../devtools'
|
|
39
|
+
|
|
40
|
+
# Asset server (handles /__brainzlab__/* requests)
|
|
41
|
+
app.middleware.insert_before ActionDispatch::Static, BrainzLab::DevTools::Middleware::AssetServer
|
|
42
|
+
|
|
43
|
+
# Database handler (handles /_brainzlab/devtools/database POST requests)
|
|
44
|
+
# Allows running migrations from the error page
|
|
45
|
+
app.middleware.insert_before ActionDispatch::Static, BrainzLab::DevTools::Middleware::DatabaseHandler
|
|
46
|
+
|
|
47
|
+
# Error page (catches exceptions and renders branded error page)
|
|
48
|
+
# Insert BEFORE DebugExceptions so we can intercept the HTML error page
|
|
49
|
+
# that DebugExceptions renders and replace it with our own
|
|
50
|
+
app.middleware.insert_before ActionDispatch::DebugExceptions, BrainzLab::DevTools::Middleware::ErrorPage if defined?(ActionDispatch::DebugExceptions)
|
|
51
|
+
|
|
52
|
+
# Debug panel (injects panel into HTML responses)
|
|
53
|
+
app.middleware.use BrainzLab::DevTools::Middleware::DebugPanel
|
|
54
|
+
end
|
|
23
55
|
end
|
|
24
56
|
|
|
25
57
|
config.after_initialize do
|
|
26
58
|
# Set up custom log formatter
|
|
27
|
-
setup_log_formatter if BrainzLab.configuration.log_formatter_enabled
|
|
59
|
+
BrainzLab::Rails::Railtie.setup_log_formatter if BrainzLab.configuration.log_formatter_enabled
|
|
28
60
|
|
|
29
61
|
# Install instrumentation (HTTP tracking, etc.)
|
|
30
62
|
BrainzLab::Instrumentation.install!
|
|
@@ -38,14 +70,10 @@ module BrainzLab
|
|
|
38
70
|
end
|
|
39
71
|
|
|
40
72
|
# Hook into ActiveJob
|
|
41
|
-
if defined?(ActiveJob::Base)
|
|
42
|
-
ActiveJob::Base.include(BrainzLab::Rails::ActiveJobExtension)
|
|
43
|
-
end
|
|
73
|
+
ActiveJob::Base.include(BrainzLab::Rails::ActiveJobExtension) if defined?(ActiveJob::Base)
|
|
44
74
|
|
|
45
75
|
# Hook into ActionController for rescue_from fallback
|
|
46
|
-
if defined?(ActionController::Base)
|
|
47
|
-
ActionController::Base.include(BrainzLab::Rails::ControllerExtension)
|
|
48
|
-
end
|
|
76
|
+
ActionController::Base.include(BrainzLab::Rails::ControllerExtension) if defined?(ActionController::Base)
|
|
49
77
|
|
|
50
78
|
# Hook into Sidekiq if available
|
|
51
79
|
if defined?(Sidekiq)
|
|
@@ -58,8 +86,8 @@ module BrainzLab
|
|
|
58
86
|
class << self
|
|
59
87
|
def setup_log_formatter
|
|
60
88
|
# Lazy require to ensure Rails is fully loaded
|
|
61
|
-
require_relative
|
|
62
|
-
require_relative
|
|
89
|
+
require_relative 'log_formatter'
|
|
90
|
+
require_relative 'log_subscriber'
|
|
63
91
|
|
|
64
92
|
config = BrainzLab.configuration
|
|
65
93
|
|
|
@@ -91,37 +119,23 @@ module BrainzLab
|
|
|
91
119
|
null_logger.level = Logger::FATAL
|
|
92
120
|
|
|
93
121
|
# Silence ActiveRecord SQL logging
|
|
94
|
-
if defined?(ActiveRecord::Base)
|
|
95
|
-
ActiveRecord::Base.logger = null_logger
|
|
96
|
-
end
|
|
122
|
+
ActiveRecord::Base.logger = null_logger if defined?(ActiveRecord::Base)
|
|
97
123
|
|
|
98
124
|
# Silence ActionController logging (the "Completed" message)
|
|
99
|
-
if defined?(ActionController::Base)
|
|
100
|
-
ActionController::Base.logger = null_logger
|
|
101
|
-
end
|
|
125
|
+
ActionController::Base.logger = null_logger if defined?(ActionController::Base)
|
|
102
126
|
|
|
103
127
|
# Silence ActionView logging
|
|
104
|
-
if defined?(ActionView::Base)
|
|
105
|
-
ActionView::Base.logger = null_logger
|
|
106
|
-
end
|
|
128
|
+
ActionView::Base.logger = null_logger if defined?(ActionView::Base)
|
|
107
129
|
|
|
108
130
|
# Silence the class-level loggers for specific subscribers
|
|
109
|
-
if defined?(ActionController::LogSubscriber)
|
|
110
|
-
ActionController::LogSubscriber.logger = null_logger
|
|
111
|
-
end
|
|
131
|
+
ActionController::LogSubscriber.logger = null_logger if defined?(ActionController::LogSubscriber)
|
|
112
132
|
|
|
113
|
-
if defined?(ActionView::LogSubscriber)
|
|
114
|
-
ActionView::LogSubscriber.logger = null_logger
|
|
115
|
-
end
|
|
133
|
+
ActionView::LogSubscriber.logger = null_logger if defined?(ActionView::LogSubscriber)
|
|
116
134
|
|
|
117
|
-
if defined?(ActiveRecord::LogSubscriber)
|
|
118
|
-
ActiveRecord::LogSubscriber.logger = null_logger
|
|
119
|
-
end
|
|
135
|
+
ActiveRecord::LogSubscriber.logger = null_logger if defined?(ActiveRecord::LogSubscriber)
|
|
120
136
|
|
|
121
137
|
# Silence ActionCable logging
|
|
122
|
-
if defined?(ActionCable::Server::Base)
|
|
123
|
-
ActionCable.server.config.logger = null_logger
|
|
124
|
-
end
|
|
138
|
+
ActionCable.server.config.logger = null_logger if defined?(ActionCable::Server::Base)
|
|
125
139
|
|
|
126
140
|
if defined?(ActionCable::Connection::TaggedLoggerProxy)
|
|
127
141
|
# ActionCable uses a tagged logger proxy that we need to quiet
|
|
@@ -151,7 +165,7 @@ module BrainzLab
|
|
|
151
165
|
/^Rendering/,
|
|
152
166
|
/^Rendered/,
|
|
153
167
|
/^\[ActionCable\] Broadcasting/,
|
|
154
|
-
/^\s*$/
|
|
168
|
+
/^\s*$/ # Empty lines
|
|
155
169
|
].freeze
|
|
156
170
|
|
|
157
171
|
def call(severity, datetime, progname, msg)
|
|
@@ -182,7 +196,7 @@ module BrainzLab
|
|
|
182
196
|
|
|
183
197
|
# Set request context
|
|
184
198
|
context = BrainzLab::Context.current
|
|
185
|
-
request_id = request.request_id || env[
|
|
199
|
+
request_id = request.request_id || env['action_dispatch.request_id']
|
|
186
200
|
context.request_id = request_id
|
|
187
201
|
|
|
188
202
|
# Store request_id in thread local for log subscriber
|
|
@@ -191,7 +205,11 @@ module BrainzLab
|
|
|
191
205
|
# Capture session_id - access session to ensure it's loaded
|
|
192
206
|
if request.session.respond_to?(:id)
|
|
193
207
|
# Force session load by accessing it
|
|
194
|
-
session_id =
|
|
208
|
+
session_id = begin
|
|
209
|
+
request.session.id
|
|
210
|
+
rescue StandardError
|
|
211
|
+
nil
|
|
212
|
+
end
|
|
195
213
|
context.session_id = session_id.to_s if session_id.present?
|
|
196
214
|
end
|
|
197
215
|
|
|
@@ -205,7 +223,7 @@ module BrainzLab
|
|
|
205
223
|
# Add breadcrumb for request start
|
|
206
224
|
BrainzLab::Reflex.add_breadcrumb(
|
|
207
225
|
"#{request.request_method} #{request.path}",
|
|
208
|
-
category:
|
|
226
|
+
category: 'http.request',
|
|
209
227
|
level: :info,
|
|
210
228
|
data: { url: request.url }
|
|
211
229
|
)
|
|
@@ -229,7 +247,7 @@ module BrainzLab
|
|
|
229
247
|
Thread.current[:brainzlab_pulse_breakdown] = nil
|
|
230
248
|
BrainzLab::Pulse.start_trace(
|
|
231
249
|
"#{request.request_method} #{request.path}",
|
|
232
|
-
kind:
|
|
250
|
+
kind: 'request',
|
|
233
251
|
parent_context: parent_context
|
|
234
252
|
)
|
|
235
253
|
end
|
|
@@ -239,7 +257,7 @@ module BrainzLab
|
|
|
239
257
|
# Add breadcrumb for response
|
|
240
258
|
BrainzLab::Reflex.add_breadcrumb(
|
|
241
259
|
"Response #{status}",
|
|
242
|
-
category:
|
|
260
|
+
category: 'http.response',
|
|
243
261
|
level: status >= 400 ? :error : :info,
|
|
244
262
|
data: { status: status }
|
|
245
263
|
)
|
|
@@ -257,9 +275,7 @@ module BrainzLab
|
|
|
257
275
|
raise
|
|
258
276
|
ensure
|
|
259
277
|
# Finish Pulse trace for successful requests
|
|
260
|
-
if should_trace &&
|
|
261
|
-
record_pulse_trace(request, started_at, status)
|
|
262
|
-
end
|
|
278
|
+
record_pulse_trace(request, started_at, status) if should_trace && !$ERROR_INFO
|
|
263
279
|
|
|
264
280
|
Thread.current[:brainzlab_request_id] = nil
|
|
265
281
|
BrainzLab::Context.clear!
|
|
@@ -273,8 +289,8 @@ module BrainzLab
|
|
|
273
289
|
path = request.path
|
|
274
290
|
|
|
275
291
|
# Check if path matches any excluded pattern
|
|
276
|
-
|
|
277
|
-
if pattern.include?(
|
|
292
|
+
excluded.none? do |pattern|
|
|
293
|
+
if pattern.include?('*')
|
|
278
294
|
File.fnmatch?(pattern, path)
|
|
279
295
|
else
|
|
280
296
|
path.start_with?(pattern)
|
|
@@ -290,7 +306,6 @@ module BrainzLab
|
|
|
290
306
|
spans = Thread.current[:brainzlab_pulse_spans] || []
|
|
291
307
|
breakdown = Thread.current[:brainzlab_pulse_breakdown] || {}
|
|
292
308
|
|
|
293
|
-
|
|
294
309
|
# Format spans for API
|
|
295
310
|
formatted_spans = spans.map do |span|
|
|
296
311
|
{
|
|
@@ -306,7 +321,7 @@ module BrainzLab
|
|
|
306
321
|
|
|
307
322
|
BrainzLab::Pulse.record_trace(
|
|
308
323
|
"#{request.request_method} #{request.path}",
|
|
309
|
-
kind:
|
|
324
|
+
kind: 'request',
|
|
310
325
|
started_at: started_at,
|
|
311
326
|
ended_at: ended_at,
|
|
312
327
|
request_id: context.request_id,
|
|
@@ -344,11 +359,11 @@ module BrainzLab
|
|
|
344
359
|
case obj
|
|
345
360
|
when Hash
|
|
346
361
|
obj.each_with_object({}) do |(k, v), h|
|
|
347
|
-
if BrainzLab::Reflex::FILTERED_PARAMS.include?(k.to_s)
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
362
|
+
h[k] = if BrainzLab::Reflex::FILTERED_PARAMS.include?(k.to_s)
|
|
363
|
+
'[FILTERED]'
|
|
364
|
+
else
|
|
365
|
+
deep_filter(v)
|
|
366
|
+
end
|
|
352
367
|
end
|
|
353
368
|
when Array
|
|
354
369
|
obj.map { |v| deep_filter(v) }
|
|
@@ -375,11 +390,11 @@ module BrainzLab
|
|
|
375
390
|
def extract_headers(env)
|
|
376
391
|
headers = {}
|
|
377
392
|
env.each do |key, value|
|
|
378
|
-
next unless key.start_with?(
|
|
379
|
-
next if key ==
|
|
380
|
-
next if key ==
|
|
393
|
+
next unless key.start_with?('HTTP_')
|
|
394
|
+
next if key == 'HTTP_COOKIE'
|
|
395
|
+
next if key == 'HTTP_AUTHORIZATION'
|
|
381
396
|
|
|
382
|
-
header_name = key.sub(
|
|
397
|
+
header_name = key.sub('HTTP_', '').split('_').map(&:capitalize).join('-')
|
|
383
398
|
headers[header_name] = value
|
|
384
399
|
end
|
|
385
400
|
headers
|
|
@@ -391,11 +406,10 @@ module BrainzLab
|
|
|
391
406
|
def report(error, handled:, severity:, context: {}, source: nil)
|
|
392
407
|
# Capture both handled and unhandled, but mark them
|
|
393
408
|
BrainzLab::Reflex.capture(error,
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
)
|
|
409
|
+
handled: handled,
|
|
410
|
+
severity: severity.to_s,
|
|
411
|
+
source: source,
|
|
412
|
+
extra: context)
|
|
399
413
|
rescue StandardError => e
|
|
400
414
|
BrainzLab.configuration.logger&.error("[BrainzLab] ErrorSubscriber failed: #{e.message}")
|
|
401
415
|
end
|
|
@@ -421,7 +435,7 @@ module BrainzLab
|
|
|
421
435
|
# Add breadcrumb
|
|
422
436
|
BrainzLab::Reflex.add_breadcrumb(
|
|
423
437
|
"#{self.class.name}##{action_name}",
|
|
424
|
-
category:
|
|
438
|
+
category: 'controller',
|
|
425
439
|
level: :info
|
|
426
440
|
)
|
|
427
441
|
|
|
@@ -458,7 +472,7 @@ module BrainzLab
|
|
|
458
472
|
|
|
459
473
|
BrainzLab::Reflex.add_breadcrumb(
|
|
460
474
|
"Job #{self.class.name}",
|
|
461
|
-
category:
|
|
475
|
+
category: 'job',
|
|
462
476
|
level: :info,
|
|
463
477
|
data: { job_id: job_id, queue: queue_name }
|
|
464
478
|
)
|
|
@@ -468,7 +482,7 @@ module BrainzLab
|
|
|
468
482
|
if should_trace
|
|
469
483
|
Thread.current[:brainzlab_pulse_spans] = []
|
|
470
484
|
Thread.current[:brainzlab_pulse_breakdown] = nil
|
|
471
|
-
BrainzLab::Pulse.start_trace(self.class.name, kind:
|
|
485
|
+
BrainzLab::Pulse.start_trace(self.class.name, kind: 'job')
|
|
472
486
|
end
|
|
473
487
|
|
|
474
488
|
error_occurred = nil
|
|
@@ -480,9 +494,7 @@ module BrainzLab
|
|
|
480
494
|
end
|
|
481
495
|
ensure
|
|
482
496
|
# Record Pulse trace for job
|
|
483
|
-
if should_trace
|
|
484
|
-
record_pulse_job_trace(started_at, error_occurred)
|
|
485
|
-
end
|
|
497
|
+
record_pulse_job_trace(started_at, error_occurred) if should_trace
|
|
486
498
|
|
|
487
499
|
BrainzLab::Context.clear!
|
|
488
500
|
end
|
|
@@ -517,7 +529,7 @@ module BrainzLab
|
|
|
517
529
|
|
|
518
530
|
BrainzLab::Pulse.record_trace(
|
|
519
531
|
self.class.name,
|
|
520
|
-
kind:
|
|
532
|
+
kind: 'job',
|
|
521
533
|
started_at: started_at,
|
|
522
534
|
ended_at: ended_at,
|
|
523
535
|
job_class: self.class.name,
|
|
@@ -556,32 +568,30 @@ module BrainzLab
|
|
|
556
568
|
|
|
557
569
|
def brainzlab_rescue_job(exception)
|
|
558
570
|
BrainzLab::Reflex.capture(exception,
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
)
|
|
571
|
+
tags: { type: 'background_job' },
|
|
572
|
+
extra: {
|
|
573
|
+
job_class: self.class.name,
|
|
574
|
+
job_id: job_id,
|
|
575
|
+
queue_name: queue_name,
|
|
576
|
+
executions: executions,
|
|
577
|
+
arguments: arguments.map(&:to_s).first(5)
|
|
578
|
+
})
|
|
568
579
|
raise exception # Re-raise to let ActiveJob handle retries
|
|
569
580
|
end
|
|
570
581
|
end
|
|
571
582
|
|
|
572
|
-
# Sidekiq error handler
|
|
583
|
+
# Sidekiq error handler - Sidekiq 7.x+ requires 3 arguments
|
|
573
584
|
class SidekiqErrorHandler
|
|
574
|
-
def call(exception, context)
|
|
585
|
+
def call(exception, context, _config = nil)
|
|
575
586
|
BrainzLab::Reflex.capture(exception,
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
)
|
|
587
|
+
tags: { type: 'sidekiq' },
|
|
588
|
+
extra: {
|
|
589
|
+
job_class: context[:job]['class'],
|
|
590
|
+
job_id: context[:job]['jid'],
|
|
591
|
+
queue: context[:job]['queue'],
|
|
592
|
+
args: context[:job]['args']&.map(&:to_s)&.first(5),
|
|
593
|
+
retry_count: context[:job]['retry_count']
|
|
594
|
+
})
|
|
585
595
|
rescue StandardError => e
|
|
586
596
|
BrainzLab.configuration.logger&.error("[BrainzLab] Sidekiq handler failed: #{e.message}")
|
|
587
597
|
end
|