sentry-ruby 5.9.0 → 5.16.1

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 (40) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile +2 -10
  3. data/README.md +9 -9
  4. data/lib/sentry/background_worker.rb +8 -1
  5. data/lib/sentry/backpressure_monitor.rb +75 -0
  6. data/lib/sentry/breadcrumb.rb +8 -2
  7. data/lib/sentry/check_in_event.rb +60 -0
  8. data/lib/sentry/client.rb +48 -10
  9. data/lib/sentry/configuration.rb +89 -17
  10. data/lib/sentry/cron/configuration.rb +23 -0
  11. data/lib/sentry/cron/monitor_check_ins.rb +75 -0
  12. data/lib/sentry/cron/monitor_config.rb +53 -0
  13. data/lib/sentry/cron/monitor_schedule.rb +42 -0
  14. data/lib/sentry/envelope.rb +1 -1
  15. data/lib/sentry/event.rb +6 -28
  16. data/lib/sentry/hub.rb +74 -2
  17. data/lib/sentry/integrable.rb +6 -0
  18. data/lib/sentry/interfaces/single_exception.rb +5 -3
  19. data/lib/sentry/net/http.rb +26 -20
  20. data/lib/sentry/profiler.rb +18 -7
  21. data/lib/sentry/propagation_context.rb +134 -0
  22. data/lib/sentry/puma.rb +11 -4
  23. data/lib/sentry/rack/capture_exceptions.rb +1 -4
  24. data/lib/sentry/rake.rb +0 -13
  25. data/lib/sentry/redis.rb +9 -3
  26. data/lib/sentry/release_detector.rb +1 -1
  27. data/lib/sentry/scope.rb +29 -13
  28. data/lib/sentry/span.rb +39 -2
  29. data/lib/sentry/test_helper.rb +18 -12
  30. data/lib/sentry/transaction.rb +18 -19
  31. data/lib/sentry/transaction_event.rb +0 -3
  32. data/lib/sentry/transport/configuration.rb +74 -1
  33. data/lib/sentry/transport/http_transport.rb +68 -37
  34. data/lib/sentry/transport/spotlight_transport.rb +50 -0
  35. data/lib/sentry/transport.rb +21 -17
  36. data/lib/sentry/utils/argument_checking_helper.rb +9 -3
  37. data/lib/sentry/version.rb +1 -1
  38. data/lib/sentry-ruby.rb +83 -25
  39. metadata +10 -3
  40. data/CODE_OF_CONDUCT.md +0 -74
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 7a114a391fe058601b40369376152bee8ba9b1b96ab94710344283cfe1a4490b
4
- data.tar.gz: 7a17648c7a5d06f22d2d643f6ff1cb25d3fed2348609e372fc5762043ebd538a
3
+ metadata.gz: 565289974a80625ef7ecac6926cfb7292569dfafd84f12584a8214619bdd5ed5
4
+ data.tar.gz: 005b3e5c27b7188058c412d36981779eb759f7be1032429eea17a2e4a7ffcb9f
5
5
  SHA512:
6
- metadata.gz: dab19469a8e68201380e2be405332bb7c8caef7bf46fd4dae70f4dc3beaac2902ce51b2b2c902b39ca048e1bc5ff47f85b1e7d3a824b5f20ff002dcbdca2c354
7
- data.tar.gz: 6bf0cba7b42dd9bfd5078e4353ad7f92d932b959a87c748cb30e505ffd8d205899c819ea4f72e5f3099f0b861139864062b870219678abdeda0a0629604963d3
6
+ metadata.gz: d0e68db7961263d6aa070feaa131fae4c2039884e565cd248795298a99f08d66e7c921df1a6f6175e68ac0ed554185b4b94769f8b0fcba12d76e6aa042fc3359
7
+ data.tar.gz: 5e10f1406d7a7809aed02cef80e1d4355e61af54ed42ffd9a1b3a54a730229abfef4f0a690980c646757b93fffa6219d6500c5700b08a6eef61b6eb682d63620
data/Gemfile CHANGED
@@ -12,19 +12,9 @@ gem "redis", "~> #{redis_rb_version}"
12
12
 
13
13
  gem "puma"
14
14
 
15
- gem "rake", "~> 12.0"
16
- gem "rspec", "~> 3.0"
17
- gem "rspec-retry"
18
15
  gem "timecop"
19
- gem "simplecov"
20
- gem "simplecov-cobertura", "~> 1.4"
21
- gem "rexml"
22
16
  gem "stackprof" unless RUBY_PLATFORM == "java"
23
17
 
24
- gem "object_tracer"
25
- gem "debug", github: "ruby/debug", platform: :ruby if RUBY_VERSION.to_f >= 2.6
26
- gem "pry"
27
-
28
18
  gem "benchmark-ips"
29
19
  gem "benchmark_driver"
30
20
  gem "benchmark-ipsa"
@@ -32,3 +22,5 @@ gem "benchmark-memory"
32
22
 
