dontbugme 0.1.5 → 0.1.6
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 +20 -0
- data/app/helpers/dontbugme/traces_helper.rb +38 -0
- data/app/views/dontbugme/traces/index.html.erb +1 -0
- data/app/views/dontbugme/traces/show.html.erb +9 -1
- data/app/views/layouts/dontbugme/application.html.erb +3 -0
- data/lib/dontbugme/configuration.rb +4 -0
- data/lib/dontbugme/middleware/rack.rb +4 -1
- data/lib/dontbugme/railtie.rb +1 -0
- data/lib/dontbugme/recorder.rb +1 -0
- data/lib/dontbugme/variable_tracker.rb +140 -0
- data/lib/dontbugme/version.rb +1 -1
- data/lib/dontbugme.rb +11 -0
- data/lib/generators/dontbugme/install/templates/dontbugme.rb +3 -0
- metadata +3 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 6f71bd0ef418ea0d47ca94a5242d3677a00d82cf36e2fd0d8b69f3f4ce323f9c
|
|
4
|
+
data.tar.gz: c9692d06855f24fc322e218588958881a0a8d9ee4b278125961143b7527fdf3a
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 85e2c35aa316839754b10c22353ef1b89970c893f78f537708fce9a5af7653c4532219a98bd035c833bfdc9e3f3d4fbd3c61a6ad8fb9f60a6d8bff2f1a301511
|
|
7
|
+
data.tar.gz: 646af28b63c0f177fcc68b48c0892f3a92c0b1dd102aad33b70d2c54915b85fa325bdcf5b6661c4e2b8c5b13af735d10a6b088643df9335f8967ab8569e918b9
|
data/README.md
CHANGED
|
@@ -128,6 +128,26 @@ end
|
|
|
128
128
|
|
|
129
129
|
Use `capture_output: false` to skip capturing the return value for sensitive data.
|
|
130
130
|
|
|
131
|
+
### Automatic Variable Tracking
|
|
132
|
+
|
|
133
|
+
Dontbugme automatically captures local variable changes between lines in your app code. No manual instrumentation needed — when a variable like `token` changes from `abc123` to `abc124`, you'll see an observe span with Input and Output in the UI.
|
|
134
|
+
|
|
135
|
+
```ruby
|
|
136
|
+
token = Member.find(1).confirmation_token
|
|
137
|
+
token += 1
|
|
138
|
+
Member.find(1).update(confirmation_token: token)
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
The UI will show both **Input** and **Output** for the transformation. Enabled by default in development; disable with `config.capture_variable_changes = false`. Only tracks simple types (String, Integer, Float, etc.) to avoid noise.
|
|
142
|
+
|
|
143
|
+
### Manual Observe (optional)
|
|
144
|
+
|
|
145
|
+
For explicit control, use `Dontbugme.observe`:
|
|
146
|
+
|
|
147
|
+
```ruby
|
|
148
|
+
token = Dontbugme.observe('token increment', token) { token + 1 }
|
|
149
|
+
```
|
|
150
|
+
|
|
131
151
|
### Span Categories
|
|
132
152
|
|
|
133
153
|
Access spans by category for assertions or analysis:
|
|
@@ -17,6 +17,15 @@ module Dontbugme
|
|
|
17
17
|
end
|
|
18
18
|
|
|
19
19
|
OUTPUT_KEYS = %w[output result response_body].freeze
|
|
20
|
+
DEDICATED_INPUT_KEYS = %w[input].freeze
|
|
21
|
+
|
|
22
|
+
def span_input(span)
|
|
23
|
+
return nil if span.payload.blank?
|
|
24
|
+
|
|
25
|
+
payload = span.payload.is_a?(Hash) ? span.payload : {}
|
|
26
|
+
key = DEDICATED_INPUT_KEYS.find { |k| payload[k.to_sym] || payload[k] }
|
|
27
|
+
key ? (payload[key.to_sym] || payload[key]) : nil
|
|
28
|
+
end
|
|
20
29
|
|
|
21
30
|
def span_output(span)
|
|
22
31
|
return nil if span.payload.blank?
|
|
@@ -33,6 +42,7 @@ module Dontbugme
|
|
|
33
42
|
payload.map do |key, value|
|
|
34
43
|
next if value.nil?
|
|
35
44
|
next if OUTPUT_KEYS.include?(key.to_s)
|
|
45
|
+
next if DEDICATED_INPUT_KEYS.include?(key.to_s)
|
|
36
46
|
|
|
37
47
|
display_value = case value
|
|
38
48
|
when Array then value.map { |v| v.is_a?(String) ? v : v.inspect }.join(', ')
|
|
@@ -54,5 +64,33 @@ module Dontbugme
|
|
|
54
64
|
return str if str.length <= max_len
|
|
55
65
|
"#{str[0, max_len - 3]}..."
|
|
56
66
|
end
|
|
67
|
+
|
|
68
|
+
def trace_started_at_formatted(trace)
|
|
69
|
+
return nil unless trace.respond_to?(:started_at_utc) && trace.started_at_utc
|
|
70
|
+
|
|
71
|
+
trace.started_at_utc.respond_to?(:strftime) ? trace.started_at_utc.strftime('%Y-%m-%d %H:%M:%S.%3N UTC') : trace.started_at_utc.to_s
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
def trace_started_at_short(trace)
|
|
75
|
+
return nil unless trace.respond_to?(:started_at_utc) && trace.started_at_utc
|
|
76
|
+
|
|
77
|
+
trace.started_at_utc.respond_to?(:strftime) ? trace.started_at_utc.strftime('%Y-%m-%d %H:%M:%S') : trace.started_at_utc.to_s
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
def trace_finished_at_formatted(trace)
|
|
81
|
+
return nil unless trace.respond_to?(:duration_ms) && trace.duration_ms
|
|
82
|
+
|
|
83
|
+
finished = trace.started_at_utc + (trace.duration_ms / 1000.0)
|
|
84
|
+
finished.respond_to?(:strftime) ? finished.strftime('%Y-%m-%d %H:%M:%S.%3N UTC') : finished.to_s
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
def span_timestamp_formatted(trace, span)
|
|
88
|
+
return nil unless trace.respond_to?(:started_at_utc) && trace.started_at_utc
|
|
89
|
+
return nil unless span.respond_to?(:started_at) && span.started_at
|
|
90
|
+
|
|
91
|
+
offset_sec = (span.started_at.to_f / 1000.0)
|
|
92
|
+
at = trace.started_at_utc + offset_sec
|
|
93
|
+
at.respond_to?(:strftime) ? at.strftime('%H:%M:%S.%3N') : at.to_s
|
|
94
|
+
end
|
|
57
95
|
end
|
|
58
96
|
end
|
|
@@ -22,6 +22,7 @@
|
|
|
22
22
|
<span class="trace-identifier"><%= trace.identifier %></span>
|
|
23
23
|
<span class="badge <%= trace.status == :success ? 'badge-success' : 'badge-error' %>"><%= trace.status %></span>
|
|
24
24
|
<span class="trace-duration"><%= trace.duration_ms ? "#{trace.duration_ms.round}ms" : '-' %></span>
|
|
25
|
+
<span class="trace-timestamp" title="<%= trace_started_at_formatted(trace) %>"><%= trace_started_at_short(trace) %></span>
|
|
25
26
|
<% end %>
|
|
26
27
|
<% end %>
|
|
27
28
|
<% else %>
|
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
<div class="trace-meta">
|
|
5
5
|
<span class="badge <%= @trace.status == :success ? 'badge-success' : 'badge-error' %>"><%= @trace.status %></span>
|
|
6
6
|
<span class="trace-meta-item"><%= @trace.duration_ms ? "#{@trace.duration_ms.round}ms" : 'N/A' %></span>
|
|
7
|
+
<span class="trace-meta-item trace-timestamp" title="<%= trace_started_at_formatted(@trace) %>"><%= trace_started_at_formatted(@trace) %></span>
|
|
7
8
|
<span class="trace-meta-item"><%= @trace.id %></span>
|
|
8
9
|
<% if @trace.correlation_id.present? %>
|
|
9
10
|
<%= link_to "Follow chain", root_path(correlation_id: @trace.correlation_id), class: 'trace-link' %>
|
|
@@ -35,6 +36,7 @@
|
|
|
35
36
|
<% spans_to_show.each_with_index do |span, idx| %>
|
|
36
37
|
<details class="span-card card" data-category="<%= span.category %>">
|
|
37
38
|
<summary class="span-summary">
|
|
39
|
+
<span class="span-timestamp" title="<%= trace_started_at_formatted(@trace) %>"><%= span_timestamp_formatted(@trace, span) %></span>
|
|
38
40
|
<span class="span-offset"><%= span.started_at.to_f.round(1) %>ms</span>
|
|
39
41
|
<span class="span-duration"><%= span.duration_ms ? "#{span.duration_ms.round(1)}ms" : '-' %></span>
|
|
40
42
|
<span class="badge <%= span_category_badge_class(span.category) %>"><%= span.category.to_s.upcase %></span>
|
|
@@ -42,6 +44,12 @@
|
|
|
42
44
|
<span class="span-detail-preview"><%= truncate_detail(span.detail, 80) %></span>
|
|
43
45
|
</summary>
|
|
44
46
|
<div class="span-details">
|
|
47
|
+
<% if span_input(span).present? %>
|
|
48
|
+
<div class="span-section span-input-section">
|
|
49
|
+
<h4>Input <button type="button" class="copy-btn" data-copy-target="input-<%= idx %>" title="Copy">Copy</button></h4>
|
|
50
|
+
<pre class="code-block input-block" id="input-<%= idx %>"><code><%= h(span_input(span)) %></code></pre>
|
|
51
|
+
</div>
|
|
52
|
+
<% end %>
|
|
45
53
|
<% if span_output(span).present? %>
|
|
46
54
|
<div class="span-section span-output-section">
|
|
47
55
|
<h4>Output <button type="button" class="copy-btn" data-copy-target="output-<%= idx %>" title="Copy">Copy</button></h4>
|
|
@@ -81,7 +89,7 @@
|
|
|
81
89
|
<% end %>
|
|
82
90
|
|
|
83
91
|
<div class="trace-footer">
|
|
84
|
-
<p>Filter by category: <%= link_to 'All', trace_path(@trace.id) %> · <%= link_to 'SQL', trace_path(@trace.id, only: 'sql') %> · <%= link_to 'HTTP', trace_path(@trace.id, only: 'http') %> · <%= link_to 'Redis', trace_path(@trace.id, only: 'redis') %> · <%= link_to 'Cache', trace_path(@trace.id, only: 'cache') %> · <%= link_to 'Mailer', trace_path(@trace.id, only: 'mailer') %> · <%= link_to 'Enqueue', trace_path(@trace.id, only: 'enqueue') %></p>
|
|
92
|
+
<p>Filter by category: <%= link_to 'All', trace_path(@trace.id) %> · <%= link_to 'SQL', trace_path(@trace.id, only: 'sql') %> · <%= link_to 'HTTP', trace_path(@trace.id, only: 'http') %> · <%= link_to 'Redis', trace_path(@trace.id, only: 'redis') %> · <%= link_to 'Cache', trace_path(@trace.id, only: 'cache') %> · <%= link_to 'Mailer', trace_path(@trace.id, only: 'mailer') %> · <%= link_to 'Enqueue', trace_path(@trace.id, only: 'enqueue') %> · <%= link_to 'Custom', trace_path(@trace.id, only: 'custom') %> · <%= link_to 'Snapshot', trace_path(@trace.id, only: 'snapshot') %></p>
|
|
85
93
|
</div>
|
|
86
94
|
|
|
87
95
|
<% if @trace.error.present? %>
|
|
@@ -36,6 +36,8 @@
|
|
|
36
36
|
.badge-snapshot { background: #312e81; color: #a5b4fc; }
|
|
37
37
|
.badge-default { background: #334155; color: #94a3b8; }
|
|
38
38
|
.trace-duration { color: #64748b; min-width: 60px; font-family: monospace; }
|
|
39
|
+
.trace-timestamp { font-family: monospace; font-size: 12px; color: #64748b; }
|
|
40
|
+
.span-timestamp { font-family: monospace; font-size: 11px; color: #64748b; min-width: 90px; }
|
|
39
41
|
.filters { display: flex; gap: 12px; margin-bottom: 16px; flex-wrap: wrap; }
|
|
40
42
|
.filters input, .filters select { padding: 10px 14px; border: 1px solid #334155; border-radius: 6px; font-size: 13px; background: #1e293b; color: #e2e8f0; }
|
|
41
43
|
.filters button { padding: 10px 20px; background: #38bdf8; color: #0f172a; border: none; border-radius: 6px; cursor: pointer; font-size: 13px; font-weight: 600; }
|
|
@@ -90,6 +92,7 @@
|
|
|
90
92
|
.backtrace { font-size: 11px; color: #94a3b8; }
|
|
91
93
|
.span-section h4 { display: flex; align-items: center; gap: 8px; }
|
|
92
94
|
.span-output-section .output-block { border-left: 3px solid #38bdf8; }
|
|
95
|
+
.span-input-section .input-block { border-left: 3px solid #94a3b8; }
|
|
93
96
|
.copy-btn { background: #334155; color: #94a3b8; border: none; padding: 4px 10px; border-radius: 4px; font-size: 11px; cursor: pointer; }
|
|
94
97
|
.copy-btn:hover { background: #475569; color: #e2e8f0; }
|
|
95
98
|
</style>
|
|
@@ -18,6 +18,7 @@ module Dontbugme
|
|
|
18
18
|
:capture_redis_values,
|
|
19
19
|
:capture_redis_return_values,
|
|
20
20
|
:capture_span_output,
|
|
21
|
+
:capture_variable_changes,
|
|
21
22
|
:source_mode,
|
|
22
23
|
:source_filter,
|
|
23
24
|
:source_depth,
|
|
@@ -67,6 +68,7 @@ module Dontbugme
|
|
|
67
68
|
self.capture_redis_values = false
|
|
68
69
|
self.capture_redis_return_values = true
|
|
69
70
|
self.capture_span_output = true
|
|
71
|
+
self.capture_variable_changes = true
|
|
70
72
|
self.source_mode = :full
|
|
71
73
|
self.source_filter = %w[app/ lib/]
|
|
72
74
|
self.source_depth = 3
|
|
@@ -89,6 +91,7 @@ module Dontbugme
|
|
|
89
91
|
self.recording_mode = :off
|
|
90
92
|
self.enable_web_ui = false
|
|
91
93
|
self.record_on_error = false
|
|
94
|
+
self.capture_variable_changes = false
|
|
92
95
|
self.max_trace_buffer_bytes = 5 * 1024 * 1024 # 5 MB
|
|
93
96
|
end
|
|
94
97
|
|
|
@@ -101,6 +104,7 @@ module Dontbugme
|
|
|
101
104
|
self.capture_sql_binds = false
|
|
102
105
|
self.capture_redis_return_values = false
|
|
103
106
|
self.capture_span_output = false
|
|
107
|
+
self.capture_variable_changes = false
|
|
104
108
|
self.source_mode = :shallow
|
|
105
109
|
self.source_depth = 1
|
|
106
110
|
self.source_stack_limit = 30
|
|
@@ -12,12 +12,15 @@ module Dontbugme
|
|
|
12
12
|
return @app.call(env) unless Dontbugme.config.should_record_request?(env)
|
|
13
13
|
|
|
14
14
|
request = ::Rack::Request.new(env)
|
|
15
|
+
path = request.path.to_s
|
|
16
|
+
mount_path = Dontbugme.config.web_ui_mount_path.to_s.chomp('/')
|
|
17
|
+
return @app.call(env) if mount_path.to_s != '' && (path == mount_path || path.start_with?("#{mount_path}/"))
|
|
18
|
+
|
|
15
19
|
request_id = env['action_dispatch.request_id'] || request.get_header('HTTP_X_REQUEST_ID') || SecureRandom.uuid
|
|
16
20
|
correlation_id = env['HTTP_X_CORRELATION_ID'] || Correlation.generate
|
|
17
21
|
Correlation.current = correlation_id
|
|
18
22
|
|
|
19
23
|
method = request.request_method
|
|
20
|
-
path = request.path
|
|
21
24
|
identifier = "#{method} #{path}"
|
|
22
25
|
|
|
23
26
|
metadata = {
|
data/lib/dontbugme/railtie.rb
CHANGED
data/lib/dontbugme/recorder.rb
CHANGED
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Dontbugme
|
|
4
|
+
# Automatically captures local variable changes between lines using TracePoint.
|
|
5
|
+
# Emits observe-style spans when variables change, so you can inspect value
|
|
6
|
+
# transformations without manual Dontbugme.observe calls.
|
|
7
|
+
class VariableTracker
|
|
8
|
+
THREAD_KEY = :dontbugme_variable_tracker_state
|
|
9
|
+
THREAD_PATH_KEY = :dontbugme_variable_tracker_path
|
|
10
|
+
IN_TRACKER_KEY = :dontbugme_variable_tracker_in_callback
|
|
11
|
+
SKIP_VARS = %w[_ result trace e ex].freeze
|
|
12
|
+
TRACKABLE_CLASSES = [String, Integer, Float, Symbol, TrueClass, FalseClass, NilClass].freeze
|
|
13
|
+
|
|
14
|
+
class << self
|
|
15
|
+
def subscribe
|
|
16
|
+
return if @subscribed
|
|
17
|
+
|
|
18
|
+
@trace_point = TracePoint.new(:line) { |tp| handle_line(tp) }
|
|
19
|
+
@trace_point.enable
|
|
20
|
+
@subscribed = true
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def unsubscribe
|
|
24
|
+
return unless @subscribed
|
|
25
|
+
|
|
26
|
+
@trace_point&.disable
|
|
27
|
+
@trace_point = nil
|
|
28
|
+
@subscribed = false
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def handle_line(tp)
|
|
32
|
+
return if Thread.current[IN_TRACKER_KEY]
|
|
33
|
+
return unless Dontbugme.config.recording?
|
|
34
|
+
return unless Dontbugme.config.capture_variable_changes
|
|
35
|
+
return unless Context.active?
|
|
36
|
+
|
|
37
|
+
path = tp.path.to_s
|
|
38
|
+
return if path.include?('dontbugme') || path.include?('/gems/') || path.include?('bundler')
|
|
39
|
+
return unless Dontbugme.config.source_filter.any? { |f| path.include?(f) }
|
|
40
|
+
|
|
41
|
+
binding = tp.binding
|
|
42
|
+
return unless binding
|
|
43
|
+
|
|
44
|
+
Thread.current[IN_TRACKER_KEY] = true
|
|
45
|
+
begin
|
|
46
|
+
current = extract_locals(binding)
|
|
47
|
+
prev = Thread.current[THREAD_KEY]
|
|
48
|
+
prev_path = Thread.current[THREAD_PATH_KEY]
|
|
49
|
+
# Only diff when we're in the same file (avoid cross-scope false positives)
|
|
50
|
+
if prev && prev_path == path
|
|
51
|
+
diff_and_emit(prev, current, tp)
|
|
52
|
+
end
|
|
53
|
+
Thread.current[THREAD_KEY] = current
|
|
54
|
+
Thread.current[THREAD_PATH_KEY] = path
|
|
55
|
+
ensure
|
|
56
|
+
Thread.current[IN_TRACKER_KEY] = false
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
def clear_state!
|
|
61
|
+
Thread.current[THREAD_KEY] = nil
|
|
62
|
+
Thread.current[THREAD_PATH_KEY] = nil
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
private
|
|
66
|
+
|
|
67
|
+
def extract_locals(binding)
|
|
68
|
+
return {} unless binding.respond_to?(:local_variables)
|
|
69
|
+
|
|
70
|
+
binding.local_variables.each_with_object({}) do |name, h|
|
|
71
|
+
next if SKIP_VARS.include?(name.to_s)
|
|
72
|
+
next if name.to_s.start_with?('_')
|
|
73
|
+
|
|
74
|
+
begin
|
|
75
|
+
h[name] = binding.local_variable_get(name)
|
|
76
|
+
rescue StandardError
|
|
77
|
+
# Some vars (e.g. from C extensions) may not be readable
|
|
78
|
+
end
|
|
79
|
+
end
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
def diff_and_emit(prev, current, tp)
|
|
83
|
+
changed = current.select do |name, new_val|
|
|
84
|
+
prev_val = prev[name]
|
|
85
|
+
!values_equal?(prev_val, new_val)
|
|
86
|
+
end
|
|
87
|
+
return if changed.empty?
|
|
88
|
+
|
|
89
|
+
changed.each do |name, new_val|
|
|
90
|
+
prev_val = prev[name]
|
|
91
|
+
emit_observe(name, prev_val, new_val, tp)
|
|
92
|
+
end
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
def values_equal?(a, b)
|
|
96
|
+
return true if a.equal?(b)
|
|
97
|
+
return a == b if a.nil? || b.nil?
|
|
98
|
+
|
|
99
|
+
a == b
|
|
100
|
+
rescue StandardError
|
|
101
|
+
false
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
def emit_observe(name, input, output, tp)
|
|
105
|
+
return unless (input.nil? || trackable_value?(input)) && (output.nil? || trackable_value?(output))
|
|
106
|
+
|
|
107
|
+
detail = "#{name} changed"
|
|
108
|
+
payload = {
|
|
109
|
+
input: format_value(input),
|
|
110
|
+
output: format_value(output)
|
|
111
|
+
}
|
|
112
|
+
Recorder.add_span(
|
|
113
|
+
category: :custom,
|
|
114
|
+
operation: 'observe',
|
|
115
|
+
detail: detail,
|
|
116
|
+
payload: payload,
|
|
117
|
+
duration_ms: 0,
|
|
118
|
+
started_at: Time.now
|
|
119
|
+
)
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
def format_value(val)
|
|
123
|
+
return nil if val.nil?
|
|
124
|
+
|
|
125
|
+
Dontbugme.send(:format_output_value, val)
|
|
126
|
+
rescue StandardError
|
|
127
|
+
val.class.name
|
|
128
|
+
end
|
|
129
|
+
|
|
130
|
+
def trackable_value?(val)
|
|
131
|
+
return true if val.nil?
|
|
132
|
+
return true if TRACKABLE_CLASSES.any? { |c| val.is_a?(c) }
|
|
133
|
+
return true if val.is_a?(Array) && val.size <= 10 && val.all? { |v| trackable_value?(v) }
|
|
134
|
+
return true if val.is_a?(Hash) && val.size <= 10 && val.values.all? { |v| trackable_value?(v) }
|
|
135
|
+
|
|
136
|
+
false
|
|
137
|
+
end
|
|
138
|
+
end
|
|
139
|
+
end
|
|
140
|
+
end
|
data/lib/dontbugme/version.rb
CHANGED
data/lib/dontbugme.rb
CHANGED
|
@@ -48,6 +48,16 @@ module Dontbugme
|
|
|
48
48
|
result
|
|
49
49
|
end
|
|
50
50
|
|
|
51
|
+
# Captures input and output of in-house calculations for value transformation inspection.
|
|
52
|
+
# Example: token = Dontbugme.observe('token increment', token) { token + 1 }
|
|
53
|
+
def observe(name, input = nil, &block)
|
|
54
|
+
return yield unless Context.active?
|
|
55
|
+
|
|
56
|
+
payload = {}
|
|
57
|
+
payload[:input] = format_output_value(input) unless input.nil?
|
|
58
|
+
span(name, payload: payload, capture_output: true, &block)
|
|
59
|
+
end
|
|
60
|
+
|
|
51
61
|
def snapshot(data)
|
|
52
62
|
return unless Context.active?
|
|
53
63
|
|
|
@@ -136,6 +146,7 @@ end
|
|
|
136
146
|
require 'dontbugme/version'
|
|
137
147
|
require 'dontbugme/configuration'
|
|
138
148
|
require 'dontbugme/span'
|
|
149
|
+
require 'dontbugme/variable_tracker'
|
|
139
150
|
require 'dontbugme/span_collection'
|
|
140
151
|
require 'dontbugme/trace'
|
|
141
152
|
require 'dontbugme/context'
|
|
@@ -8,6 +8,9 @@ Dontbugme.configure do |config|
|
|
|
8
8
|
# config.enable_web_ui = true
|
|
9
9
|
# config.web_ui_mount_path = "/inspector"
|
|
10
10
|
|
|
11
|
+
# Automatic variable tracking (dev only): captures input/output for local var changes
|
|
12
|
+
# config.capture_variable_changes = true
|
|
13
|
+
|
|
11
14
|
# Production: use PostgreSQL, async writes, selective recording
|
|
12
15
|
# config.store = :postgresql
|
|
13
16
|
# config.async_store = true
|
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: dontbugme
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.1.
|
|
4
|
+
version: 0.1.6
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Inspector Contributors
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2026-
|
|
11
|
+
date: 2026-03-01 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: thor
|
|
@@ -160,6 +160,7 @@ files:
|
|
|
160
160
|
- lib/dontbugme/subscribers/net_http.rb
|
|
161
161
|
- lib/dontbugme/subscribers/redis.rb
|
|
162
162
|
- lib/dontbugme/trace.rb
|
|
163
|
+
- lib/dontbugme/variable_tracker.rb
|
|
163
164
|
- lib/dontbugme/version.rb
|
|
164
165
|
- lib/generators/dontbugme/install/install_generator.rb
|
|
165
166
|
- lib/generators/dontbugme/install/templates/dontbugme.rb
|