sentry-ruby 5.13.0 → 5.19.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.
Files changed (67) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile +4 -18
  3. data/README.md +20 -10
  4. data/Rakefile +1 -1
  5. data/bin/console +1 -0
  6. data/lib/sentry/attachment.rb +42 -0
  7. data/lib/sentry/background_worker.rb +9 -2
  8. data/lib/sentry/backpressure_monitor.rb +45 -0
  9. data/lib/sentry/backtrace.rb +7 -3
  10. data/lib/sentry/check_in_event.rb +1 -1
  11. data/lib/sentry/client.rb +69 -16
  12. data/lib/sentry/configuration.rb +57 -14
  13. data/lib/sentry/cron/configuration.rb +23 -0
  14. data/lib/sentry/cron/monitor_check_ins.rb +40 -26
  15. data/lib/sentry/cron/monitor_schedule.rb +1 -1
  16. data/lib/sentry/dsn.rb +1 -1
  17. data/lib/sentry/envelope.rb +18 -1
  18. data/lib/sentry/error_event.rb +2 -2
  19. data/lib/sentry/event.rb +13 -39
  20. data/lib/sentry/faraday.rb +77 -0
  21. data/lib/sentry/graphql.rb +9 -0
  22. data/lib/sentry/hub.rb +17 -4
  23. data/lib/sentry/integrable.rb +4 -0
  24. data/lib/sentry/interface.rb +1 -0
  25. data/lib/sentry/interfaces/exception.rb +5 -3
  26. data/lib/sentry/interfaces/mechanism.rb +20 -0
  27. data/lib/sentry/interfaces/request.rb +2 -2
  28. data/lib/sentry/interfaces/single_exception.rb +7 -4
  29. data/lib/sentry/interfaces/stacktrace_builder.rb +8 -0
  30. data/lib/sentry/metrics/aggregator.rb +248 -0
  31. data/lib/sentry/metrics/configuration.rb +47 -0
  32. data/lib/sentry/metrics/counter_metric.rb +25 -0
  33. data/lib/sentry/metrics/distribution_metric.rb +25 -0
  34. data/lib/sentry/metrics/gauge_metric.rb +35 -0
  35. data/lib/sentry/metrics/local_aggregator.rb +53 -0
  36. data/lib/sentry/metrics/metric.rb +19 -0
  37. data/lib/sentry/metrics/set_metric.rb +28 -0
  38. data/lib/sentry/metrics/timing.rb +43 -0
  39. data/lib/sentry/metrics.rb +56 -0
  40. data/lib/sentry/net/http.rb +22 -39
  41. data/lib/sentry/propagation_context.rb +9 -8
  42. data/lib/sentry/puma.rb +1 -1
  43. data/lib/sentry/rack/capture_exceptions.rb +14 -2
  44. data/lib/sentry/rake.rb +3 -14
  45. data/lib/sentry/redis.rb +2 -1
  46. data/lib/sentry/release_detector.rb +1 -1
  47. data/lib/sentry/scope.rb +47 -37
  48. data/lib/sentry/session.rb +2 -2
  49. data/lib/sentry/session_flusher.rb +6 -38
  50. data/lib/sentry/span.rb +40 -5
  51. data/lib/sentry/test_helper.rb +2 -1
  52. data/lib/sentry/threaded_periodic_worker.rb +39 -0
  53. data/lib/sentry/transaction.rb +25 -16
  54. data/lib/sentry/transaction_event.rb +5 -0
  55. data/lib/sentry/transport/configuration.rb +73 -1
  56. data/lib/sentry/transport/http_transport.rb +68 -37
  57. data/lib/sentry/transport/spotlight_transport.rb +50 -0
  58. data/lib/sentry/transport.rb +32 -37
  59. data/lib/sentry/utils/argument_checking_helper.rb +6 -0
  60. data/lib/sentry/utils/http_tracing.rb +41 -0
  61. data/lib/sentry/utils/logging_helper.rb +0 -4
  62. data/lib/sentry/utils/real_ip.rb +1 -1
  63. data/lib/sentry/utils/request_id.rb +1 -1
  64. data/lib/sentry/version.rb +1 -1
  65. data/lib/sentry-ruby.rb +57 -24
  66. data/sentry-ruby.gemspec +12 -5
  67. metadata +42 -7
