debug-agent 0.2.6 → 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 +4 -4
- data/README.md +110 -4
- data/lib/debug_agent/inspectors/active_record_stats.rb +131 -0
- data/lib/debug_agent/inspectors/cache.rb +194 -0
- data/lib/debug_agent/inspectors/concurrent.rb +78 -0
- data/lib/debug_agent/inspectors/core_ext.rb +105 -0
- data/lib/debug_agent/inspectors/faraday.rb +79 -0
- data/lib/debug_agent/inspectors/gc.rb +53 -0
- data/lib/debug_agent/inspectors/http_client.rb +145 -0
- data/lib/debug_agent/inspectors/logging.rb +163 -0
- data/lib/debug_agent/inspectors/metrics.rb +71 -0
- data/lib/debug_agent/inspectors/puma.rb +73 -0
- data/lib/debug_agent/inspectors/rails.rb +127 -0
- data/lib/debug_agent/inspectors/redis.rb +205 -0
- data/lib/debug_agent/inspectors/sidekiq.rb +121 -0
- data/lib/debug_agent/version.rb +1 -1
- data/lib/debug_agent.rb +12 -0
- metadata +112 -2
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
module DebugAgent
|
|
2
|
+
register_tool('get_rails_routes',
|
|
3
|
+
'List all Rails routes: verb, path, controller#action ' \
|
|
4
|
+
'(requires Rails.application.routes)') do
|
|
5
|
+
unless defined?(::Rails) && defined?(::ActionDispatch)
|
|
6
|
+
return { error: 'Rails is not loaded (Rails::Application not found)' }
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
routes_set = ::Rails.application.routes.routes
|
|
10
|
+
|
|
11
|
+
routes = routes_set.map do |route|
|
|
12
|
+
{
|
|
13
|
+
name: route.name,
|
|
14
|
+
verb: route.verb.source.gsub(/[$^]/, ''),
|
|
15
|
+
path: route.path.spec.to_s,
|
|
16
|
+
controller: route.defaults[:controller]&.to_s,
|
|
17
|
+
action: route.defaults[:action]&.to_s,
|
|
18
|
+
internal: route.internal?
|
|
19
|
+
}
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
{
|
|
23
|
+
total: routes.size,
|
|
24
|
+
routes: routes
|
|
25
|
+
}
|
|
26
|
+
rescue => e
|
|
27
|
+
{ error: e.message }
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
register_tool('get_rails_models',
|
|
31
|
+
'List ActiveRecord models: class name, table name, columns ' \
|
|
32
|
+
'(iterates ActiveRecord::Base.descendants)') do
|
|
33
|
+
unless defined?(::ActiveRecord)
|
|
34
|
+
return { error: 'ActiveRecord is not loaded (ActiveRecord::Base not found)' }
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
models = ::ActiveRecord::Base.descendants.map do |model|
|
|
38
|
+
columns =
|
|
39
|
+
begin
|
|
40
|
+
if model.table_exists?
|
|
41
|
+
model.columns.map do |col|
|
|
42
|
+
{
|
|
43
|
+
name: col.name,
|
|
44
|
+
type: col.sql_type_metadata&.type.to_s,
|
|
45
|
+
sql_type: col.sql_type_metadata&.sql_type.to_s,
|
|
46
|
+
null: col.null,
|
|
47
|
+
default: col.default,
|
|
48
|
+
primary: col.name == model.primary_key
|
|
49
|
+
}
|
|
50
|
+
end
|
|
51
|
+
else
|
|
52
|
+
[]
|
|
53
|
+
end
|
|
54
|
+
rescue => e
|
|
55
|
+
[{ error: e.message }]
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
table_name = begin
|
|
59
|
+
model.table_name
|
|
60
|
+
rescue
|
|
61
|
+
nil
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
table_exists = begin
|
|
65
|
+
model.table_exists?
|
|
66
|
+
rescue
|
|
67
|
+
false
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
{
|
|
71
|
+
class_name: model.name,
|
|
72
|
+
table_name: table_name,
|
|
73
|
+
table_exists: table_exists,
|
|
74
|
+
column_count: columns.size,
|
|
75
|
+
columns: columns
|
|
76
|
+
}
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
{
|
|
80
|
+
total: models.size,
|
|
81
|
+
models: models.sort_by { |m| m[:class_name].to_s }
|
|
82
|
+
}
|
|
83
|
+
rescue => e
|
|
84
|
+
{ error: e.message }
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
register_tool('get_rails_schema',
|
|
88
|
+
'Get ActiveRecord schema cache: table names and column definitions ' \
|
|
89
|
+
'(uses ActiveRecord::Base.connection.tables)') do
|
|
90
|
+
unless defined?(::ActiveRecord)
|
|
91
|
+
return { error: 'ActiveRecord is not loaded (ActiveRecord::Base not found)' }
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
connection = ::ActiveRecord::Base.connection
|
|
95
|
+
tables = connection.tables
|
|
96
|
+
|
|
97
|
+
schema = tables.map do |table|
|
|
98
|
+
columns =
|
|
99
|
+
begin
|
|
100
|
+
connection.columns(table).map do |col|
|
|
101
|
+
{
|
|
102
|
+
name: col.name,
|
|
103
|
+
type: col.sql_type_metadata&.type.to_s,
|
|
104
|
+
sql_type: col.sql_type_metadata&.sql_type.to_s,
|
|
105
|
+
null: col.null,
|
|
106
|
+
default: col.default
|
|
107
|
+
}
|
|
108
|
+
end
|
|
109
|
+
rescue => e
|
|
110
|
+
[{ error: e.message }]
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
{
|
|
114
|
+
table: table,
|
|
115
|
+
column_count: columns.size,
|
|
116
|
+
columns: columns
|
|
117
|
+
}
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
{
|
|
121
|
+
total_tables: schema.size,
|
|
122
|
+
tables: schema
|
|
123
|
+
}
|
|
124
|
+
rescue => e
|
|
125
|
+
{ error: e.message }
|
|
126
|
+
end
|
|
127
|
+
end
|
|
@@ -0,0 +1,205 @@
|
|
|
1
|
+
module DebugAgent
|
|
2
|
+
# Registry for Redis clients (redis-rb). Applications register their
|
|
3
|
+
# Redis / connection-pool objects so the inspector can introspect them.
|
|
4
|
+
#
|
|
5
|
+
# DebugAgent.register_redis_client(:cache, Redis.new(url: ENV['REDIS_URL']))
|
|
6
|
+
@redis_clients = {}
|
|
7
|
+
|
|
8
|
+
class << self
|
|
9
|
+
attr_reader :redis_clients
|
|
10
|
+
|
|
11
|
+
def register_redis_client(name, client)
|
|
12
|
+
@redis_clients[name.to_s] = client
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
# Resolve a registered Redis client. Accepts a bare Redis object or a
|
|
17
|
+
# ConnectionPool (redis-rb ships ConnectionPool support). We yield a
|
|
18
|
+
# usable connection object to the block.
|
|
19
|
+
def self.with_redis(name = nil)
|
|
20
|
+
name, client = if name
|
|
21
|
+
[name.to_s, redis_clients[name.to_s]]
|
|
22
|
+
else
|
|
23
|
+
redis_clients.first
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
return [nil, nil] unless client
|
|
27
|
+
|
|
28
|
+
[name, client]
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
register_tool('get_redis_pool_stats',
|
|
32
|
+
'Get Redis connection pool stats: registered clients, pool size, ' \
|
|
33
|
+
'available/in-use connections, host, port, db') do |name: nil|
|
|
34
|
+
return { error: 'Redis is not loaded (redis gem not installed)' } unless defined?(::Redis)
|
|
35
|
+
return { error: 'No Redis clients registered. Call DebugAgent.register_redis_client(:name, client).' } if redis_clients.empty?
|
|
36
|
+
|
|
37
|
+
targets = name ? { name.to_s => redis_clients[name.to_s] } : redis_clients
|
|
38
|
+
targets = targets.reject { |_, c| c.nil? }
|
|
39
|
+
return { error: "No Redis client registered under '#{name}'" } if targets.empty?
|
|
40
|
+
|
|
41
|
+
stats = targets.map do |client_name, client|
|
|
42
|
+
begin
|
|
43
|
+
info = {}
|
|
44
|
+
|
|
45
|
+
# ConnectionPool vs bare Redis
|
|
46
|
+
pool = nil
|
|
47
|
+
redis_conn = nil
|
|
48
|
+
|
|
49
|
+
if defined?(::ConnectionPool) && client.is_a?(::ConnectionPool)
|
|
50
|
+
pool = client
|
|
51
|
+
client.with { |c| redis_conn = c }
|
|
52
|
+
else
|
|
53
|
+
redis_conn = client
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
# Basic server connection details
|
|
57
|
+
info[:client_name] = client_name
|
|
58
|
+
info[:type] = pool ? 'connection_pool' : 'redis'
|
|
59
|
+
|
|
60
|
+
if redis_conn.respond_to?(:connection)
|
|
61
|
+
conn = redis_conn.connection rescue {}
|
|
62
|
+
info[:host] = conn[:host]
|
|
63
|
+
info[:port] = conn[:port]
|
|
64
|
+
info[:db] = conn[:db]
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
if pool
|
|
68
|
+
# ConnectionPool does not expose live counters publicly; report
|
|
69
|
+
# configured size. Available/in-use are best-effort via instance vars.
|
|
70
|
+
info[:pool_configured_size] = pool.instance_variable_get(:@size)
|
|
71
|
+
info[:pool_available] = pool.instance_variable_get(:@available)&.length
|
|
72
|
+
info[:pool_in_use] = info[:pool_configured_size].to_i - info[:pool_available].to_i
|
|
73
|
+
else
|
|
74
|
+
info[:pool_configured_size] = 1
|
|
75
|
+
info[:pool_available] = 1
|
|
76
|
+
info[:pool_in_use] = 0
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
# Ping to confirm reachability
|
|
80
|
+
info[:connected] = begin
|
|
81
|
+
redis_conn.ping == 'PONG'
|
|
82
|
+
rescue => e
|
|
83
|
+
info[:ping_error] = e.message
|
|
84
|
+
false
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
info
|
|
88
|
+
rescue => e
|
|
89
|
+
{ client_name: client_name, error: e.message }
|
|
90
|
+
end
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
{ clients: stats }
|
|
94
|
+
rescue => e
|
|
95
|
+
{ error: e.message }
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
register_tool('get_redis_info',
|
|
99
|
+
'Execute Redis INFO command and parse key sections ' \
|
|
100
|
+
'(Server, Clients, Memory, Stats, Keyspace)') do |name: nil|
|
|
101
|
+
return { error: 'Redis is not loaded (redis gem not installed)' } unless defined?(::Redis)
|
|
102
|
+
|
|
103
|
+
_name, client = DebugAgent.with_redis(name)
|
|
104
|
+
return { error: 'No Redis clients registered. Call DebugAgent.register_redis_client(:name, client).' } unless client
|
|
105
|
+
|
|
106
|
+
redis_conn =
|
|
107
|
+
if defined?(::ConnectionPool) && client.is_a?(::ConnectionPool)
|
|
108
|
+
client.with { |c| c }
|
|
109
|
+
else
|
|
110
|
+
client
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
raw = redis_conn.info
|
|
114
|
+
sections = {}
|
|
115
|
+
|
|
116
|
+
# Group known INFO keys into sections
|
|
117
|
+
section_keys = {
|
|
118
|
+
'Server' => %w[redis_version redis_mode os arch_bits tcp_port uptime_in_seconds uptime_in_days],
|
|
119
|
+
'Clients' => %w[connected_clients blocked_clients tracking_clients],
|
|
120
|
+
'Memory' => %w[used_memory used_memory_human used_memory_peak used_memory_peak_human used_memory_rss mem_fragmentation_ratio maxmemory maxmemory_human],
|
|
121
|
+
'Stats' => %w[total_connections_received total_commands_processed instantaneous_ops_per_sec keyspace_hits keyspace_misses expired_keys evicted_keys pubsub_channels pubsub_patterns],
|
|
122
|
+
'Persistence' => %w[rdb_last_bgsave_status rdb_changes_since_last_save aof_enabled]
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
section_keys.each do |section, keys|
|
|
126
|
+
sections[section] = keys.each_with_object({}) do |k, h|
|
|
127
|
+
h[k] = raw[k] if raw.key?(k)
|
|
128
|
+
end
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
# Keyspace section looks like "db0:keys=10,expires=0,avg_ttl=0"
|
|
132
|
+
keyspace = {}
|
|
133
|
+
raw.each do |k, v|
|
|
134
|
+
next unless k =~ /^db\d+$/
|
|
135
|
+
parsed = v.split(',').each_with_object({}) do |pair, h|
|
|
136
|
+
key, val = pair.split('=')
|
|
137
|
+
h[key] = val
|
|
138
|
+
end
|
|
139
|
+
keyspace[k] = parsed
|
|
140
|
+
end
|
|
141
|
+
sections['Keyspace'] = keyspace unless keyspace.empty?
|
|
142
|
+
|
|
143
|
+
{ sections: sections, raw_keys: raw.size }
|
|
144
|
+
rescue => e
|
|
145
|
+
{ error: e.message }
|
|
146
|
+
end
|
|
147
|
+
|
|
148
|
+
register_tool('get_redis_latency',
|
|
149
|
+
'Measure Redis PING latency over 10 samples (min/avg/max in ms)') do |name: nil, samples: 10|
|
|
150
|
+
return { error: 'Redis is not loaded (redis gem not installed)' } unless defined?(::Redis)
|
|
151
|
+
|
|
152
|
+
_name, client = DebugAgent.with_redis(name)
|
|
153
|
+
return { error: 'No Redis clients registered. Call DebugAgent.register_redis_client(:name, client).' } unless client
|
|
154
|
+
|
|
155
|
+
samples = samples.to_i
|
|
156
|
+
samples = 10 if samples <= 0
|
|
157
|
+
|
|
158
|
+
redis_conn =
|
|
159
|
+
if defined?(::ConnectionPool) && client.is_a?(::ConnectionPool)
|
|
160
|
+
client.with { |c| c }
|
|
161
|
+
else
|
|
162
|
+
client
|
|
163
|
+
end
|
|
164
|
+
|
|
165
|
+
latencies = []
|
|
166
|
+
samples.times do
|
|
167
|
+
start = Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
|
168
|
+
redis_conn.ping
|
|
169
|
+
finish = Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
|
170
|
+
latencies << ((finish - start) * 1000.0)
|
|
171
|
+
end
|
|
172
|
+
|
|
173
|
+
{
|
|
174
|
+
samples: latencies.size,
|
|
175
|
+
min_ms: latencies.min.round(3),
|
|
176
|
+
avg_ms: (latencies.sum / latencies.size).round(3),
|
|
177
|
+
max_ms: latencies.max.round(3),
|
|
178
|
+
all_ms: latencies.map { |l| l.round(3) }
|
|
179
|
+
}
|
|
180
|
+
rescue => e
|
|
181
|
+
{ error: e.message }
|
|
182
|
+
end
|
|
183
|
+
|
|
184
|
+
register_tool('get_redis_db_size',
|
|
185
|
+
'Execute Redis DBSIZE command (number of keys in current db)') do |name: nil|
|
|
186
|
+
return { error: 'Redis is not loaded (redis gem not installed)' } unless defined?(::Redis)
|
|
187
|
+
|
|
188
|
+
_name, client = DebugAgent.with_redis(name)
|
|
189
|
+
return { error: 'No Redis clients registered. Call DebugAgent.register_redis_client(:name, client).' } unless client
|
|
190
|
+
|
|
191
|
+
redis_conn =
|
|
192
|
+
if defined?(::ConnectionPool) && client.is_a?(::ConnectionPool)
|
|
193
|
+
client.with { |c| c }
|
|
194
|
+
else
|
|
195
|
+
client
|
|
196
|
+
end
|
|
197
|
+
|
|
198
|
+
{
|
|
199
|
+
db_size: redis_conn.dbsize,
|
|
200
|
+
client: _name
|
|
201
|
+
}
|
|
202
|
+
rescue => e
|
|
203
|
+
{ error: e.message }
|
|
204
|
+
end
|
|
205
|
+
end
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
module DebugAgent
|
|
2
|
+
# Registry for Sidekiq queue objects (Sidekiq::Queue instances). Applications
|
|
3
|
+
# register named queues so the inspector can read live stats.
|
|
4
|
+
#
|
|
5
|
+
# DebugAgent.register_sidekiq_queue(:default, Sidekiq::Queue.new('default'))
|
|
6
|
+
@sidekiq_queues = {}
|
|
7
|
+
|
|
8
|
+
class << self
|
|
9
|
+
attr_reader :sidekiq_queues
|
|
10
|
+
|
|
11
|
+
def register_sidekiq_queue(name, queue)
|
|
12
|
+
@sidekiq_queues[name.to_s] = queue
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
register_tool('get_sidekiq_queues',
|
|
17
|
+
'Get Sidekiq queue stats: processed, failed, enqueued totals and ' \
|
|
18
|
+
'per-queue sizes (requires sidekiq)') do
|
|
19
|
+
unless defined?(::Sidekiq)
|
|
20
|
+
return { error: 'Sidekiq is not loaded (sidekiq gem not installed)' }
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
stats = ::Sidekiq::Stats.new
|
|
24
|
+
|
|
25
|
+
queues =
|
|
26
|
+
if sidekiq_queues.any?
|
|
27
|
+
sidekiq_queues.map do |name, queue|
|
|
28
|
+
{
|
|
29
|
+
name: name,
|
|
30
|
+
size: queue.size,
|
|
31
|
+
latency_seconds: queue.latency.to_f.round(3)
|
|
32
|
+
}
|
|
33
|
+
end
|
|
34
|
+
else
|
|
35
|
+
::Sidekiq::Queue.all.map do |queue|
|
|
36
|
+
{
|
|
37
|
+
name: queue.name,
|
|
38
|
+
size: queue.size,
|
|
39
|
+
latency_seconds: queue.latency.to_f.round(3)
|
|
40
|
+
}
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
{
|
|
45
|
+
processed: stats.processed,
|
|
46
|
+
failed: stats.failed,
|
|
47
|
+
enqueued: stats.enqueued,
|
|
48
|
+
queues: queues
|
|
49
|
+
}
|
|
50
|
+
rescue => e
|
|
51
|
+
{ error: e.message }
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
register_tool('get_sidekiq_workers',
|
|
55
|
+
'Get Sidekiq worker stats: busy workers, processes, total concurrency ' \
|
|
56
|
+
'(requires sidekiq)') do
|
|
57
|
+
unless defined?(::Sidekiq)
|
|
58
|
+
return { error: 'Sidekiq is not loaded (sidekiq gem not installed)' }
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
processes = ::Sidekiq::ProcessSet.new.to_a
|
|
62
|
+
|
|
63
|
+
total_busy = processes.sum(&:busy)
|
|
64
|
+
total_concurrency = processes.sum(&:concurrency)
|
|
65
|
+
|
|
66
|
+
process_list = processes.map do |p|
|
|
67
|
+
{
|
|
68
|
+
identity: p.identity,
|
|
69
|
+
hostname: p['hostname'],
|
|
70
|
+
pid: p['pid'],
|
|
71
|
+
started_at: p['started_at'],
|
|
72
|
+
concurrency: p.concurrency,
|
|
73
|
+
busy: p.busy,
|
|
74
|
+
queues: p['queues'] || []
|
|
75
|
+
}
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
{
|
|
79
|
+
busy: total_busy,
|
|
80
|
+
processes: process_list.size,
|
|
81
|
+
total_concurrency: total_concurrency,
|
|
82
|
+
process_list: process_list
|
|
83
|
+
}
|
|
84
|
+
rescue => e
|
|
85
|
+
{ error: e.message }
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
register_tool('get_sidekiq_retries',
|
|
89
|
+
'Get Sidekiq retry set: count and sample jobs (requires sidekiq)') do |sample_size: 10|
|
|
90
|
+
unless defined?(::Sidekiq)
|
|
91
|
+
return { error: 'Sidekiq is not loaded (sidekiq gem not installed)' }
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
retry_set = ::Sidekiq::RetrySet.new
|
|
95
|
+
sample_size = sample_size.to_i
|
|
96
|
+
sample_size = 10 if sample_size <= 0
|
|
97
|
+
|
|
98
|
+
samples = []
|
|
99
|
+
retry_set.first(sample_size).each do |job|
|
|
100
|
+
samples << {
|
|
101
|
+
class: job.klass,
|
|
102
|
+
queue: job.queue,
|
|
103
|
+
args: job.args,
|
|
104
|
+
retry_count: job['retry_count'],
|
|
105
|
+
failed_at: job['failed_at'],
|
|
106
|
+
next_retry: job['next_at'] || job.at,
|
|
107
|
+
jid: job.jid,
|
|
108
|
+
error_message: job['error_message'],
|
|
109
|
+
error_class: job['error_class']
|
|
110
|
+
}
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
{
|
|
114
|
+
retry_count: retry_set.size,
|
|
115
|
+
sample_size: samples.size,
|
|
116
|
+
sample_jobs: samples
|
|
117
|
+
}
|
|
118
|
+
rescue => e
|
|
119
|
+
{ error: e.message }
|
|
120
|
+
end
|
|
121
|
+
end
|
data/lib/debug_agent/version.rb
CHANGED
data/lib/debug_agent.rb
CHANGED
|
@@ -18,6 +18,18 @@ require_relative 'debug_agent/inspectors/object_space'
|
|
|
18
18
|
require_relative 'debug_agent/inspectors/threads'
|
|
19
19
|
require_relative 'debug_agent/inspectors/routes'
|
|
20
20
|
require_relative 'debug_agent/inspectors/process_info'
|
|
21
|
+
require_relative 'debug_agent/inspectors/core_ext'
|
|
22
|
+
require_relative 'debug_agent/inspectors/redis'
|
|
23
|
+
require_relative 'debug_agent/inspectors/rails'
|
|
24
|
+
require_relative 'debug_agent/inspectors/sidekiq'
|
|
25
|
+
require_relative 'debug_agent/inspectors/puma'
|
|
26
|
+
require_relative 'debug_agent/inspectors/logging'
|
|
27
|
+
require_relative 'debug_agent/inspectors/cache'
|
|
28
|
+
require_relative 'debug_agent/inspectors/http_client'
|
|
29
|
+
require_relative 'debug_agent/inspectors/metrics'
|
|
30
|
+
require_relative 'debug_agent/inspectors/active_record_stats'
|
|
31
|
+
require_relative 'debug_agent/inspectors/faraday'
|
|
32
|
+
require_relative 'debug_agent/inspectors/concurrent'
|
|
21
33
|
|
|
22
34
|
module DebugAgent
|
|
23
35
|
class Error < StandardError; end
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: debug-agent
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.4.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- ggcode
|
|
@@ -51,8 +51,106 @@ dependencies:
|
|
|
51
51
|
- - "~>"
|
|
52
52
|
- !ruby/object:Gem::Version
|
|
53
53
|
version: '3.0'
|
|
54
|
+
- !ruby/object:Gem::Dependency
|
|
55
|
+
name: sinatra
|
|
56
|
+
requirement: !ruby/object:Gem::Requirement
|
|
57
|
+
requirements:
|
|
58
|
+
- - "~>"
|
|
59
|
+
- !ruby/object:Gem::Version
|
|
60
|
+
version: '4.0'
|
|
61
|
+
type: :development
|
|
62
|
+
prerelease: false
|
|
63
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
64
|
+
requirements:
|
|
65
|
+
- - "~>"
|
|
66
|
+
- !ruby/object:Gem::Version
|
|
67
|
+
version: '4.0'
|
|
68
|
+
- !ruby/object:Gem::Dependency
|
|
69
|
+
name: redis
|
|
70
|
+
requirement: !ruby/object:Gem::Requirement
|
|
71
|
+
requirements:
|
|
72
|
+
- - "~>"
|
|
73
|
+
- !ruby/object:Gem::Version
|
|
74
|
+
version: '5.0'
|
|
75
|
+
type: :development
|
|
76
|
+
prerelease: false
|
|
77
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
78
|
+
requirements:
|
|
79
|
+
- - "~>"
|
|
80
|
+
- !ruby/object:Gem::Version
|
|
81
|
+
version: '5.0'
|
|
82
|
+
- !ruby/object:Gem::Dependency
|
|
83
|
+
name: connection_pool
|
|
84
|
+
requirement: !ruby/object:Gem::Requirement
|
|
85
|
+
requirements:
|
|
86
|
+
- - "~>"
|
|
87
|
+
- !ruby/object:Gem::Version
|
|
88
|
+
version: '2.4'
|
|
89
|
+
type: :development
|
|
90
|
+
prerelease: false
|
|
91
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
92
|
+
requirements:
|
|
93
|
+
- - "~>"
|
|
94
|
+
- !ruby/object:Gem::Version
|
|
95
|
+
version: '2.4'
|
|
96
|
+
- !ruby/object:Gem::Dependency
|
|
97
|
+
name: sidekiq
|
|
98
|
+
requirement: !ruby/object:Gem::Requirement
|
|
99
|
+
requirements:
|
|
100
|
+
- - "~>"
|
|
101
|
+
- !ruby/object:Gem::Version
|
|
102
|
+
version: '7.0'
|
|
103
|
+
type: :development
|
|
104
|
+
prerelease: false
|
|
105
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
106
|
+
requirements:
|
|
107
|
+
- - "~>"
|
|
108
|
+
- !ruby/object:Gem::Version
|
|
109
|
+
version: '7.0'
|
|
110
|
+
- !ruby/object:Gem::Dependency
|
|
111
|
+
name: sqlite3
|
|
112
|
+
requirement: !ruby/object:Gem::Requirement
|
|
113
|
+
requirements:
|
|
114
|
+
- - "~>"
|
|
115
|
+
- !ruby/object:Gem::Version
|
|
116
|
+
version: '2.0'
|
|
117
|
+
type: :development
|
|
118
|
+
prerelease: false
|
|
119
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
120
|
+
requirements:
|
|
121
|
+
- - "~>"
|
|
122
|
+
- !ruby/object:Gem::Version
|
|
123
|
+
version: '2.0'
|
|
124
|
+
- !ruby/object:Gem::Dependency
|
|
125
|
+
name: puma
|
|
126
|
+
requirement: !ruby/object:Gem::Requirement
|
|
127
|
+
requirements:
|
|
128
|
+
- - "~>"
|
|
129
|
+
- !ruby/object:Gem::Version
|
|
130
|
+
version: '6.0'
|
|
131
|
+
type: :development
|
|
132
|
+
prerelease: false
|
|
133
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
134
|
+
requirements:
|
|
135
|
+
- - "~>"
|
|
136
|
+
- !ruby/object:Gem::Version
|
|
137
|
+
version: '6.0'
|
|
138
|
+
- !ruby/object:Gem::Dependency
|
|
139
|
+
name: rackup
|
|
140
|
+
requirement: !ruby/object:Gem::Requirement
|
|
141
|
+
requirements:
|
|
142
|
+
- - "~>"
|
|
143
|
+
- !ruby/object:Gem::Version
|
|
144
|
+
version: '2.0'
|
|
145
|
+
type: :development
|
|
146
|
+
prerelease: false
|
|
147
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
148
|
+
requirements:
|
|
149
|
+
- - "~>"
|
|
150
|
+
- !ruby/object:Gem::Version
|
|
151
|
+
version: '2.0'
|
|
54
152
|
description: Embed an AI debugging assistant into your Ruby web app. Inspect GC, threads,
|
|
55
|
-
memory, ObjectSpace, routes, HTTP requests, and more.
|
|
153
|
+
memory, ObjectSpace, routes, HTTP requests, Redis, Sidekiq, Puma, and more.
|
|
56
154
|
email:
|
|
57
155
|
- noreply@ggcode.dev
|
|
58
156
|
executables: []
|
|
@@ -66,12 +164,24 @@ files:
|
|
|
66
164
|
- lib/debug_agent/config.rb
|
|
67
165
|
- lib/debug_agent/context_compressor.rb
|
|
68
166
|
- lib/debug_agent/engine.rb
|
|
167
|
+
- lib/debug_agent/inspectors/active_record_stats.rb
|
|
168
|
+
- lib/debug_agent/inspectors/cache.rb
|
|
169
|
+
- lib/debug_agent/inspectors/concurrent.rb
|
|
170
|
+
- lib/debug_agent/inspectors/core_ext.rb
|
|
171
|
+
- lib/debug_agent/inspectors/faraday.rb
|
|
69
172
|
- lib/debug_agent/inspectors/gc.rb
|
|
173
|
+
- lib/debug_agent/inspectors/http_client.rb
|
|
70
174
|
- lib/debug_agent/inspectors/http_tracker.rb
|
|
175
|
+
- lib/debug_agent/inspectors/logging.rb
|
|
176
|
+
- lib/debug_agent/inspectors/metrics.rb
|
|
71
177
|
- lib/debug_agent/inspectors/object_space.rb
|
|
72
178
|
- lib/debug_agent/inspectors/process_info.rb
|
|
179
|
+
- lib/debug_agent/inspectors/puma.rb
|
|
180
|
+
- lib/debug_agent/inspectors/rails.rb
|
|
181
|
+
- lib/debug_agent/inspectors/redis.rb
|
|
73
182
|
- lib/debug_agent/inspectors/routes.rb
|
|
74
183
|
- lib/debug_agent/inspectors/runtime.rb
|
|
184
|
+
- lib/debug_agent/inspectors/sidekiq.rb
|
|
75
185
|
- lib/debug_agent/inspectors/system.rb
|
|
76
186
|
- lib/debug_agent/inspectors/threads.rb
|
|
77
187
|
- lib/debug_agent/llm_client.rb
|