33
23
  gem "yard", github: "lsegal/yard"
34
24
  gem "webrick"
25
+
26
+ eval_gemfile File.expand_path("../Gemfile", __dir__)
data/README.md CHANGED
@@ -13,14 +13,14 @@ _Bad software is everywhere, and we're tired of it. Sentry is on a mission to he
13
13
  Sentry SDK for Ruby
14
14
  ===========
15
15
 
16
- | current version | build | coverage | downloads |
17
- | --- | ----- | -------- | --------- |
18
- | [![Gem Version](https://img.shields.io/gem/v/sentry-ruby?label=sentry-ruby)](https://rubygems.org/gems/sentry-ruby) | [![Build Status](https://github.com/getsentry/sentry-ruby/workflows/sentry-ruby%20Test/badge.svg)](https://github.com/getsentry/sentry-ruby/actions/workflows/sentry_ruby_test.yml) | [![Coverage Status](https://img.shields.io/codecov/c/github/getsentry/sentry-ruby/master?logo=codecov)](https://codecov.io/gh/getsentry/sentry-ruby/branch/master) | [![Downloads](https://img.shields.io/gem/dt/sentry-ruby.svg)](https://rubygems.org/gems/sentry-ruby/) |
19
- | [![Gem Version](https://img.shields.io/gem/v/sentry-rails?label=sentry-rails)](https://rubygems.org/gems/sentry-rails) | [![Build Status](https://github.com/getsentry/sentry-ruby/workflows/sentry-rails%20Test/badge.svg)](https://github.com/getsentry/sentry-ruby/actions/workflows/sentry_rails_test.yml) | [![Coverage Status](https://img.shields.io/codecov/c/github/getsentry/sentry-ruby/master?logo=codecov)](https://codecov.io/gh/getsentry/sentry-ruby/branch/master) | [![Downloads](https://img.shields.io/gem/dt/sentry-rails.svg)](https://rubygems.org/gems/sentry-rails/) |
20
- | [![Gem Version](https://img.shields.io/gem/v/sentry-sidekiq?label=sentry-sidekiq)](https://rubygems.org/gems/sentry-sidekiq) | [![Build Status](https://github.com/getsentry/sentry-ruby/workflows/sentry-sidekiq%20Test/badge.svg)](https://github.com/getsentry/sentry-ruby/actions/workflows/sentry_sidekiq_test.yml) | [![Coverage Status](https://img.shields.io/codecov/c/github/getsentry/sentry-ruby/master?logo=codecov)](https://codecov.io/gh/getsentry/sentry-ruby/branch/master) | [![Downloads](https://img.shields.io/gem/dt/sentry-sidekiq.svg)](https://rubygems.org/gems/sentry-sidekiq/) |
21
- | [![Gem Version](https://img.shields.io/gem/v/sentry-delayed_job?label=sentry-delayed_job)](https://rubygems.org/gems/sentry-delayed_job) | [![Build Status](https://github.com/getsentry/sentry-ruby/workflows/sentry-delayed_job%20Test/badge.svg)](https://github.com/getsentry/sentry-ruby/actions/workflows/sentry_delayed_job_test.yml) | [![Coverage Status](https://img.shields.io/codecov/c/github/getsentry/sentry-ruby/master?logo=codecov)](https://codecov.io/gh/getsentry/sentry-ruby/branch/master) | [![Downloads](https://img.shields.io/gem/dt/sentry-delayed_job.svg)](https://rubygems.org/gems/sentry-delayed_job/) |
22
- | [![Gem Version](https://img.shields.io/gem/v/sentry-resque?label=sentry-resque)](https://rubygems.org/gems/sentry-resque) | [![Build Status](https://github.com/getsentry/sentry-ruby/workflows/sentry-resque%20Test/badge.svg)](https://github.com/getsentry/sentry-ruby/actions/workflows/sentry_resque_test.yml) | [![Coverage Status](https://img.shields.io/codecov/c/github/getsentry/sentry-ruby/master?logo=codecov)](https://codecov.io/gh/getsentry/sentry-ruby/branch/master) | [![Downloads](https://img.shields.io/gem/dt/sentry-resque.svg)](https://rubygems.org/gems/sentry-resque/) |
23
- | [![Gem Version](https://img.shields.io/gem/v/sentry-opentelemetry?label=sentry-opentelemetry)](https://rubygems.org/gems/sentry-opentelemetry) | [![Build Status](https://github.com/getsentry/sentry-ruby/workflows/sentry-opentelemetry%20Test/badge.svg)](https://github.com/getsentry/sentry-ruby/actions/workflows/sentry_opentelemetry_test.yml) | [![Coverage Status](https://img.shields.io/codecov/c/github/getsentry/sentry-ruby/master?logo=codecov)](https://codecov.io/gh/getsentry/sentry-ruby/branch/master) | [![Downloads](https://img.shields.io/gem/dt/sentry-opentelemetry.svg)](https://rubygems.org/gems/sentry-opentelemetry/) |
16
+ | current version | build | coverage | downloads |
17
+ | --- | ----- | -------- | --------- |
18
+ | [![Gem Version](https://img.shields.io/gem/v/sentry-ruby?label=sentry-ruby)](https://rubygems.org/gems/sentry-ruby) | [![Build Status](https://github.com/getsentry/sentry-ruby/actions/workflows/sentry_ruby_test.yml/badge.svg)](https://github.com/getsentry/sentry-ruby/actions/workflows/sentry_ruby_test.yml) | [![Coverage Status](https://img.shields.io/codecov/c/github/getsentry/sentry-ruby/master?logo=codecov)](https://codecov.io/gh/getsentry/sentry-ruby/branch/master) | [![Downloads](https://img.shields.io/gem/dt/sentry-ruby.svg)](https://rubygems.org/gems/sentry-ruby/) |
19
+ | [![Gem Version](https://img.shields.io/gem/v/sentry-rails?label=sentry-rails)](https://rubygems.org/gems/sentry-rails) | [![Build Status](https://github.com/getsentry/sentry-ruby/actions/workflows/sentry_rails_test.yml/badge.svg)](https://github.com/getsentry/sentry-ruby/actions/workflows/sentry_rails_test.yml) | [![Coverage Status](https://img.shields.io/codecov/c/github/getsentry/sentry-ruby/master?logo=codecov)](https://codecov.io/gh/getsentry/sentry-ruby/branch/master) | [![Downloads](https://img.shields.io/gem/dt/sentry-rails.svg)](https://rubygems.org/gems/sentry-rails/) |
20
+ | [![Gem Version](https://img.shields.io/gem/v/sentry-sidekiq?label=sentry-sidekiq)](https://rubygems.org/gems/sentry-sidekiq) | [![Build Status](https://github.com/getsentry/sentry-ruby/actions/workflows/sentry_sidekiq_test.yml/badge.svg)](https://github.com/getsentry/sentry-ruby/actions/workflows/sentry_sidekiq_test.yml) | [![Coverage Status](https://img.shields.io/codecov/c/github/getsentry/sentry-ruby/master?logo=codecov)](https://codecov.io/gh/getsentry/sentry-ruby/branch/master) | [![Downloads](https://img.shields.io/gem/dt/sentry-sidekiq.svg)](https://rubygems.org/gems/sentry-sidekiq/) |
21
+ | [![Gem Version](https://img.shields.io/gem/v/sentry-delayed_job?label=sentry-delayed_job)](https://rubygems.org/gems/sentry-delayed_job) | [![Build Status](https://github.com/getsentry/sentry-ruby/actions/workflows/sentry_delayed_job_test.yml/badge.svg)](https://github.com/getsentry/sentry-ruby/actions/workflows/sentry_delayed_job_test.yml) | [![Coverage Status](https://img.shields.io/codecov/c/github/getsentry/sentry-ruby/master?logo=codecov)](https://codecov.io/gh/getsentry/sentry-ruby/branch/master) | [![Downloads](https://img.shields.io/gem/dt/sentry-delayed_job.svg)](https://rubygems.org/gems/sentry-delayed_job/) |
22
+ | [![Gem Version](https://img.shields.io/gem/v/sentry-resque?label=sentry-resque)](https://rubygems.org/gems/sentry-resque) | [![Build Status](https://github.com/getsentry/sentry-ruby/actions/workflows/sentry_resque_test.yml/badge.svg)](https://github.com/getsentry/sentry-ruby/actions/workflows/sentry_resque_test.yml) | [![Coverage Status](https://img.shields.io/codecov/c/github/getsentry/sentry-ruby/master?logo=codecov)](https://codecov.io/gh/getsentry/sentry-ruby/branch/master) | [![Downloads](https://img.shields.io/gem/dt/sentry-resque.svg)](https://rubygems.org/gems/sentry-resque/) |
23
+ | [![Gem Version](https://img.shields.io/gem/v/sentry-opentelemetry?label=sentry-opentelemetry)](https://rubygems.org/gems/sentry-opentelemetry) | [![Build Status](https://github.com/getsentry/sentry-ruby/actions/workflows/sentry_opentelemetry_test.yml/badge.svg)](https://github.com/getsentry/sentry-ruby/actions/workflows/sentry_opentelemetry_test.yml) | [![Coverage Status](https://img.shields.io/codecov/c/github/getsentry/sentry-ruby/master?logo=codecov)](https://codecov.io/gh/getsentry/sentry-ruby/branch/master) | [![Downloads](https://img.shields.io/gem/dt/sentry-opentelemetry.svg)](https://rubygems.org/gems/sentry-opentelemetry/) |
24
24
 
25
25
 
26
26
 
@@ -33,7 +33,7 @@ If you're using `sentry-raven`, we recommend you to migrate to this new SDK. You
33
33
 
34
34
  ## Requirements
35
35
 
36
- We test on Ruby 2.4, 2.5, 2.6, 2.7, 3.0, and 3.1 at the latest patchlevel/teeny version. We also support JRuby 9.0.
36
+ We test from Ruby 2.4 to Ruby 3.2 at the latest patchlevel/teeny version. We also support JRuby 9.0.
37
37
 
38
38
  If you use self-hosted Sentry, please also make sure its version is above `20.6.0`.
39
39
 
@@ -13,10 +13,12 @@ module Sentry
13
13
  attr_reader :logger
14
14
  attr_accessor :shutdown_timeout
15
15
 
16
+ DEFAULT_MAX_QUEUE = 30
17
+
16
18
  def initialize(configuration)
17
- @max_queue = 30
18
19
  @shutdown_timeout = 1
19
20
  @number_of_threads = configuration.background_worker_threads
21
+ @max_queue = configuration.background_worker_max_queue
20
22
  @logger = configuration.logger
21
23
  @debug = configuration.debug
22
24
  @shutdown_callback = nil
@@ -63,6 +65,11 @@ module Sentry
63
65
  @shutdown_callback&.call
64
66
  end
65
67
 
68
+ def full?
69
+ @executor.is_a?(Concurrent::ThreadPoolExecutor) &&
70
+ @executor.remaining_capacity == 0
71
+ end
72
+
66
73
  private
67
74
 
68
75
  def _perform(&block)
@@ -0,0 +1,75 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Sentry
4
+ class BackpressureMonitor
5
+ include LoggingHelper
6
+
7
+ DEFAULT_INTERVAL = 10
8
+ MAX_DOWNSAMPLE_FACTOR = 10
9
+
10
+ def initialize(configuration, client, interval: DEFAULT_INTERVAL)
11
+ @interval = interval
12
+ @client = client
13
+ @logger = configuration.logger
14
+
15
+ @thread = nil
16
+ @exited = false
17
+
18
+ @healthy = true
19
+ @downsample_factor = 0
20
+ end
21
+
22
+ def healthy?
23
+ ensure_thread
24
+ @healthy
25
+ end
26
+
27
+ def downsample_factor
28
+ ensure_thread
29
+ @downsample_factor
30
+ end
31
+
32
+ def run
33
+ check_health
34
+ set_downsample_factor
35
+ end
36
+
37
+ def check_health
38
+ @healthy = !(@client.transport.any_rate_limited? || Sentry.background_worker&.full?)
39
+ end
40
+
41
+ def set_downsample_factor
42
+ if @healthy
43
+ log_debug("[BackpressureMonitor] health check positive, reverting to normal sampling") if @downsample_factor.positive?
44
+ @downsample_factor = 0
45
+ else
46
+ @downsample_factor += 1 if @downsample_factor < MAX_DOWNSAMPLE_FACTOR
47
+ log_debug("[BackpressureMonitor] health check negative, downsampling with a factor of #{@downsample_factor}")
48
+ end
49
+ end
50
+
51
+ def kill
52
+ log_debug("[BackpressureMonitor] killing monitor")
53
+
54
+ @exited = true
55
+ @thread&.kill
56
+ end
57
+
58
+ private
59
+
60
+ def ensure_thread
61
+ return if @exited
62
+ return if @thread&.alive?
63
+
64
+ @thread = Thread.new do
65
+ loop do
66
+ sleep(@interval)
67
+ run
68
+ end
69
+ end
70
+ rescue ThreadError
71
+ log_debug("[BackpressureMonitor] Thread creation failed")
72
+ @exited = true
73
+ end
74
+ end
75
+ end
@@ -9,7 +9,7 @@ module Sentry
9
9
  # @return [Hash, nil]
10
10
  attr_accessor :data
11
11
  # @return [String, nil]
12
- attr_accessor :level
12
+ attr_reader :level
13
13
  # @return [Time, Integer, nil]
14
14
  attr_accessor :timestamp
15
15
  # @return [String, nil]
@@ -26,10 +26,10 @@ module Sentry
26
26
  def initialize(category: nil, data: nil, message: nil, timestamp: nil, level: nil, type: nil)
27
27
  @category = category
28
28
  @data = data || {}
29
- @level = level
30
29
  @timestamp = timestamp || Sentry.utc_now.to_i
31
30
  @type = type
32
31
  self.message = message
32
+ self.level = level
33
33
  end
34
34
 
35
35
  # @return [Hash]
@@ -50,6 +50,12 @@ module Sentry
50
50
  @message = (message || "").byteslice(0..Event::MAX_MESSAGE_SIZE_IN_BYTES)
51
51
  end
52
52
 
53
+ # @param level [String]
54
+ # @return [void]
55
+ def level=(level) # needed to meet the Sentry spec
56
+ @level = level == "warn" ? "warning" : level
57
+ end
58
+
53
59
  private
54
60
 
55
61
  def serialized_data
@@ -0,0 +1,60 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'securerandom'
4
+ require 'sentry/cron/monitor_config'
5
+
6
+ module Sentry
7
+ class CheckInEvent < Event
8
+ TYPE = 'check_in'
9
+
10
+ # uuid to identify this check-in.
11
+ # @return [String]
12
+ attr_accessor :check_in_id
13
+
14
+ # Identifier of the monitor for this check-in.
15
+ # @return [String]
16
+ attr_accessor :monitor_slug
17
+
18
+ # Duration of this check since it has started in seconds.
19
+ # @return [Integer, nil]
20
+ attr_accessor :duration
21
+
22
+ # Monitor configuration to support upserts.
23
+ # @return [Cron::MonitorConfig, nil]
24
+ attr_accessor :monitor_config
25
+
26
+ # Status of this check-in.
27
+ # @return [Symbol]
28
+ attr_accessor :status
29
+
30
+ VALID_STATUSES = %i(ok in_progress error)
31
+
32
+ def initialize(
33
+ slug:,
34
+ status:,
35
+ duration: nil,
36
+ monitor_config: nil,
37
+ check_in_id: nil,
38
+ **options
39
+ )
40
+ super(**options)
41
+
42
+ self.monitor_slug = slug
43
+ self.status = status
44
+ self.duration = duration
45
+ self.monitor_config = monitor_config
46
+ self.check_in_id = check_in_id || SecureRandom.uuid.delete('-')
47
+ end
48
+
49
+ # @return [Hash]
50
+ def to_hash
51
+ data = super
52
+ data[:check_in_id] = check_in_id
53
+ data[:monitor_slug] = monitor_slug
54
+ data[:status] = status
55
+ data[:duration] = duration if duration
56
+ data[:monitor_config] = monitor_config.to_hash if monitor_config
57
+ data
58
+ end
59
+ end
60
+ end
data/lib/sentry/client.rb CHANGED
@@ -10,6 +10,10 @@ module Sentry
10
10
  # @return [Transport]
11
11
  attr_reader :transport
12
12
 
13
+ # The Transport object that'll send events for the client.
14
+ # @return [SpotlightTransport, nil]
15
+ attr_reader :spotlight_transport
16
+
13
17
  # @!macro configuration
14
18
  attr_reader :configuration
15
19
 
@@ -32,6 +36,8 @@ module Sentry
32
36
  DummyTransport.new(configuration)
33
37
  end
34
38
  end
39
+
40
+ @spotlight_transport = SpotlightTransport.new(configuration) if configuration.spotlight
35
41
  end
36
42
 
37
43
  # Applies the given scope's data to the event and sends it to Sentry.
@@ -42,7 +48,7 @@ module Sentry
42
48
  def capture_event(event, scope, hint = {})
43
49
  return unless configuration.sending_allowed?
44
50
 
45
- unless event.is_a?(TransactionEvent) || configuration.sample_allowed?
51
+ if event.is_a?(ErrorEvent) && !configuration.sample_allowed?
46
52
  transport.record_lost_event(:sample_rate, 'event')
47
53
  return
48
54
  end
@@ -51,7 +57,7 @@ module Sentry
51
57
  event = scope.apply_to_event(event, hint)
52
58
 
53
59
  if event.nil?
54
- log_info("Discarded event because one of the event processors returned nil")
60
+ log_debug("Discarded event because one of the event processors returned nil")
55
61
  transport.record_lost_event(:event_processor, event_type)
56
62
  return
57
63
  end
@@ -104,6 +110,37 @@ module Sentry
104
110
  event
105
111
  end
106
112
 
113
+ # Initializes a CheckInEvent object with the given options.
114
+ #
115
+ # @param slug [String] identifier of this monitor
116
+ # @param status [Symbol] status of this check-in, one of {CheckInEvent::VALID_STATUSES}
117
+ # @param hint [Hash] the hint data that'll be passed to `before_send` callback and the scope's event processors.
118
+ # @param duration [Integer, nil] seconds elapsed since this monitor started
119
+ # @param monitor_config [Cron::MonitorConfig, nil] configuration for this monitor
120
+ # @param check_in_id [String, nil] for updating the status of an existing monitor
121
+ #
122
+ # @return [Event]
123
+ def event_from_check_in(
124
+ slug,
125
+ status,
126
+ hint = {},
127
+ duration: nil,
128
+ monitor_config: nil,
129
+ check_in_id: nil
130
+ )
131
+ return unless configuration.sending_allowed?
132
+
133
+ CheckInEvent.new(
134
+ configuration: configuration,
135
+ integration_meta: Sentry.integrations[hint[:integration]],
136
+ slug: slug,
137
+ status: status,
138
+ duration: duration,
139
+ monitor_config: monitor_config,
140
+ check_in_id: check_in_id
141
+ )
142
+ end
143
+
107
144
  # Initializes an Event object with the given Transaction object.
108
145
  # @param transaction [Transaction] the transaction to be recorded.
109
146
  # @return [TransactionEvent]
@@ -119,7 +156,7 @@ module Sentry
119
156
  event = configuration.before_send.call(event, hint)
120
157
 
121
158
  if event.nil?
122
- log_info("Discarded event because before_send returned nil")
159
+ log_debug("Discarded event because before_send returned nil")
123
160
  transport.record_lost_event(:before_send, 'event')
124
161
  return
125
162
  end
@@ -129,25 +166,24 @@ module Sentry
129
166
  event = configuration.before_send_transaction.call(event, hint)
130
167
 
131
168
  if event.nil?
132
- log_info("Discarded event because before_send_transaction returned nil")
169
+ log_debug("Discarded event because before_send_transaction returned nil")
133
170
  transport.record_lost_event(:before_send, 'transaction')
134
171
  return
135
172
  end
136
173
  end
137
174
 
138
175
  transport.send_event(event)
176
+ spotlight_transport&.send_event(event)
139
177
 
140
178
  event
141
179
  rescue => e
142
- loggable_event_type = event_type.capitalize
143
- log_error("#{loggable_event_type} sending failed", e, debug: configuration.debug)
144
-
145
- event_info = Event.get_log_message(event.to_hash)
146
- log_info("Unreported #{loggable_event_type}: #{event_info}")
180
+ log_error("Event sending failed", e, debug: configuration.debug)
147
181
  transport.record_lost_event(:network_error, event_type)
148
182
  raise
149
183
  end
150
184
 
185
+ # @deprecated use Sentry.get_traceparent instead.
186
+ #
151
187
  # Generates a Sentry trace for distribted tracing from the given Span.
152
188
  # Returns `nil` if `config.propagate_traces` is `false`.
153
189
  # @param span [Span] the span to generate trace from.
@@ -160,7 +196,9 @@ module Sentry
160
196
  trace
161
197
  end
162
198
 
163
- # Generates a W3C Baggage header for distribted tracing from the given Span.
199
+ # @deprecated Use Sentry.get_baggage instead.
200
+ #
201
+ # Generates a W3C Baggage header for distributed tracing from the given Span.
164
202
  # Returns `nil` if `config.propagate_traces` is `false`.
165
203
  # @param span [Span] the span to generate trace from.
166
204
  # @return [String, nil]
@@ -7,6 +7,7 @@ require 'sentry/utils/custom_inspection'
7
7
  require "sentry/dsn"
8
8
  require "sentry/release_detector"
9
9
  require "sentry/transport/configuration"
10
+ require "sentry/cron/configuration"
10
11
  require "sentry/linecache"
11
12
  require "sentry/interfaces/stacktrace_builder"
12
13
 
@@ -14,6 +15,8 @@ module Sentry
14
15
  class Configuration
15
16
  include CustomInspection
16
17
  include LoggingHelper
18
+ include ArgumentCheckingHelper
19
+
17
20
  # Directories to be recognized as part of your app. e.g. if you
18
21
  # have an `engines` dir at the root of your project, you may want
19
22
  # to set this to something like /(app|config|engines|lib)/
@@ -38,6 +41,13 @@ module Sentry
38
41
  # @return [Integer]
39
42
  attr_accessor :background_worker_threads
40
43
 
44
+ # The maximum queue size for the background worker.
45
+ # Jobs will be rejected above this limit.
46
+ #
47
+ # Default is {BackgroundWorker::DEFAULT_MAX_QUEUE}.
48
+ # @return [Integer]
49
+ attr_accessor :background_worker_max_queue
50
+
41
51
  # a proc/lambda that takes an array of stack traces
42
52
  # it'll be used to silence (reduce) backtrace of the exception
43
53
  #
@@ -140,6 +150,14 @@ module Sentry
140
150
  # @return [Boolean]
141
151
  attr_accessor :include_local_variables
142
152
 
153
+ # Whether to capture events and traces into Spotlight. Default is false.
154
+ # If you set this to true, Sentry will send events and traces to the local
155
+ # Sidecar proxy at http://localhost:8969/stream.
156
+ # If you want to use a different Sidecar proxy address, set this to String
157
+ # with the proxy URL.
158
+ # @return [Boolean, String]
159
+ attr_accessor :spotlight
160
+
143
161
  # @deprecated Use {#include_local_variables} instead.
144
162
  alias_method :capture_exception_frame_locals, :include_local_variables
145
163
 
@@ -179,7 +197,7 @@ module Sentry
179
197
  # Release tag to be passed with every event sent to Sentry.
180
198
  # We automatically try to set this to a git SHA or Capistrano release.
181
199
  # @return [String]
182
- attr_accessor :release
200
+ attr_reader :release
183
201
 
184
202
  # The sampling factor to apply to events. A value of 0.0 will not send
185
203
  # any events, and a value of 1.0 will send 100% of events.
@@ -209,13 +227,17 @@ module Sentry
209
227
  # @return [String]
210
228
  attr_accessor :server_name
211
229
 
212
- # Return a Transport::Configuration object for transport-related configurations.
213
- # @return [Transport]
230
+ # Transport related configuration.
231
+ # @return [Transport::Configuration]
214
232
  attr_reader :transport
215
233
 
234
+ # Cron related configuration.
235
+ # @return [Cron::Configuration]
236
+ attr_reader :cron
237
+
216
238
  # Take a float between 0.0 and 1.0 as the sample rate for tracing events (transactions).
217
- # @return [Float]
218
- attr_accessor :traces_sample_rate
239
+ # @return [Float, nil]
240
+ attr_reader :traces_sample_rate
219
241
 
220
242
  # Take a Proc that controls the sample rate for every tracing event, e.g.
221
243
  # @example
@@ -241,6 +263,17 @@ module Sentry
241
263
  # @return [Boolean]
242
264
  attr_accessor :auto_session_tracking
243
265
 
266
+ # Whether to downsample transactions automatically because of backpressure.
267
+ # Starts a new monitor thread to check health of the SDK every 10 seconds.
268
+ # Default is false
269
+ # @return [Boolean]
270
+ attr_accessor :enable_backpressure_handling
271
+
272
+ # Allowlist of outgoing request targets to which sentry-trace and baggage headers are attached.
273
+ # Default is all (/.*/)
274
+ # @return [Array<String, Regexp>]
275
+ attr_accessor :trace_propagation_targets
276
+
244
277
  # The instrumenter to use, :sentry or :otel
245
278
  # @return [Symbol]
246
279
  attr_reader :instrumenter
@@ -251,10 +284,24 @@ module Sentry
251
284
  # @return [Float, nil]
252
285
  attr_reader :profiles_sample_rate
253
286
 
287
+ # Array of patches to apply.
288
+ # Default is {DEFAULT_PATCHES}
289
+ # @return [Array<Symbol>]
290
+ attr_accessor :enabled_patches
291
+
254
292
  # these are not config options
255
293
  # @!visibility private
256
294
  attr_reader :errors, :gem_specs
257
295
 
296
+ # These exceptions could enter Puma's `lowlevel_error_handler` callback and the SDK's Puma integration
297
+ # But they are mostly considered as noise and should be ignored by default
298
+ # Please see https://github.com/getsentry/sentry-ruby/pull/2026 for more information
299
+ PUMA_IGNORE_DEFAULT = [
300
+ 'Puma::MiniSSL::SSLError',
301
+ 'Puma::HttpParserError',
302
+ 'Puma::HttpParserError501'
303
+ ].freeze
304
+
258
305
  # Most of these errors generate 4XX responses. In general, Sentry clients
259
306
  # only automatically report 5xx responses.
260
307
  IGNORE_DEFAULT = [
@@ -279,6 +326,10 @@ module Sentry
279
326
 
280
327
  INSTRUMENTERS = [:sentry, :otel]
281
328
 
329
+ PROPAGATION_TARGETS_MATCH_ALL = /.*/.freeze
330
+
331
+ DEFAULT_PATCHES = %i(redis puma http).freeze
332
+
282
333
  class << self
283
334
  # Post initialization callbacks are called at the end of initialization process
284
335
  # allowing extending the configuration of sentry-ruby by multiple extensions
@@ -296,6 +347,7 @@ module Sentry
296
347
  self.app_dirs_pattern = nil
297
348
  self.debug = false
298
349
  self.background_worker_threads = Concurrent.processor_count
350
+ self.background_worker_max_queue = BackgroundWorker::DEFAULT_MAX_QUEUE
299
351
  self.backtrace_cleanup_callback = nil
300
352
  self.max_breadcrumbs = BreadcrumbBuffer::DEFAULT_SIZE
301
353
  self.breadcrumbs_logger = []
@@ -304,7 +356,7 @@ module Sentry
304
356
  self.environment = environment_from_env
305
357
  self.enabled_environments = []
306
358
  self.exclude_loggers = []
307
- self.excluded_exceptions = IGNORE_DEFAULT.dup
359
+ self.excluded_exceptions = IGNORE_DEFAULT + PUMA_IGNORE_DEFAULT
308
360
  self.inspect_exception_causes_for_exclusion = true
309
361
  self.linecache = ::Sentry::LineCache.new
310
362
  self.logger = ::Sentry::Logger.new(STDOUT)
@@ -317,19 +369,23 @@ module Sentry
317
369
  self.skip_rake_integration = false
318
370
  self.send_client_reports = true
319
371
  self.auto_session_tracking = true
372
+ self.enable_backpressure_handling = false
320
373
  self.trusted_proxies = []
321
374
  self.dsn = ENV['SENTRY_DSN']
375
+ self.spotlight = false
322
376
  self.server_name = server_name_from_env
323
377
  self.instrumenter = :sentry
378
+ self.trace_propagation_targets = [PROPAGATION_TARGETS_MATCH_ALL]
379
+ self.enabled_patches = DEFAULT_PATCHES.dup
324
380
 
325
381
  self.before_send = nil
326
382
  self.before_send_transaction = nil
327
383
  self.rack_env_whitelist = RACK_ENV_WHITELIST_DEFAULT
328
- self.traces_sample_rate = nil
329
384
  self.traces_sampler = nil
330
385
  self.enable_tracing = nil
331
386
 
332
387
  @transport = Transport::Configuration.new
388
+ @cron = Cron::Configuration.new
333
389
  @gem_specs = Hash[Gem::Specification.map { |spec| [spec.name, spec.version.to_s] }] if Gem::Specification.respond_to?(:map)
334
390
 
335
391
  run_post_initialization_callbacks
@@ -341,6 +397,12 @@ module Sentry
341
397
 
342
398
  alias server= dsn=
343
399
 
400
+ def release=(value)
401
+ check_argument_type!(value, String, NilClass)
402
+
403
+ @release = value
404
+ end
405
+
344
406
  def async=(value)
345
407
  check_callable!("async", value)
346
408
 
@@ -401,15 +463,25 @@ module Sentry
401
463
  @traces_sample_rate ||= 1.0 if enable_tracing
402
464
  end
403
465
 
466
+ def is_numeric_or_nil?(value)
467
+ value.is_a?(Numeric) || value.nil?
468
+ end
469
+
470
+ def traces_sample_rate=(traces_sample_rate)
471
+ raise ArgumentError, "traces_sample_rate must be a Numeric or nil" unless is_numeric_or_nil?(traces_sample_rate)
472
+ @traces_sample_rate = traces_sample_rate
473
+ end
474
+
404
475
  def profiles_sample_rate=(profiles_sample_rate)
405
- log_info("Please make sure to include the 'stackprof' gem in your Gemfile to use Profiling with Sentry.") unless defined?(StackProf)
476
+ raise ArgumentError, "profiles_sample_rate must be a Numeric or nil" unless is_numeric_or_nil?(profiles_sample_rate)
477
+ log_warn("Please make sure to include the 'stackprof' gem in your Gemfile to use Profiling with Sentry.") unless defined?(StackProf)
406
478
  @profiles_sample_rate = profiles_sample_rate
407
479
  end
408
480
 
409
481
  def sending_allowed?
410
482
  @errors = []
411
483
 
412
- valid? && capture_in_environment?
484
+ spotlight || (valid? && capture_in_environment?)
413
485
  end
414
486
 
415
487
  def sample_allowed?
@@ -435,19 +507,19 @@ module Sentry
435
507
  enabled_environments.empty? || enabled_environments.include?(environment)
436
508
  end
437
509
 
510
+ def valid_sample_rate?(sample_rate)
511
+ return false unless sample_rate.is_a?(Numeric)
512
+ sample_rate >= 0.0 && sample_rate <= 1.0
513
+ end
514
+
438
515
  def tracing_enabled?
439
- valid_sampler = !!((@traces_sample_rate &&
440
- @traces_sample_rate >= 0.0 &&
441
- @traces_sample_rate <= 1.0) ||
442
- @traces_sampler)
516
+ valid_sampler = !!((valid_sample_rate?(@traces_sample_rate)) || @traces_sampler)
443
517
 
444
518
  (@enable_tracing != false) && valid_sampler && sending_allowed?
445
519
  end
446
520
 
447
521
  def profiling_enabled?
448
- valid_sampler = !!(@profiles_sample_rate &&
449
- @profiles_sample_rate >= 0.0 &&
450
- @profiles_sample_rate <= 1.0)
522
+ valid_sampler = !!(valid_sample_rate?(@profiles_sample_rate))
451
523
 
452
524
  tracing_enabled? && valid_sampler && sending_allowed?
453
525
  end
@@ -477,7 +549,7 @@ module Sentry
477
549
  def detect_release
478
550
  return unless sending_allowed?
479
551
 
480
- self.release ||= ReleaseDetector.detect_release(project_root: project_root, running_on_heroku: running_on_heroku?)
552
+ @release ||= ReleaseDetector.detect_release(project_root: project_root, running_on_heroku: running_on_heroku?)
481
553
 
482
554
  if running_on_heroku? && release.nil?
483
555
  log_warn(HEROKU_DYNO_METADATA_MESSAGE)
@@ -0,0 +1,23 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Sentry
4
+ module Cron
5
+ class Configuration
6
+ # Defaults set here will apply to all {Cron::MonitorConfig} objects unless overwritten.
7
+
8
+ # How long (in minutes) after the expected checkin time will we wait
9
+ # until we consider the checkin to have been missed.
10
+ # @return [Integer, nil]
11
+ attr_accessor :default_checkin_margin
12
+
13
+ # How long (in minutes) is the checkin allowed to run for in in_progress
14
+ # before it is considered failed.
15
+ # @return [Integer, nil]
16
+ attr_accessor :default_max_runtime
17
+
18
+ # tz database style timezone string
19
+ # @return [String, nil]
20
+ attr_accessor :default_timezone
21
+ end
22
+ end
23
+ end