rails-profiler 0.34.3 → 0.34.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/CHANGELOG.md +18 -0
- data/lib/profiler/collectors/base_collector.rb +18 -0
- data/lib/profiler/collectors/flamegraph_collector.rb +1 -1
- data/lib/profiler/collectors/log_collector.rb +9 -0
- data/lib/profiler/collectors/view_collector.rb +5 -2
- data/lib/profiler/instrumentation/sidekiq_middleware.rb +12 -0
- data/lib/profiler/version.rb +1 -1
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: d4912dd474a748e657c35111e3224d83500e75960ef2592e5cd652155dc62617
|
|
4
|
+
data.tar.gz: d0d4cb0f34f25d98f5be08875bdc59ec4cc84894f7cbe0b512fd1ecd153dda4b
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 3ca18ad3dd582e68c2bb935a5ace8628dc466a4999fa655e54117879548d543c5a291ec98765eb25456b0ff8fa774ee0f98f939544a346aa9b3368d37c421f72
|
|
7
|
+
data.tar.gz: e144011d2fd93d37aa3166748bf2d3671d8d246325a475cf13d756b188dd99999ef6f95b232fd36eca444c91e24e2715c1902695697ea1930407bc932b2e952d
|
data/CHANGELOG.md
CHANGED
|
@@ -19,6 +19,24 @@ every commit of every tag interval is accounted for one way or the other.
|
|
|
19
19
|
|
|
20
20
|
## [Unreleased]
|
|
21
21
|
|
|
22
|
+
## [0.34.4] - 2026-10-16
|
|
23
|
+
|
|
24
|
+
<!-- stamped -->
|
|
25
|
+
|
|
26
|
+
### Fixed
|
|
27
|
+
|
|
28
|
+
- **Sidekiq:** A job run through the ActiveJob adapter is no longer profiled twice. It reaches the
|
|
29
|
+
Sidekiq middleware wrapped (`job["wrapped"]`) and is already profiled by the ActiveJob
|
|
30
|
+
`around_perform` callback, so the middleware now leaves it alone when ActiveJob is instrumented
|
|
31
|
+
and the same work is recorded once.
|
|
32
|
+
- **Logs:** The log buffer behind `CaptureLogger` is now guarded by a `Mutex`, so lines recorded
|
|
33
|
+
from several threads at once (a streamed body iterated by another thread, work handed the buffer)
|
|
34
|
+
keep the byte count and the stored lines consistent.
|
|
35
|
+
- **Flame graph and views:** Nested durations are counted once. The total of the flame graph and
|
|
36
|
+
the total of the views and partials now cover each moment a single time, so a template that
|
|
37
|
+
renders partials, or a controller action that runs queries, is no longer added to what it
|
|
38
|
+
contains.
|
|
39
|
+
|
|
22
40
|
## [0.34.3] - 2026-10-15
|
|
23
41
|
|
|
24
42
|
<!-- stamped -->
|
|
@@ -124,6 +124,24 @@ module Profiler
|
|
|
124
124
|
|
|
125
125
|
protected
|
|
126
126
|
|
|
127
|
+
# The time covered by +intervals+ ([started, finished] pairs of Time or Float seconds), in
|
|
128
|
+
# milliseconds, each moment counted once: a template that renders partials, or a controller
|
|
129
|
+
# action that runs queries, is not added to what it contains.
|
|
130
|
+
def covered_ms(intervals)
|
|
131
|
+
covered = 0.0
|
|
132
|
+
current_end = nil
|
|
133
|
+
intervals.sort_by(&:first).each do |started, finished|
|
|
134
|
+
if current_end.nil? || started >= current_end
|
|
135
|
+
covered += finished - started
|
|
136
|
+
current_end = finished
|
|
137
|
+
elsif finished > current_end
|
|
138
|
+
covered += finished - current_end
|
|
139
|
+
current_end = finished
|
|
140
|
+
end
|
|
141
|
+
end
|
|
142
|
+
(covered * 1000).round(2)
|
|
143
|
+
end
|
|
144
|
+
|
|
127
145
|
def store_data(data)
|
|
128
146
|
@data = data
|
|
129
147
|
@profile.add_collector_data(name, data)
|
|
@@ -139,7 +139,7 @@ module Profiler
|
|
|
139
139
|
|
|
140
140
|
store_data({
|
|
141
141
|
total_events: @events.size,
|
|
142
|
-
total_duration: @events.empty? ? 0 : @events.map
|
|
142
|
+
total_duration: @events.empty? ? 0 : covered_ms(@events.map { |e| [e.started_at, e.finished_at] }),
|
|
143
143
|
root_events: root_events.map(&:to_h)
|
|
144
144
|
})
|
|
145
145
|
end
|
|
@@ -20,13 +20,22 @@ module Profiler
|
|
|
20
20
|
@bytes = 0
|
|
21
21
|
@dropped = 0
|
|
22
22
|
@truncated = false
|
|
23
|
+
@mutex = Mutex.new
|
|
23
24
|
end
|
|
24
25
|
|
|
25
26
|
def truncated?
|
|
26
27
|
@truncated
|
|
27
28
|
end
|
|
28
29
|
|
|
30
|
+
# A streamed body iterated by another thread, or work handed the buffer, can record while
|
|
31
|
+
# the request's own thread does: the byte count and the lines are updated together.
|
|
29
32
|
def record(entry)
|
|
33
|
+
@mutex.synchronize { record_unlocked(entry) }
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
private
|
|
37
|
+
|
|
38
|
+
def record_unlocked(entry)
|
|
30
39
|
message = entry[:message]
|
|
31
40
|
if @limit
|
|
32
41
|
room = @limit - @bytes
|
|
@@ -9,6 +9,7 @@ module Profiler
|
|
|
9
9
|
super
|
|
10
10
|
@views = []
|
|
11
11
|
@partials = []
|
|
12
|
+
@intervals = []
|
|
12
13
|
@subscriptions = []
|
|
13
14
|
end
|
|
14
15
|
|
|
@@ -36,6 +37,7 @@ module Profiler
|
|
|
36
37
|
|
|
37
38
|
@subscriptions << subscribe_notification("render_template.action_view") do |name, started, finished, unique_id, payload|
|
|
38
39
|
duration = ((finished - started) * 1000).round(2)
|
|
40
|
+
@intervals << [started, finished]
|
|
39
41
|
@views << {
|
|
40
42
|
identifier: payload[:identifier],
|
|
41
43
|
layout: payload[:layout],
|
|
@@ -45,6 +47,7 @@ module Profiler
|
|
|
45
47
|
|
|
46
48
|
@subscriptions << subscribe_notification("render_partial.action_view") do |name, started, finished, unique_id, payload|
|
|
47
49
|
duration = ((finished - started) * 1000).round(2)
|
|
50
|
+
@intervals << [started, finished]
|
|
48
51
|
@partials << {
|
|
49
52
|
identifier: payload[:identifier],
|
|
50
53
|
duration: duration
|
|
@@ -60,7 +63,7 @@ module Profiler
|
|
|
60
63
|
partials: @partials,
|
|
61
64
|
total_views: @views.size,
|
|
62
65
|
total_partials: @partials.size,
|
|
63
|
-
total_duration: (@
|
|
66
|
+
total_duration: covered_ms(@intervals)
|
|
64
67
|
}
|
|
65
68
|
|
|
66
69
|
store_data(data)
|
|
@@ -76,7 +79,7 @@ module Profiler
|
|
|
76
79
|
end
|
|
77
80
|
|
|
78
81
|
def toolbar_summary
|
|
79
|
-
total_duration = (@
|
|
82
|
+
total_duration = covered_ms(@intervals)
|
|
80
83
|
|
|
81
84
|
{
|
|
82
85
|
text: "#{@views.size} views, #{@partials.size} partials (#{total_duration}ms)",
|
|
@@ -12,6 +12,11 @@ module Profiler
|
|
|
12
12
|
class SidekiqMiddleware
|
|
13
13
|
def call(worker, job, queue, &block)
|
|
14
14
|
Profiler.env_override_store.apply!
|
|
15
|
+
# A job run through the ActiveJob adapter reaches Sidekiq wrapped (job["wrapped"] names the
|
|
16
|
+
# real class) and is profiled by ActiveJobInstrumentation's around_perform: profiling the
|
|
17
|
+
# wrapper too would record the same work twice.
|
|
18
|
+
return block.call if job["wrapped"] && active_job_profiled?
|
|
19
|
+
|
|
15
20
|
Profiler::JobProfiler.profile(
|
|
16
21
|
job_class: job["class"],
|
|
17
22
|
job_id: job["jid"],
|
|
@@ -22,6 +27,13 @@ module Profiler
|
|
|
22
27
|
&block
|
|
23
28
|
)
|
|
24
29
|
end
|
|
30
|
+
|
|
31
|
+
private
|
|
32
|
+
|
|
33
|
+
def active_job_profiled?
|
|
34
|
+
defined?(ActiveJob::Base) && defined?(Profiler::Instrumentation::ActiveJobInstrumentation) &&
|
|
35
|
+
ActiveJob::Base.include?(Profiler::Instrumentation::ActiveJobInstrumentation)
|
|
36
|
+
end
|
|
25
37
|
end
|
|
26
38
|
end
|
|
27
39
|
end
|
data/lib/profiler/version.rb
CHANGED