rails_error_dashboard 0.5.6 → 0.5.7

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 1082f04d59310d3ee29619e7a1380df6d815a67a87de41d77e184b20291c1426
4
- data.tar.gz: 31429aa4cdaaad3ff9a7890fd40261ffd6c3b5414172db33df28cb8063264e23
3
+ metadata.gz: 0cfea703e2d74b6771503d098e3bc5f048ec781923ee08a1692e34bbf28c9734
4
+ data.tar.gz: 4b77fb5c2dd00920d2eaca85b92d35dcbb19282408dee936584deffd2e7126a6
5
5
  SHA512:
6
- metadata.gz: 4cf8ec345cbf28bcb13fd22b14d13120f689563ca51db774d78008138fd5b854e3ccb85e98edf992955fd1590e47d3f456a62891e4669d44e6e0f4c56a808967
7
- data.tar.gz: 2cb2e5fe8d7231363687e0d161fe0ae4333f922894dc481a416a1f7561c560538363cbfa3ebbf18bbf0de4dd34e8769e6d74e32dfdbd3d71a9120840b7e29502
6
+ metadata.gz: 1bcc5f0f868d3e864deccdfbe23a0a3c3e1da03f3cfb49208724f61deceaf7047bba7c1d3f19358a75e1a254336059894a55bec79656f67e55217eec18c34d19
7
+ data.tar.gz: 810aa6c9a5db5300d4f6f5964ce92aa06dada34dfc7b513e782a914de8a3f0baf7e35b8b03056b9a1060b018b8dca0d6b51534f528211e779c008a77060fa10d
@@ -36,6 +36,7 @@ module RailsErrorDashboard
36
36
 
37
37
  sections << heading_section
38
38
  sections << backtrace_section
39
+ sections << source_code_section
39
40
  sections << cause_chain_section
40
41
  sections << local_variables_section
41
42
  sections << instance_variables_section
@@ -69,6 +70,45 @@ module RailsErrorDashboard
69
70
  "## Backtrace\n\n```\n#{app_lines.join("\n")}\n```"
70
71
  end
71
72
 
73
+ def source_code_section
74
+ return nil unless defined?(RailsErrorDashboard) &&
75
+ RailsErrorDashboard.configuration.enable_source_code_integration
76
+
77
+ raw = @error.backtrace
78
+ return nil if raw.blank?
79
+
80
+ lines = raw.split("\n")
81
+ app_lines = lines.reject { |l| l.include?("/gems/") || l.include?("/ruby/") || l.include?("/vendor/") }
82
+ return nil if app_lines.empty?
83
+
84
+ snippets = []
85
+ app_lines.first(3).each do |frame|
86
+ # Parse "file:line:in 'method'" format
87
+ match = frame.match(/^(.+?):(\d+)/)
88
+ next unless match
89
+
90
+ file_path = match[1]
91
+ line_number = match[2].to_i
92
+
93
+ reader = Services::SourceCodeReader.new(file_path, line_number)
94
+ source_lines = reader.read_lines(context: 3)
95
+ next unless source_lines&.any?
96
+
97
+ snippet = source_lines.map { |sl|
98
+ marker = sl[:highlight] ? ">" : " "
99
+ "#{marker} #{sl[:number].to_s.rjust(4)} | #{sl[:content]}"
100
+ }.join("\n")
101
+
102
+ snippets << "**#{file_path}:#{line_number}**\n```ruby\n#{snippet}\n```"
103
+ end
104
+
105
+ return nil if snippets.empty?
106
+
107
+ "## Source Code\n\n#{snippets.join("\n\n")}"
108
+ rescue => e
109
+ nil
110
+ end
111
+
72
112
  def cause_chain_section
73
113
  raw = @error.exception_cause
74
114
  return nil if raw.blank?
@@ -135,12 +175,18 @@ module RailsErrorDashboard
135
175
  return nil if @error.request_url.blank?
136
176
 
137
177
  items = []
178
+ items << "- **Controller:** #{@error.controller_name}##{@error.action_name}" if @error.controller_name.present?
138
179
  items << "- **Method:** #{@error.http_method}" if @error.http_method.present?
