sentry-ruby-core 6.4.1 → 6.5.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: a700d6777f6c9ff19d7878858806085e70d6d8f003995d44c15d37a29c125f87
4
- data.tar.gz: a463f19fe5fe50719398470b2926509ebd5708cb7702948207d164846c873a29
3
+ metadata.gz: e2784eb13a9b7e212a5bc3c607cd305e835631596418688c3a1e0e2fbcdfaa7a
4
+ data.tar.gz: 4d15760d10a1093a747d74caa928b6812d8b5b24c81698c01833bc52b5a775a3
5
5
  SHA512:
6
- metadata.gz: 1953d81802e79fd96c1187b6f084bf952f2a1082e7a60293570781777423cdd419062fd235741b3e7bd90f1362966e8dc9ed8a26d6ae9d72a6d13f8afa0addd4
7
- data.tar.gz: 9e6975759fb9a06307ab201f7377822f5fa4d4f407eacb656371f083cce2ff65ba98c0b4cc418f81e608d0e8e78449ee9c53e3c26e6706a3b8b4bfe8e77c37d2
6
+ metadata.gz: cd34baa8e81dbe92f33bb98ceedb8f526efd1406ca724e98eb86a0ea8d78585e31302037394961679850b11766a2ad777f9987dca3a94075421330043764ee10
7
+ data.tar.gz: f3cca1b504d70ac8f9f6e2083826c4f2fbcc7df5bf8da3438daf0a74ec1c7034ba4c0a98859ad291ac7cd453e88ca7be4af2a5722c1bb432f096f56e014ce83d
@@ -25,7 +25,7 @@ module Sentry
25
25
  # The presence of a Sentry item makes the baggage object immutable.
26
26
  #
27
27
  # @param header [String] The incoming Baggage header string.
28
- # @return [Baggage, nil]
28
+ # @return [Baggage]
29
29
  def self.from_incoming_header(header)
30
30
  items = {}
31
31
  mutable = true
@@ -371,6 +371,24 @@ module Sentry
371
371
  # @return [Proc, nil]
372
372
  attr_reader :std_lib_logger_filter
373
373
 
374
+ # An optional organization ID. The SDK will try to extract it from the DSN in most cases
375
+ # but you can provide it explicitly for self-hosted and Relay setups.
376
+ # This value is used for trace propagation and for features like strict_trace_continuation.
377
+ # @return [String, nil]
378
+ attr_reader :org_id
379
+
380
+ # If set to true, the SDK will only continue a trace if the org_id of the incoming trace found in the
381
+ # baggage header matches the org_id of the current Sentry client and only if BOTH are present.
382
+ #
383
+ # If set to false, consistency of org_id will only be enforced if both are present.
384
+ # If either are missing, the trace will be continued.
385
+ #
386
+ # The client's organization ID is extracted from the DSN or can be set with the org_id option.
387
+ # If the organization IDs do not match, the SDK will start a new trace instead of continuing the incoming one.
388
+ # This is useful to prevent traces of unknown third-party services from being continued in your application.
389
+ # @return [Boolean]
390
+ attr_accessor :strict_trace_continuation
391
+
374
392
  # these are not config options
375
393
  # @!visibility private
376
394
  attr_reader :errors, :gem_specs
@@ -520,6 +538,8 @@ module Sentry
520
538
  self.trusted_proxies = []
521
539
  self.dsn = ENV["SENTRY_DSN"]
522
540
  self.capture_queue_time = true
541
+ self.org_id = nil
542
+ self.strict_trace_continuation = false
523
543
 
524
544
  spotlight_env = ENV["SENTRY_SPOTLIGHT"]
525
545
  spotlight_bool = Sentry::Utils::EnvHelper.env_to_bool(spotlight_env, strict: true)
@@ -673,6 +693,16 @@ module Sentry
673
693
  @profiler_class = profiler_class
674
694
  end
675
695
 
696
+ def org_id=(value)
697
+ @org_id = value&.to_s
698
+ end
699
+
700
+ # Returns the effective org ID, preferring the explicit config option over the DSN-parsed value.
701
+ # @return [String, nil]
702
+ def effective_org_id
703
+ org_id || dsn&.org_id
704
+ end
705
+
676
706
  def sending_allowed?
677
707
  spotlight || sending_to_dsn_allowed?
678
708
  end
data/lib/sentry/dsn.rb CHANGED
@@ -11,8 +11,9 @@ module Sentry
11
11
  REQUIRED_ATTRIBUTES = %w[host path public_key project_id].freeze
12
12
  LOCALHOST_NAMES = %w[localhost 127.0.0.1 ::1 [::1]].freeze
