debugbundle 2.0.0 → 2.1.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 +133 -0
- data/lib/debugbundle/acknowledgement.rb +6 -2
- data/lib/debugbundle/before_send.rb +58 -30
- data/lib/debugbundle/bounded_http_response.rb +24 -0
- data/lib/debugbundle/canonical_event_schema.rb +137 -0
- data/lib/debugbundle/client.rb +55 -75
- data/lib/debugbundle/client_event_support.rb +11 -12
- data/lib/debugbundle/client_evidence_support.rb +169 -0
- data/lib/debugbundle/client_exception_support.rb +54 -0
- data/lib/debugbundle/client_process_support.rb +5 -12
- data/lib/debugbundle/client_queue_support.rb +59 -4
- data/lib/debugbundle/config.rb +15 -2
- data/lib/debugbundle/diagnostic_budget.rb +155 -0
- data/lib/debugbundle/event-envelope.schema.json +4904 -0
- data/lib/debugbundle/event_batch.rb +48 -0
- data/lib/debugbundle/evidence_delivery.rb +50 -0
- data/lib/debugbundle/evidence_graph.rb +73 -0
- data/lib/debugbundle/evidence_json.rb +55 -0
- data/lib/debugbundle/evidence_resources.rb +60 -0
- data/lib/debugbundle/evidence_value.rb +107 -0
- data/lib/debugbundle/evidence_wire.rb +193 -0
- data/lib/debugbundle/exception_evidence.rb +179 -0
- data/lib/debugbundle/framework_operation.rb +13 -0
- data/lib/debugbundle/operation_context.rb +90 -0
- data/lib/debugbundle/rack/middleware.rb +24 -21
- data/lib/debugbundle/rack/relay_middleware.rb +4 -1
- data/lib/debugbundle/rails/railtie.rb +4 -1
- data/lib/debugbundle/rails/relay_endpoint.rb +2 -1
- data/lib/debugbundle/relay/forwarder.rb +83 -0
- data/lib/debugbundle/relay/handler.rb +83 -19
- data/lib/debugbundle/remote_config.rb +5 -0
- data/lib/debugbundle/sidekiq/server_middleware.rb +22 -18
- data/lib/debugbundle/telemetry_privacy.rb +9 -1
- data/lib/debugbundle/transport.rb +49 -19
- data/lib/debugbundle/version.rb +1 -1
- data/lib/debugbundle.rb +11 -1
- metadata +19 -2
data/lib/debugbundle/config.rb
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
+
require 'debugbundle/evidence_wire'
|
|
4
|
+
|
|
3
5
|
module DebugBundle
|
|
4
6
|
class Config
|
|
5
7
|
DEFAULT_ENDPOINT = 'https://api.debugbundle.com/v1/events'
|
|
@@ -40,7 +42,10 @@ module DebugBundle
|
|
|
40
42
|
:max_probe_entries_per_label,
|
|
41
43
|
:probe_flush_on_error,
|
|
42
44
|
:probes_poll_interval,
|
|
43
|
-
:before_send
|
|
45
|
+
:before_send,
|
|
46
|
+
:capture_exception_evidence,
|
|
47
|
+
:capture_recovery_observations,
|
|
48
|
+
:evidence_capabilities
|
|
44
49
|
|
|
45
50
|
def initialize(
|
|
46
51
|
project_token: nil,
|
|
@@ -63,7 +68,10 @@ module DebugBundle
|
|
|
63
68
|
max_probe_entries_per_label: DEFAULT_MAX_PROBE_ENTRIES_PER_LABEL,
|
|
64
69
|
probe_flush_on_error: DEFAULT_PROBE_FLUSH_ON_ERROR,
|
|
65
70
|
probes_poll_interval: DEFAULT_PROBES_POLL_INTERVAL,
|
|
66
|
-
before_send: nil
|
|
71
|
+
before_send: nil,
|
|
72
|
+
capture_exception_evidence: true,
|
|
73
|
+
capture_recovery_observations: false,
|
|
74
|
+
evidence_capabilities: nil
|
|
67
75
|
)
|
|
68
76
|
@project_token = project_token
|
|
69
77
|
@enabled = enabled
|
|
@@ -92,6 +100,11 @@ module DebugBundle
|
|
|
92
100
|
@probe_flush_on_error = probe_flush_on_error
|
|
93
101
|
@probes_poll_interval = normalize_positive_number(probes_poll_interval, DEFAULT_PROBES_POLL_INTERVAL)
|
|
94
102
|
@before_send = before_send.respond_to?(:call) ? before_send : nil
|
|
103
|
+
@capture_exception_evidence = !false.equal?(capture_exception_evidence)
|
|
104
|
+
@capture_recovery_observations = true.equal?(capture_recovery_observations)
|
|
105
|
+
@evidence_capabilities = EvidenceWire.descriptor(EvidenceValue.options(evidence_capabilities))
|
|
106
|
+
@evidence_capabilities['features'].freeze
|
|
107
|
+
@evidence_capabilities.freeze
|
|
95
108
|
freeze
|
|
96
109
|
end
|
|
97
110
|
|
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'json'
|
|
4
|
+
|
|
5
|
+
module DebugBundle
|
|
6
|
+
# Caller holds the destination's buffer mutex. Metadata and pending serialized
|
|
7
|
+
# ownership share a 64 KiB budget; ACK/retry cannot renew an expired window.
|
|
8
|
+
class DiagnosticBudget
|
|
9
|
+
def initialize
|
|
10
|
+
@windows = {}
|
|
11
|
+
@pending = {}
|
|
12
|
+
@held = 0
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def accounted_bytes = @held + @windows.values.sum { |window| window[:cost] }
|
|
16
|
+
|
|
17
|
+
def key(payload)
|
|
18
|
+
scope = payload.fetch('scope')
|
|
19
|
+
JSON.generate([payload.fetch('failure_ref'), scope.fetch('kind'), scope['operation_id'] || scope['page_id']])
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def metadata_cost(scope, window) = JSON.generate([scope, window.except(:cost)]).bytesize
|
|
23
|
+
|
|
24
|
+
def prune(now)
|
|
25
|
+
@windows.delete_if { |_key, window| window[:pending].zero? && now >= window[:starts] + 300 }
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def window(now)
|
|
29
|
+
{ starts: now, failure_at: nil, failure_id: nil, attempts: [], observations: 0,
|
|
30
|
+
resource: false, ambiguous: false, pending: 0, cost: 0 }
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def register_failure(payload, event_id, now)
|
|
34
|
+
return false unless now.is_a?(Numeric) && now.finite?
|
|
35
|
+
|
|
36
|
+
prune(now)
|
|
37
|
+
scope = key(payload)
|
|
38
|
+
previous = @windows[scope]
|
|
39
|
+
if previous && previous[:failure_id] && previous[:failure_id] != event_id
|
|
40
|
+
previous[:ambiguous] = true
|
|
41
|
+
previous[:cost] = metadata_cost(scope, previous)
|
|
42
|
+
return false
|
|
43
|
+
end
|
|
44
|
+
return false if previous.nil? && @windows.length >= 50
|
|
45
|
+
|
|
46
|
+
value = previous ? previous.dup : window(now)
|
|
47
|
+
value[:failure_id] = event_id
|
|
48
|
+
value[:failure_at] ||= now
|
|
49
|
+
store(scope, previous, value)
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def store(scope, previous, value, extra = 0)
|
|
53
|
+
value[:cost] = metadata_cost(scope, value)
|
|
54
|
+
return false if accounted_bytes - (previous ? previous[:cost] : 0) + value[:cost] + extra > 65_536
|
|
55
|
+
|
|
56
|
+
@windows[scope] = value
|
|
57
|
+
!value[:ambiguous]
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
def reserve(event_id, payload, bytes, now)
|
|
61
|
+
return false unless valid_size?(bytes) && now.is_a?(Numeric) && now.finite? && !@pending.key?(event_id)
|
|
62
|
+
|
|
63
|
+
prune(now)
|
|
64
|
+
scope = key(payload)
|
|
65
|
+
previous = @windows[scope]
|
|
66
|
+
return false if (previous.nil? && @windows.length >= 50) || (previous && previous[:ambiguous])
|
|
67
|
+
|
|
68
|
+
value = previous ? previous.merge(attempts: previous[:attempts].dup) : window(now)
|
|
69
|
+
return false if now < value[:starts] || now > (value[:failure_at] || value[:starts]) + 300
|
|
70
|
+
return false if value[:observations] >= 20 || !admit_kind?(value, payload, now)
|
|
71
|
+
|
|
72
|
+
value[:pending] += 1
|
|
73
|
+
value[:observations] += 1
|
|
74
|
+
cost = bytes + event_id.bytesize + scope.bytesize + 64
|
|
75
|
+
return false unless store(scope, previous, value, cost)
|
|
76
|
+
|
|
77
|
+
@pending[event_id] =
|
|
78
|
+
{ scope: scope, kind: payload['kind'], attempt: payload['attempt_id'], size: bytes, cost: cost }
|
|
79
|
+
@held += cost
|
|
80
|
+
true
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
def admit_kind?(value, payload, now)
|
|
84
|
+
if payload['kind'] == 'recovery'
|
|
85
|
+
attempt = payload['attempt_id']
|
|
86
|
+
return false if !value[:attempts].include?(attempt) && value[:attempts].length >= 5
|
|
87
|
+
|
|
88
|
+
value[:attempts] |= [attempt]
|
|
89
|
+
else
|
|
90
|
+
return false if value[:resource] || value[:failure_id] != payload['source_event_id']
|
|
91
|
+
return false unless value[:failure_at] && now <= value[:failure_at] + 30
|
|
92
|
+
|
|
93
|
+
value[:resource] = true
|
|
94
|
+
end
|
|
95
|
+
true
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
def replace(original_id, event_id, payload, bytes, now)
|
|
99
|
+
entry = @pending[original_id]
|
|
100
|
+
same_scope = entry && entry[:scope] == key(payload)
|
|
101
|
+
unless same_scope && entry[:kind] == payload['kind'] && entry[:attempt] == payload['attempt_id']
|
|
102
|
+
release([original_id])
|
|
103
|
+
return reserve(event_id, payload, bytes, now)
|
|
104
|
+
end
|
|
105
|
+
if event_id != original_id
|
|
106
|
+
if @pending.key?(event_id)
|
|
107
|
+
release([original_id])
|
|
108
|
+
return false
|
|
109
|
+
end
|
|
110
|
+
@pending[event_id] = @pending.delete(original_id)
|
|
111
|
+
delta = event_id.bytesize - original_id.bytesize
|
|
112
|
+
entry[:cost] += delta
|
|
113
|
+
@held += delta
|
|
114
|
+
end
|
|
115
|
+
if !adjust(event_id, bytes) || !eligible(event_id, payload)
|
|
116
|
+
release([event_id])
|
|
117
|
+
return false
|
|
118
|
+
end
|
|
119
|
+
true
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
def adjust(event_id, bytes)
|
|
123
|
+
entry = @pending[event_id]
|
|
124
|
+
return false unless entry && valid_size?(bytes) && accounted_bytes + bytes - entry[:size] <= 65_536
|
|
125
|
+
|
|
126
|
+
@held += bytes - entry[:size]
|
|
127
|
+
entry[:cost] += bytes - entry[:size]
|
|
128
|
+
entry[:size] = bytes
|
|
129
|
+
true
|
|
130
|
+
end
|
|
131
|
+
|
|
132
|
+
def eligible(event_id, payload)
|
|
133
|
+
entry = @pending[event_id]
|
|
134
|
+
value = @windows[key(payload)]
|
|
135
|
+
entry && entry[:scope] == key(payload) && value && !value[:ambiguous] &&
|
|
136
|
+
(payload['kind'] == 'recovery' || value[:failure_id] == payload['source_event_id'])
|
|
137
|
+
end
|
|
138
|
+
|
|
139
|
+
def release(event_ids)
|
|
140
|
+
event_ids.compact.uniq.each do |id|
|
|
141
|
+
entry = @pending.delete(id)
|
|
142
|
+
next unless entry
|
|
143
|
+
|
|
144
|
+
@held -= entry[:cost]
|
|
145
|
+
value = @windows[entry[:scope]]
|
|
146
|
+
next unless value
|
|
147
|
+
|
|
148
|
+
value[:pending] = [value[:pending] - 1, 0].max
|
|
149
|
+
value[:cost] = metadata_cost(entry[:scope], value)
|
|
150
|
+
end
|
|
151
|
+
end
|
|
152
|
+
|
|
153
|
+
def valid_size?(size) = size.is_a?(Integer) && size.between?(0, 65_536)
|
|
154
|
+
end
|
|
155
|
+
end
|