debugbundle 1.1.0 → 1.1.3

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 55cf00eddb0f7fc23cdc0b574d5c8c95f1b06575dfcef315e7c8f5c31278942b
4
- data.tar.gz: 43e388045674fc42d1cc44f11365cb392aae7f3a09f64f3784c776cd96c79b6f
3
+ metadata.gz: a7c3b7839eda588014c28fc36b2c0337a754c42b2c75db59b3a18569d0c18dad
4
+ data.tar.gz: 39dabc4e2221bebede01e96faf24f6baa517cdeddb6495f28c0c25d2c5cd41df
5
5
  SHA512:
6
- metadata.gz: e0beea973c239fdbe571ffbe9e62f48e206112414b9372466e6c99252bd4ed85dc6c349bed7986875d04b77d8e8a6525e35d9fcd3291b8066ffa60fad4d0d73a
7
- data.tar.gz: 374744696cbd5d1cf8b05791ac67e9e275e33c88fa4d1d3e363056db7b44f3e225fff2c60d223ff4508440e69ec32ab045eb7d9271daea7601d7d758ec6c0569
6
+ metadata.gz: 117cc67dd6b26bd728563e901abe113efbb83b3a661c572f0d29e02bdc1828d326e89f604701fe8320af2b31ba9b1c0c747d87c61dc6f6f3f9cb7bf5319cac52
7
+ data.tar.gz: ea39d1fd9cf648ba1341f9aeb72900f899bb8ab3f384f6f02c6e50ccdd51fd666cf213492933ecb53911d1c224da6a5432a6e9a0e42f36dd07c712fd870d0a0b
data/README.md CHANGED
@@ -314,7 +314,7 @@ This repository also ships a clean-install app-driven smoke harness that validat
314
314
 
315
315
  ```sh
316
316
  make smoke
317
- make smoke-published VERSION=1.1.0
317
+ make smoke-published VERSION=1.1.2
318
318
  ```
319
319
 
320
320
  `make smoke` builds the gem, installs it into a fresh RubyGems home, drives a Rack request plus a browser relay batch through the public SDK surface, validates event envelope shape, and confirms the mock ingestion endpoint receives the expected service, environment, SDK metadata, and correlation fields.
@@ -41,9 +41,7 @@ module DebugBundle
41
41
  class << self
42
42
  attr_accessor :thread_exception_client
43
43
 
44
- def dispatch_thread_exception(error)
45
- thread_exception_client&.__send__(:capture_thread_exception, error)
46
- end
44
+ def dispatch_thread_exception(error) = thread_exception_client&.__send__(:capture_thread_exception, error)
47
45
 
48
46
  def install_thread_exception_hook!
49
47
  THREAD_HOOK_MUTEX.synchronize do
@@ -128,18 +126,17 @@ module DebugBundle
128
126
  }
129
127
 
130
128
  causes = exception_causes(error)
131
- payload['causes'] = causes unless causes.empty?
132
129
 
133
130
  probe_data = probe_snapshot
134
131
  payload['probe_data'] = probe_data unless probe_data.empty?
135
132
 
136
133
  extra_context = merged_context.except('request', 'response', 'correlation')
137
- payload['context'] = extra_context unless extra_context.empty?
134
+ extra_context['causes'] = causes unless causes.empty?
138
135
 
139
136
  suppression_key = [payload['name'], payload['message'], payload['stack']].join(':')
140
137
  return unless @suppression.should_capture(suppression_key, now: monotonic_now)
141
138
 
142
- enqueue_event(base_event('backend_exception', payload, merged_context))
139
+ enqueue_event(base_event('backend_exception', payload, extra_context))
143
140
  end
144
141
 
145
142
  def capture_error(error, context: nil, handled: true) = capture_exception(error, context: context, handled: handled)
@@ -181,8 +178,6 @@ module DebugBundle
181
178
  'response_status' => response_status,
182
179
  'duration_ms' => extract_duration_ms(merged_context, sanitized_response),
183
180
  'route_template' => merged_context['route_template'],
184
- 'controller' => merged_context['controller'],
185
- 'action' => merged_context['action'],
186
181
  'response_headers' => sanitized_response['headers'],
187
182
  'response_body' => sanitized_response['body']
188
183
  }
@@ -414,8 +409,8 @@ module DebugBundle
414
409
  def request_payload(request)
415
410
  source = object_to_hash(request)
