brainzlab 0.1.3 → 0.1.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/lib/brainzlab/configuration.rb +20 -0
- data/lib/brainzlab/instrumentation/action_cable.rb +351 -0
- data/lib/brainzlab/instrumentation/action_controller.rb +649 -0
- data/lib/brainzlab/instrumentation/action_dispatch.rb +259 -0
- data/lib/brainzlab/instrumentation/action_mailbox.rb +197 -0
- data/lib/brainzlab/instrumentation/action_view.rb +380 -0
- data/lib/brainzlab/instrumentation/active_job.rb +569 -0
- data/lib/brainzlab/instrumentation/active_record.rb +458 -25
- data/lib/brainzlab/instrumentation/active_storage.rb +541 -0
- data/lib/brainzlab/instrumentation/active_support_cache.rb +700 -0
- data/lib/brainzlab/instrumentation/rails_deprecation.rb +139 -0
- data/lib/brainzlab/instrumentation/railties.rb +134 -0
- data/lib/brainzlab/instrumentation.rb +91 -1
- data/lib/brainzlab/version.rb +1 -1
- metadata +11 -1
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module BrainzLab
|
|
4
|
+
module Instrumentation
|
|
5
|
+
class RailsDeprecation
|
|
6
|
+
class << self
|
|
7
|
+
def install!
|
|
8
|
+
return unless defined?(::Rails)
|
|
9
|
+
return if @installed
|
|
10
|
+
|
|
11
|
+
install_deprecation_subscriber!
|
|
12
|
+
|
|
13
|
+
@installed = true
|
|
14
|
+
BrainzLab.debug_log('Rails deprecation instrumentation installed')
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def installed?
|
|
18
|
+
@installed == true
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
private
|
|
22
|
+
|
|
23
|
+
# ============================================
|
|
24
|
+
# deprecation.rails
|
|
25
|
+
# Fired when a deprecated Rails API is used
|
|
26
|
+
# ============================================
|
|
27
|
+
def install_deprecation_subscriber!
|
|
28
|
+
ActiveSupport::Notifications.subscribe('deprecation.rails') do |*args|
|
|
29
|
+
event = ActiveSupport::Notifications::Event.new(*args)
|
|
30
|
+
handle_deprecation(event)
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def handle_deprecation(event)
|
|
35
|
+
payload = event.payload
|
|
36
|
+
|
|
37
|
+
message = payload[:message]
|
|
38
|
+
callstack = payload[:callstack]
|
|
39
|
+
gem_name = payload[:gem_name]
|
|
40
|
+
deprecation_horizon = payload[:deprecation_horizon]
|
|
41
|
+
|
|
42
|
+
# Extract relevant caller info
|
|
43
|
+
caller_info = extract_caller_info(callstack)
|
|
44
|
+
|
|
45
|
+
# Record breadcrumb with warning level
|
|
46
|
+
if BrainzLab.configuration.reflex_effectively_enabled?
|
|
47
|
+
BrainzLab::Reflex.add_breadcrumb(
|
|
48
|
+
"Deprecation: #{truncate_message(message)}",
|
|
49
|
+
category: 'rails.deprecation',
|
|
50
|
+
level: :warning,
|
|
51
|
+
data: {
|
|
52
|
+
message: truncate_message(message, 500),
|
|
53
|
+
gem_name: gem_name,
|
|
54
|
+
deprecation_horizon: deprecation_horizon,
|
|
55
|
+
caller: caller_info
|
|
56
|
+
}.compact
|
|
57
|
+
)
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
# Log to Recall for tracking
|
|
61
|
+
if BrainzLab.configuration.recall_effectively_enabled?
|
|
62
|
+
BrainzLab::Recall.warn(
|
|
63
|
+
"Rails deprecation warning",
|
|
64
|
+
message: truncate_message(message, 500),
|
|
65
|
+
gem_name: gem_name,
|
|
66
|
+
deprecation_horizon: deprecation_horizon,
|
|
67
|
+
caller: caller_info,
|
|
68
|
+
callstack: truncate_callstack(callstack)
|
|
69
|
+
)
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
# Track deprecation count in Pulse metrics
|
|
73
|
+
record_deprecation_metric(gem_name, deprecation_horizon)
|
|
74
|
+
rescue StandardError => e
|
|
75
|
+
BrainzLab.debug_log("Rails deprecation instrumentation failed: #{e.message}")
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
# ============================================
|
|
79
|
+
# Helper Methods
|
|
80
|
+
# ============================================
|
|
81
|
+
def extract_caller_info(callstack)
|
|
82
|
+
return nil unless callstack.is_a?(Array) && callstack.any?
|
|
83
|
+
|
|
84
|
+
# Find the first non-Rails, non-gem caller
|
|
85
|
+
app_caller = callstack.find do |line|
|
|
86
|
+
line_str = line.to_s
|
|
87
|
+
!line_str.include?('/gems/') &&
|
|
88
|
+
!line_str.include?('/ruby/') &&
|
|
89
|
+
!line_str.include?('/bundler/')
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
(app_caller || callstack.first).to_s
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
def truncate_callstack(callstack, max_lines = 5)
|
|
96
|
+
return nil unless callstack.is_a?(Array)
|
|
97
|
+
|
|
98
|
+
callstack.first(max_lines).map(&:to_s)
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
def truncate_message(message, max_length = 200)
|
|
102
|
+
return 'unknown' unless message
|
|
103
|
+
|
|
104
|
+
msg_str = message.to_s
|
|
105
|
+
if msg_str.length > max_length
|
|
106
|
+
"#{msg_str[0, max_length - 3]}..."
|
|
107
|
+
else
|
|
108
|
+
msg_str
|
|
109
|
+
end
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
def record_deprecation_metric(gem_name, horizon)
|
|
113
|
+
return unless BrainzLab.configuration.pulse_effectively_enabled?
|
|
114
|
+
|
|
115
|
+
# If Pulse has a counter/metric API, use it here
|
|
116
|
+
# For now, we just add a span to track it
|
|
117
|
+
tracer = BrainzLab::Pulse.tracer
|
|
118
|
+
return unless tracer.current_trace
|
|
119
|
+
|
|
120
|
+
span_data = {
|
|
121
|
+
span_id: SecureRandom.uuid,
|
|
122
|
+
name: 'rails.deprecation',
|
|
123
|
+
kind: 'internal',
|
|
124
|
+
started_at: Time.now,
|
|
125
|
+
ended_at: Time.now,
|
|
126
|
+
duration_ms: 0,
|
|
127
|
+
error: false,
|
|
128
|
+
data: {
|
|
129
|
+
'deprecation.gem_name' => gem_name,
|
|
130
|
+
'deprecation.horizon' => horizon
|
|
131
|
+
}.compact
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
tracer.current_spans << span_data
|
|
135
|
+
end
|
|
136
|
+
end
|
|
137
|
+
end
|
|
138
|
+
end
|
|
139
|
+
end
|
|
@@ -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
|
|
@@ -31,9 +31,17 @@ module BrainzLab
|
|
|
31
31
|
# Elasticsearch instrumentation
|
|
32
32
|
install_elasticsearch! if config.instrument_elasticsearch
|
|
33
33
|
|
|
34
|
-
#
|
|
34
|
+
# Rails MVC instrumentation
|
|
35
|
+
install_action_controller! if config.instrument_action_controller
|
|
36
|
+
install_action_view! if config.instrument_action_view
|
|
35
37
|
install_action_mailer! if config.instrument_action_mailer
|
|
36
38
|
|
|
39
|
+
# ActiveJob instrumentation (covers all job backends)
|
|
40
|
+
install_active_job! if config.instrument_active_job
|
|
41
|
+
|
|
42
|
+
# ActiveSupport::Cache instrumentation
|
|
43
|
+
install_active_support_cache! if config.instrument_active_support_cache
|
|
44
|
+
|
|
37
45
|
# Delayed::Job instrumentation
|
|
38
46
|
install_delayed_job! if config.instrument_delayed_job
|
|
39
47
|
|
|
@@ -55,6 +63,18 @@ module BrainzLab
|
|
|
55
63
|
# Cloud & Payment
|
|
56
64
|
install_aws! if config.instrument_aws
|
|
57
65
|
install_stripe! if config.instrument_stripe
|
|
66
|
+
|
|
67
|
+
# File storage
|
|
68
|
+
install_active_storage! if config.instrument_active_storage
|
|
69
|
+
|
|
70
|
+
# WebSocket
|
|
71
|
+
install_action_cable! if config.instrument_action_cable
|
|
72
|
+
|
|
73
|
+
# Rails framework events
|
|
74
|
+
install_action_dispatch! if config.instrument_action_dispatch
|
|
75
|
+
install_rails_deprecation! if config.instrument_rails_deprecation
|
|
76
|
+
install_action_mailbox! if config.instrument_action_mailbox
|
|
77
|
+
install_railties! if config.instrument_railties
|
|
58
78
|
end
|
|
59
79
|
|
|
60
80
|
def install_net_http!
|
|
@@ -116,6 +136,20 @@ module BrainzLab
|
|
|
116
136
|
ElasticsearchInstrumentation.install!
|
|
117
137
|
end
|
|
118
138
|
|
|
139
|
+
def install_action_controller!
|
|
140
|
+
return unless defined?(::ActionController)
|
|
141
|
+
|
|
142
|
+
require_relative 'instrumentation/action_controller'
|
|
143
|
+
ActionController.install!
|
|
144
|
+
end
|
|
145
|
+
|
|
146
|
+
def install_action_view!
|
|
147
|
+
return unless defined?(::ActionView)
|
|
148
|
+
|
|
149
|
+
require_relative 'instrumentation/action_view'
|
|
150
|
+
ActionView.install!
|
|
151
|
+
end
|
|
152
|
+
|
|
119
153
|
def install_action_mailer!
|
|
120
154
|
return unless defined?(::ActionMailer)
|
|
121
155
|
|
|
@@ -123,6 +157,20 @@ module BrainzLab
|
|
|
123
157
|
ActionMailerInstrumentation.install!
|
|
124
158
|
end
|
|
125
159
|
|
|
160
|
+
def install_active_job!
|
|
161
|
+
return unless defined?(::ActiveJob)
|
|
162
|
+
|
|
163
|
+
require_relative 'instrumentation/active_job'
|
|
164
|
+
ActiveJob.install!
|
|
165
|
+
end
|
|
166
|
+
|
|
167
|
+
def install_active_support_cache!
|
|
168
|
+
return unless defined?(::ActiveSupport::Cache)
|
|
169
|
+
|
|
170
|
+
require_relative 'instrumentation/active_support_cache'
|
|
171
|
+
ActiveSupportCache.install!
|
|
172
|
+
end
|
|
173
|
+
|
|
126
174
|
def install_delayed_job!
|
|
127
175
|
return unless defined?(::Delayed::Job) || defined?(::Delayed::Backend)
|
|
128
176
|
|
|
@@ -193,6 +241,48 @@ module BrainzLab
|
|
|
193
241
|
StripeInstrumentation.install!
|
|
194
242
|
end
|
|
195
243
|
|
|
244
|
+
def install_active_storage!
|
|
245
|
+
return unless defined?(::ActiveStorage)
|
|
246
|
+
|
|
247
|
+
require_relative 'instrumentation/active_storage'
|
|
248
|
+
ActiveStorage.install!
|
|
249
|
+
end
|
|
250
|
+
|
|
251
|
+
def install_action_cable!
|
|
252
|
+
return unless defined?(::ActionCable)
|
|
253
|
+
|
|
254
|
+
require_relative 'instrumentation/action_cable'
|
|
255
|
+
ActionCable.install!
|
|
256
|
+
end
|
|
257
|
+
|
|
258
|
+
def install_action_dispatch!
|
|
259
|
+
return unless defined?(::ActionDispatch)
|
|
260
|
+
|
|
261
|
+
require_relative 'instrumentation/action_dispatch'
|
|
262
|
+
ActionDispatch.install!
|
|
263
|
+
end
|
|
264
|
+
|
|
265
|
+
def install_rails_deprecation!
|
|
266
|
+
return unless defined?(::Rails)
|
|
267
|
+
|
|
268
|
+
require_relative 'instrumentation/rails_deprecation'
|
|
269
|
+
RailsDeprecation.install!
|
|
270
|
+
end
|
|
271
|
+
|
|
272
|
+
def install_action_mailbox!
|
|
273
|
+
return unless defined?(::ActionMailbox)
|
|
274
|
+
|
|
275
|
+
require_relative 'instrumentation/action_mailbox'
|
|
276
|
+
ActionMailbox.install!
|
|
277
|
+
end
|
|
278
|
+
|
|
279
|
+
def install_railties!
|
|
280
|
+
return unless defined?(::Rails)
|
|
281
|
+
|
|
282
|
+
require_relative 'instrumentation/railties'
|
|
283
|
+
Railties.install!
|
|
284
|
+
end
|
|
285
|
+
|
|
196
286
|
# Manual installation methods for lazy-loaded libraries
|
|
197
287
|
def install_http!
|
|
198
288
|
install_net_http!
|
data/lib/brainzlab/version.rb
CHANGED
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: brainzlab
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.1.
|
|
4
|
+
version: 0.1.4
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Brainz Lab
|
|
@@ -123,8 +123,16 @@ files:
|
|
|
123
123
|
- lib/brainzlab/flux/client.rb
|
|
124
124
|
- lib/brainzlab/flux/provisioner.rb
|
|
125
125
|
- lib/brainzlab/instrumentation.rb
|
|
126
|
+
- lib/brainzlab/instrumentation/action_cable.rb
|
|
127
|
+
- lib/brainzlab/instrumentation/action_controller.rb
|
|
128
|
+
- lib/brainzlab/instrumentation/action_dispatch.rb
|
|
129
|
+
- lib/brainzlab/instrumentation/action_mailbox.rb
|
|
126
130
|
- lib/brainzlab/instrumentation/action_mailer.rb
|
|
131
|
+
- lib/brainzlab/instrumentation/action_view.rb
|
|
132
|
+
- lib/brainzlab/instrumentation/active_job.rb
|
|
127
133
|
- lib/brainzlab/instrumentation/active_record.rb
|
|
134
|
+
- lib/brainzlab/instrumentation/active_storage.rb
|
|
135
|
+
- lib/brainzlab/instrumentation/active_support_cache.rb
|
|
128
136
|
- lib/brainzlab/instrumentation/aws.rb
|
|
129
137
|
- lib/brainzlab/instrumentation/dalli.rb
|
|
130
138
|
- lib/brainzlab/instrumentation/delayed_job.rb
|
|
@@ -137,6 +145,8 @@ files:
|
|
|
137
145
|
- lib/brainzlab/instrumentation/httparty.rb
|
|
138
146
|
- lib/brainzlab/instrumentation/mongodb.rb
|
|
139
147
|
- lib/brainzlab/instrumentation/net_http.rb
|
|
148
|
+
- lib/brainzlab/instrumentation/rails_deprecation.rb
|
|
149
|
+
- lib/brainzlab/instrumentation/railties.rb
|
|
140
150
|
- lib/brainzlab/instrumentation/redis.rb
|
|
141
151
|
- lib/brainzlab/instrumentation/resque.rb
|
|
142
152
|
- lib/brainzlab/instrumentation/sidekiq.rb
|