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
|
@@ -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
|
|
|
@@ -4,13 +4,15 @@ require 'json'
|
|
|
4
4
|
require 'net/http'
|
|
5
5
|
require 'securerandom'
|
|
6
6
|
require 'uri'
|
|
7
|
+
require 'time'
|
|
7
8
|
require 'fileutils'
|
|
9
|
+
require 'debugbundle/bounded_http_response'
|
|
8
10
|
|
|
9
11
|
module DebugBundle
|
|
10
12
|
module Transport
|
|
11
13
|
RETRY_AFTER_CAP_SECONDS = 300
|
|
12
14
|
|
|
13
|
-
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)
|
|
14
16
|
|
|
15
17
|
def self.sdk_config_endpoint(events_endpoint)
|
|
16
18
|
uri = URI.parse(events_endpoint)
|
|
@@ -47,24 +49,35 @@ module DebugBundle
|
|
|
47
49
|
end
|
|
48
50
|
|
|
49
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
|
+
|
|
50
59
|
http = Net::HTTP.new(@uri.host, @uri.port)
|
|
51
60
|
http.use_ssl = @uri.scheme == 'https'
|
|
52
|
-
http.open_timeout =
|
|
53
|
-
http.read_timeout =
|
|
54
|
-
|
|
55
|
-
response =
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
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
|
|
63
76
|
|
|
64
77
|
Result.new(
|
|
65
78
|
status_code: response.code.to_i,
|
|
66
79
|
retry_after_seconds: parse_retry_after(response['Retry-After']),
|
|
67
|
-
body: parse_body(
|
|
80
|
+
body: parse_body(raw)
|
|
68
81
|
)
|
|
69
82
|
rescue StandardError
|
|
70
83
|
Result.new(status_code: 500)
|
|
@@ -83,8 +96,11 @@ module DebugBundle
|
|
|
83
96
|
def parse_retry_after(value)
|
|
84
97
|
return nil if value.nil? || value.strip.empty?
|
|
85
98
|
|
|
86
|
-
seconds =
|
|
87
|
-
seconds.
|
|
99
|
+
seconds = Float(value, exception: false)
|
|
100
|
+
seconds = Time.httpdate(value) - Time.now if seconds.nil?
|
|
101
|
+
return nil unless seconds.finite?
|
|
102
|
+
|
|
103
|
+
seconds.clamp(0, RETRY_AFTER_CAP_SECONDS).to_i
|
|
88
104
|
rescue ArgumentError, TypeError
|
|
89
105
|
nil
|
|
90
106
|
end
|
|
@@ -104,15 +120,18 @@ module DebugBundle
|
|
|
104
120
|
http.open_timeout = 5
|
|
105
121
|
http.read_timeout = 5
|
|
106
122
|
|
|
107
|
-
request = Net::HTTP::Get.new(@uri.request_uri)
|
|
123
|
+
request = Net::HTTP::Get.new(@uri.request_uri, 'Accept-Encoding' => 'identity')
|
|
108
124
|
request['Authorization'] = "Bearer #{@project_token}"
|
|
109
125
|
request['Accept'] = 'application/json'
|
|
110
126
|
request['X-DebugBundle-SDK'] = @sdk_name
|
|
111
127
|
request['X-DebugBundle-SDK-Version'] = @sdk_version
|
|
112
128
|
request['If-None-Match'] = etag if etag
|
|
129
|
+
request['X-DebugBundle-Evidence'] = '1'
|
|
113
130
|
|
|
114
|
-
response =
|
|
115
|
-
|
|
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)
|
|
116
135
|
{ status_code: response.code.to_i, etag: response['ETag'], body: body }
|
|
117
136
|
rescue StandardError
|
|
118
137
|
{ status_code: 500, body: {} }
|
|
@@ -146,13 +165,24 @@ module DebugBundle
|
|
|
146
165
|
File.rename(temp_path, final_path)
|
|
147
166
|
File.chmod(FILE_MODE, final_path)
|
|
148
167
|
|
|
149
|
-
Result.new(status_code: 202)
|
|
168
|
+
Result.new(status_code: 202, written_file_path: final_path)
|
|
150
169
|
rescue StandardError
|
|
151
170
|
Result.new(status_code: 500)
|
|
152
171
|
ensure
|
|
153
172
|
File.delete(temp_path) if defined?(temp_path) && temp_path && File.exist?(temp_path)
|
|
154
173
|
end
|
|
155
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
|
+
|
|
156
186
|
private
|
|
157
187
|
|
|
158
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.
|
|
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
|