dead_bro 0.2.13 → 0.2.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/CHANGELOG.md +9 -0
- data/lib/dead_bro/db_connection_subscriber.rb +49 -0
- data/lib/dead_bro/gc_tracker.rb +47 -0
- data/lib/dead_bro/job_sql_tracking_middleware.rb +5 -0
- data/lib/dead_bro/job_subscriber.rb +35 -1
- data/lib/dead_bro/railtie.rb +4 -0
- data/lib/dead_bro/sql_tracking_middleware.rb +49 -4
- data/lib/dead_bro/subscriber.rb +18 -0
- data/lib/dead_bro/version.rb +1 -1
- data/lib/dead_bro.rb +1 -0
- metadata +3 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 2251b88befeee4d2b7668bc504cc99d94cd154dd4539258545c1084e7a388709
|
|
4
|
+
data.tar.gz: 10141477e9697b91d95290ccfc695abb304e8dfbdfb526f95646269be03be0ef
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 39ec9243a8792016f7cf9bf5f8d81856c121365b5cebef91ef7d318f642e7480a66b32a92b63ec944ee3aebb9e92ffdfc27c9e284e41da7daa081661e893b38a
|
|
7
|
+
data.tar.gz: 519f6df31fb8dc567a6974052e58787098021078514eaa2877a2f582651c5a5fec316f4da58a625a4b6d00eed7fae614fe5dd79f1da17ae2d4d7227ce8f7061b
|
data/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,14 @@
|
|
|
1
1
|
## [Unreleased]
|
|
2
2
|
|
|
3
|
+
## [0.2.14] - 2026-05-23
|
|
4
|
+
|
|
5
|
+
### Added
|
|
6
|
+
- `DbConnectionSubscriber`: measures the time threads spend waiting to acquire a connection from the ActiveRecord connection pool and counts checkouts per request. Uses `prepend` on `ActiveRecord::ConnectionAdapters::ConnectionPool#checkout` so the overhead is minimal and the instrumentation is invisible to application code. `db_connection_wait_ms` and `db_connection_checkouts` are included in both request and job payloads.
|
|
7
|
+
- Queue duration tracking for web requests via `X-Request-Start` / `X-Queue-Start` headers. Parses both the Heroku microsecond format (`t=<µs>`) and the nginx seconds format (`t=<s.ms>`), and applies a 60 s clock-skew cap so a misconfigured proxy timestamp cannot produce a nonsensical value.
|
|
8
|
+
- Queue duration tracking for background jobs: time from `job.enqueued_at` to when `perform` begins is reported as `queue_duration_ms` in every job payload.
|
|
9
|
+
- `RedisSubscriber`: prepend-based instrumentation on `Redis::Client` that records every individual command, pipeline, and `MULTI`/`EXEC` block with command name, key, duration in ms, and database index. Falls back to an `ActiveSupport::Notifications` subscription for `redis.*` events emitted by other libraries. Capped at 1 000 events per request to bound memory growth.
|
|
10
|
+
- DB connection tracking wired into background jobs: `DbConnectionSubscriber` is started and stopped around job execution in both the normal completion path and the exception handler fallback path, with cleanup in `drain_job_tracking`.
|
|
11
|
+
|
|
3
12
|
## [0.1.0] - 2025-08-28
|
|
4
13
|
|
|
5
14
|
- Initial release
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module DeadBro
|
|
4
|
+
module DbConnectionSubscriber
|
|
5
|
+
WAIT_KEY = :dead_bro_db_connection_wait_ms
|
|
6
|
+
COUNT_KEY = :dead_bro_db_connection_checkouts
|
|
7
|
+
|
|
8
|
+
# Prepended onto ConnectionPool so every checkout is timed.
|
|
9
|
+
# Only accumulates when a request is being tracked (thread-local is a Numeric).
|
|
10
|
+
module CheckoutInstrumentation
|
|
11
|
+
def checkout(*args)
|
|
12
|
+
return super unless Thread.current[DbConnectionSubscriber::WAIT_KEY].is_a?(Numeric)
|
|
13
|
+
|
|
14
|
+
# Initialize conn before calling super so the rescue block can tell whether
|
|
15
|
+
# checkout succeeded before timing code raised (avoids double-checkout).
|
|
16
|
+
conn = nil
|
|
17
|
+
t0 = Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
|
18
|
+
conn = super
|
|
19
|
+
Thread.current[DbConnectionSubscriber::WAIT_KEY] += (Process.clock_gettime(Process::CLOCK_MONOTONIC) - t0) * 1000.0
|
|
20
|
+
Thread.current[DbConnectionSubscriber::COUNT_KEY] += 1
|
|
21
|
+
conn
|
|
22
|
+
rescue
|
|
23
|
+
conn || super
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def self.install!
|
|
28
|
+
return unless defined?(ActiveRecord::ConnectionAdapters::ConnectionPool)
|
|
29
|
+
return if ActiveRecord::ConnectionAdapters::ConnectionPool.ancestors.include?(CheckoutInstrumentation)
|
|
30
|
+
|
|
31
|
+
ActiveRecord::ConnectionAdapters::ConnectionPool.prepend(CheckoutInstrumentation)
|
|
32
|
+
rescue StandardError => e
|
|
33
|
+
warn "[DeadBro] DbConnectionSubscriber install failed: #{e.class}: #{e.message}"
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def self.start_request_tracking
|
|
37
|
+
Thread.current[WAIT_KEY] = 0.0
|
|
38
|
+
Thread.current[COUNT_KEY] = 0
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def self.stop_request_tracking
|
|
42
|
+
wait_ms = Thread.current[WAIT_KEY]
|
|
43
|
+
checkouts = Thread.current[COUNT_KEY]
|
|
44
|
+
Thread.current[WAIT_KEY] = nil
|
|
45
|
+
Thread.current[COUNT_KEY] = nil
|
|
46
|
+
{ wait_ms: wait_ms&.round(2), checkouts: checkouts }
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
end
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module DeadBro
|
|
4
|
+
module GcTracker
|
|
5
|
+
THREAD_KEY = :dead_bro_gc_start
|
|
6
|
+
|
|
7
|
+
def self.start_request_tracking
|
|
8
|
+
Thread.current[THREAD_KEY] = snapshot
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def self.stop_request_tracking
|
|
12
|
+
before = Thread.current[THREAD_KEY]
|
|
13
|
+
return {} if before.nil? || before.empty?
|
|
14
|
+
diff(before, snapshot)
|
|
15
|
+
ensure
|
|
16
|
+
Thread.current[THREAD_KEY] = nil
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def self.snapshot
|
|
20
|
+
return {} unless defined?(GC) && GC.respond_to?(:stat)
|
|
21
|
+
stat = GC.stat
|
|
22
|
+
{
|
|
23
|
+
minor_gc_count: stat[:minor_gc_count] || 0,
|
|
24
|
+
major_gc_count: stat[:major_gc_count] || 0,
|
|
25
|
+
total_allocated_objects: stat[:total_allocated_objects] || 0,
|
|
26
|
+
gc_time_ns: GC.respond_to?(:total_time) ? GC.total_time : nil
|
|
27
|
+
}
|
|
28
|
+
rescue
|
|
29
|
+
{}
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def self.diff(before, after)
|
|
33
|
+
return {} if before.empty? || after.empty?
|
|
34
|
+
gc_time_ms = if before[:gc_time_ns] && after[:gc_time_ns]
|
|
35
|
+
((after[:gc_time_ns] - before[:gc_time_ns]) / 1_000_000.0).round(3)
|
|
36
|
+
end
|
|
37
|
+
{
|
|
38
|
+
minor_gc_runs: (after[:minor_gc_count] || 0) - (before[:minor_gc_count] || 0),
|
|
39
|
+
major_gc_runs: (after[:major_gc_count] || 0) - (before[:major_gc_count] || 0),
|
|
40
|
+
allocated_objects: (after[:total_allocated_objects] || 0) - (before[:total_allocated_objects] || 0),
|
|
41
|
+
gc_time_ms: gc_time_ms
|
|
42
|
+
}
|
|
43
|
+
rescue
|
|
44
|
+
{}
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
end
|
|
@@ -24,6 +24,11 @@ module DeadBro
|
|
|
24
24
|
if DeadBro.configuration.allocation_tracking_enabled && defined?(DeadBro::MemoryTrackingSubscriber)
|
|
25
25
|
DeadBro::MemoryTrackingSubscriber.start_request_tracking
|
|
26
26
|
end
|
|
27
|
+
|
|
28
|
+
# Start DB connection pool wait tracking
|
|
29
|
+
if defined?(DeadBro::DbConnectionSubscriber)
|
|
30
|
+
DeadBro::DbConnectionSubscriber.start_request_tracking
|
|
31
|
+
end
|
|
27
32
|
end
|
|
28
33
|
rescue
|
|
29
34
|
# Never raise from instrumentation install
|
|
@@ -12,6 +12,12 @@ module DeadBro
|
|
|
12
12
|
JOB_EXCEPTION_EVENT_NAME = "exception.active_job"
|
|
13
13
|
|
|
14
14
|
def self.subscribe!(client: Client.new)
|
|
15
|
+
# Snap GC state before the job runs so stop_request_tracking gets a valid diff
|
|
16
|
+
ActiveSupport::Notifications.subscribe("perform_start.active_job") do |_name, _started, _finished, _unique_id, _data|
|
|
17
|
+
DeadBro::GcTracker.start_request_tracking if defined?(DeadBro::GcTracker)
|
|
18
|
+
rescue
|
|
19
|
+
end
|
|
20
|
+
|
|
15
21
|
# Track job execution
|
|
16
22
|
ActiveSupport::Notifications.subscribe(JOB_EVENT_NAME) do |name, started, finished, _unique_id, data|
|
|
17
23
|
begin
|
|
@@ -43,6 +49,7 @@ module DeadBro
|
|
|
43
49
|
end
|
|
44
50
|
|
|
45
51
|
duration_ms = ((finished - started) * 1000.0).round(2)
|
|
52
|
+
queue_duration_ms = job_queue_duration_ms(data[:job], started)
|
|
46
53
|
|
|
47
54
|
# Ensure tracking was started (fallback if perform_start.active_job didn't fire)
|
|
48
55
|
# This handles job backends that don't emit perform_start events
|
|
@@ -50,6 +57,7 @@ module DeadBro
|
|
|
50
57
|
DeadBro.logger.clear
|
|
51
58
|
Thread.current[DeadBro::TRACKING_START_TIME_KEY] = Time.now
|
|
52
59
|
DeadBro::SqlSubscriber.start_request_tracking
|
|
60
|
+
DeadBro::DbConnectionSubscriber.start_request_tracking if defined?(DeadBro::DbConnectionSubscriber)
|
|
53
61
|
if DeadBro.configuration.allocation_tracking_enabled && defined?(DeadBro::MemoryTrackingSubscriber)
|
|
54
62
|
DeadBro::MemoryTrackingSubscriber.start_request_tracking
|
|
55
63
|
else
|
|
@@ -59,6 +67,8 @@ module DeadBro
|
|
|
59
67
|
|
|
60
68
|
# Get SQL queries executed during this job
|
|
61
69
|
sql_queries = DeadBro::SqlSubscriber.stop_request_tracking
|
|
70
|
+
db_connection_stats = defined?(DeadBro::DbConnectionSubscriber) ? DeadBro::DbConnectionSubscriber.stop_request_tracking : {}
|
|
71
|
+
gc_pressure = defined?(DeadBro::GcTracker) ? DeadBro::GcTracker.stop_request_tracking : {}
|
|
62
72
|
|
|
63
73
|
# Stop memory tracking and get collected memory data
|
|
64
74
|
if DeadBro.configuration.allocation_tracking_enabled && defined?(DeadBro::MemoryTrackingSubscriber)
|
|
@@ -94,6 +104,10 @@ module DeadBro
|
|
|
94
104
|
queue_name: data[:job].queue_name,
|
|
95
105
|
arguments: safe_arguments(data[:job].arguments),
|
|
96
106
|
duration_ms: duration_ms,
|
|
107
|
+
queue_duration_ms: queue_duration_ms,
|
|
108
|
+
db_connection_wait_ms: db_connection_stats[:wait_ms],
|
|
109
|
+
db_connection_checkouts: db_connection_stats[:checkouts],
|
|
110
|
+
gc_pressure: gc_pressure,
|
|
97
111
|
status: "completed",
|
|
98
112
|
sql_queries: sql_queries,
|
|
99
113
|
rails_env: DeadBro.env,
|
|
@@ -129,13 +143,14 @@ module DeadBro
|
|
|
129
143
|
|
|
130
144
|
duration_ms = ((finished - started) * 1000.0).round(2)
|
|
131
145
|
exception = data[:exception_object]
|
|
132
|
-
data[:job]
|
|
146
|
+
queue_duration_ms = job_queue_duration_ms(data[:job], started)
|
|
133
147
|
|
|
134
148
|
# Ensure tracking was started (fallback if perform_start.active_job didn't fire)
|
|
135
149
|
unless DeadBro::SqlSubscriber.tracking_active?
|
|
136
150
|
DeadBro.logger.clear
|
|
137
151
|
Thread.current[DeadBro::TRACKING_START_TIME_KEY] = Time.now
|
|
138
152
|
DeadBro::SqlSubscriber.start_request_tracking
|
|
153
|
+
DeadBro::DbConnectionSubscriber.start_request_tracking if defined?(DeadBro::DbConnectionSubscriber)
|
|
139
154
|
if DeadBro.configuration.allocation_tracking_enabled && defined?(DeadBro::MemoryTrackingSubscriber)
|
|
140
155
|
DeadBro::MemoryTrackingSubscriber.start_request_tracking
|
|
141
156
|
else
|
|
@@ -145,6 +160,8 @@ module DeadBro
|
|
|
145
160
|
|
|
146
161
|
# Get SQL queries executed during this job
|
|
147
162
|
sql_queries = DeadBro::SqlSubscriber.stop_request_tracking
|
|
163
|
+
db_connection_stats = defined?(DeadBro::DbConnectionSubscriber) ? DeadBro::DbConnectionSubscriber.stop_request_tracking : {}
|
|
164
|
+
gc_pressure = defined?(DeadBro::GcTracker) ? DeadBro::GcTracker.stop_request_tracking : {}
|
|
148
165
|
|
|
149
166
|
# Stop memory tracking and get collected memory data
|
|
150
167
|
if DeadBro.configuration.allocation_tracking_enabled && defined?(DeadBro::MemoryTrackingSubscriber)
|
|
@@ -180,6 +197,10 @@ module DeadBro
|
|
|
180
197
|
queue_name: data[:job].queue_name,
|
|
181
198
|
arguments: safe_arguments(data[:job].arguments),
|
|
182
199
|
duration_ms: duration_ms,
|
|
200
|
+
queue_duration_ms: queue_duration_ms,
|
|
201
|
+
db_connection_wait_ms: db_connection_stats[:wait_ms],
|
|
202
|
+
db_connection_checkouts: db_connection_stats[:checkouts],
|
|
203
|
+
gc_pressure: gc_pressure,
|
|
183
204
|
status: "failed",
|
|
184
205
|
sql_queries: sql_queries,
|
|
185
206
|
exception_class: exception&.class&.name,
|
|
@@ -205,6 +226,8 @@ module DeadBro
|
|
|
205
226
|
# build a payload (excluded job / sampled out). Matches Subscriber.drain_request_tracking.
|
|
206
227
|
def self.drain_job_tracking
|
|
207
228
|
DeadBro::SqlSubscriber.stop_request_tracking if defined?(DeadBro::SqlSubscriber)
|
|
229
|
+
DeadBro::DbConnectionSubscriber.stop_request_tracking if defined?(DeadBro::DbConnectionSubscriber)
|
|
230
|
+
DeadBro::GcTracker.stop_request_tracking if defined?(DeadBro::GcTracker)
|
|
208
231
|
DeadBro::LightweightMemoryTracker.stop_request_tracking if defined?(DeadBro::LightweightMemoryTracker)
|
|
209
232
|
if DeadBro.configuration.allocation_tracking_enabled && defined?(DeadBro::MemoryTrackingSubscriber)
|
|
210
233
|
DeadBro::MemoryTrackingSubscriber.stop_request_tracking
|
|
@@ -215,6 +238,17 @@ module DeadBro
|
|
|
215
238
|
|
|
216
239
|
private
|
|
217
240
|
|
|
241
|
+
def self.job_queue_duration_ms(job, perform_started)
|
|
242
|
+
enqueued_at = job.enqueued_at
|
|
243
|
+
return nil if enqueued_at.nil?
|
|
244
|
+
|
|
245
|
+
enqueued_time = enqueued_at.is_a?(Time) ? enqueued_at : Time.parse(enqueued_at.to_s)
|
|
246
|
+
diff_ms = ((perform_started - enqueued_time) * 1000.0).round(2)
|
|
247
|
+
diff_ms >= 0 ? diff_ms : nil
|
|
248
|
+
rescue
|
|
249
|
+
nil
|
|
250
|
+
end
|
|
251
|
+
|
|
218
252
|
def self.safe_arguments(arguments)
|
|
219
253
|
return [] unless arguments.is_a?(Array)
|
|
220
254
|
|
data/lib/dead_bro/railtie.rb
CHANGED
|
@@ -36,6 +36,10 @@ if defined?(Rails) && defined?(Rails::Railtie)
|
|
|
36
36
|
require "dead_bro/elasticsearch_subscriber"
|
|
37
37
|
DeadBro::ElasticsearchSubscriber.subscribe!
|
|
38
38
|
|
|
39
|
+
# Install DB connection pool wait tracking
|
|
40
|
+
require "dead_bro/db_connection_subscriber"
|
|
41
|
+
DeadBro::DbConnectionSubscriber.install!
|
|
42
|
+
|
|
39
43
|
# Install view rendering tracking
|
|
40
44
|
require "dead_bro/view_rendering_subscriber"
|
|
41
45
|
DeadBro::ViewRenderingSubscriber.subscribe!(client: shared_client)
|
|
@@ -9,6 +9,15 @@ module DeadBro
|
|
|
9
9
|
def call(env)
|
|
10
10
|
return @app.call(env) if DeadBro.configuration.skip_tracking?
|
|
11
11
|
|
|
12
|
+
# Capture rack entry time before any setup so middleware overhead is accurately measured.
|
|
13
|
+
rack_entry = Time.now
|
|
14
|
+
Thread.current[DeadBro::TRACKING_START_TIME_KEY] = rack_entry
|
|
15
|
+
|
|
16
|
+
# Queue time: gap between when the upstream proxy accepted the connection and when a Rack
|
|
17
|
+
# worker picked it up. Heroku sets X-Request-Start as "t=<microseconds>"; nginx typically
|
|
18
|
+
# uses "t=<seconds.ms>". Both are parsed below.
|
|
19
|
+
Thread.current[:dead_bro_queue_duration_ms] = parse_queue_start(env, rack_entry)
|
|
20
|
+
|
|
12
21
|
# Clear logs for this request
|
|
13
22
|
DeadBro.logger.clear
|
|
14
23
|
|
|
@@ -47,12 +56,17 @@ module DeadBro
|
|
|
47
56
|
DeadBro::ElasticsearchSubscriber.start_request_tracking
|
|
48
57
|
end
|
|
49
58
|
|
|
59
|
+
# Start DB connection pool wait tracking
|
|
60
|
+
if defined?(DeadBro::DbConnectionSubscriber)
|
|
61
|
+
DeadBro::DbConnectionSubscriber.start_request_tracking
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
# Start GC pressure tracking — snapshot before any app code runs
|
|
65
|
+
DeadBro::GcTracker.start_request_tracking if defined?(DeadBro::GcTracker)
|
|
66
|
+
|
|
50
67
|
# Start outgoing HTTP accumulation for this request
|
|
51
68
|
Thread.current[:dead_bro_http_events] = []
|
|
52
69
|
|
|
53
|
-
# Set tracking start time once for all subscribers (before starting any tracking)
|
|
54
|
-
Thread.current[DeadBro::TRACKING_START_TIME_KEY] = Time.now
|
|
55
|
-
|
|
56
70
|
@app.call(env)
|
|
57
71
|
ensure
|
|
58
72
|
# Clean up thread-local storage
|
|
@@ -81,10 +95,41 @@ module DeadBro
|
|
|
81
95
|
Thread.current[:dead_bro_lightweight_memory] = nil
|
|
82
96
|
end
|
|
83
97
|
|
|
84
|
-
# Clean up HTTP events, ES events, and tracking start time
|
|
98
|
+
# Clean up HTTP events, ES events, DB connection tracking, and tracking start time
|
|
85
99
|
Thread.current[:dead_bro_elasticsearch_events] = nil
|
|
86
100
|
Thread.current[:dead_bro_http_events] = nil
|
|
101
|
+
Thread.current[:dead_bro_queue_duration_ms] = nil
|
|
102
|
+
DeadBro::DbConnectionSubscriber.stop_request_tracking if defined?(DeadBro::DbConnectionSubscriber)
|
|
103
|
+
Thread.current[DeadBro::GcTracker::THREAD_KEY] = nil if defined?(DeadBro::GcTracker)
|
|
87
104
|
Thread.current[DeadBro::TRACKING_START_TIME_KEY] = nil
|
|
88
105
|
end
|
|
106
|
+
|
|
107
|
+
private
|
|
108
|
+
|
|
109
|
+
def parse_queue_start(env, rack_entry)
|
|
110
|
+
raw = env["HTTP_X_REQUEST_START"] || env["HTTP_X_QUEUE_START"]
|
|
111
|
+
return nil if raw.nil? || raw.empty?
|
|
112
|
+
|
|
113
|
+
# Strip "t=" prefix used by Heroku and nginx
|
|
114
|
+
raw = raw.sub(/\At=/, "")
|
|
115
|
+
num = raw.to_f
|
|
116
|
+
return nil if num <= 0
|
|
117
|
+
|
|
118
|
+
request_start =
|
|
119
|
+
if num > 1_000_000_000_000_000 # microseconds (Heroku)
|
|
120
|
+
Time.at(num / 1_000_000.0)
|
|
121
|
+
elsif num > 1_000_000_000_000 # milliseconds
|
|
122
|
+
Time.at(num / 1_000.0)
|
|
123
|
+
else # seconds (nginx)
|
|
124
|
+
Time.at(num)
|
|
125
|
+
end
|
|
126
|
+
|
|
127
|
+
# Guard against clocks being out of sync or wildly misconfigured proxy timestamps.
|
|
128
|
+
# Cap at 60 s — anything larger almost certainly means the header value is wrong.
|
|
129
|
+
diff_ms = ((rack_entry - request_start) * 1000.0).round(2)
|
|
130
|
+
diff_ms >= 0 && diff_ms <= 60_000 ? diff_ms : nil
|
|
131
|
+
rescue
|
|
132
|
+
nil
|
|
133
|
+
end
|
|
89
134
|
end
|
|
90
135
|
end
|
data/lib/dead_bro/subscriber.rb
CHANGED
|
@@ -49,6 +49,11 @@ module DeadBro
|
|
|
49
49
|
end
|
|
50
50
|
|
|
51
51
|
duration_ms = ((finished - started) * 1000.0).round(2)
|
|
52
|
+
|
|
53
|
+
# Time spent in Rack middleware before ActionController took over (routing, session, auth, etc.)
|
|
54
|
+
rack_start = Thread.current[DeadBro::TRACKING_START_TIME_KEY]
|
|
55
|
+
rack_duration_ms = rack_start ? ([((started - rack_start) * 1000.0), 0].max).round(2) : nil
|
|
56
|
+
|
|
52
57
|
# Stop SQL tracking and get collected queries (this was started by the request)
|
|
53
58
|
sql_queries = DeadBro::SqlSubscriber.stop_request_tracking
|
|
54
59
|
|
|
@@ -57,6 +62,12 @@ module DeadBro
|
|
|
57
62
|
redis_events = defined?(DeadBro::RedisSubscriber) ? DeadBro::RedisSubscriber.stop_request_tracking : []
|
|
58
63
|
elasticsearch_events = defined?(DeadBro::ElasticsearchSubscriber) ? DeadBro::ElasticsearchSubscriber.stop_request_tracking : []
|
|
59
64
|
|
|
65
|
+
# Stop DB connection pool wait tracking
|
|
66
|
+
db_connection_stats = defined?(DeadBro::DbConnectionSubscriber) ? DeadBro::DbConnectionSubscriber.stop_request_tracking : {}
|
|
67
|
+
|
|
68
|
+
# Stop GC pressure tracking — diff minor/major runs, objects allocated, GC time
|
|
69
|
+
gc_pressure = defined?(DeadBro::GcTracker) ? DeadBro::GcTracker.stop_request_tracking : {}
|
|
70
|
+
|
|
60
71
|
# Stop view rendering tracking and get collected view events
|
|
61
72
|
view_events = DeadBro::ViewRenderingSubscriber.stop_request_tracking
|
|
62
73
|
view_performance = DeadBro::ViewRenderingSubscriber.analyze_view_performance(view_events)
|
|
@@ -166,6 +177,11 @@ module DeadBro
|
|
|
166
177
|
view_performance: view_performance,
|
|
167
178
|
memory_events: memory_events,
|
|
168
179
|
memory_performance: memory_performance,
|
|
180
|
+
rack_duration_ms: rack_duration_ms,
|
|
181
|
+
queue_duration_ms: Thread.current[:dead_bro_queue_duration_ms],
|
|
182
|
+
db_connection_wait_ms: db_connection_stats[:wait_ms],
|
|
183
|
+
db_connection_checkouts: db_connection_stats[:checkouts],
|
|
184
|
+
gc_pressure: gc_pressure,
|
|
169
185
|
logs: DeadBro.logger.logs
|
|
170
186
|
}
|
|
171
187
|
client.post_metric(event_name: name, payload: payload)
|
|
@@ -186,6 +202,8 @@ module DeadBro
|
|
|
186
202
|
DeadBro::MemoryTrackingSubscriber.stop_request_tracking
|
|
187
203
|
end
|
|
188
204
|
Thread.current[:dead_bro_http_events] = nil
|
|
205
|
+
DeadBro::DbConnectionSubscriber.stop_request_tracking if defined?(DeadBro::DbConnectionSubscriber)
|
|
206
|
+
DeadBro::GcTracker.stop_request_tracking if defined?(DeadBro::GcTracker)
|
|
189
207
|
rescue
|
|
190
208
|
# Best effort — draining must never raise from the notifications callback.
|
|
191
209
|
end
|
data/lib/dead_bro/version.rb
CHANGED
data/lib/dead_bro.rb
CHANGED
|
@@ -18,6 +18,7 @@ module DeadBro
|
|
|
18
18
|
autoload :MemoryTrackingSubscriber, "dead_bro/memory_tracking_subscriber"
|
|
19
19
|
autoload :MemoryLeakDetector, "dead_bro/memory_leak_detector"
|
|
20
20
|
autoload :LightweightMemoryTracker, "dead_bro/lightweight_memory_tracker"
|
|
21
|
+
autoload :GcTracker, "dead_bro/gc_tracker"
|
|
21
22
|
autoload :MemoryHelpers, "dead_bro/memory_helpers"
|
|
22
23
|
autoload :JobSubscriber, "dead_bro/job_subscriber"
|
|
23
24
|
autoload :JobSqlTrackingMiddleware, "dead_bro/job_sql_tracking_middleware"
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: dead_bro
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.2.
|
|
4
|
+
version: 0.2.15
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Emanuel Comsa
|
|
@@ -33,9 +33,11 @@ files:
|
|
|
33
33
|
- lib/dead_bro/collectors/sample_store.rb
|
|
34
34
|
- lib/dead_bro/collectors/system.rb
|
|
35
35
|
- lib/dead_bro/configuration.rb
|
|
36
|
+
- lib/dead_bro/db_connection_subscriber.rb
|
|
36
37
|
- lib/dead_bro/dispatcher.rb
|
|
37
38
|
- lib/dead_bro/elasticsearch_subscriber.rb
|
|
38
39
|
- lib/dead_bro/error_middleware.rb
|
|
40
|
+
- lib/dead_bro/gc_tracker.rb
|
|
39
41
|
- lib/dead_bro/http_instrumentation.rb
|
|
40
42
|
- lib/dead_bro/job_sql_tracking_middleware.rb
|
|
41
43
|
- lib/dead_bro/job_subscriber.rb
|