139
180
  items << "- **URL:** #{@error.request_url}"
140
181
  items << "- **Hostname:** #{@error.hostname}" if @error.hostname.present?
141
182
  items << "- **Content-Type:** #{@error.content_type}" if @error.content_type.present?
142
183
  items << "- **Duration:** #{@error.request_duration_ms}ms" if @error.request_duration_ms.present?
143
- items << "- **IP:** #{@error.ip_address}" if @error.ip_address.present?
184
+ items << "- **User-Agent:** #{@error.user_agent}" if @error.user_agent.present?
185
+
186
+ params = parse_json(@error.request_params) if @error.request_params.present?
187
+ if params.is_a?(Hash) && params.any?
188
+ items << "\n**Request Params:**\n```json\n#{JSON.pretty_generate(params)}\n```"
189
+ end
144
190
 
145
191
  "## Request Context\n\n#{items.join("\n")}"
146
192
  end
@@ -196,19 +242,81 @@ module RailsErrorDashboard
196
242
  return nil unless health.is_a?(Hash) && health.any?
197
243
 
198
244
  items = []
199
- items << "- **Memory:** #{health["process_memory_mb"]} MB RSS" if health["process_memory_mb"]
245
+
246
+ # Process memory
247
+ mem = health["process_memory"]
248
+ if mem.is_a?(Hash)
249
+ parts = []
250
+ parts << "#{mem["rss_mb"]} MB RSS" if mem["rss_mb"]
251
+ parts << "peak #{mem["rss_peak_mb"]} MB" if mem["rss_peak_mb"]
252
+ parts << "swap #{mem["swap_mb"]} MB" if mem["swap_mb"] && mem["swap_mb"] > 0
253
+ parts << "#{mem["os_threads"]} OS threads" if mem["os_threads"]
254
+ items << "- **Memory:** #{parts.join(", ")}" if parts.any?
255
+ elsif health["process_memory_mb"]
256
+ items << "- **Memory:** #{health["process_memory_mb"]} MB RSS"
257
+ end
258
+
200
259
  items << "- **Threads:** #{health["thread_count"]}" if health["thread_count"]
201
260
 
261
+ # DB connection pool
202
262
  pool = health["connection_pool"]
203
- if pool.is_a?(Hash)
204
- items << "- **DB Pool:** #{pool["busy"]}/#{pool["size"]} busy" if pool["size"]
263
+ if pool.is_a?(Hash) && pool["size"]
264
+ pool_parts = [ "#{pool["busy"]}/#{pool["size"]} busy" ]
265
+ pool_parts << "#{pool["dead"]} dead" if pool["dead"].to_i > 0
266
+ pool_parts << "#{pool["waiting"]} waiting" if pool["waiting"].to_i > 0
267
+ items << "- **DB Pool:** #{pool_parts.join(", ")}"
205
268
  end
206
269
 
207
- gc = health["gc_stats"]
270
+ # GC stats
271
+ gc = health["gc"] || health["gc_stats"]
208
272
  if gc.is_a?(Hash)
209
- items << "- **GC:** #{gc["major_gc_count"]} major cycles" if gc["major_gc_count"]
273
+ gc_parts = []
274
+ gc_parts << "#{gc["major_gc_count"]} major" if gc["major_gc_count"]
275
+ gc_parts << "#{gc["heap_live_slots"]} live slots" if gc["heap_live_slots"]
276
+ gc_parts << "#{gc["total_allocated_objects"]} total allocated" if gc["total_allocated_objects"]
277
+ items << "- **GC:** #{gc_parts.join(", ")}" if gc_parts.any?
278
+ end
279
+
280
+ # GC latest
281
+ gc_latest = health["gc_latest"]
282
+ if gc_latest.is_a?(Hash)
283
+ latest_parts = []
284
+ latest_parts << "triggered by #{gc_latest["gc_by"]}" if gc_latest["gc_by"]
285
+ latest_parts << "state: #{gc_latest["state"]}" if gc_latest["state"]
286
+ items << "- **Last GC:** #{latest_parts.join(", ")}" if latest_parts.any?
287
+ end
288
+
289
+ # Note: Puma and job queue stats are omitted — they are server-wide metrics,
290
+ # not error-specific. The LLM can infer job context from the backtrace.
291
+
292
+ # File descriptors
293
+ fd = health["file_descriptors"]
294
+ if fd.is_a?(Hash) && fd["open"]
295
+ items << "- **File Descriptors:** #{fd["open"]}/#{fd["limit"]} (#{fd["utilization_pct"]}%)"
210
296
  end
