apm_bro 0.1.14 → 0.1.16

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.
@@ -4,7 +4,7 @@ require "active_support/notifications"
4
4
 
5
5
  module ApmBro
6
6
  class Subscriber
7
- EVENT_NAME = "process_action.action_controller".freeze
7
+ EVENT_NAME = "process_action.action_controller"
8
8
 
9
9
  def self.subscribe!(client: Client.new)
10
10
  ActiveSupport::Notifications.subscribe(EVENT_NAME) do |name, started, finished, _unique_id, data|
@@ -15,13 +15,13 @@ module ApmBro
15
15
  if ApmBro.configuration.excluded_controller_action?(controller_name, action_name)
16
16
  next
17
17
  end
18
- rescue StandardError
18
+ rescue
19
19
  end
20
20
 
21
21
  duration_ms = ((finished - started) * 1000.0).round(2)
22
22
  # Stop SQL tracking and get collected queries (this was started by the request)
23
23
  sql_queries = ApmBro::SqlSubscriber.stop_request_tracking
24
-
24
+
25
25
  # Stop cache and redis tracking
26
26
  cache_events = defined?(ApmBro::CacheSubscriber) ? ApmBro::CacheSubscriber.stop_request_tracking : []
27
27
  redis_events = defined?(ApmBro::RedisSubscriber) ? ApmBro::RedisSubscriber.stop_request_tracking : []
@@ -29,7 +29,7 @@ module ApmBro
29
29
  # Stop view rendering tracking and get collected view events
30
30
  view_events = ApmBro::ViewRenderingSubscriber.stop_request_tracking
31
31
  view_performance = ApmBro::ViewRenderingSubscriber.analyze_view_performance(view_events)
32
-
32
+
33
33
  # Stop memory tracking and get collected memory data
34
34
  if ApmBro.configuration.allocation_tracking_enabled && defined?(ApmBro::MemoryTrackingSubscriber)
35
35
  detailed_memory = ApmBro::MemoryTrackingSubscriber.stop_request_tracking
@@ -57,7 +57,7 @@ module ApmBro
57
57
  duration_seconds: lightweight_memory[:duration_seconds]
58
58
  }
59
59
  end
60
-
60
+
61
61
  # Record memory sample for leak detection (only if memory tracking enabled)
62
62
  if ApmBro.configuration.memory_tracking_enabled
63
63
  ApmBro::MemoryLeakDetector.record_memory_sample({
@@ -70,7 +70,7 @@ module ApmBro
70
70
  action: data[:action]
71
71
  })
72
72
  end
73
-
73
+
74
74
  # Report exceptions attached to this action (e.g. controller/view errors)
75
75
  if data[:exception] || data[:exception_object]
76
76
  begin
@@ -90,9 +90,8 @@ module ApmBro
90
90
  host: safe_host,
91
91
  params: safe_params(data),
92
92
  user_agent: safe_user_agent(data),
93
- user_email: extract_user_email(data),
94
93
  user_id: extract_user_id(data),
95
- exception_class: (exception_class || exception_obj&.class&.name),
94
+ exception_class: exception_class || exception_obj&.class&.name,
96
95
  message: (exception_message || exception_obj&.message).to_s[0, 1000],
97
96
  backtrace: backtrace,
98
97
  error: true,
@@ -101,7 +100,7 @@ module ApmBro
101
100
 
102
101
  event_name = (exception_class || exception_obj&.class&.name || "exception").to_s
103
102
  client.post_metric(event_name: event_name, payload: error_payload)
104
- rescue StandardError
103
+ rescue
105
104
  ensure
106
105
  next
107
106
  end
@@ -121,13 +120,12 @@ module ApmBro
121
120
  rails_env: rails_env,
122
121
  params: safe_params(data),
123
122
  user_agent: safe_user_agent(data),
124
- user_email: extract_user_email(data),
125
123
  user_id: extract_user_id(data),
126
124
  memory_usage: memory_usage_mb,
127
125
  gc_stats: gc_stats,
128
126
  sql_count: sql_count(data),
129
127
  sql_queries: sql_queries,
130
- http_outgoing: (Thread.current[:apm_bro_http_events] || []),
128
+ http_outgoing: Thread.current[:apm_bro_http_events] || [],
131
129
  cache_events: cache_events,
132
130
  redis_events: redis_events,
133
131
  cache_hits: cache_hits(data),
@@ -145,13 +143,17 @@ module ApmBro
145
143
  def self.safe_path(data)
