errorgap 0.3.0 → 0.4.0

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: cabe7a082d767651c4e56f067d77fa21486056cde995842eb4ef7f8531adbf00
4
- data.tar.gz: 10cc33121d252ce987eb92ccd2e57301ea4399bafa42c2b8cada4ab0dd4d1f63
3
+ metadata.gz: 3a6d206f60530a163bfebf7cbc25d4ef0f889a81d1b15c78f440c2141e4efc3a
4
+ data.tar.gz: d9346233cd3aec6e191a13efcec513fdfec9fadececb304549afc1527f4da3a7
5
5
  SHA512:
6
- metadata.gz: 283fcdf5b95bcbf34a175b2e9249c5e2a5b4c16f46bedfe9852f088214ada0e6aa022d84f816a4c692b36291c9a3d8a0f099ee0f18beb15cf5c856d014ecdb14
7
- data.tar.gz: a66f945ca3d16ee996aeec660dc24351baa3799e7b7404ce209202e98a3c10e6df7eac940c4bdea82e4b7ca86391c98ed3b718c174c0cfe5499485cfb14bd1b2
6
+ metadata.gz: c35e78949f9cbd4e30e24acacfbd5857f3d3003aa763f6d41c26a1713dde9c3e55a62bfe0bc24caa6941029910423ba1c5b0609ee4f48fed890ff1dbc4364e5a
7
+ data.tar.gz: 73a7f26536826709ceb3feb1624f48caa336dc2f6981c558a7d692a2ddbbef93206ff6397f4fa4192cc6f2306a486d8125f80df5750d4ecce168845ed6466e61
data/CHANGELOG.md CHANGED
@@ -5,6 +5,28 @@ All notable changes to this project will be documented in this file.
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
6
6
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
7
 
8
+ ## [0.4.0] - 2026-07-16
9
+
10
+ ### Fixed
11
+
12
+ - Backtrace frames are now parsed on Ruby 3.4+, which formats frames as
13
+ `file.rb:12:in 'Class#method'` (straight quote) instead of the pre-3.4
14
+ `` file.rb:12:in `method' `` (backtick). Unparsed frames carried no line
15
+ number or function, which also prevented source excerpts from being
16
+ attached — the UI showed "Source is not available for this frame yet."
17
+ for every frame.
18
+
19
+ - DB span durations are now computed from the notification event's
20
+ start/finish timestamps. The `sql.active_record` payload carries no
21
+ `:duration` key, so every query previously shipped `0.0` — showing as
22
+ 0.0ms 50th/95th percentiles on the Performance > Queries page.
23
+
24
+ ### Added
25
+
26
+ - DB spans now carry the application call site (`file`, `line`, `fn_name`),
27
+ captured from the first non-gem backtrace frame, so Performance > Queries
28
+ can attribute each query to app code.
29
+
8
30
  ## [0.3.0] - 2026-07-16
9
31
 
10
32
  ### Fixed
@@ -102,8 +102,10 @@ module Errorgap
102
102
  nil
103
103
  end
104
104
 
105
+ # Ruby <= 3.3 formats frames as `file.rb:42:in `method'` (backtick),
106
+ # Ruby >= 3.4 as `file.rb:42:in 'Class#method'` (straight quote).
105
107
  def parse_backtrace_line(line)
106
- match = line.match(/\A(.+?):(\d+)(?::in `(.*)')?\z/)
108
+ match = line.match(/\A(.+?):(\d+)(?::in [`'](.*)')?\z/)
107
109
  return [line, nil, nil] unless match
108
110
 
109
111
  [match[1], match[2].to_i, match[3]]
@@ -25,15 +25,22 @@ module Errorgap
25
25
 
26
26
  @installed = true
27
27
 
28
- ActiveSupport::Notifications.subscribe("sql.active_record") do |*, payload|
28
+ # The 5-arg block form receives the event's start/finish times; the
29
+ # sql.active_record payload itself carries no duration key.
30
+ ActiveSupport::Notifications.subscribe("sql.active_record") do |_name, started, finished, _id, payload|
29
31
  next unless Thread.current[THREAD_KEY]
30
32
  next if SKIP_NAMES.any? { |n| payload[:name]&.start_with?(n) }
31
33
  next if payload[:cached]
32
34
 
33
- duration_ms = payload[:duration] || 0.0
35
+ duration_ms = ((finished - started) * 1000.0).to_f
34
36
  sql = normalize_sql(payload[:sql].to_s)
37
+ file, line, fn_name = app_call_site
35
38
 
36
- store << Span.new(kind: "db", sql: sql, duration_ms: duration_ms.round(3))
39
+ store << Span.new(
40
+ kind: "db", sql: sql,
41
+ file: file, line: line, fn_name: fn_name,
42
+ duration_ms: duration_ms.round(3)
43
+ )
37
44
  end
38
45
 
39
46
  # Rails reports view_runtime with database time already subtracted, so
@@ -65,6 +72,31 @@ module Errorgap
65
72
  def normalize_sql(sql)
66
73
  sql.gsub(NORMALIZE_PATTERN, "?").gsub(/\s+/, " ").strip
67
74
  end
75
+
76
+ # The first backtrace frame belonging to the application — skipping this
77
+ # gem, other gems, and the Ruby standard library — so a query can be
78
+ # attributed to the app code that ran it. Returns [file, line, fn_name]
79
+ # with the file relative to Rails.root when available, or nils when no
80
+ # app frame is present (e.g. queries run from a console or gem).
81
+ def app_call_site
82
+ root = defined?(Rails) && Rails.respond_to?(:root) && Rails.root ? Rails.root.to_s : nil
83
+ location = caller_locations(1, 60)&.find do |loc|
84
+ path = loc.absolute_path || loc.path
85
+ next false unless path
86
+ next false if path.start_with?(LIB_ROOT)
87
+ next false if GEM_PATH_MARKERS.any? { |marker| path.include?(marker) }
88
+
89
+ root ? path.start_with?(root) : true
90
+ end
91
+ return [nil, nil, nil] unless location
92
+
93
+ path = location.absolute_path || location.path
94
+ path = path.delete_prefix("#{root}/") if root
95
+ [path, location.lineno, location.label]
96
+ end
68
97
  end
98
+
99
+ LIB_ROOT = File.expand_path("..", __dir__)
100
+ GEM_PATH_MARKERS = ["/gems/", "/rubygems/", "/lib/ruby/", "/bundler/"].freeze
69
101
  end
70
102
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Errorgap
4
- VERSION = "0.3.0"
4
+ VERSION = "0.4.0"
5
5
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: errorgap
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.0
4
+ version: 0.4.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Errorgap
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2026-07-16 00:00:00.000000000 Z
11
+ date: 2026-07-17 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rake