@@ -50,14 +50,15 @@ module Sentry
50
50
  if sentry_trace_data
51
51
  @trace_id, @parent_span_id, @parent_sampled = sentry_trace_data
52
52
 
53
- @baggage = if baggage_header && !baggage_header.empty?
54
- Baggage.from_incoming_header(baggage_header)
55
- else
56
- # If there's an incoming sentry-trace but no incoming baggage header,
57
- # for instance in traces coming from older SDKs,
58
- # baggage will be empty and frozen and won't be populated as head SDK.
59
- Baggage.new({})
60
- end
53
+ @baggage =
54
+ if baggage_header && !baggage_header.empty?
55
+ Baggage.from_incoming_header(baggage_header)
56
+ else
57
+ # If there's an incoming sentry-trace but no incoming baggage header,
58
+ # for instance in traces coming from older SDKs,
59
+ # baggage will be empty and frozen and won't be populated as head SDK.
60
+ Baggage.new({})
61
+ end
61
62
 
62
63
  @baggage.freeze!
63
64
  @incoming_trace = true
data/lib/sentry/puma.rb CHANGED
@@ -7,7 +7,7 @@ module Sentry
7
7
  module Server
8
8
  PUMA_4_AND_PRIOR = Gem::Version.new(::Puma::Const::PUMA_VERSION) < Gem::Version.new("5.0.0")
9
9
 
10
- def lowlevel_error(e, env, status=500)
10
+ def lowlevel_error(e, env, status = 500)
11
11
  result =
12
12
  if PUMA_4_AND_PRIOR
13
13
  super(e, env)
@@ -4,6 +4,8 @@ module Sentry
4
4
  module Rack
5
5
  class CaptureExceptions
6
6
  ERROR_EVENT_ID_KEY = "sentry.error_event_id"
7
+ MECHANISM_TYPE = "rack"
8
+ SPAN_ORIGIN = "auto.http.rack"
7
9
 
8
10
  def initialize(app)
9
11
  @app = app
@@ -56,13 +58,19 @@ module Sentry
56
58
  end
57
59
 
58
60
  def capture_exception(exception, env)
59
- Sentry.capture_exception(exception).tap do |event|
61
+ Sentry.capture_exception(exception, hint: { mechanism: mechanism }).tap do |event|
60
62
  env[ERROR_EVENT_ID_KEY] = event.event_id if event
61
63
  end
62
64
  end
63
65
 
64
66
  def start_transaction(env, scope)
65
- options = { name: scope.transaction_name, source: scope.transaction_source, op: transaction_op }
67
+ options = {
68
+ name: scope.transaction_name,
69
+ source: scope.transaction_source,
70
+ op: transaction_op,
71
+ origin: SPAN_ORIGIN
72
+ }
73
+
66
74
  transaction = Sentry.continue_trace(env, **options)
67
75
  Sentry.start_transaction(transaction: transaction, custom_sampling_context: { env: env }, **options)
68
76
  end
@@ -74,6 +82,10 @@ module Sentry
74
82
  transaction.set_http_status(status_code)
75
83
  transaction.finish
76
84
  end
85
+
86
+ def mechanism
87
+ Sentry::Mechanism.new(type: MECHANISM_TYPE, handled: false)
88
+ end
77
89
  end
78
90
  end
79
91
  end
data/lib/sentry/rake.rb CHANGED
@@ -8,7 +8,9 @@ module Sentry
8
8
  module Application
9
9
  # @api private
10
10
  def display_error_message(ex)
11
- Sentry.capture_exception(ex) do |scope|
11
+ mechanism = Sentry::Mechanism.new(type: 'rake', handled: false)
12
+
13
+ Sentry.capture_exception(ex, hint: { mechanism: mechanism }) do |scope|
12
14
  task_name = top_level_tasks.join(' ')
13
15
  scope.set_transaction_name(task_name, source: :task)
14
16
  scope.set_tag("rake_task", task_name)
@@ -17,15 +19,6 @@ module Sentry
17
19
  super
18
20
  end
19
21
  end
20
-
21
- module Task
22
- # @api private
23
- def execute(args=nil)
24
- return super unless Sentry.initialized? && Sentry.get_current_hub
25
-
26
- super
27
- end
28
- end
29
22
  end
30
23
  end
31
24
 
@@ -34,8 +27,4 @@ module Rake
34
27
  class Application