146
144
  path = data[:path] || (data[:request] && data[:request].path)
147
145
  path.to_s
148
- rescue StandardError
146
+ rescue
149
147
  ""
150
148
  end
151
149
 
152
150
  def self.safe_host
153
151
  if defined?(Rails) && Rails.respond_to?(:application)
154
- Rails.application.class.module_parent_name rescue ""
152
+ begin
153
+ Rails.application.class.module_parent_name
154
+ rescue
155
+ ""
156
+ end
155
157
  else
156
158
  ""
157
159
  end
@@ -171,7 +173,7 @@ module ApmBro
171
173
  params = data[:params]
172
174
  begin
173
175
  params = params.to_unsafe_h if params.respond_to?(:to_unsafe_h)
174
- rescue StandardError
176
+ rescue
175
177
  end
176
178
 
177
179
  unless params.is_a?(Hash)
@@ -190,7 +192,7 @@ module ApmBro
190
192
 
191
193
  # Truncate deeply to keep payload small and safe
192
194
  truncate_value(filtered)
193
- rescue StandardError
195
+ rescue
194
196
  {}
195
197
  end
196
198
 
@@ -198,7 +200,7 @@ module ApmBro
198
200
  def self.truncate_value(value, max_str: 200, max_array: 20, max_hash_keys: 30)
199
201
  case value
200
202
  when String
201
- value.length > max_str ? value[0, max_str] + "…" : value
203
+ (value.length > max_str) ? value[0, max_str] + "…" : value
202
204
  when Numeric, TrueClass, FalseClass, NilClass
203
205
  value
204
206
  when Array
@@ -209,7 +211,7 @@ module ApmBro
209
211
  memo[k] = truncate_value(v, max_str: max_str, max_array: max_array, max_hash_keys: max_hash_keys)
210
212
  end
211
213
  else
212
- value.to_s.length > max_str ? value.to_s[0, max_str] + "…" : value.to_s
214
+ (value.to_s.length > max_str) ? value.to_s[0, max_str] + "…" : value.to_s
213
215
  end
214
216
  end
215
217
 
@@ -233,7 +235,11 @@ module ApmBro
233
235
  ua = headers["HTTP_USER_AGENT"] || headers["User-Agent"] || headers["user-agent"]
234
236
  return ua.to_s[0..200]
235
237
  elsif headers.respond_to?(:to_h)
236
- h = headers.to_h rescue {}
238
+ h = begin
239
+ headers.to_h
240
+ rescue
241
+ {}
242
+ end
237
243
  ua = h["HTTP_USER_AGENT"] || h["User-Agent"] || h["user-agent"]
238
244
  return ua.to_s[0..200]
239
245
  end
@@ -246,22 +252,26 @@ module ApmBro
246
252
  end
247
253
 
248
254
  ""
249
- rescue StandardError
255
+ rescue
250
256
  ""
251
257
  end
252
- rescue StandardError
258
+ rescue
253
259
  ""
254
260
  end
255
261
 
256
262
  def self.memory_usage_mb
257
263
  if defined?(GC) && GC.respond_to?(:stat)
258
264
  # Get memory usage in MB
259
- memory_kb = `ps -o rss= -p #{Process.pid}`.to_i rescue 0
265
+ memory_kb = begin
266
+ `ps -o rss= -p #{Process.pid}`.to_i
267
+ rescue
268
+ 0
269
+ end
260
270
  (memory_kb / 1024.0).round(2)
261
271
  else
262
272
  0
263
273
  end
264
- rescue StandardError
274
+ rescue
265
275
  0
266
276
  end
267
277
 
@@ -277,7 +287,7 @@ module ApmBro
277
287
  else
278
288
  {}
279
289
  end
280
- rescue StandardError
290
+ rescue
281
291
  {}
282
292
  end
283
293
 
@@ -287,11 +297,15 @@ module ApmBro
287
297
  data[:sql_count]
288
298
  elsif defined?(ActiveRecord) && ActiveRecord::Base.connection
289
299
  # Try to get from ActiveRecord connection
290
- ActiveRecord::Base.connection.query_cache.size rescue 0
300
+ begin
301
+ ActiveRecord::Base.connection.query_cache.size
302
+ rescue
303
+ 0
304
+ end
291
305
  else
292
306
  0
293
307
  end
294
- rescue StandardError
308
+ rescue
295
309
  0
296
310
  end