211
297
 
298
+ # System load
299
+ load = health["system_load"]
300
+ if load.is_a?(Hash) && load["load_1m"]
301
+ items << "- **System Load:** #{load["load_1m"]}/#{load["load_5m"]}/#{load["load_15m"]} (#{load["cpu_count"]} CPUs)"
302
+ end
303
+
304
+ # System memory
305
+ sys_mem = health["system_memory"]
306
+ if sys_mem.is_a?(Hash) && sys_mem["total_mb"]
307
+ items << "- **System Memory:** #{sys_mem["available_mb"]}/#{sys_mem["total_mb"]} MB available (#{sys_mem["used_pct"]}% used)"
308
+ end
309
+
310
+ # TCP connections
311
+ tcp = health["tcp_connections"]
312
+ if tcp.is_a?(Hash) && tcp.values.any? { |v| v.to_i > 0 }
313
+ tcp_parts = tcp.map { |state, count| "#{state}: #{count}" if count.to_i > 0 }.compact
314
+ items << "- **TCP:** #{tcp_parts.join(", ")}"
315
+ end
316
+
317
+ # Note: RubyVM, YJIT, and ActionCable stats are omitted — they are process-wide
318
+ # counters, not error-specific context. They add noise for LLM debugging.
319
+
212
320
  return nil if items.empty?
213
321
 
214
322
  "## System Health at Error Time\n\n#{items.join("\n")}"
@@ -233,13 +341,12 @@ module RailsErrorDashboard
233
341
 
234
342
  def metadata_section
235
343
  items = []
236
- items << "- **Severity:** #{@error.severity}" if @error.severity.present?
237
- items << "- **Status:** #{@error.status}" if @error.status.present?
238
- items << "- **Priority:** P#{3 - @error.priority_level}" if @error.priority_level.present?
239
344
  items << "- **Platform:** #{@error.platform}" if @error.platform.present?
240
345
  items << "- **First seen:** #{@error.first_seen_at&.utc&.strftime("%Y-%m-%d %H:%M:%S UTC")}" if @error.first_seen_at
241
346
  items << "- **Occurrences:** #{@error.occurrence_count}" if @error.occurrence_count
242
- items << "- **Assigned to:** #{@error.assigned_to}" if @error.assigned_to.present?
347
+ items << "- **User ID:** #{@error.user_id}" if @error.user_id.present?
348
+
349
+ return nil if items.empty?
243
350
 
244
351
  "## Metadata\n\n#{items.join("\n")}"
245
352
  end
@@ -1,3 +1,3 @@
1
1
  module RailsErrorDashboard
2
- VERSION = "0.5.6"
2
+ VERSION = "0.5.7"
3
3
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rails_error_dashboard
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.5.6
4
+ version: 0.5.7
5
5
  platform: ruby
6
6
  authors:
7
7
  - Anjan Jagirdar
@@ -467,7 +467,7 @@ metadata:
467
467
  bug_tracker_uri: https://github.com/AnjanJ/rails_error_dashboard/issues
468
468
  funding_uri: https://buymeacoffee.com/anjanj
469
469
  post_install_message: "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n
470
- \ Rails Error Dashboard v0.5.6\n━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n\n\U0001F195
470
+ \ Rails Error Dashboard v0.5.7\n━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n\n\U0001F195
471
471
  First time? Quick start:\n rails generate rails_error_dashboard:install\n rails
472
472
  db:migrate\n # Add to config/routes.rb:\n mount RailsErrorDashboard::Engine
473
473
  => '/error_dashboard'\n\n\U0001F504 Upgrading from v0.1.x?\n rails db:migrate\n