35
28
  prepend(Sentry::Rake::Application)
36
29
  end
37
-
38
- class Task
39
- prepend(Sentry::Rake::Task)
40
- end
41
30
  end
data/lib/sentry/redis.rb CHANGED
@@ -4,6 +4,7 @@ module Sentry
4
4
  # @api private
5
5
  class Redis
6
6
  OP_NAME = "db.redis"
7
+ SPAN_ORIGIN = "auto.db.redis"
7
8
  LOGGER_NAME = :redis_logger
8
9
 
9
10
  def initialize(commands, host, port, db)
@@ -13,7 +14,7 @@ module Sentry
13
14
  def instrument
14
15
  return yield unless Sentry.initialized?
15
16
 
16
- Sentry.with_child_span(op: OP_NAME, start_timestamp: Sentry.utc_now.to_f) do |span|
17
+ Sentry.with_child_span(op: OP_NAME, start_timestamp: Sentry.utc_now.to_f, origin: SPAN_ORIGIN) do |span|
17
18
  yield.tap do
18
19
  record_breadcrumb
19
20
 
@@ -28,7 +28,7 @@ module Sentry
28
28
  end
29
29
 
30
30
  def detect_release_from_git
31
- Sentry.sys_command("git rev-parse --short HEAD") if File.directory?(".git")
31
+ Sentry.sys_command("git rev-parse HEAD") if File.directory?(".git")
32
32
  end
33
33
 
34
34
  def detect_release_from_env
data/lib/sentry/scope.rb CHANGED
@@ -2,6 +2,7 @@
2
2
 
3
3
  require "sentry/breadcrumb_buffer"
4
4
  require "sentry/propagation_context"
5
+ require "sentry/attachment"
5
6
  require "etc"
6
7
 
7
8
  module Sentry
@@ -9,8 +10,8 @@ module Sentry
9
10
  include ArgumentCheckingHelper
10
11
 
11
12
  ATTRIBUTES = [
12
- :transaction_names,
13
- :transaction_sources,
13
+ :transaction_name,
14
+ :transaction_source,
14
15
  :contexts,
15
16
  :extra,
16
17
  :tags,
@@ -22,6 +23,7 @@ module Sentry
22
23
  :rack_env,
23
24
  :span,
24
25
  :session,
26
+ :attachments,
25
27
  :propagation_context
26
28
  ]
27
29
 
@@ -44,12 +46,19 @@ module Sentry
44
46
  # @param hint [Hash] the hint data that'll be passed to event processors.
45
47
  # @return [Event]
46
48
  def apply_to_event(event, hint = nil)
47
- event.tags = tags.merge(event.tags)
48
- event.user = user.merge(event.user)
49
- event.extra = extra.merge(event.extra)
50
- event.contexts = contexts.merge(event.contexts)
51
- event.transaction = transaction_name if transaction_name
52
- event.transaction_info = { source: transaction_source } if transaction_source
49
+ unless event.is_a?(CheckInEvent)
50
+ event.tags = tags.merge(event.tags)
51
+ event.user = user.merge(event.user)
52
+ event.extra = extra.merge(event.extra)
53
+ event.contexts = contexts.merge(event.contexts)
54
+ event.transaction = transaction_name if transaction_name
55
+ event.transaction_info = { source: transaction_source } if transaction_source
56
+ event.fingerprint = fingerprint
57
+ event.level = level
58
+ event.breadcrumbs = breadcrumbs
59
+ event.rack_env = rack_env if rack_env
60
+ event.attachments = attachments
61
+ end
53
62
 
54
63
  if span
55
64
  event.contexts[:trace] ||= span.get_trace_context
@@ -58,11 +67,6 @@ module Sentry
58
67
  event.dynamic_sampling_context ||= propagation_context.get_dynamic_sampling_context
59
68
  end
60
69
 
61
- event.fingerprint = fingerprint
62
- event.level = level
63
- event.breadcrumbs = breadcrumbs
64
- event.rack_env = rack_env if rack_env
65
-
66
70
  all_event_processors = self.class.global_event_processors + @event_processors
67
71
 
68
72
  unless all_event_processors.empty?
@@ -95,12 +99,13 @@ module Sentry
95
99
  copy.extra = extra.deep_dup
96
100
  copy.tags = tags.deep_dup