13
13
  LOCALHOST_PATTERN = /\.local(host|domain)?$/i
14
+ ORG_ID_REGEX = /\Ao(\d+)\./
14
15
 
15
- attr_reader :scheme, :secret_key, :port, *REQUIRED_ATTRIBUTES
16
+ attr_reader :scheme, :secret_key, :port, :org_id, *REQUIRED_ATTRIBUTES
16
17
 
17
18
  def initialize(dsn_string)
18
19
  @raw_value = dsn_string
@@ -31,6 +32,8 @@ module Sentry
31
32
  @host = uri.host
32
33
  @port = uri.port if uri.port
33
34
  @path = uri_path.join("/")
35
+
36
+ @org_id = extract_org_id_from_host
34
37
  end
35
38
 
36
39
  def valid?
@@ -101,5 +104,14 @@ module Sentry
101
104
 
102
105
  "Sentry " + fields.map { |key, value| "#{key}=#{value}" }.join(", ")
103
106
  end
107
+
108
+ private
109
+
110
+ def extract_org_id_from_host
111
+ return nil unless @host
112
+
113
+ match = ORG_ID_REGEX.match(@host)
114
+ match ? match[1] : nil
115
+ end
104
116
  end
105
117
  end
@@ -6,4 +6,7 @@ module Sentry
6
6
 
7
7
  class ExternalError < Error
8
8
  end
9
+
10
+ class SizeExceededError < ExternalError
11
+ end
9
12
  end
@@ -43,7 +43,7 @@ module Sentry
43
43
  private
44
44
 
45
45
  def serialize_attributes
46
- @attributes.transform_values! { |v| attribute_hash(v) }
46
+ @attributes.transform_values { |v| attribute_hash(v) }
47
47
  end
48
48
  end
49
49
  end
@@ -53,6 +53,44 @@ module Sentry
53
53
  [trace_id, parent_span_id, parent_sampled]
54
54
  end
55
55
 
56
+ # Determines whether we should continue an incoming trace based on org_id matching
57
+ # and the strict_trace_continuation configuration option.
58
+ #
59
+ # @param incoming_baggage [Baggage] the baggage from the incoming request
60
+ # @return [Boolean]
61
+ def self.should_continue_trace?(incoming_baggage)
62
+ return true unless Sentry.initialized?
63
+
64
+ configuration = Sentry.configuration
65
+ sdk_org_id = configuration.effective_org_id
66
+ baggage_org_id = incoming_baggage.items["org_id"]
67
+
68
+ # Mismatched org IDs always start a new trace regardless of strict mode
69
+ if sdk_org_id && baggage_org_id && sdk_org_id != baggage_org_id
70
+ Sentry.sdk_logger.debug(LOGGER_PROGNAME) do
71
+ "Starting a new trace because org IDs don't match (incoming baggage org_id: #{baggage_org_id}, SDK org_id: #{sdk_org_id})"
72
+ end
73
+
74
+ return false
75
+ end
76
+
77
+ return true unless configuration.strict_trace_continuation
78
+
79
+ # In strict mode, both must be present and match (unless both are missing)
80
+ if sdk_org_id.nil? && baggage_org_id.nil?
81
+ true
82
+ elsif sdk_org_id.nil? || baggage_org_id.nil?
83
+ Sentry.sdk_logger.debug(LOGGER_PROGNAME) do
84
+ "Starting a new trace because strict trace continuation is enabled and one org ID is missing " \
85
+ "(incoming baggage org_id: #{baggage_org_id.inspect}, SDK org_id: #{sdk_org_id.inspect})"
86
+ end
87
+
88
+ false
89
+ else
90
+ true
91
+ end
92
+ end
93
+
56
94
  def self.extract_sample_rand_from_baggage(baggage, trace_id = nil)
57
95
  return unless baggage&.items
58
96
 
@@ -96,9 +134,7 @@ module Sentry
96
134
  sentry_trace_data = self.class.extract_sentry_trace(sentry_trace_header)
97
135
 
98
136
  if sentry_trace_data
99
- @trace_id, @parent_span_id, @parent_sampled = sentry_trace_data
100
-
101
- @baggage =
137
+ incoming_baggage =
102
138
  if baggage_header && !baggage_header.empty?
103
139
  Baggage.from_incoming_header(baggage_header)
104
140
  else
@@ -108,10 +144,13 @@ module Sentry
108
144
  Baggage.new({})
109
145
  end
110
146
 