416
411
  {
417
- 'method' => source['method'],
418
- 'path' => source['path'],
412
+ 'method' => source['method'] || 'UNKNOWN',
413
+ 'path' => source['path'] || '/',
419
414
  'query' => @redactor.redact_value(source['query'] || {}),
420
415
  'headers' => sanitized_headers(source['headers'] || {}),
421
416
  'body' => @redactor.redact_value(source['body'] || {})
@@ -494,7 +489,7 @@ module DebugBundle
494
489
  end
495
490
 
496
491
  def base_event(event_type, payload, context)
497
- {
492
+ event = {
498
493
  'schema_version' => SCHEMA_VERSION,
499
494
  'event_id' => SecureRandom.uuid,
500
495
  'event_type' => event_type,
@@ -511,6 +506,9 @@ module DebugBundle
511
506
  'correlation' => correlation_payload(context),
512
507
  'payload' => @redactor.redact_value(payload)
513
508
  }
509
+ envelope_context = event_context(context)
510
+ event['context'] = envelope_context unless envelope_context.empty?
511
+ event
514
512
  end
515
513
 
516
514
  def service_name = config.service || DEFAULT_SERVICE_NAME
@@ -528,6 +526,18 @@ module DebugBundle
528
526
  }
529
527
  end
530
528
 
529
+ def event_context(context)
530
+ object_to_hash(context).except(
531
+ 'request',
532
+ 'response',
533
+ 'correlation',
534
+ 'request_id',
535
+ 'trace_id',
536
+ 'session_id',
537
+ 'user_id_hash'
538
+ )
539
+ end
540
+
531
541
  def object_to_hash(value)
532
542
  case value
533
543
  when Hash
@@ -11,6 +11,7 @@ module DebugBundle
11
11
  frontend_breadcrumb
12
12
  request_event
13
13
  probe_event
14
+ analytics_event
14
15
  ].freeze
15
16
  BROWSER_SDK_NAME = '@debugbundle/sdk-browser'
16
17
  DEFAULT_MAX_BODY_BYTES = 262_144
@@ -192,20 +193,22 @@ module DebugBundle
192
193
  'name' => @service || service['name'].to_s,
193
194
  'environment' => @environment || service['environment'].to_s
194
195
  },
195
- 'correlation' => sanitize_correlation(correlation),
196
+ 'correlation' => sanitize_correlation(correlation, event_type),
196
197
  'payload' => payload,
197
198
  'project_token' => @project_token
198
199
  }
199
200
  end
200
201
 
201
- def sanitize_correlation(value)
202
+ def sanitize_correlation(value, event_type)
202
203
  correlation = value.is_a?(Hash) ? value : {}
203
- {
204
- 'request_id' => string_or_nil(correlation['request_id']),
205
- 'trace_id' => string_or_nil(correlation['trace_id']),
206
- 'session_id' => string_or_nil(correlation['session_id']),
207
- 'user_id_hash' => string_or_nil(correlation['user_id_hash'])
208
- }
204
+ keys = if event_type == 'analytics_event'
205
+ %w[session_id visitor_id_hash user_id_hash trace_id deploy_id]
206
+ else
207
+ %w[request_id trace_id session_id user_id_hash]
208
+ end
209
+ keys.each_with_object({}) do |key, sanitized|
210
+ sanitized[key] = string_or_nil(correlation[key]) if correlation.key?(key)
211
+ end
209
212
  end
210
213
 
211
214
  def string_or_nil(value)
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module DebugBundle
4
- VERSION = '1.1.0'
4
+ VERSION = '1.1.3'
5
5
  end
@@ -57,11 +57,16 @@
57
57
  "event_id": { "type": "string", "format": "uuid" },
58
58
  "event_type": { "type": "string", "minLength": 1 },
59
59
  "project_token": { "type": "string", "minLength": 1 },
60
+ "project_id": { "type": ["string", "null"], "format": "uuid" },
60
61
  "sdk_name": { "type": "string", "minLength": 1 },
61
62
  "sdk_version": { "type": "string", "minLength": 1 },
62
63
  "service": { "$ref": "#/$defs/service" },
63
64
  "occurred_at": { "type": "string", "format": "date-time" },
64
65
  "correlation": { "$ref": "#/$defs/correlation" },
66
+ "context": {
67
+ "type": "object",
68
+ "additionalProperties": true
69
+ },
65
70
  "payload": {}
