debugbundle 2.0.1 → 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 +127 -0
- 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 +41 -67
- 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 +43 -17
- data/lib/debugbundle/version.rb +1 -1
- data/lib/debugbundle.rb +11 -1
- metadata +19 -2
|
@@ -10,6 +10,7 @@ module DebugBundle
|
|
|
10
10
|
:capture_request_events,
|
|
11
11
|
:capture_breadcrumbs,
|
|
12
12
|
:capture_probe_events,
|
|
13
|
+
:capture_diagnostic_context,
|
|
13
14
|
:immediate_client_error_statuses,
|
|
14
15
|
:immediate_client_error_path_rules,
|
|
15
16
|
keyword_init: true
|
|
@@ -72,6 +73,7 @@ module DebugBundle
|
|
|
72
73
|
capture_request_events: 'failures_only',
|
|
73
74
|
capture_breadcrumbs: 'local_only',
|
|
74
75
|
capture_probe_events: 'buffer_only',
|
|
76
|
+
capture_diagnostic_context: false,
|
|
75
77
|
immediate_client_error_statuses: [],
|
|
76
78
|
immediate_client_error_path_rules: []
|
|
77
79
|
)
|
|
@@ -84,6 +86,7 @@ module DebugBundle
|
|
|
84
86
|
capture_request_events: 'failures_only',
|
|
85
87
|
capture_breadcrumbs: 'exception_only',
|
|
86
88
|
capture_probe_events: 'buffer_only',
|
|
89
|
+
capture_diagnostic_context: true,
|
|
87
90
|
immediate_client_error_statuses: [],
|
|
88
91
|
immediate_client_error_path_rules: []
|
|
89
92
|
)
|
|
@@ -125,6 +128,8 @@ module DebugBundle
|
|
|
125
128
|
payload['capture_request_events'] || payload[:capture_request_events] || 'failures_only',
|
|
126
129
|
capture_breadcrumbs: payload['capture_breadcrumbs'] || payload[:capture_breadcrumbs] || 'exception_only',
|
|
127
130
|
capture_probe_events: payload['capture_probe_events'] || payload[:capture_probe_events] || 'buffer_only',
|
|
131
|
+
capture_diagnostic_context: (payload['preset'] || payload[:preset]) != 'minimal' &&
|
|
132
|
+
payload['capture_diagnostic_context'] != false && payload[:capture_diagnostic_context] != false,
|
|
128
133
|
immediate_client_error_statuses: Array(
|
|
129
134
|
payload['immediate_client_error_statuses'] || payload[:immediate_client_error_statuses]
|
|
130
135
|
).grep(Integer),
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
+
require 'debugbundle/framework_operation'
|
|
4
|
+
|
|
3
5
|
module DebugBundle
|
|
4
6
|
module Sidekiq
|
|
5
7
|
class ServerMiddleware
|
|
@@ -9,25 +11,27 @@ module DebugBundle
|
|
|
9
11
|
end
|
|
10
12
|
|
|
11
13
|
def call(_worker, job, queue)
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
14
|
+
FrameworkOperation.run(@client, kind: 'job', correlation: { trace_id: job['trace_id'] }) do
|
|
15
|
+
yield
|
|
16
|
+
rescue StandardError => e
|
|
17
|
+
@client.capture_exception(
|
|
18
|
+
e,
|
|
19
|
+
context: {
|
|
20
|
+
queue: queue,
|
|
21
|
+
job: {
|
|
22
|
+
class: job['class'],
|
|
23
|
+
queue: job['queue'] || queue,
|
|
24
|
+
jid: job['jid'],
|
|
25
|
+
retry_count: job['retry_count'] || job['retry'],
|
|
26
|
+
args_summary: Array(job['args']).first(5).map { |value| value.class.name }
|
|
27
|
+
},
|
|
28
|
+
job_id: job['jid'],
|
|
29
|
+
trace_id: job['trace_id']
|
|
24
30
|
},
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
)
|
|
30
|
-
raise
|
|
31
|
+
handled: false
|
|
32
|
+
)
|
|
33
|
+
raise
|
|
34
|
+
end
|
|
31
35
|
end
|
|
32
36
|
end
|
|
33
37
|
end
|
|
@@ -29,7 +29,11 @@ module DebugBundle
|
|
|
29
29
|
end
|
|
30
30
|
|
|
31
31
|
work = { nodes: 0, bytes: 0, seen: {}.compare_by_identity,
|
|
32
|
-
fields:
|
|
32
|
+
fields: if additional_fields.empty?
|
|
33
|
+
CANONICAL_FIELDS
|
|
34
|
+
else
|
|
35
|
+
CANONICAL_FIELDS + additional_fields.map { |field| canonical(field) }
|
|
36
|
+
end,
|
|
33
37
|
extra: additional_fields,
|
|
34
38
|
assignment_pattern: assignment_pattern(additional_fields) }
|
|
35
39
|
result = visit(value, work, 0, true)
|
|
@@ -54,6 +58,10 @@ module DebugBundle
|
|
|
54
58
|
value.downcase.gsub(/[^a-z0-9]/, '')
|
|
55
59
|
end
|
|
56
60
|
|
|
61
|
+
# Mandatory terms are immutable policy. Reuse their normalised form while
|
|
62
|
+
# each projection still owns its counters, seen set and configured keys.
|
|
63
|
+
CANONICAL_FIELDS = FIELDS.map { |field| canonical(field).freeze }.freeze
|
|
64
|
+
|
|
57
65
|
def assignment_pattern(additional_fields)
|
|
58
66
|
return DEFAULT_ASSIGNMENT_PATTERN if additional_fields.empty?
|
|
59
67
|
|
|
@@ -6,12 +6,13 @@ require 'securerandom'
|
|
|
6
6
|
require 'uri'
|
|
7
7
|
require 'time'
|
|
8
8
|
require 'fileutils'
|
|
9
|
+
require 'debugbundle/bounded_http_response'
|
|
9
10
|
|
|
10
11
|
module DebugBundle
|
|
11
12
|
module Transport
|
|
12
13
|
RETRY_AFTER_CAP_SECONDS = 300
|
|
13
14
|
|
|
14
|
-
Result = Struct.new(:status_code, :retry_after_seconds, :body, keyword_init: true)
|
|
15
|
+
Result = Struct.new(:status_code, :retry_after_seconds, :body, :written_file_path, keyword_init: true)
|
|
15
16
|
|
|
16
17
|
def self.sdk_config_endpoint(events_endpoint)
|
|
17
18
|
uri = URI.parse(events_endpoint)
|
|
@@ -48,24 +49,35 @@ module DebugBundle
|
|
|
48
49
|
end
|
|
49
50
|
|
|
50
51
|
def call(request)
|
|
52
|
+
deadline = request.fetch(:deadline) { Process.clock_gettime(Process::CLOCK_MONOTONIC) + 5 }
|
|
53
|
+
remaining = deadline - Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
|
54
|
+
return Result.new(status_code: 500) unless remaining.positive?
|
|
55
|
+
|
|
56
|
+
body = JSON.generate(events: request.fetch(:events))
|
|
57
|
+
return Result.new(status_code: 413) if body.bytesize > 262_144
|
|
58
|
+
|
|
51
59
|
http = Net::HTTP.new(@uri.host, @uri.port)
|
|
52
60
|
http.use_ssl = @uri.scheme == 'https'
|
|
53
|
-
http.open_timeout =
|
|
54
|
-
http.read_timeout =
|
|
55
|
-
|
|
56
|
-
response =
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
61
|
+
http.open_timeout = remaining
|
|
62
|
+
http.read_timeout = remaining
|
|
63
|
+
|
|
64
|
+
response, raw = BoundedHttpResponse.read(deadline: deadline) do |consume|
|
|
65
|
+
http.post(
|
|
66
|
+
@uri.request_uri,
|
|
67
|
+
body,
|
|
68
|
+
{
|
|
69
|
+
'Authorization' => "Bearer #{request.fetch(:project_token)}",
|
|
70
|
+
'Content-Type' => 'application/json',
|
|
71
|
+
'Accept-Encoding' => 'identity',
|
|
72
|
+
'X-DebugBundle-Evidence' => '1'
|
|
73
|
+
}, &consume
|
|
74
|
+
)
|
|
75
|
+
end
|
|
64
76
|
|
|
65
77
|
Result.new(
|
|
66
78
|
status_code: response.code.to_i,
|
|
67
79
|
retry_after_seconds: parse_retry_after(response['Retry-After']),
|
|
68
|
-
body: parse_body(
|
|
80
|
+
body: parse_body(raw)
|
|
69
81
|
)
|
|
70
82
|
rescue StandardError
|
|
71
83
|
Result.new(status_code: 500)
|
|
@@ -108,15 +120,18 @@ module DebugBundle
|
|
|
108
120
|
http.open_timeout = 5
|
|
109
121
|
http.read_timeout = 5
|
|
110
122
|
|
|
111
|
-
request = Net::HTTP::Get.new(@uri.request_uri)
|
|
123
|
+
request = Net::HTTP::Get.new(@uri.request_uri, 'Accept-Encoding' => 'identity')
|
|
112
124
|
request['Authorization'] = "Bearer #{@project_token}"
|
|
113
125
|
request['Accept'] = 'application/json'
|
|
114
126
|
request['X-DebugBundle-SDK'] = @sdk_name
|
|
115
127
|
request['X-DebugBundle-SDK-Version'] = @sdk_version
|
|
116
128
|
request['If-None-Match'] = etag if etag
|
|
129
|
+
request['X-DebugBundle-Evidence'] = '1'
|
|
117
130
|
|
|
118
|
-
response =
|
|
119
|
-
|
|
131
|
+
response, raw = BoundedHttpResponse.read do |consume|
|
|
132
|
+
http.request(request) { |reply| reply.read_body(&consume) }
|
|
133
|
+
end
|
|
134
|
+
body = raw.empty? ? {} : JSON.parse(raw)
|
|
120
135
|
{ status_code: response.code.to_i, etag: response['ETag'], body: body }
|
|
121
136
|
rescue StandardError
|
|
122
137
|
{ status_code: 500, body: {} }
|
|
@@ -150,13 +165,24 @@ module DebugBundle
|
|
|
150
165
|
File.rename(temp_path, final_path)
|
|
151
166
|
File.chmod(FILE_MODE, final_path)
|
|
152
167
|
|
|
153
|
-
Result.new(status_code: 202)
|
|
168
|
+
Result.new(status_code: 202, written_file_path: final_path)
|
|
154
169
|
rescue StandardError
|
|
155
170
|
Result.new(status_code: 500)
|
|
156
171
|
ensure
|
|
157
172
|
File.delete(temp_path) if defined?(temp_path) && temp_path && File.exist?(temp_path)
|
|
158
173
|
end
|
|
159
174
|
|
|
175
|
+
def mark_delivered(path)
|
|
176
|
+
ensure_secure_directory!(@directory)
|
|
177
|
+
return false unless File.dirname(File.expand_path(path)) == @directory && path.end_with?('.events.json')
|
|
178
|
+
return false unless File.file?(path) && !File.symlink?(path)
|
|
179
|
+
|
|
180
|
+
write_secure_file("#{path}.delivered", '')
|
|
181
|
+
true
|
|
182
|
+
rescue StandardError
|
|
183
|
+
false
|
|
184
|
+
end
|
|
185
|
+
|
|
160
186
|
private
|
|
161
187
|
|
|
162
188
|
def ensure_secure_directory!(directory)
|
data/lib/debugbundle/version.rb
CHANGED
data/lib/debugbundle.rb
CHANGED
|
@@ -22,7 +22,9 @@ require_relative 'debugbundle/rails' if defined?(Rails::Railtie)
|
|
|
22
22
|
module DebugBundle
|
|
23
23
|
class << self
|
|
24
24
|
def init(**options)
|
|
25
|
-
|
|
25
|
+
replacement = Client.new(**options)
|
|
26
|
+
@client.dispose if @client.is_a?(Client)
|
|
27
|
+
self.client = replacement
|
|
26
28
|
client.capture_exceptions
|
|
27
29
|
client
|
|
28
30
|
end
|
|
@@ -41,6 +43,14 @@ module DebugBundle
|
|
|
41
43
|
client.capture_error(error, context: context)
|
|
42
44
|
end
|
|
43
45
|
|
|
46
|
+
def capture_recovery(observation = nil, context: nil, **options)
|
|
47
|
+
client.capture_recovery(observation, context: context, **options)
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def with_operation_context(...)
|
|
51
|
+
client.with_operation_context(...)
|
|
52
|
+
end
|
|
53
|
+
|
|
44
54
|
def capture_log(message, level: :warning, context: nil)
|
|
45
55
|
client.capture_log(message, level: level, context: context)
|
|
46
56
|
end
|
metadata
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: debugbundle
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 2.0
|
|
4
|
+
version: 2.1.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- DebugBundle
|
|
8
8
|
bindir: exe
|
|
9
9
|
cert_chain: []
|
|
10
|
-
date: 2026-
|
|
10
|
+
date: 2026-11-01 00:00:00.000000000 Z
|
|
11
11
|
dependencies:
|
|
12
12
|
- !ruby/object:Gem::Dependency
|
|
13
13
|
name: base64
|
|
@@ -49,14 +49,30 @@ files:
|
|
|
49
49
|
- lib/debugbundle.rb
|
|
50
50
|
- lib/debugbundle/acknowledgement.rb
|
|
51
51
|
- lib/debugbundle/before_send.rb
|
|
52
|
+
- lib/debugbundle/bounded_http_response.rb
|
|
53
|
+
- lib/debugbundle/canonical_event_schema.rb
|
|
52
54
|
- lib/debugbundle/client.rb
|
|
53
55
|
- lib/debugbundle/client_event_support.rb
|
|
56
|
+
- lib/debugbundle/client_evidence_support.rb
|
|
57
|
+
- lib/debugbundle/client_exception_support.rb
|
|
54
58
|
- lib/debugbundle/client_process_support.rb
|
|
55
59
|
- lib/debugbundle/client_queue_support.rb
|
|
56
60
|
- lib/debugbundle/config.rb
|
|
57
61
|
- lib/debugbundle/config_worker.rb
|
|
58
62
|
- lib/debugbundle/delivery_worker.rb
|
|
63
|
+
- lib/debugbundle/diagnostic_budget.rb
|
|
64
|
+
- lib/debugbundle/event-envelope.schema.json
|
|
65
|
+
- lib/debugbundle/event_batch.rb
|
|
66
|
+
- lib/debugbundle/evidence_delivery.rb
|
|
67
|
+
- lib/debugbundle/evidence_graph.rb
|
|
68
|
+
- lib/debugbundle/evidence_json.rb
|
|
69
|
+
- lib/debugbundle/evidence_resources.rb
|
|
70
|
+
- lib/debugbundle/evidence_value.rb
|
|
71
|
+
- lib/debugbundle/evidence_wire.rb
|
|
72
|
+
- lib/debugbundle/exception_evidence.rb
|
|
73
|
+
- lib/debugbundle/framework_operation.rb
|
|
59
74
|
- lib/debugbundle/logging.rb
|
|
75
|
+
- lib/debugbundle/operation_context.rb
|
|
60
76
|
- lib/debugbundle/rack/middleware.rb
|
|
61
77
|
- lib/debugbundle/rack/relay_middleware.rb
|
|
62
78
|
- lib/debugbundle/rails.rb
|
|
@@ -64,6 +80,7 @@ files:
|
|
|
64
80
|
- lib/debugbundle/rails/relay_endpoint.rb
|
|
65
81
|
- lib/debugbundle/redaction.rb
|
|
66
82
|
- lib/debugbundle/relay.rb
|
|
83
|
+
- lib/debugbundle/relay/forwarder.rb
|
|
67
84
|
- lib/debugbundle/relay/handler.rb
|
|
68
85
|
- lib/debugbundle/remote_config.rb
|
|
69
86
|
- lib/debugbundle/runtime.rb
|