97
101
  copy.user = user.deep_dup
98
- copy.transaction_names = transaction_names.dup
99
- copy.transaction_sources = transaction_sources.dup
102
+ copy.transaction_name = transaction_name.dup
103
+ copy.transaction_source = transaction_source.dup
100
104
  copy.fingerprint = fingerprint.deep_dup
101
105
  copy.span = span.deep_dup
102
106
  copy.session = session.deep_dup
103
107
  copy.propagation_context = propagation_context.deep_dup
108
+ copy.attachments = attachments.dup
104
109
  copy
105
110
  end
106
111
 
@@ -113,11 +118,12 @@ module Sentry
113
118
  self.extra = scope.extra
114
119
  self.tags = scope.tags
115
120
  self.user = scope.user
116
- self.transaction_names = scope.transaction_names
117
- self.transaction_sources = scope.transaction_sources
121
+ self.transaction_name = scope.transaction_name
122
+ self.transaction_source = scope.transaction_source
118
123
  self.fingerprint = scope.fingerprint
119
124
  self.span = scope.span
120
125
  self.propagation_context = scope.propagation_context
126
+ self.attachments = scope.attachments
121
127
  end
122
128
 
123
129
  # Updates the scope's data from the given options.
@@ -127,14 +133,17 @@ module Sentry
127
133
  # @param user [Hash]
128
134
  # @param level [String, Symbol]
129
135
  # @param fingerprint [Array]
130
- # @return [void]
136
+ # @param attachments [Array<Attachment>]
137
+ # @return [Array]
131
138
  def update_from_options(
132
139
  contexts: nil,
133
140
  extra: nil,
134
141
  tags: nil,
135
142
  user: nil,
136
143
  level: nil,
137
- fingerprint: nil
144
+ fingerprint: nil,
145
+ attachments: nil,
146
+ **options
138
147
  )
139
148
  self.contexts.merge!(contexts) if contexts
140
149
  self.extra.merge!(extra) if extra
@@ -142,6 +151,9 @@ module Sentry
142
151
  self.user = user if user
143
152
  self.level = level if level
144
153
  self.fingerprint = fingerprint if fingerprint
154
+
155
+ # Returns unsupported option keys so we can notify users.
156
+ options.keys
145
157
  end
146
158
 
147
159
  # Sets the scope's rack_env attribute.
@@ -226,8 +238,8 @@ module Sentry
226
238
  # @param transaction_name [String]
227
239
  # @return [void]
228
240
  def set_transaction_name(transaction_name, source: :custom)
229
- @transaction_names << transaction_name
230
- @transaction_sources << source
241
+ @transaction_name = transaction_name
242
+ @transaction_source = source
231
243
  end
232
244
 
233
245
  # Sets the currently active session on the scope.
@@ -237,18 +249,10 @@ module Sentry
237
249
  @session = session
238
250
  end
239
251
 
240
- # Returns current transaction name.
241
- # The "transaction" here does not refer to `Transaction` objects.
242
- # @return [String, nil]
243
- def transaction_name
244
- @transaction_names.last
245
- end
246
-
247
- # Returns current transaction source.
248
- # The "transaction" here does not refer to `Transaction` objects.
249
- # @return [String, nil]
250
- def transaction_source
251
- @transaction_sources.last
252
+ # These are high cardinality and thus bad.
253
+ # @return [Boolean]
254
+ def transaction_source_low_quality?
255
+ transaction_source == :url
252
256
  end
253
257
 
254
258
  # Returns the associated Transaction object.
@@ -286,6 +290,12 @@ module Sentry
286
290
  @propagation_context = PropagationContext.new(self, env)
287
291
  end
288
292
 
293
+ # Add a new attachment to the scope.
294
+ def add_attachment(**opts)
295
+ attachments << (attachment = Attachment.new(**opts))
296
+ attachment
297
+ end
298
+
289
299
  protected
290
300
 
291
301
  # for duplicating scopes internally
@@ -294,18 +304,19 @@ module Sentry
294
304
  private
295
305
 
296
306
  def set_default_value
297
- @contexts = { :os => self.class.os_context, :runtime => self.class.runtime_context }
307
+ @contexts = { os: self.class.os_context, runtime: self.class.runtime_context }
298
308
  @extra = {}
299
309
  @tags = {}