297
311
 
@@ -299,11 +313,15 @@ module ApmBro
299
313
  if data[:cache_hits]
300
314
  data[:cache_hits]
301
315
  elsif defined?(Rails) && Rails.cache.respond_to?(:stats)
302
- Rails.cache.stats[:hits] rescue 0
316
+ begin
317
+ Rails.cache.stats[:hits]
318
+ rescue
319
+ 0
320
+ end
303
321
  else
304
322
  0
305
323
  end
306
- rescue StandardError
324
+ rescue
307
325
  0
308
326
  end
309
327
 
@@ -311,26 +329,22 @@ module ApmBro
311
329
  if data[:cache_misses]
312
330
  data[:cache_misses]
313
331
  elsif defined?(Rails) && Rails.cache.respond_to?(:stats)
314
- Rails.cache.stats[:misses] rescue 0
332
+ begin
333
+ Rails.cache.stats[:misses]
334
+ rescue
335
+ 0
336
+ end
315
337
  else
316
338
  0
317
339
  end
318
- rescue StandardError
340
+ rescue
319
341
  0
320
342
  end
321
343
 
322
- def self.extract_user_email(data)
323
- data[:headers].env['warden'].user.email
324
- rescue StandardError
325
- nil
326
- end
327
-
328
344
  def self.extract_user_id(data)
329
- data[:headers].env['warden'].user.id
330
- rescue StandardError
345
+ data[:headers].env["warden"].user.id
346
+ rescue
331
347
  nil
332
348
  end
333
349
  end
334
350
  end
335
-
336
-
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module ApmBro
4
- VERSION = "0.1.14"
4
+ VERSION = "0.1.16"
5
5
  end
@@ -5,17 +5,17 @@ require "active_support/notifications"
5
5
  module ApmBro
6
6
  class ViewRenderingSubscriber
7
7
  # Rails view rendering events
8
- RENDER_TEMPLATE_EVENT = "render_template.action_view".freeze
9
- RENDER_PARTIAL_EVENT = "render_partial.action_view".freeze
10
- RENDER_COLLECTION_EVENT = "render_collection.action_view".freeze
11
-
8
+ RENDER_TEMPLATE_EVENT = "render_template.action_view"
9
+ RENDER_PARTIAL_EVENT = "render_partial.action_view"
10
+ RENDER_COLLECTION_EVENT = "render_collection.action_view"
11
+
12
12
  THREAD_LOCAL_KEY = :apm_bro_view_events
13
13
 
14
14
  def self.subscribe!(client: Client.new)
15
15
  # Track template rendering
16
16
  ActiveSupport::Notifications.subscribe(RENDER_TEMPLATE_EVENT) do |name, started, finished, _unique_id, data|
17
17
  duration_ms = ((finished - started) * 1000.0).round(2)
18
-
18
+
19
19
  view_info = {
20
20
  type: "template",
21
21
  identifier: safe_identifier(data[:identifier]),
@@ -24,14 +24,14 @@ module ApmBro
24
24
  virtual_path: data[:virtual_path],
25
25
  rendered_at: Time.now.utc.to_i
26
26
  }
27
-
27
+
28
28
  add_view_event(view_info)
29
29
  end
30
30
 
31
31
  # Track partial rendering
32
32
  ActiveSupport::Notifications.subscribe(RENDER_PARTIAL_EVENT) do |name, started, finished, _unique_id, data|
33
33
  duration_ms = ((finished - started) * 1000.0).round(2)
34
-
34
+
35
35
  view_info = {
36
36
  type: "partial",
37
37
  identifier: safe_identifier(data[:identifier]),
@@ -41,14 +41,14 @@ module ApmBro
41
41
  cache_key: data[:cache_key],
42
42
  rendered_at: Time.now.utc.to_i
43
43
  }
44
-
44
+
45
45
  add_view_event(view_info)
46
46
  end
47
47
 
48
48
  # Track collection rendering (for partials rendered in loops)
49
49
  ActiveSupport::Notifications.subscribe(RENDER_COLLECTION_EVENT) do |name, started, finished, _unique_id, data|
50
50
  duration_ms = ((finished - started) * 1000.0).round(2)
51
-
51
+
52
52
  view_info = {
53
53
  type: "collection",
54
54
  identifier: safe_identifier(data[:identifier]),
@@ -60,10 +60,10 @@ module ApmBro
60
60
  cached_count: data[:cached_count],
61
61
  rendered_at: Time.now.utc.to_i
62
62
  }