111
- @sample_rand = self.class.extract_sample_rand_from_baggage(@baggage, @trace_id)
112
-
113
- @baggage.freeze!
114
- @incoming_trace = true
147
+ if self.class.should_continue_trace?(incoming_baggage)
148
+ @trace_id, @parent_span_id, @parent_sampled = sentry_trace_data
149
+ @baggage = incoming_baggage
150
+ @sample_rand = self.class.extract_sample_rand_from_baggage(@baggage, @trace_id)
151
+ @baggage.freeze!
152
+ @incoming_trace = true
153
+ end
115
154
  end
116
155
  end
117
156
  end
@@ -162,7 +201,8 @@ module Sentry
162
201
  "sample_rand" => Utils::SampleRand.format(@sample_rand),
163
202
  "environment" => configuration.environment,
164
203
  "release" => configuration.release,
165
- "public_key" => configuration.dsn&.public_key
204
+ "public_key" => configuration.dsn&.public_key,
205
+ "org_id" => configuration.effective_org_id
166
206
  }
167
207
 
168
208
  items.compact!
@@ -13,7 +13,7 @@ module Sentry
13
13
 
14
14
  def detect_release_from_heroku(running_on_heroku)
15
15
  return unless running_on_heroku
16
- ENV["HEROKU_SLUG_COMMIT"]
16
+ ENV["HEROKU_BUILD_COMMIT"] || ENV["HEROKU_SLUG_COMMIT"]
17
17
  end
18
18
 
19
19
  def detect_release_from_capistrano(project_root)
data/lib/sentry/scope.rb CHANGED
@@ -135,6 +135,7 @@ module Sentry
135
135
  copy.session = session.deep_dup
136
136
  copy.propagation_context = propagation_context.deep_dup
137
137
  copy.attachments = attachments.dup
138
+ copy.event_processors = event_processors.dup
138
139
  copy
139
140
  end
140
141
 
@@ -295,7 +295,8 @@ module Sentry
295
295
  "sampled" => sampled&.to_s,
296
296
  "environment" => configuration&.environment,
297
297
  "release" => configuration&.release,
298
- "public_key" => configuration&.dsn&.public_key
298
+ "public_key" => configuration&.dsn&.public_key,
299
+ "org_id" => configuration&.effective_org_id
299
300
  }
300
301
 
301
302
  items["transaction"] = name unless source_low_quality?
@@ -49,6 +49,12 @@ module Sentry
49
49
 
50
50
  if response.code.match?(/\A2\d{2}/)
51
51
  handle_rate_limited_response(response) if has_rate_limited_header?(response)
52
+ elsif response.code == "413"
53
+ error_message = "HTTP 413: Envelope dropped due to exceeded size limit"
54
+ error_message += " (body: #{response.body})" if response.body && !response.body.empty?
55
+ log_warn(error_message)
56
+
57
+ raise Sentry::SizeExceededError, error_message
52
58
  elsif response.code == "429"
53
59
  log_debug("the server responded with status 429")
54
60
  handle_rate_limited_response(response)
@@ -19,7 +19,8 @@ module Sentry
19
19
  :before_send,
20
20
  :event_processor,
21
21
  :insufficient_data,
22
- :backpressure
22
+ :backpressure,
23
+ :send_error
23
24
  ]
24
25
 
25
26
  include LoggingHelper
@@ -61,6 +62,10 @@ module Sentry
61
62
  log_debug("[Transport] Sending envelope with items [#{serialized_items.map(&:type).join(', ')}] #{envelope.event_id} to Sentry")
62
63
  send_data(data)
63
64
  end
65
+ rescue Sentry::SizeExceededError
66
+ serialized_items&.each do |item|
67
+ record_lost_event(:send_error, item.data_category)
68
+ end
64
69
  end
65
70
 
66
71
  def serialize_envelope(envelope)
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Sentry
4
- VERSION = "6.4.1"
4
+ VERSION = "6.5.0"
5
5
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: sentry-ruby-core
3
3
  version: !ruby/object:Gem::Version
4
- version: 6.4.1
4
+ version: 6.5.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Sentry Team
@@ -15,14 +15,14 @@ dependencies:
15
15
  requirements:
16
16
  - - '='
17
17
  - !ruby/object:Gem::Version
18
- version: 6.4.1
18
+ version: 6.5.0
19
19
  type: :runtime
20
20
  prerelease: false
21
21
  version_requirements: !ruby/object:Gem::Requirement
22
22
  requirements:
23
23
  - - '='
24
24
  - !ruby/object:Gem::Version
25
- version: 6.4.1
25
+ version: 6.5.0
26
26
  - !ruby/object:Gem::Dependency
27
27
  name: concurrent-ruby
28
28
  requirement: !ruby/object:Gem::Requirement