300
310
  @user = {}
301
311
  @level = :error
302
312
  @fingerprint = []
303
- @transaction_names = []
304
- @transaction_sources = []
313
+ @transaction_name = nil
314
+ @transaction_source = nil
305
315
  @event_processors = []
306
316
  @rack_env = {}
307
317
  @span = nil
308
318
  @session = nil
319
+ @attachments = []
309
320
  generate_propagation_context
310
321
  set_new_breadcrumb_buffer
311
322
  end
@@ -354,6 +365,5 @@ module Sentry
354
365
  global_event_processors << block
355
366
  end
356
367
  end
357
-
358
368
  end
359
369
  end
@@ -5,8 +5,8 @@ module Sentry
5
5
  attr_reader :started, :status, :aggregation_key
6
6
 
7
7
  # TODO-neel add :crashed after adding handled mechanism
8
- STATUSES = %i(ok errored exited)
9
- AGGREGATE_STATUSES = %i(errored exited)
8
+ STATUSES = %i[ok errored exited]
9
+ AGGREGATE_STATUSES = %i[errored exited]
10
10
 
11
11
  def initialize
12
12
  @started = Sentry.utc_now
@@ -1,58 +1,38 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Sentry
4
- class SessionFlusher
5
- include LoggingHelper
6
-
4
+ class SessionFlusher < ThreadedPeriodicWorker
7
5
  FLUSH_INTERVAL = 60
8
6
 
9
7
  def initialize(configuration, client)
10
- @thread = nil
11
- @exited = false
8
+ super(configuration.logger, FLUSH_INTERVAL)
12
9
  @client = client
13
10
  @pending_aggregates = {}
14
11
  @release = configuration.release
15
12
  @environment = configuration.environment
16
- @logger = configuration.logger
17
13
 
18
14
  log_debug("[Sessions] Sessions won't be captured without a valid release") unless @release
19
15
  end
20
16
 
21
17
  def flush
22
18
  return if @pending_aggregates.empty?
23
- envelope = pending_envelope
24
-
25
- Sentry.background_worker.perform do
26
- @client.transport.send_envelope(envelope)
27
- end
28
19
 
20
+ @client.capture_envelope(pending_envelope)
29
21
  @pending_aggregates = {}
30
22
  end
31
23
 
24
+ alias_method :run, :flush
25
+
32
26
  def add_session(session)
33
- return if @exited
34
27
  return unless @release
35
28
 
36
- begin
37
- ensure_thread
38
- rescue ThreadError
39
- log_debug("Session flusher thread creation failed")
40
- @exited = true
41
- return
42
- end
29
+ return unless ensure_thread
43
30
 
44
31
  return unless Session::AGGREGATE_STATUSES.include?(session.status)
45
32
  @pending_aggregates[session.aggregation_key] ||= init_aggregates(session.aggregation_key)
46
33
  @pending_aggregates[session.aggregation_key][session.status] += 1
47
34
  end
48
35
 
49
- def kill
50
- log_debug("Killing session flusher")
51
-
52
- @exited = true
53
- @thread&.kill
54
- end
55
-
56
36
  private
57
37
 
58
38
  def init_aggregates(aggregation_key)
@@ -74,17 +54,5 @@ module Sentry
74
54
  def attrs
75
55
  { release: @release, environment: @environment }
76
56
  end
77
-
78
- def ensure_thread
79
- return if @thread&.alive?
80
-
81
- @thread = Thread.new do
82
- loop do
83
- sleep(FLUSH_INTERVAL)
84
- flush
85
- end
86
- end
87
- end
88
-
89
57
  end
90
58
  end
data/lib/sentry/span.rb CHANGED
@@ -1,10 +1,10 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "securerandom"
4
+ require "sentry/metrics/local_aggregator"
4
5
 
5
6
  module Sentry
6
7
  class Span
7
-
8
8
  # We will try to be consistent with OpenTelemetry on this front going forward.
9
9
  # https://develop.sentry.dev/sdk/performance/span-data-conventions/
10
10
  module DataConventions
@@ -39,6 +39,11 @@ module Sentry
39
39
  # Recommended: If different than server.port.
40
40
  # Example: 16456
41
41
  SERVER_SOCKET_PORT = "server.socket.port"
