apm_bro 0.1.14 → 0.1.15
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/README.md +0 -35
- data/lib/apm_bro/cache_subscriber.rb +15 -25
- data/lib/apm_bro/circuit_breaker.rb +9 -17
- data/lib/apm_bro/client.rb +28 -36
- data/lib/apm_bro/configuration.rb +44 -62
- data/lib/apm_bro/error_middleware.rb +16 -30
- data/lib/apm_bro/http_instrumentation.rb +22 -16
- data/lib/apm_bro/job_sql_tracking_middleware.rb +1 -1
- data/lib/apm_bro/job_subscriber.rb +41 -25
- data/lib/apm_bro/lightweight_memory_tracker.rb +8 -8
- data/lib/apm_bro/logger.rb +8 -9
- data/lib/apm_bro/memory_helpers.rb +13 -13
- data/lib/apm_bro/memory_leak_detector.rb +37 -37
- data/lib/apm_bro/memory_tracking_subscriber.rb +62 -54
- data/lib/apm_bro/railtie.rb +28 -30
- data/lib/apm_bro/redis_subscriber.rb +34 -37
- data/lib/apm_bro/sql_subscriber.rb +60 -59
- data/lib/apm_bro/sql_tracking_middleware.rb +11 -11
- data/lib/apm_bro/subscriber.rb +53 -39
- data/lib/apm_bro/version.rb +1 -1
- data/lib/apm_bro/view_rendering_subscriber.rb +23 -23
- metadata +4 -4
|
@@ -5,12 +5,12 @@ require "active_support/notifications"
|
|
|
5
5
|
module ApmBro
|
|
6
6
|
class MemoryTrackingSubscriber
|
|
7
7
|
# Object allocation events
|
|
8
|
-
ALLOCATION_EVENT = "object_allocations.active_support"
|
|
9
|
-
|
|
8
|
+
ALLOCATION_EVENT = "object_allocations.active_support"
|
|
9
|
+
|
|
10
10
|
THREAD_LOCAL_KEY = :apm_bro_memory_events
|
|
11
11
|
# Consider objects larger than this many bytes as "large"
|
|
12
12
|
LARGE_OBJECT_THRESHOLD = 1_000_000 # 1MB threshold for large objects
|
|
13
|
-
|
|
13
|
+
|
|
14
14
|
# Performance optimization settings
|
|
15
15
|
ALLOCATION_SAMPLING_RATE = 1 # Track all when enabled (adjust in production)
|
|
16
16
|
MAX_ALLOCATIONS_PER_REQUEST = 1000 # Limit allocations tracked per request
|
|
@@ -28,18 +28,18 @@ module ApmBro
|
|
|
28
28
|
next unless rand < ALLOCATION_SAMPLING_RATE
|
|
29
29
|
track_allocation(data, started, finished)
|
|
30
30
|
end
|
|
31
|
-
rescue
|
|
31
|
+
rescue
|
|
32
32
|
# Allocation tracking might not be available in all Ruby versions
|
|
33
33
|
end
|
|
34
34
|
end
|
|
35
|
-
rescue
|
|
35
|
+
rescue
|
|
36
36
|
# Never raise from instrumentation install
|
|
37
37
|
end
|
|
38
38
|
|
|
39
39
|
def self.start_request_tracking
|
|
40
40
|
# Only track if memory tracking is enabled
|
|
41
41
|
return unless ApmBro.configuration.memory_tracking_enabled
|
|
42
|
-
|
|
42
|
+
|
|
43
43
|
Thread.current[THREAD_LOCAL_KEY] = {
|
|
44
44
|
allocations: [],
|
|
45
45
|
memory_snapshots: [],
|
|
@@ -54,7 +54,7 @@ module ApmBro
|
|
|
54
54
|
def self.stop_request_tracking
|
|
55
55
|
events = Thread.current[THREAD_LOCAL_KEY]
|
|
56
56
|
Thread.current[THREAD_LOCAL_KEY] = nil
|
|
57
|
-
|
|
57
|
+
|
|
58
58
|
if events
|
|
59
59
|
events[:gc_after] = gc_stats
|
|
60
60
|
events[:memory_after] = memory_usage_mb
|
|
@@ -67,20 +67,20 @@ module ApmBro
|
|
|
67
67
|
events[:large_objects] = sample_large_objects
|
|
68
68
|
end
|
|
69
69
|
end
|
|
70
|
-
|
|
70
|
+
|
|
71
71
|
events || {}
|
|
72
72
|
end
|
|
73
73
|
|
|
74
74
|
def self.track_allocation(data, started, finished)
|
|
75
75
|
return unless Thread.current[THREAD_LOCAL_KEY]
|
|
76
|
-
|
|
76
|
+
|
|
77
77
|
# Only track if we have meaningful allocation data
|
|
78
78
|
return unless data.is_a?(Hash) && data[:count] && data[:size]
|
|
79
|
-
|
|
79
|
+
|
|
80
80
|
# Limit allocations per request to prevent memory bloat
|
|
81
81
|
allocations = Thread.current[THREAD_LOCAL_KEY][:allocations]
|
|
82
82
|
return if allocations.length >= MAX_ALLOCATIONS_PER_REQUEST
|
|
83
|
-
|
|
83
|
+
|
|
84
84
|
# Simplified allocation tracking (avoid expensive operations)
|
|
85
85
|
allocation = {
|
|
86
86
|
class_name: data[:class_name] || "Unknown",
|
|
@@ -88,7 +88,7 @@ module ApmBro
|
|
|
88
88
|
size: data[:size]
|
|
89
89
|
# Removed expensive fields: duration_ms, timestamp, memory_usage
|
|
90
90
|
}
|
|
91
|
-
|
|
91
|
+
|
|
92
92
|
# Track large object allocations (these are rare and important)
|
|
93
93
|
if data[:size] > LARGE_OBJECT_THRESHOLD
|
|
94
94
|
large_object = allocation.merge(
|
|
@@ -97,13 +97,13 @@ module ApmBro
|
|
|
97
97
|
)
|
|
98
98
|
Thread.current[THREAD_LOCAL_KEY][:large_objects] << large_object
|
|
99
99
|
end
|
|
100
|
-
|
|
100
|
+
|
|
101
101
|
Thread.current[THREAD_LOCAL_KEY][:allocations] << allocation
|
|
102
102
|
end
|
|
103
103
|
|
|
104
104
|
def self.take_memory_snapshot(label = nil)
|
|
105
105
|
return unless Thread.current[THREAD_LOCAL_KEY]
|
|
106
|
-
|
|
106
|
+
|
|
107
107
|
snapshot = {
|
|
108
108
|
label: label || "snapshot_#{Time.now.to_i}",
|
|
109
109
|
memory_usage: memory_usage_mb,
|
|
@@ -112,45 +112,45 @@ module ApmBro
|
|
|
112
112
|
object_count: object_count,
|
|
113
113
|
heap_pages: heap_pages
|
|
114
114
|
}
|
|
115
|
-
|
|
115
|
+
|
|
116
116
|
Thread.current[THREAD_LOCAL_KEY][:memory_snapshots] << snapshot
|
|
117
117
|
end
|
|
118
118
|
|
|
119
119
|
def self.analyze_memory_performance(memory_events)
|
|
120
120
|
return {} if memory_events.empty?
|
|
121
|
-
|
|
121
|
+
|
|
122
122
|
allocations = memory_events[:allocations] || []
|
|
123
123
|
large_objects = memory_events[:large_objects] || []
|
|
124
124
|
snapshots = memory_events[:memory_snapshots] || []
|
|
125
|
-
|
|
125
|
+
|
|
126
126
|
# Calculate memory growth
|
|
127
127
|
memory_growth = 0
|
|
128
128
|
if memory_events[:memory_before] && memory_events[:memory_after]
|
|
129
129
|
memory_growth = memory_events[:memory_after] - memory_events[:memory_before]
|
|
130
130
|
end
|
|
131
|
-
|
|
131
|
+
|
|
132
132
|
# Calculate allocation totals
|
|
133
133
|
total_allocations = allocations.sum { |a| a[:count] }
|
|
134
134
|
total_allocated_size = allocations.sum { |a| a[:size] }
|
|
135
|
-
|
|
135
|
+
|
|
136
136
|
# Group allocations by class
|
|
137
137
|
allocations_by_class = allocations.group_by { |a| a[:class_name] }
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
138
|
+
.transform_values { |allocs|
|
|
139
|
+
{
|
|
140
|
+
count: allocs.sum { |a| a[:count] },
|
|
141
|
+
size: allocs.sum { |a| a[:size] }
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
|
|
145
145
|
# Find top allocating classes
|
|
146
146
|
top_allocating_classes = allocations_by_class.sort_by { |_, data| -data[:size] }.first(10)
|
|
147
|
-
|
|
147
|
+
|
|
148
148
|
# Analyze large objects
|
|
149
149
|
large_object_analysis = analyze_large_objects(large_objects)
|
|
150
|
-
|
|
150
|
+
|
|
151
151
|
# Analyze memory snapshots for trends
|
|
152
152
|
memory_trends = analyze_memory_trends(snapshots)
|
|
153
|
-
|
|
153
|
+
|
|
154
154
|
# Calculate GC efficiency
|
|
155
155
|
gc_efficiency = calculate_gc_efficiency(memory_events[:gc_before], memory_events[:gc_after])
|
|
156
156
|
|
|
@@ -164,13 +164,13 @@ module ApmBro
|
|
|
164
164
|
object_type_deltas[k] = (after[k] || 0) - (before[k] || 0)
|
|
165
165
|
end
|
|
166
166
|
end
|
|
167
|
-
|
|
167
|
+
|
|
168
168
|
{
|
|
169
169
|
memory_growth_mb: memory_growth.round(2),
|
|
170
170
|
total_allocations: total_allocations,
|
|
171
171
|
total_allocated_size: total_allocated_size,
|
|
172
172
|
total_allocated_size_mb: (total_allocated_size / 1_000_000.0).round(2),
|
|
173
|
-
allocations_per_second: memory_events[:duration_seconds] > 0 ?
|
|
173
|
+
allocations_per_second: (memory_events[:duration_seconds] > 0) ?
|
|
174
174
|
(total_allocations.to_f / memory_events[:duration_seconds]).round(2) : 0,
|
|
175
175
|
top_allocating_classes: top_allocating_classes.map { |class_name, data|
|
|
176
176
|
{
|
|
@@ -190,13 +190,13 @@ module ApmBro
|
|
|
190
190
|
|
|
191
191
|
def self.analyze_large_objects(large_objects)
|
|
192
192
|
return {} if large_objects.empty?
|
|
193
|
-
|
|
193
|
+
|
|
194
194
|
{
|
|
195
195
|
count: large_objects.count,
|
|
196
196
|
total_size_mb: large_objects.sum { |obj| obj[:size_mb] }.round(2),
|
|
197
197
|
largest_object_mb: large_objects.max_by { |obj| obj[:size_mb] }[:size_mb],
|
|
198
198
|
by_class: large_objects.group_by { |obj| obj[:class_name] }
|
|
199
|
-
|
|
199
|
+
.transform_values(&:count)
|
|
200
200
|
}
|
|
201
201
|
end
|
|
202
202
|
|
|
@@ -219,15 +219,23 @@ module ApmBro
|
|
|
219
219
|
# Randomly sample to control overhead
|
|
220
220
|
next unless rand < LARGE_OBJECT_SAMPLE_RATE
|
|
221
221
|
|
|
222
|
-
size =
|
|
222
|
+
size = begin
|
|
223
|
+
ObjectSpace.memsize_of(obj)
|
|
224
|
+
rescue
|
|
225
|
+
0
|
|
226
|
+
end
|
|
223
227
|
next unless size && size > LARGE_OBJECT_THRESHOLD
|
|
224
228
|
|
|
225
|
-
klass =
|
|
226
|
-
|
|
229
|
+
klass = begin
|
|
230
|
+
(obj.respond_to?(:class) && obj.class) ? obj.class.name : "Unknown"
|
|
231
|
+
rescue
|
|
232
|
+
"Unknown"
|
|
233
|
+
end
|
|
234
|
+
results << {class_name: klass, size: size, size_mb: (size / 1_000_000.0).round(2)}
|
|
227
235
|
|
|
228
236
|
break if results.length >= MAX_LARGE_OBJECTS
|
|
229
237
|
end
|
|
230
|
-
rescue
|
|
238
|
+
rescue
|
|
231
239
|
# Best-effort only
|
|
232
240
|
end
|
|
233
241
|
|
|
@@ -241,24 +249,24 @@ module ApmBro
|
|
|
241
249
|
else
|
|
242
250
|
{}
|
|
243
251
|
end
|
|
244
|
-
rescue
|
|
252
|
+
rescue
|
|
245
253
|
{}
|
|
246
254
|
end
|
|
247
255
|
|
|
248
256
|
def self.analyze_memory_trends(snapshots)
|
|
249
257
|
return {} if snapshots.length < 2
|
|
250
|
-
|
|
258
|
+
|
|
251
259
|
# Calculate memory growth rate between snapshots
|
|
252
260
|
memory_values = snapshots.map { |s| s[:memory_usage] }
|
|
253
261
|
memory_growth_rates = []
|
|
254
|
-
|
|
262
|
+
|
|
255
263
|
(1...memory_values.length).each do |i|
|
|
256
|
-
growth = memory_values[i] - memory_values[i-1]
|
|
257
|
-
time_diff = snapshots[i][:timestamp] - snapshots[i-1][:timestamp]
|
|
258
|
-
rate = time_diff > 0 ? growth / time_diff : 0
|
|
264
|
+
growth = memory_values[i] - memory_values[i - 1]
|
|
265
|
+
time_diff = snapshots[i][:timestamp] - snapshots[i - 1][:timestamp]
|
|
266
|
+
rate = (time_diff > 0) ? growth / time_diff : 0
|
|
259
267
|
memory_growth_rates << rate
|
|
260
268
|
end
|
|
261
|
-
|
|
269
|
+
|
|
262
270
|
{
|
|
263
271
|
average_growth_rate_mb_per_second: memory_growth_rates.sum / memory_growth_rates.length,
|
|
264
272
|
max_growth_rate_mb_per_second: memory_growth_rates.max,
|
|
@@ -270,12 +278,12 @@ module ApmBro
|
|
|
270
278
|
|
|
271
279
|
def self.calculate_gc_efficiency(gc_before, gc_after)
|
|
272
280
|
return {} unless gc_before && gc_after
|
|
273
|
-
|
|
281
|
+
|
|
274
282
|
{
|
|
275
283
|
gc_count_increase: (gc_after[:count] || 0) - (gc_before[:count] || 0),
|
|
276
284
|
heap_pages_increase: (gc_after[:heap_allocated_pages] || 0) - (gc_before[:heap_allocated_pages] || 0),
|
|
277
285
|
objects_allocated: (gc_after[:total_allocated_objects] || 0) - (gc_before[:total_allocated_objects] || 0),
|
|
278
|
-
gc_frequency: gc_after[:count] && gc_before[:count] ?
|
|
286
|
+
gc_frequency: (gc_after[:count] && gc_before[:count]) ?
|
|
279
287
|
(gc_after[:count] - gc_before[:count]).to_f / [gc_after[:count], 1].max : 0
|
|
280
288
|
}
|
|
281
289
|
end
|
|
@@ -284,12 +292,12 @@ module ApmBro
|
|
|
284
292
|
# Use cached memory calculation to avoid expensive system calls
|
|
285
293
|
@memory_cache ||= {}
|
|
286
294
|
cache_key = Process.pid
|
|
287
|
-
|
|
295
|
+
|
|
288
296
|
# Cache memory usage for 1 second to avoid repeated system calls
|
|
289
297
|
if @memory_cache[cache_key] && (Time.now - @memory_cache[cache_key][:timestamp]) < 1
|
|
290
298
|
return @memory_cache[cache_key][:memory]
|
|
291
299
|
end
|
|
292
|
-
|
|
300
|
+
|
|
293
301
|
memory = if defined?(GC) && GC.respond_to?(:stat)
|
|
294
302
|
# Use GC stats as a proxy for memory usage (much faster than ps)
|
|
295
303
|
gc_stats = GC.stat
|
|
@@ -299,10 +307,10 @@ module ApmBro
|
|
|
299
307
|
else
|
|
300
308
|
0
|
|
301
309
|
end
|
|
302
|
-
|
|
303
|
-
@memory_cache[cache_key] = {
|
|
310
|
+
|
|
311
|
+
@memory_cache[cache_key] = {memory: memory, timestamp: Time.now}
|
|
304
312
|
memory
|
|
305
|
-
rescue
|
|
313
|
+
rescue
|
|
306
314
|
0
|
|
307
315
|
end
|
|
308
316
|
|
|
@@ -321,7 +329,7 @@ module ApmBro
|
|
|
321
329
|
else
|
|
322
330
|
{}
|
|
323
331
|
end
|
|
324
|
-
rescue
|
|
332
|
+
rescue
|
|
325
333
|
{}
|
|
326
334
|
end
|
|
327
335
|
|
|
@@ -331,7 +339,7 @@ module ApmBro
|
|
|
331
339
|
else
|
|
332
340
|
0
|
|
333
341
|
end
|
|
334
|
-
rescue
|
|
342
|
+
rescue
|
|
335
343
|
0
|
|
336
344
|
end
|
|
337
345
|
|
|
@@ -341,7 +349,7 @@ module ApmBro
|
|
|
341
349
|
else
|
|
342
350
|
0
|
|
343
351
|
end
|
|
344
|
-
rescue
|
|
352
|
+
rescue
|
|
345
353
|
0
|
|
346
354
|
end
|
|
347
355
|
|
data/lib/apm_bro/railtie.rb
CHANGED
|
@@ -1,13 +1,19 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
begin
|
|
4
|
+
require "rails/railtie"
|
|
5
|
+
rescue LoadError
|
|
6
|
+
# Rails not available, skip railtie definition
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
# Only define Railtie if Rails is available
|
|
10
|
+
if defined?(Rails) && defined?(Rails::Railtie)
|
|
11
|
+
module ApmBro
|
|
12
|
+
class Railtie < ::Rails::Railtie
|
|
13
|
+
initializer "apm_bro.configure" do |_app|
|
|
14
|
+
# Allow host app to set config in Rails config, credentials, or ENV.
|
|
15
|
+
# If host app sets config.x.apm_bro, mirror into gem configuration.
|
|
4
16
|
|
|
5
|
-
module ApmBro
|
|
6
|
-
class Railtie < ::Rails::Railtie
|
|
7
|
-
initializer "apm_bro.configure" do |_app|
|
|
8
|
-
# Allow host app to set config in Rails config, credentials, or ENV.
|
|
9
|
-
# If host app sets config.x.apm_bro, mirror into gem configuration.
|
|
10
|
-
begin
|
|
11
17
|
if Rails.application.config.x.respond_to?(:apm_bro)
|
|
12
18
|
xcfg = Rails.application.config.x.apm_bro
|
|
13
19
|
ApmBro.configure do |cfg|
|
|
@@ -15,22 +21,20 @@ module ApmBro
|
|
|
15
21
|
cfg.enabled = xcfg.enabled if xcfg.respond_to?(:enabled)
|
|
16
22
|
end
|
|
17
23
|
end
|
|
18
|
-
rescue
|
|
24
|
+
rescue
|
|
19
25
|
end
|
|
20
|
-
end
|
|
21
26
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
begin
|
|
27
|
+
initializer "apm_bro.subscribe" do |app|
|
|
28
|
+
app.config.after_initialize do
|
|
25
29
|
ApmBro::Subscriber.subscribe!(client: ApmBro::Client.new)
|
|
26
30
|
# Install outgoing HTTP instrumentation
|
|
27
31
|
require "apm_bro/http_instrumentation"
|
|
28
32
|
ApmBro::HttpInstrumentation.install!(client: ApmBro::Client.new)
|
|
29
|
-
|
|
33
|
+
|
|
30
34
|
# Install SQL query tracking
|
|
31
35
|
require "apm_bro/sql_subscriber"
|
|
32
36
|
ApmBro::SqlSubscriber.subscribe!
|
|
33
|
-
|
|
37
|
+
|
|
34
38
|
# Install Rails cache tracking
|
|
35
39
|
require "apm_bro/cache_subscriber"
|
|
36
40
|
ApmBro::CacheSubscriber.subscribe!
|
|
@@ -42,18 +46,18 @@ module ApmBro
|
|
|
42
46
|
# Install view rendering tracking
|
|
43
47
|
require "apm_bro/view_rendering_subscriber"
|
|
44
48
|
ApmBro::ViewRenderingSubscriber.subscribe!(client: ApmBro::Client.new)
|
|
45
|
-
|
|
49
|
+
|
|
46
50
|
# Install lightweight memory tracking (default)
|
|
47
51
|
require "apm_bro/lightweight_memory_tracker"
|
|
48
52
|
require "apm_bro/memory_leak_detector"
|
|
49
53
|
ApmBro::MemoryLeakDetector.initialize_history
|
|
50
|
-
|
|
54
|
+
|
|
51
55
|
# Install detailed memory tracking only if enabled
|
|
52
56
|
if ApmBro.configuration.allocation_tracking_enabled
|
|
53
57
|
require "apm_bro/memory_tracking_subscriber"
|
|
54
58
|
ApmBro::MemoryTrackingSubscriber.subscribe!(client: ApmBro::Client.new)
|
|
55
59
|
end
|
|
56
|
-
|
|
60
|
+
|
|
57
61
|
# Install job tracking if ActiveJob is available
|
|
58
62
|
if defined?(ActiveJob)
|
|
59
63
|
require "apm_bro/job_subscriber"
|
|
@@ -61,15 +65,13 @@ module ApmBro
|
|
|
61
65
|
ApmBro::JobSqlTrackingMiddleware.subscribe!
|
|
62
66
|
ApmBro::JobSubscriber.subscribe!(client: ApmBro::Client.new)
|
|
63
67
|
end
|
|
64
|
-
rescue
|
|
68
|
+
rescue
|
|
65
69
|
# Never raise in Railtie init
|
|
66
70
|
end
|
|
67
71
|
end
|
|
68
|
-
end
|
|
69
72
|
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
begin
|
|
73
|
+
# Insert Rack middleware early enough to observe uncaught exceptions
|
|
74
|
+
initializer "apm_bro.middleware" do |app|
|
|
73
75
|
require "apm_bro/error_middleware"
|
|
74
76
|
|
|
75
77
|
if defined?(::ActionDispatch::DebugExceptions)
|
|
@@ -79,21 +81,17 @@ module ApmBro
|
|
|
79
81
|
else
|
|
80
82
|
app.config.middleware.use(::ApmBro::ErrorMiddleware)
|
|
81
83
|
end
|
|
82
|
-
rescue
|
|
84
|
+
rescue
|
|
83
85
|
# Never raise in Railtie init
|
|
84
86
|
end
|
|
85
|
-
end
|
|
86
87
|
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
begin
|
|
88
|
+
# Insert SQL tracking middleware
|
|
89
|
+
initializer "apm_bro.sql_tracking_middleware" do |app|
|
|
90
90
|
require "apm_bro/sql_tracking_middleware"
|
|
91
91
|
app.config.middleware.use(::ApmBro::SqlTrackingMiddleware)
|
|
92
|
-
rescue
|
|
92
|
+
rescue
|
|
93
93
|
# Never raise in Railtie init
|
|
94
94
|
end
|
|
95
95
|
end
|
|
96
96
|
end
|
|
97
97
|
end
|
|
98
|
-
|
|
99
|
-
|
|
@@ -6,7 +6,7 @@ module ApmBro
|
|
|
6
6
|
|
|
7
7
|
def self.subscribe!
|
|
8
8
|
install_redis_instrumentation!
|
|
9
|
-
rescue
|
|
9
|
+
rescue
|
|
10
10
|
# Never raise from instrumentation install
|
|
11
11
|
end
|
|
12
12
|
|
|
@@ -25,7 +25,7 @@ module ApmBro
|
|
|
25
25
|
# Only instrument if Redis::Client actually has the call method
|
|
26
26
|
# Check both public and private methods
|
|
27
27
|
has_call = ::Redis::Client.instance_methods(false).include?(:call) ||
|
|
28
|
-
|
|
28
|
+
::Redis::Client.private_instance_methods(false).include?(:call)
|
|
29
29
|
return unless has_call
|
|
30
30
|
|
|
31
31
|
mod = Module.new do
|
|
@@ -41,7 +41,7 @@ module ApmBro
|
|
|
41
41
|
end
|
|
42
42
|
else
|
|
43
43
|
# If not tracking, just pass through unchanged
|
|
44
|
-
super
|
|
44
|
+
super
|
|
45
45
|
end
|
|
46
46
|
end
|
|
47
47
|
|
|
@@ -63,7 +63,6 @@ module ApmBro
|
|
|
63
63
|
return yield unless Thread.current[RedisSubscriber::THREAD_LOCAL_KEY]
|
|
64
64
|
|
|
65
65
|
start_time = Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
|
66
|
-
result = nil
|
|
67
66
|
error = nil
|
|
68
67
|
begin
|
|
69
68
|
result = yield
|
|
@@ -90,7 +89,7 @@ module ApmBro
|
|
|
90
89
|
if Thread.current[RedisSubscriber::THREAD_LOCAL_KEY]
|
|
91
90
|
Thread.current[RedisSubscriber::THREAD_LOCAL_KEY] << event
|
|
92
91
|
end
|
|
93
|
-
rescue
|
|
92
|
+
rescue
|
|
94
93
|
end
|
|
95
94
|
end
|
|
96
95
|
end
|
|
@@ -99,7 +98,6 @@ module ApmBro
|
|
|
99
98
|
return yield unless Thread.current[RedisSubscriber::THREAD_LOCAL_KEY]
|
|
100
99
|
|
|
101
100
|
start_time = Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
|
102
|
-
result = nil
|
|
103
101
|
begin
|
|
104
102
|
result = yield
|
|
105
103
|
result
|
|
@@ -119,7 +117,7 @@ module ApmBro
|
|
|
119
117
|
if Thread.current[RedisSubscriber::THREAD_LOCAL_KEY]
|
|
120
118
|
Thread.current[RedisSubscriber::THREAD_LOCAL_KEY] << event
|
|
121
119
|
end
|
|
122
|
-
rescue
|
|
120
|
+
rescue
|
|
123
121
|
end
|
|
124
122
|
end
|
|
125
123
|
end
|
|
@@ -128,7 +126,6 @@ module ApmBro
|
|
|
128
126
|
return yield unless Thread.current[RedisSubscriber::THREAD_LOCAL_KEY]
|
|
129
127
|
|
|
130
128
|
start_time = Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
|
131
|
-
result = nil
|
|
132
129
|
begin
|
|
133
130
|
result = yield
|
|
134
131
|
result
|
|
@@ -148,7 +145,7 @@ module ApmBro
|
|
|
148
145
|
if Thread.current[RedisSubscriber::THREAD_LOCAL_KEY]
|
|
149
146
|
Thread.current[RedisSubscriber::THREAD_LOCAL_KEY] << event
|
|
150
147
|
end
|
|
151
|
-
rescue
|
|
148
|
+
rescue
|
|
152
149
|
end
|
|
153
150
|
end
|
|
154
151
|
end
|
|
@@ -157,39 +154,41 @@ module ApmBro
|
|
|
157
154
|
parts = Array(command).map(&:to_s)
|
|
158
155
|
command_name = parts.first&.upcase
|
|
159
156
|
key = parts[1]
|
|
160
|
-
args_count = parts.length > 1 ? parts.length - 1 : 0
|
|
157
|
+
args_count = (parts.length > 1) ? parts.length - 1 : 0
|
|
161
158
|
|
|
162
159
|
{
|
|
163
160
|
command: safe_command(command_name),
|
|
164
161
|
key: safe_key(key),
|
|
165
162
|
args_count: args_count
|
|
166
163
|
}
|
|
167
|
-
rescue
|
|
168
|
-
{
|
|
164
|
+
rescue
|
|
165
|
+
{command: nil, key: nil, args_count: nil}
|
|
169
166
|
end
|
|
170
167
|
|
|
171
168
|
def safe_command(cmd)
|
|
172
169
|
return nil if cmd.nil?
|
|
173
170
|
cmd.to_s[0, 20]
|
|
174
|
-
rescue
|
|
171
|
+
rescue
|
|
175
172
|
nil
|
|
176
173
|
end
|
|
177
174
|
|
|
178
175
|
def safe_key(key)
|
|
179
176
|
return nil if key.nil?
|
|
180
177
|
s = key.to_s
|
|
181
|
-
s.length > 200 ? s[0, 200] + "…" : s
|
|
182
|
-
rescue
|
|
178
|
+
(s.length > 200) ? s[0, 200] + "…" : s
|
|
179
|
+
rescue
|
|
183
180
|
nil
|
|
184
181
|
end
|
|
185
182
|
|
|
186
183
|
def safe_db(db)
|
|
187
|
-
Integer(db)
|
|
184
|
+
Integer(db)
|
|
185
|
+
rescue
|
|
186
|
+
nil
|
|
188
187
|
end
|
|
189
188
|
end
|
|
190
189
|
|
|
191
190
|
::Redis::Client.prepend(mod) unless ::Redis::Client.ancestors.include?(mod)
|
|
192
|
-
rescue
|
|
191
|
+
rescue
|
|
193
192
|
# Redis::Client may not be available or may have different structure
|
|
194
193
|
end
|
|
195
194
|
|
|
@@ -204,7 +203,7 @@ module ApmBro
|
|
|
204
203
|
event = build_event(name, data, duration_ms)
|
|
205
204
|
Thread.current[THREAD_LOCAL_KEY] << event if event
|
|
206
205
|
end
|
|
207
|
-
rescue
|
|
206
|
+
rescue
|
|
208
207
|
end
|
|
209
208
|
end
|
|
210
209
|
end
|
|
@@ -221,7 +220,7 @@ module ApmBro
|
|
|
221
220
|
|
|
222
221
|
def self.build_event(name, data, duration_ms)
|
|
223
222
|
cmd = extract_command(data)
|
|
224
|
-
|
|
223
|
+
{
|
|
225
224
|
event: name.to_s,
|
|
226
225
|
command: cmd[:command],
|
|
227
226
|
key: cmd[:key],
|
|
@@ -229,22 +228,21 @@ module ApmBro
|
|
|
229
228
|
duration_ms: duration_ms,
|
|
230
229
|
db: safe_db(data[:db])
|
|
231
230
|
}
|
|
232
|
-
|
|
233
|
-
rescue StandardError
|
|
231
|
+
rescue
|
|
234
232
|
nil
|
|
235
233
|
end
|
|
236
234
|
|
|
237
235
|
def self.extract_command(data)
|
|
238
|
-
return {
|
|
236
|
+
return {command: nil, key: nil, args_count: nil} unless data.is_a?(Hash)
|
|
239
237
|
|
|
240
|
-
if data[:command]
|
|
241
|
-
|
|
238
|
+
parts = if data[:command]
|
|
239
|
+
Array(data[:command]).map(&:to_s)
|
|
242
240
|
elsif data[:commands]
|
|
243
|
-
|
|
241
|
+
Array(data[:commands]).flatten.map(&:to_s)
|
|
244
242
|
elsif data[:cmd]
|
|
245
|
-
|
|
243
|
+
Array(data[:cmd]).map(&:to_s)
|
|
246
244
|
else
|
|
247
|
-
|
|
245
|
+
[]
|
|
248
246
|
end
|
|
249
247
|
|
|
250
248
|
command_name = parts.first&.upcase
|
|
@@ -256,30 +254,29 @@ module ApmBro
|
|
|
256
254
|
key: safe_key(key),
|
|
257
255
|
args_count: args_count
|
|
258
256
|
}
|
|
259
|
-
rescue
|
|
260
|
-
{
|
|
257
|
+
rescue
|
|
258
|
+
{command: nil, key: nil, args_count: nil}
|
|
261
259
|
end
|
|
262
260
|
|
|
263
261
|
def self.safe_command(cmd)
|
|
264
262
|
return nil if cmd.nil?
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
rescue StandardError
|
|
263
|
+
cmd.to_s[0, 20]
|
|
264
|
+
rescue
|
|
268
265
|
nil
|
|
269
266
|
end
|
|
270
267
|
|
|
271
268
|
def self.safe_key(key)
|
|
272
269
|
return nil if key.nil?
|
|
273
270
|
s = key.to_s
|
|
274
|
-
s.length > 200 ? s[0, 200] + "…" : s
|
|
275
|
-
rescue
|
|
271
|
+
(s.length > 200) ? s[0, 200] + "…" : s
|
|
272
|
+
rescue
|
|
276
273
|
nil
|
|
277
274
|
end
|
|
278
275
|
|
|
279
276
|
def self.safe_db(db)
|
|
280
|
-
Integer(db)
|
|
277
|
+
Integer(db)
|
|
278
|
+
rescue
|
|
279
|
+
nil
|
|
281
280
|
end
|
|
282
281
|
end
|
|
283
282
|
end
|
|
284
|
-
|
|
285
|
-
|