railscope 0.1.9 → 0.1.11
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 +58 -1
- data/lib/generators/railscope/templates/initializer.rb +22 -0
- data/lib/railscope/context.rb +25 -0
- data/lib/railscope/middleware.rb +14 -11
- data/lib/railscope/subscribers/base_subscriber.rb +8 -2
- data/lib/railscope/subscribers/command_subscriber.rb +1 -0
- data/lib/railscope/subscribers/job_subscriber.rb +7 -2
- data/lib/railscope/subscribers/model_subscriber.rb +8 -0
- data/lib/railscope/version.rb +1 -1
- data/lib/railscope.rb +63 -1
- metadata +3 -3
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: c7b44de114284ee8955d48c35c4966b45e8b798b1624d6a8abefb2212b9e4cd5
|
|
4
|
+
data.tar.gz: 44ba0061f1987733edf5a5dde93ce591fc9eb2ff63f3aae59854051313b59d84
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 3b054e8e101d09dcd88c954eaf82452c9e18629398eb2270d2eddcd89769562eac8325a0c55c77505d5a1f2ecf5f2c7fa644d65e9eec318045c37832dbc3d92f
|
|
7
|
+
data.tar.gz: 9c7feb08613c99ef5fb206170dedb9e841a846e6d7077f4e06c8fa0673f19764f0ea5acff74da590e4879f7e78cb72213b9ceea35e76990f6b38e1aebabef26f
|
data/README.md
CHANGED
|
@@ -65,6 +65,15 @@ Railscope.configure do |config|
|
|
|
65
65
|
# Paths to ignore (defaults: /railscope, /assets, /packs, /cable)
|
|
66
66
|
config.add_ignore_paths("/health", "/ping", "/metrics")
|
|
67
67
|
|
|
68
|
+
# Jobs to ignore (class names or regex patterns)
|
|
69
|
+
config.add_ignore_jobs("SomeFrequentJob", "Turbo::Streams::.*")
|
|
70
|
+
|
|
71
|
+
# Rake tasks to ignore (task names or regex patterns)
|
|
72
|
+
config.add_ignore_commands("db:.*", "assets:.*", "tmp:.*")
|
|
73
|
+
|
|
74
|
+
# Use an existing Redis connection (useful for SSL/connection sharing)
|
|
75
|
+
# config.redis = $redis
|
|
76
|
+
|
|
68
77
|
# Additional sensitive keys to filter
|
|
69
78
|
config.add_sensitive_keys(:cpf, :ssn, :bank_account)
|
|
70
79
|
end
|
|
@@ -208,6 +217,51 @@ Railscope::PurgeJob.perform_now
|
|
|
208
217
|
Railscope::PurgeJob.perform_later
|
|
209
218
|
```
|
|
210
219
|
|
|
220
|
+
## Filtering Entries
|
|
221
|
+
|
|
222
|
+
### Ignore Paths
|
|
223
|
+
|
|
224
|
+
Requests matching these path prefixes will not be recorded:
|
|
225
|
+
|
|
226
|
+
```ruby
|
|
227
|
+
# Defaults: /railscope, /assets, /packs, /cable
|
|
228
|
+
config.add_ignore_paths("/health", "/ping", "/metrics", "/up")
|
|
229
|
+
```
|
|
230
|
+
|
|
231
|
+
### Ignore Jobs
|
|
232
|
+
|
|
233
|
+
Background jobs matching these patterns will not be recorded (enqueue, perform, or exceptions). Accepts exact class names or regex patterns:
|
|
234
|
+
|
|
235
|
+
```ruby
|
|
236
|
+
# Exact match
|
|
237
|
+
config.add_ignore_jobs("HeartbeatJob")
|
|
238
|
+
|
|
239
|
+
# Regex patterns
|
|
240
|
+
config.add_ignore_jobs("SolidQueue::.*", "Turbo::Streams::.*", "ActionMailbox::.*")
|
|
241
|
+
```
|
|
242
|
+
|
|
243
|
+
### Ignore Commands
|
|
244
|
+
|
|
245
|
+
Rake tasks matching these patterns will not be instrumented. Accepts exact task names or regex patterns:
|
|
246
|
+
|
|
247
|
+
```ruby
|
|
248
|
+
# Exact match
|
|
249
|
+
config.add_ignore_commands("db:migrate")
|
|
250
|
+
|
|
251
|
+
# Regex patterns — ignore entire namespaces
|
|
252
|
+
config.add_ignore_commands("db:.*", "assets:.*", "tmp:.*", "log:.*")
|
|
253
|
+
```
|
|
254
|
+
|
|
255
|
+
### Custom Redis Connection
|
|
256
|
+
|
|
257
|
+
By default, Railscope creates its own Redis connection from `RAILSCOPE_REDIS_URL` or `REDIS_URL`. If your app already has a configured Redis instance (e.g., with SSL on Heroku), you can pass it directly:
|
|
258
|
+
|
|
259
|
+
```ruby
|
|
260
|
+
config.redis = $redis
|
|
261
|
+
```
|
|
262
|
+
|
|
263
|
+
This avoids SSL certificate issues and shares the existing connection configuration.
|
|
264
|
+
|
|
211
265
|
## Filtered Parameters
|
|
212
266
|
|
|
213
267
|
Railscope automatically filters sensitive data:
|
|
@@ -279,8 +333,11 @@ Railscope is designed to have minimal impact:
|
|
|
279
333
|
For high-traffic production environments, consider:
|
|
280
334
|
- Using `:redis` backend for lower request latency
|
|
281
335
|
- Shorter retention periods
|
|
282
|
-
-
|
|
336
|
+
- Ignoring noisy paths (`/health`, `/ping`, polling endpoints)
|
|
337
|
+
- Ignoring high-frequency jobs (`SolidQueue::.*`, `Turbo::Streams::.*`)
|
|
338
|
+
- Ignoring routine rake tasks (`db:.*`, `assets:.*`)
|
|
283
339
|
- Running purge job more frequently
|
|
340
|
+
- Using `config.redis = $redis` to share the app's existing Redis connection
|
|
284
341
|
|
|
285
342
|
## License
|
|
286
343
|
|
|
@@ -49,6 +49,28 @@ Railscope.configure do |config|
|
|
|
49
49
|
#
|
|
50
50
|
# config.add_ignore_paths("/health", "/ping", "/metrics")
|
|
51
51
|
|
|
52
|
+
# Ignored Jobs
|
|
53
|
+
# ------------
|
|
54
|
+
# Jobs matching these patterns will not be recorded.
|
|
55
|
+
# Accepts class names or regex patterns.
|
|
56
|
+
#
|
|
57
|
+
# config.add_ignore_jobs("SomeFrequentJob", "Turbo::Streams::.*")
|
|
58
|
+
|
|
59
|
+
# Ignored Commands
|
|
60
|
+
# ----------------
|
|
61
|
+
# Rake tasks matching these patterns will not be recorded.
|
|
62
|
+
# Accepts task names or regex patterns.
|
|
63
|
+
#
|
|
64
|
+
# config.add_ignore_commands("db:.*", "assets:.*", "tmp:.*")
|
|
65
|
+
|
|
66
|
+
# Redis Connection
|
|
67
|
+
# ----------------
|
|
68
|
+
# By default, Railscope creates its own Redis connection from
|
|
69
|
+
# RAILSCOPE_REDIS_URL or REDIS_URL. You can pass an existing
|
|
70
|
+
# Redis instance instead (useful for SSL/connection sharing):
|
|
71
|
+
#
|
|
72
|
+
# config.redis = $redis
|
|
73
|
+
|
|
52
74
|
# Sensitive Keys
|
|
53
75
|
# --------------
|
|
54
76
|
# Additional parameter names to filter from payloads.
|
data/lib/railscope/context.rb
CHANGED
|
@@ -87,5 +87,30 @@ module Railscope
|
|
|
87
87
|
end
|
|
88
88
|
|
|
89
89
|
delegate :empty?, to: :@store
|
|
90
|
+
|
|
91
|
+
# Conditional recording: buffer entries until a trigger fires
|
|
92
|
+
|
|
93
|
+
def pending_entries
|
|
94
|
+
self[:pending_entries] ||= []
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
def triggered?
|
|
98
|
+
self[:triggered] == true
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
def trigger!
|
|
102
|
+
self[:triggered] = true
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
def buffer_entry(entry_data)
|
|
106
|
+
pending_entries << entry_data
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
def flush_pending!
|
|
110
|
+
pending_entries.each do |entry_data|
|
|
111
|
+
Railscope.storage.write(**entry_data)
|
|
112
|
+
end
|
|
113
|
+
pending_entries.clear
|
|
114
|
+
end
|
|
90
115
|
end
|
|
91
116
|
end
|
data/lib/railscope/middleware.rb
CHANGED
|
@@ -18,17 +18,20 @@ module Railscope
|
|
|
18
18
|
# Capture response body for recording
|
|
19
19
|
context = Context.current
|
|
20
20
|
if context[:recording]
|
|
21
|
-
#
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
21
|
+
# In conditional mode, skip persistence if trigger never fired
|
|
22
|
+
unless Railscope.conditional_recording? && !context.triggered?
|
|
23
|
+
# Read body from env (where Rails stores the response)
|
|
24
|
+
body_content = extract_body_from_env(env)
|
|
25
|
+
|
|
26
|
+
context_data = {
|
|
27
|
+
batch_id: context.batch_id,
|
|
28
|
+
env: env,
|
|
29
|
+
headers: headers
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
# Update entry with response data
|
|
33
|
+
update_entry_async(context_data, body_content)
|
|
34
|
+
end
|
|
32
35
|
end
|
|
33
36
|
|
|
34
37
|
[status, headers, response]
|
|
@@ -29,7 +29,7 @@ module Railscope
|
|
|
29
29
|
|
|
30
30
|
filtered_payload = Railscope.filter(payload.merge(context_payload))
|
|
31
31
|
|
|
32
|
-
|
|
32
|
+
entry_data = {
|
|
33
33
|
entry_type: entry_type,
|
|
34
34
|
batch_id: context.batch_id,
|
|
35
35
|
family_hash: family_hash,
|
|
@@ -37,7 +37,13 @@ module Railscope
|
|
|
37
37
|
payload: filtered_payload,
|
|
38
38
|
tags: (tags + context_tags).uniq,
|
|
39
39
|
occurred_at: Time.current
|
|
40
|
-
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
if Railscope.conditional_recording? && !context.triggered?
|
|
43
|
+
context.buffer_entry(entry_data)
|
|
44
|
+
else
|
|
45
|
+
Railscope.storage.write(**entry_data)
|
|
46
|
+
end
|
|
41
47
|
end
|
|
42
48
|
|
|
43
49
|
# Generate a family hash for grouping similar entries
|
|
@@ -76,7 +76,12 @@ module Railscope
|
|
|
76
76
|
# Also create a separate exception entry if job failed
|
|
77
77
|
create_exception_entry!(job, exception_object) if exception_object
|
|
78
78
|
|
|
79
|
-
#
|
|
79
|
+
# In conditional mode: flush if triggered, otherwise entries are discarded with context
|
|
80
|
+
if Railscope.conditional_recording? && context.triggered?
|
|
81
|
+
# Response update for jobs is already in the entry payload, nothing extra needed
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
# Clear context after job completes (discards any unflushed buffer)
|
|
80
85
|
Railscope::Context.clear!
|
|
81
86
|
rescue StandardError => e
|
|
82
87
|
Rails.logger.error("[Railscope] Failed to record job perform: #{e.message}")
|
|
@@ -199,7 +204,7 @@ module Railscope
|
|
|
199
204
|
end
|
|
200
205
|
|
|
201
206
|
def ignore_job?(job)
|
|
202
|
-
job.class.name.start_with?("Railscope::")
|
|
207
|
+
job.class.name.start_with?("Railscope::") || Railscope.ignore_job?(job.class.name)
|
|
203
208
|
end
|
|
204
209
|
|
|
205
210
|
def setup_job_context(job)
|
|
@@ -42,6 +42,7 @@ module Railscope
|
|
|
42
42
|
model_name = model.class.name
|
|
43
43
|
return if model_name.nil?
|
|
44
44
|
return if IGNORED_MODEL_PREFIXES.any? { |prefix| model_name.start_with?(prefix) }
|
|
45
|
+
return unless Railscope.should_track_model?(model_name)
|
|
45
46
|
|
|
46
47
|
create_entry!(
|
|
47
48
|
entry_type: "model",
|
|
@@ -50,6 +51,13 @@ module Railscope
|
|
|
50
51
|
family_hash: build_family_hash(action, model),
|
|
51
52
|
should_display_on_index: true
|
|
52
53
|
)
|
|
54
|
+
|
|
55
|
+
# Conditional recording: flush buffered entries when trigger matches
|
|
56
|
+
if Railscope.conditional_recording? && !context.triggered? && Railscope.matches_trigger?(model_name, action)
|
|
57
|
+
context.trigger!
|
|
58
|
+
context.flush_pending!
|
|
59
|
+
end
|
|
60
|
+
|
|
53
61
|
Rails.logger.debug("[Railscope] ModelSubscriber - entry created for #{model_name}")
|
|
54
62
|
rescue StandardError => e
|
|
55
63
|
Rails.logger.error("[Railscope] Failed to record model event: #{e.class}: #{e.message}")
|
data/lib/railscope/version.rb
CHANGED
data/lib/railscope.rb
CHANGED
|
@@ -27,7 +27,8 @@ module Railscope
|
|
|
27
27
|
STORAGE_REDIS = :redis
|
|
28
28
|
|
|
29
29
|
class << self
|
|
30
|
-
attr_writer :retention_days, :redis, :storage_backend, :ignore_paths
|
|
30
|
+
attr_writer :retention_days, :redis, :storage_backend, :ignore_paths, :ignore_jobs, :ignore_commands,
|
|
31
|
+
:ignore_models
|
|
31
32
|
attr_accessor :authenticate_with
|
|
32
33
|
|
|
33
34
|
def enabled=(value)
|
|
@@ -99,6 +100,67 @@ module Railscope
|
|
|
99
100
|
@ignore_paths = ignore_paths.concat(paths.flatten).uniq
|
|
100
101
|
end
|
|
101
102
|
|
|
103
|
+
def ignore_jobs
|
|
104
|
+
@ignore_jobs ||= []
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
def add_ignore_jobs(*job_classes)
|
|
108
|
+
@ignore_jobs = ignore_jobs.concat(job_classes.flatten.map(&:to_s)).uniq
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
def ignore_job?(job_class_name)
|
|
112
|
+
ignore_jobs.any? { |pattern| job_class_name.match?(pattern) }
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
def ignore_commands
|
|
116
|
+
@ignore_commands ||= []
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
def add_ignore_commands(*commands)
|
|
120
|
+
@ignore_commands = ignore_commands.concat(commands.flatten.map(&:to_s)).uniq
|
|
121
|
+
end
|
|
122
|
+
|
|
123
|
+
def ignore_command?(command_name)
|
|
124
|
+
ignore_commands.any? { |pattern| command_name.match?(pattern) }
|
|
125
|
+
end
|
|
126
|
+
|
|
127
|
+
# Model filtering (blocklist)
|
|
128
|
+
|
|
129
|
+
def ignore_models
|
|
130
|
+
@ignore_models ||= []
|
|
131
|
+
end
|
|
132
|
+
|
|
133
|
+
def add_ignore_models(*model_names)
|
|
134
|
+
@ignore_models = ignore_models.concat(model_names.flatten.map(&:to_s)).uniq
|
|
135
|
+
end
|
|
136
|
+
|
|
137
|
+
def should_track_model?(model_name)
|
|
138
|
+
return false if model_name.nil?
|
|
139
|
+
|
|
140
|
+
ignore_models.none? { |pattern| model_name.match?(pattern) }
|
|
141
|
+
end
|
|
142
|
+
|
|
143
|
+
# Conditional recording: only persist a batch when a watched model event fires
|
|
144
|
+
|
|
145
|
+
def watch_triggers
|
|
146
|
+
@watch_triggers ||= []
|
|
147
|
+
end
|
|
148
|
+
|
|
149
|
+
def add_watch_trigger(model_name, on:)
|
|
150
|
+
actions = Array(on).map(&:to_s)
|
|
151
|
+
@watch_triggers = watch_triggers.push({ model: model_name.to_s, on: actions }).uniq
|
|
152
|
+
end
|
|
153
|
+
|
|
154
|
+
def conditional_recording?
|
|
155
|
+
watch_triggers.any?
|
|
156
|
+
end
|
|
157
|
+
|
|
158
|
+
def matches_trigger?(model_name, action)
|
|
159
|
+
watch_triggers.any? do |trigger|
|
|
160
|
+
model_name.match?(trigger[:model]) && trigger[:on].include?(action.to_s)
|
|
161
|
+
end
|
|
162
|
+
end
|
|
163
|
+
|
|
102
164
|
def context
|
|
103
165
|
Context.current
|
|
104
166
|
end
|
metadata
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: railscope
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.1.
|
|
4
|
+
version: 0.1.11
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Phelipe Tussolini
|
|
8
8
|
bindir: exe
|
|
9
9
|
cert_chain: []
|
|
10
|
-
date:
|
|
10
|
+
date: 2026-02-18 00:00:00.000000000 Z
|
|
11
11
|
dependencies:
|
|
12
12
|
- !ruby/object:Gem::Dependency
|
|
13
13
|
name: rails
|
|
@@ -155,7 +155,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
155
155
|
- !ruby/object:Gem::Version
|
|
156
156
|
version: '0'
|
|
157
157
|
requirements: []
|
|
158
|
-
rubygems_version: 3.6.
|
|
158
|
+
rubygems_version: 3.6.2
|
|
159
159
|
specification_version: 4
|
|
160
160
|
summary: A debug assistant for Rails applications inspired by Laravel Telescope
|
|
161
161
|
test_files: []
|