66
71
  }
67
72
  },
@@ -195,10 +195,14 @@ RSpec.describe DebugBundle::Rack::Middleware do
195
195
  )
196
196
 
197
197
  client.flush
198
- payload = transport_events.fetch(0).fetch(:events).fetch(0).fetch('payload')
198
+ event = transport_events.fetch(0).fetch(:events).fetch(0)
199
+ payload = event.fetch('payload')
200
+ context = event.fetch('context')
199
201
 
200
202
  expect(payload).to include(
201
- 'route_template' => '/patients/:patient_id/checkouts',
203
+ 'route_template' => '/patients/:patient_id/checkouts'
204
+ )
205
+ expect(context).to include(
202
206
  'controller' => 'checkouts',
203
207
  'action' => 'create'
204
208
  )
data/spec/relay_spec.rb CHANGED
@@ -57,6 +57,40 @@ RSpec.describe DebugBundle::Relay::Handler do
57
57
  end
58
58
  end
59
59
 
60
+ it 'preserves analytics correlation fields for analytics relay events' do
61
+ analytics_event = browser_event.merge(
62
+ 'event_type' => 'analytics_event',
63
+ 'correlation' => {
64
+ 'session_id' => 'session-analytics',
65
+ 'visitor_id_hash' => "sha256:#{'a' * 64}",
66
+ 'user_id_hash' => nil,
67
+ 'trace_id' => 'trace-analytics',
68
+ 'deploy_id' => 'deploy-analytics'
69
+ },
70
+ 'payload' => { 'kind' => 'page_view', 'privacy' => { 'mode' => 'standard', 'consent_granted' => true } }
71
+ )
72
+
73
+ Dir.mktmpdir do |directory|
74
+ handler = described_class.new(project_mode: :local_only, project_token: 'dbundle_proj_server',
75
+ local_events_dir: directory)
76
+ response = handler.handle(
77
+ method: 'POST',
78
+ headers: {
79
+ 'host' => 'app.example.com',
80
+ 'origin' => 'https://app.example.com',
81
+ 'content-type' => 'application/json'
82
+ },
83
+ body: JSON.generate('batch' => [analytics_event]),
84
+ ip_address: '127.0.0.2'
85
+ )
86
+
87
+ expect(response.status).to eq(202)
88
+ file_name = Dir.children(directory).find { |entry| entry.end_with?('.events.json') }
89
+ event = JSON.parse(File.read(File.join(directory, file_name))).fetch(0)
90
+ expect(event.fetch('correlation')).to eq(analytics_event.fetch('correlation'))
91
+ end
92
+ end
93
+
60
94
  it 'rejects wrong origins' do
61
95
  handler = described_class.new(project_mode: :local_only, project_token: 'dbundle_proj_server')
62
96
  response = handler.handle(
@@ -50,7 +50,7 @@ if defined?(Sidekiq)
50
50
 
51
51
  expect(event.fetch('event_type')).to eq('backend_exception')
52
52
  expect(event.fetch('correlation')).to include('trace_id' => 'trace-chain-1')
53
- expect(event.fetch('payload').fetch('context').fetch('job')).to include(
53
+ expect(event.fetch('context').fetch('job')).to include(
54
54
  'class' => 'CheckoutWorker',
55
55
  'queue' => 'critical',
56
56
  'jid' => 'jid-chain-1'
@@ -40,7 +40,7 @@ RSpec.describe DebugBundle::Sidekiq::ServerMiddleware do
40
40
 
41
41
  expect(event.fetch('event_type')).to eq('backend_exception')
42
42
  expect(event.fetch('correlation')).to include('trace_id' => 'trace-job-1')
43
- expect(event.fetch('payload').fetch('context').fetch('job')).to include(
43
+ expect(event.fetch('context').fetch('job')).to include(
44
44
  'class' => 'CheckoutWorker',
45
45
  'queue' => 'critical',
46
46
  'jid' => 'jid-1',
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: 1.1.0
4
+ version: 1.1.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - DebugBundle
8
8
  bindir: exe
9
9
  cert_chain: []
10
- date: 2026-06-08 00:00:00.000000000 Z
10
+ date: 2026-07-16 00:00:00.000000000 Z
11
11
  dependencies:
12
12
  - !ruby/object:Gem::Dependency
13
13
  name: base64