42
+
43
+ FILEPATH = "code.filepath"
44
+ LINENO = "code.lineno"
45
+ FUNCTION = "code.function"
46
+ NAMESPACE = "code.namespace"
42
47
  end
43
48
 
44
49
  STATUS_MAP = {
@@ -55,6 +60,8 @@ module Sentry
55
60
  504 => "deadline_exceeded"
56
61
  }
57
62
 
63
+ DEFAULT_SPAN_ORIGIN = "manual"
64
+
58
65
  # An uuid that can be used to identify a trace.
59
66
  # @return [String]
60
67
  attr_reader :trace_id
@@ -88,6 +95,9 @@ module Sentry
88
95
  # Span data
89
96
  # @return [Hash]
90
97
  attr_reader :data
98
+ # Span origin that tracks what kind of instrumentation created a span
99
+ # @return [String]
100
+ attr_reader :origin
91
101
 
92
102
  # The SpanRecorder the current span belongs to.
93
103
  # SpanRecorder holds all spans under the same Transaction object (including the Transaction itself).
@@ -109,7 +119,8 @@ module Sentry
109
119
  parent_span_id: nil,
110
120
  sampled: nil,
111
121
  start_timestamp: nil,
112
- timestamp: nil
122
+ timestamp: nil,
123
+ origin: nil
113
124
  )
114
125
  @trace_id = trace_id || SecureRandom.uuid.delete("-")
115
126
  @span_id = span_id || SecureRandom.uuid.delete("-").slice(0, 16)
@@ -123,6 +134,7 @@ module Sentry
123
134
  @status = status
124
135
  @data = {}
125
136
  @tags = {}
137
+ @origin = origin || DEFAULT_SPAN_ORIGIN
126
138
  end
127
139
 
128
140
  # Finishes the span by adding a timestamp.
@@ -150,7 +162,7 @@ module Sentry
150
162
 
151
163
  # @return [Hash]
152
164
  def to_hash
153
- {
165
+ hash = {
154
166
  trace_id: @trace_id,
155
167
  span_id: @span_id,
156
168
  parent_span_id: @parent_span_id,
@@ -160,8 +172,14 @@ module Sentry
160
172
  op: @op,
161
173
  status: @status,
162
174
  tags: @tags,
163
- data: @data
175
+ data: @data,
176
+ origin: @origin
164
177
  }
178
+
179
+ summary = metrics_summary
180
+ hash[:_metrics_summary] = summary if summary
181
+
182
+ hash
165
183
  end
166
184
 
167
185
  # Returns the span's context that can be used to embed in an Event.
@@ -173,7 +191,9 @@ module Sentry
173
191
  parent_span_id: @parent_span_id,
174
192
  description: @description,
175
193
  op: @op,
176
- status: @status
194
+ status: @status,
195
+ origin: @origin,
196
+ data: @data
177
197
  }
178
198
  end
179
199
 
@@ -269,5 +289,20 @@ module Sentry
269
289
  def set_tag(key, value)
270
290
  @tags[key] = value
271
291
  end
292
+
293
+ # Sets the origin of the span.
294
+ # @param origin [String]
295
+ def set_origin(origin)
296
+ @origin = origin
297
+ end
298
+
299
+ # Collects gauge metrics on the span for metric summaries.
300
+ def metrics_local_aggregator
301
+ @metrics_local_aggregator ||= Sentry::Metrics::LocalAggregator.new
302
+ end
303
+
304
+ def metrics_summary
305
+ @metrics_local_aggregator&.to_hash
306
+ end
272
307
  end
273
308
  end
@@ -20,7 +20,7 @@ module Sentry
20
20
  # set transport to DummyTransport, so we can easily intercept the captured events
21
21
  dummy_config.transport.transport_class = Sentry::DummyTransport
22
22
  # make sure SDK allows sending under the current environment
23
- dummy_config.enabled_environments << dummy_config.environment unless dummy_config.enabled_environments.include?(dummy_config.environment)
23
+ dummy_config.enabled_environments += [dummy_config.environment] unless dummy_config.enabled_environments.include?(dummy_config.environment)
24
24
  # disble async event sending
25
25
  dummy_config.background_worker_threads = 0
26
26
 
@@ -50,6 +50,7 @@ module Sentry
50
50
  if Sentry.get_current_hub.instance_variable_get(:@stack).size > 1
51
51
  Sentry.get_current_hub.pop_scope