63
-
63
+
64
64
  add_view_event(view_info)
65
65
  end
66
- rescue StandardError
66
+ rescue
67
67
  # Never raise from instrumentation install
68
68
  end
69
69
 
@@ -85,11 +85,11 @@ module ApmBro
85
85
 
86
86
  def self.safe_identifier(identifier)
87
87
  return "" unless identifier.is_a?(String)
88
-
88
+
89
89
  # Extract meaningful parts of the file path
90
90
  # e.g., "/app/views/users/show.html.erb" -> "users/show.html.erb"
91
91
  identifier.split("/").last(3).join("/")
92
- rescue StandardError
92
+ rescue
93
93
  identifier.to_s
94
94
  end
95
95
 
@@ -98,29 +98,29 @@ module ApmBro
98
98
  return {} if view_events.empty?
99
99
 
100
100
  total_duration = view_events.sum { |event| event[:duration_ms] }
101
-
101
+
102
102
  # Group by view type
103
103
  by_type = view_events.group_by { |event| event[:type] }
104
-
104
+
105
105
  # Find slowest views
106
106
  slowest_views = view_events.sort_by { |event| -event[:duration_ms] }.first(5)
107
-
107
+
108
108
  # Find most frequently rendered views
109
109
  view_frequency = view_events.group_by { |event| event[:identifier] }
110
- .transform_values(&:count)
111
- .sort_by { |_, count| -count }
112
- .first(5)
113
-
110
+ .transform_values(&:count)
111
+ .sort_by { |_, count| -count }
112
+ .first(5)
113
+
114
114
  # Calculate cache hit rates for partials
115
115
  partials = view_events.select { |event| event[:type] == "partial" }
116
116
  cache_hits = partials.count { |event| event[:cache_key] }
117
117
  cache_hit_rate = partials.any? ? (cache_hits.to_f / partials.count * 100).round(2) : 0
118
-
118
+
119
119
  # Collection rendering analysis
120
120
  collections = view_events.select { |event| event[:type] == "collection" }
121
121
  total_collection_items = collections.sum { |event| event[:count] || 0 }
122
122
  total_cached_items = collections.sum { |event| event[:cached_count] || 0 }
123
- collection_cache_hit_rate = total_collection_items > 0 ?
123
+ collection_cache_hit_rate = (total_collection_items > 0) ?
124
124
  (total_cached_items.to_f / total_collection_items * 100).round(2) : 0
125
125
 
126
126
  {
@@ -128,7 +128,7 @@ module ApmBro
128
128
  total_view_duration_ms: total_duration.round(2),
129
129
  average_view_duration_ms: (total_duration / view_events.count).round(2),
130
130
  by_type: by_type.transform_values(&:count),
131
- slowest_views: slowest_views.map { |view|
131
+ slowest_views: slowest_views.map { |view|
132
132
  {
133
133
  identifier: view[:identifier],
134
134
  duration_ms: view[:duration_ms],
data/lib/apm_bro.rb CHANGED
@@ -51,4 +51,13 @@ module ApmBro
51
51
  def self.logger
52
52
  @logger ||= Logger.new
53
53
  end
54
+
55
+ # Returns the current environment (Rails.env or ENV fallback)
56
+ def self.env
57
+ if defined?(Rails) && Rails.respond_to?(:env)
58
+ Rails.env
59
+ else
60
+ ENV["RACK_ENV"] || ENV["RAILS_ENV"] || "development"
61
+ end
62
+ end
54
63
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: apm_bro
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.14
4
+ version: 0.1.16
5
5
  platform: ruby
6
6
  authors:
7
7
  - Emanuel Comsa
@@ -40,11 +40,11 @@ files:
40
40
  - lib/apm_bro/subscriber.rb
41
41
  - lib/apm_bro/version.rb
42
42
  - lib/apm_bro/view_rendering_subscriber.rb
43
- homepage: https://github.com/rubydev/apm_bro
43
+ homepage: https://www.deadbro.com
44
44
  licenses: []
45
45
  metadata:
46
- homepage_uri: https://github.com/rubydev/apm_bro
47
- source_code_uri: https://github.com/rubydev/apm_bro
46
+ homepage_uri: https://www.deadbro.com
47
+ source_code_uri: https://github.com/rubydevro/apm_bro
48
48
  rdoc_options: []
49
49
  require_paths:
50
50
  - lib