52
52
  end
53
+ Sentry::Scope.global_event_processors.clear
53
54
  end
54
55
 
55
56
  # @return [Transport]
@@ -0,0 +1,39 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Sentry
4
+ class ThreadedPeriodicWorker
5
+ include LoggingHelper
6
+
7
+ def initialize(logger, internal)
8
+ @thread = nil
9
+ @exited = false
10
+ @interval = internal
11
+ @logger = logger
12
+ end
13
+
14
+ def ensure_thread
15
+ return false if @exited
16
+ return true if @thread&.alive?
17
+
18
+ @thread = Thread.new do
19
+ loop do
20
+ sleep(@interval)
21
+ run
22
+ end
23
+ end
24
+
25
+ true
26
+ rescue ThreadError
27
+ log_debug("[#{self.class.name}] thread creation failed")
28
+ @exited = true
29
+ false
30
+ end
31
+
32
+ def kill
33
+ log_debug("[#{self.class.name}] thread killed")
34
+
35
+ @exited = true
36
+ @thread&.kill
37
+ end
38
+ end
39
+ end
@@ -13,7 +13,7 @@ module Sentry
13
13
  MESSAGE_PREFIX = "[Tracing]"
14
14
 
15
15
  # https://develop.sentry.dev/sdk/event-payloads/transaction/#transaction-annotations
16
- SOURCES = %i(custom url route view component task)
16
+ SOURCES = %i[custom url route view component task]
17
17
 
18
18
  include LoggingHelper
19
19
 
@@ -110,14 +110,15 @@ module Sentry
110
110
 
111
111
  trace_id, parent_span_id, parent_sampled = sentry_trace_data
112
112
 
113
- baggage = if baggage && !baggage.empty?
114
- Baggage.from_incoming_header(baggage)
115
- else
116
- # If there's an incoming sentry-trace but no incoming baggage header,
117
- # for instance in traces coming from older SDKs,
118
- # baggage will be empty and frozen and won't be populated as head SDK.
119
- Baggage.new({})
120
- end
113
+ baggage =
114
+ if baggage && !baggage.empty?
115
+ Baggage.from_incoming_header(baggage)
116
+ else
117
+ # If there's an incoming sentry-trace but no incoming baggage header,
118
+ # for instance in traces coming from older SDKs,
119
+ # baggage will be empty and frozen and won't be populated as head SDK.
120
+ Baggage.new({})
121
+ end
121
122
 
122
123
  baggage.freeze!
123
124
 
@@ -218,7 +219,12 @@ module Sentry
218
219
  if sample_rate == true
219
220
  @sampled = true
220
221
  else
221
- @sampled = Random.rand < sample_rate
222
+ if Sentry.backpressure_monitor
223
+ factor = Sentry.backpressure_monitor.downsample_factor
224
+ @effective_sample_rate /= 2**factor
225
+ end
226
+
227
+ @sampled = Random.rand < @effective_sample_rate
222
228
  end
223
229
 
224
230
  if @sampled
@@ -257,7 +263,10 @@ module Sentry
257
263
  event = hub.current_client.event_from_transaction(self)
258
264
  hub.capture_event(event)
259
265
  else
260
- hub.current_client.transport.record_lost_event(:sample_rate, 'transaction')
266
+ is_backpressure = Sentry.backpressure_monitor&.downsample_factor&.positive?
267
+ reason = is_backpressure ? :backpressure : :sample_rate
268
+ hub.current_client.transport.record_lost_event(reason, 'transaction')
269
+ hub.current_client.transport.record_lost_event(reason, 'span')
261
270
  end
262
271
  end
263
272
 
@@ -294,6 +303,11 @@ module Sentry
294
303
  profiler.start
295
304
  end
296
305
 
306
+ # These are high cardinality and thus bad
307
+ def source_low_quality?
308
+ source == :url
309
+ end
310
+
297
311
  protected
298
312
 
299
313
  def init_span_recorder(limit = 1000)
@@ -329,11 +343,6 @@ module Sentry
329
343
  @baggage = Baggage.new(items, mutable: false)
330
344
  end
331
345
 
332
- # These are high cardinality and thus bad
333
- def source_low_quality?
334
- source == :url
335
- end
336
-
337
346
  class SpanRecorder
338
347
  attr_reader :max_length, :spans
339
348