sentry-ruby 5.11.0 → 5.13.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: 604c762c416c92d41d143d5800edcc8d4111aa63c91fbec1209f4409ef600c8a
4
- data.tar.gz: ff18479058fdcd44227409610251a2c0b014766be1d0641ac9b83e8badd6489a
3
+ metadata.gz: 1d89549da043b049e2dd6f5c73ac6ec0c3dd98ded247be2035316e45110bde88
4
+ data.tar.gz: 38844c5d5521bcf40a749d77667e91fa5b819d98951e4a02ebc807724b91c3a9
5
5
  SHA512:
6
- metadata.gz: 54899b0d5ab63a546e5c2ea34b821c693ecf7e91b699726630c6baf9badd2b90ca8ba153aad55ea73ae6e7eadfc1625b4c1438965eb9cf52da5f8abc71d0280e
7
- data.tar.gz: 4fdbd60228c2fe23e7db4b4d7d1c2e8841eeec561ef3de01f41c0415b997bc5a5d90cc5fe08e6ccc98eb1352043a25c82e98489cd9720db65967c5a126859d78
6
+ metadata.gz: 1cc2ac4f8394b8dcafd30d2aa4bbd863e313a04953d5b072997783c54fa76285bff16b77e23ca7f6b22644f9d1a69ad2484c844b7e6bf6deefbf3cbcd874ffbf
7
+ data.tar.gz: c30a0fd380e11f93edc599320a3884464e9d0568327512e514d1586f120b217091f67912531167738efb64ffd55cec960c6083dc894556796a00f2f5c79e13f9
data/Gemfile CHANGED
@@ -21,9 +21,15 @@ gem "simplecov-cobertura", "~> 1.4"
21
21
  gem "rexml"
22
22
  gem "stackprof" unless RUBY_PLATFORM == "java"
23
23
 
24
- if RUBY_VERSION.to_f >= 2.6
24
+ ruby_version = Gem::Version.new(RUBY_VERSION)
25
+
26
+ if ruby_version >= Gem::Version.new("2.6.0")
25
27
  gem "debug", github: "ruby/debug", platform: :ruby
26
28
  gem "irb"
29
+
30
+ if ruby_version >= Gem::Version.new("3.0.0")
31
+ gem "ruby-lsp-rspec"
32
+ end
27
33
  end
28
34
 
29
35
  gem "pry"
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
 
@@ -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
@@ -104,6 +104,37 @@ module Sentry
104
104
  event
105
105
  end
106
106
 
107
+ # Initializes a CheckInEvent object with the given options.
108
+ #
109
+ # @param slug [String] identifier of this monitor
110
+ # @param status [Symbol] status of this check-in, one of {CheckInEvent::VALID_STATUSES}
111
+ # @param hint [Hash] the hint data that'll be passed to `before_send` callback and the scope's event processors.
112
+ # @param duration [Integer, nil] seconds elapsed since this monitor started
113
+ # @param monitor_config [Cron::MonitorConfig, nil] configuration for this monitor
114
+ # @param check_in_id [String, nil] for updating the status of an existing monitor
115
+ #
116
+ # @return [Event]
117
+ def event_from_check_in(
118
+ slug,
119
+ status,
120
+ hint = {},
121
+ duration: nil,
122
+ monitor_config: nil,
123
+ check_in_id: nil
124
+ )
125
+ return unless configuration.sending_allowed?
126
+
127
+ CheckInEvent.new(
128
+ configuration: configuration,
129
+ integration_meta: Sentry.integrations[hint[:integration]],
130
+ slug: slug,
131
+ status: status,
132
+ duration: duration,
133
+ monitor_config: monitor_config,
134
+ check_in_id: check_in_id
135
+ )
136
+ end
137
+
107
138
  # Initializes an Event object with the given Transaction object.
108
139
  # @param transaction [Transaction] the transaction to be recorded.
109
140
  # @return [TransactionEvent]
@@ -258,6 +258,11 @@ module Sentry
258
258
  # @return [Float, nil]
259
259
  attr_reader :profiles_sample_rate
260
260
 
261
+ # Array of patches to apply.
262
+ # Default is {DEFAULT_PATCHES}
263
+ # @return [Array<Symbol>]
264
+ attr_accessor :enabled_patches
265
+
261
266
  # these are not config options
262
267
  # @!visibility private
263
268
  attr_reader :errors, :gem_specs
@@ -297,6 +302,8 @@ module Sentry
297
302
 
298
303
  PROPAGATION_TARGETS_MATCH_ALL = /.*/.freeze
299
304
 
305
+ DEFAULT_PATCHES = %i(redis puma http).freeze
306
+
300
307
  class << self
301
308
  # Post initialization callbacks are called at the end of initialization process
302
309
  # allowing extending the configuration of sentry-ruby by multiple extensions
@@ -340,6 +347,7 @@ module Sentry
340
347
  self.server_name = server_name_from_env
341
348
  self.instrumenter = :sentry
342
349
  self.trace_propagation_targets = [PROPAGATION_TARGETS_MATCH_ALL]
350
+ self.enabled_patches = DEFAULT_PATCHES.dup
343
351
 
344
352
  self.before_send = nil
345
353
  self.before_send_transaction = nil
@@ -0,0 +1,61 @@
1
+ module Sentry
2
+ module Cron
3
+ module MonitorCheckIns
4
+ module Patch
5
+ def perform(*args)
6
+ slug = self.class.sentry_monitor_slug || self.class.name
7
+ monitor_config = self.class.sentry_monitor_config
8
+
9
+ check_in_id = Sentry.capture_check_in(slug,
10
+ :in_progress,
11
+ monitor_config: monitor_config)
12
+
13
+ start = Sentry.utc_now.to_i
14
+ ret = super
15
+ duration = Sentry.utc_now.to_i - start
16
+
17
+ Sentry.capture_check_in(slug,
18
+ :ok,
19
+ check_in_id: check_in_id,
20
+ duration: duration,
21
+ monitor_config: monitor_config)
22
+
23
+ ret
24
+ rescue Exception
25
+ duration = Sentry.utc_now.to_i - start
26
+
27
+ Sentry.capture_check_in(slug,
28
+ :error,
29
+ check_in_id: check_in_id,
30
+ duration: duration,
31
+ monitor_config: monitor_config)
32
+
33
+ raise
34
+ end
35
+ end
36
+
37
+ module ClassMethods
38
+ def sentry_monitor_check_ins(slug: nil, monitor_config: nil)
39
+ @sentry_monitor_slug = slug
40
+ @sentry_monitor_config = monitor_config
41
+
42
+ prepend Patch
43
+ end
44
+
45
+ def sentry_monitor_slug
46
+ @sentry_monitor_slug
47
+ end
48
+
49
+ def sentry_monitor_config
50
+ @sentry_monitor_config
51
+ end
52
+ end
53
+
54
+ extend ClassMethods
55
+
56
+ def self.included(base)
57
+ base.extend(ClassMethods)
58
+ end
59
+ end
60
+ end
61
+ end
@@ -0,0 +1,53 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'sentry/cron/monitor_schedule'
4
+
5
+ module Sentry
6
+ module Cron
7
+ class MonitorConfig
8
+ # The monitor schedule configuration
9
+ # @return [MonitorSchedule::Crontab, MonitorSchedule::Interval]
10
+ attr_accessor :schedule
11
+
12
+ # How long (in minutes) after the expected checkin time will we wait
13
+ # until we consider the checkin to have been missed.
14
+ # @return [Integer, nil]
15
+ attr_accessor :checkin_margin
16
+
17
+ # How long (in minutes) is the checkin allowed to run for in in_progress
18
+ # before it is considered failed.
19
+ # @return [Integer, nil]
20
+ attr_accessor :max_runtime
21
+
22
+ # tz database style timezone string
23
+ # @return [String, nil]
24
+ attr_accessor :timezone
25
+
26
+ def initialize(schedule, checkin_margin: nil, max_runtime: nil, timezone: nil)
27
+ @schedule = schedule
28
+ @checkin_margin = checkin_margin
29
+ @max_runtime = max_runtime
30
+ @timezone = timezone
31
+ end
32
+
33
+ def self.from_crontab(crontab, **options)
34
+ new(MonitorSchedule::Crontab.new(crontab), **options)
35
+ end
36
+
37
+ def self.from_interval(num, unit, **options)
38
+ return nil unless MonitorSchedule::Interval::VALID_UNITS.include?(unit)
39
+
40
+ new(MonitorSchedule::Interval.new(num, unit), **options)
41
+ end
42
+
43
+ def to_hash
44
+ {
45
+ schedule: schedule.to_hash,
46
+ checkin_margin: checkin_margin,
47
+ max_runtime: max_runtime,
48
+ timezone: timezone
49
+ }.compact
50
+ end
51
+ end
52
+ end
53
+ end
@@ -0,0 +1,42 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Sentry
4
+ module Cron
5
+ module MonitorSchedule
6
+ class Crontab
7
+ # A crontab formatted string such as "0 * * * *".
8
+ # @return [String]
9
+ attr_accessor :value
10
+
11
+ def initialize(value)
12
+ @value = value
13
+ end
14
+
15
+ def to_hash
16
+ { type: :crontab, value: value }
17
+ end
18
+ end
19
+
20
+ class Interval
21
+ # The number representing duration of the interval.
22
+ # @return [Integer]
23
+ attr_accessor :value
24
+
25
+ # The unit representing duration of the interval.
26
+ # @return [Symbol]
27
+ attr_accessor :unit
28
+
29
+ VALID_UNITS = %i(year month week day hour minute)
30
+
31
+ def initialize(value, unit)
32
+ @value = value
33
+ @unit = unit
34
+ end
35
+
36
+ def to_hash
37
+ { type: :interval, value: value, unit: unit }
38
+ end
39
+ end
40
+ end
41
+ end
42
+ end
@@ -5,7 +5,7 @@ module Sentry
5
5
  class Envelope
6
6
  class Item
7
7
  STACKTRACE_FRAME_LIMIT_ON_OVERSIZED_PAYLOAD = 500
8
- MAX_SERIALIZED_PAYLOAD_SIZE = 1024 * 200
8
+ MAX_SERIALIZED_PAYLOAD_SIZE = 1024 * 1000
9
9
 
10
10
  attr_accessor :headers, :payload
11
11
 
data/lib/sentry/hub.rb CHANGED
@@ -156,6 +156,30 @@ module Sentry
156
156
  capture_event(event, **options, &block)
157
157
  end
158
158
 
159
+ def capture_check_in(slug, status, **options, &block)
160
+ check_argument_type!(slug, ::String)
161
+ check_argument_includes!(status, Sentry::CheckInEvent::VALID_STATUSES)
162
+
163
+ return unless current_client
164
+
165
+ options[:hint] ||= {}
166
+ options[:hint][:slug] = slug
167
+
168
+ event = current_client.event_from_check_in(
169
+ slug,
170
+ status,
171
+ options[:hint],
172
+ duration: options.delete(:duration),
173
+ monitor_config: options.delete(:monitor_config),
174
+ check_in_id: options.delete(:check_in_id)
175
+ )
176
+
177
+ return unless event
178
+
179
+ capture_event(event, **options, &block)
180
+ event.check_in_id
181
+ end
182
+
159
183
  def capture_event(event, **options, &block)
160
184
  check_argument_type!(event, Sentry::Event)
161
185
 
@@ -178,7 +202,7 @@ module Sentry
178
202
  configuration.log_debug(event.to_json_compatible)
179
203
  end
180
204
 
181
- @last_event_id = event&.event_id unless event.is_a?(Sentry::TransactionEvent)
205
+ @last_event_id = event&.event_id if event.is_a?(Sentry::ErrorEvent)
182
206
  event
183
207
  end
184
208
 
@@ -22,5 +22,11 @@ module Sentry
22
22
  options[:hint][:integration] = integration_name
23
23
  Sentry.capture_message(message, **options, &block)
24
24
  end
25
+
26
+ def capture_check_in(slug, status, **options, &block)
27
+ options[:hint] ||= {}
28
+ options[:hint][:integration] = integration_name
29
+ Sentry.capture_check_in(slug, status, **options, &block)
30
+ end
25
31
  end
26
32
  end
@@ -23,7 +23,7 @@ module Sentry
23
23
  exception.message || ""
24
24
  end
25
25
 
26
- @value = exception_message.byteslice(0..Event::MAX_MESSAGE_SIZE_IN_BYTES)
26
+ @value = Utils::EncodingHelper.encode_to_utf_8(exception_message.byteslice(0..Event::MAX_MESSAGE_SIZE_IN_BYTES))
27
27
 
28
28
  @module = exception.class.to_s.split('::')[0...-1].join('::')
29
29
  @thread_id = Thread.current.object_id
@@ -51,7 +51,7 @@ module Sentry
51
51
  v = v.byteslice(0..MAX_LOCAL_BYTES - 1) + OMISSION_MARK
52
52
  end
53
53
 
54
- v
54
+ Utils::EncodingHelper.encode_to_utf_8(v)
55
55
  rescue StandardError
56
56
  PROBLEMATIC_LOCAL_VALUE_REPLACEMENT
57
57
  end
@@ -99,4 +99,4 @@ module Sentry
99
99
  end
100
100
  end
101
101
 
102
- Sentry.register_patch(Sentry::Net::HTTP, Net::HTTP)
102
+ Sentry.register_patch(:http, Sentry::Net::HTTP, Net::HTTP)
@@ -9,6 +9,7 @@ module Sentry
9
9
  # 101 Hz in microseconds
10
10
  DEFAULT_INTERVAL = 1e6 / 101
11
11
  MICRO_TO_NANO_SECONDS = 1e3
12
+ MIN_SAMPLES_REQUIRED = 2
12
13
 
13
14
  attr_reader :sampled, :started, :event_id
14
15
 
@@ -73,14 +74,19 @@ module Sentry
73
74
  end
74
75
 
75
76
  def to_hash
76
- return {} unless @sampled
77
+ unless @sampled
78
+ record_lost_event(:sample_rate)
79
+ return {}
80
+ end
81
+
77
82
  return {} unless @started
78
83
 
79
84
  results = StackProf.results
80
- return {} unless results
81
- return {} if results.empty?
82
- return {} if results[:samples] == 0
83
- return {} unless results[:raw]
85
+
86
+ if !results || results.empty? || results[:samples] == 0 || !results[:raw]
87
+ record_lost_event(:insufficient_data)
88
+ return {}
89
+ end
84
90
 
85
91
  frame_map = {}
86
92
 
@@ -103,7 +109,7 @@ module Sentry
103
109
  }
104
110
 
105
111
  frame_hash[:module] = mod if mod
106
- frame_hash[:lineno] = frame_data[:line] if frame_data[:line]
112
+ frame_hash[:lineno] = frame_data[:line] if frame_data[:line] && frame_data[:line] >= 0
107
113
 
108
114
  frame_hash
109
115
  end
@@ -157,8 +163,9 @@ module Sentry
157
163
 
158
164
  log('Some samples thrown away') if samples.size != results[:samples]
159
165
 
160
- if samples.size <= 2
166
+ if samples.size <= MIN_SAMPLES_REQUIRED
161
167
  log('Not enough samples, discarding profiler')
168
+ record_lost_event(:insufficient_data)
162
169
  return {}
163
170
  end
164
171
 
@@ -218,5 +225,9 @@ module Sentry
218
225
 
219
226
  [function, mod]
220
227
  end
228
+
229
+ def record_lost_event(reason)
230
+ Sentry.get_current_client&.transport&.record_lost_event(reason, 'profile')
231
+ end
221
232
  end
222
233
  end
data/lib/sentry/puma.rb CHANGED
@@ -1,10 +1,19 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ return unless defined?(Puma::Server)
4
+
3
5
  module Sentry
4
6
  module Puma
5
7
  module Server
8
+ PUMA_4_AND_PRIOR = Gem::Version.new(::Puma::Const::PUMA_VERSION) < Gem::Version.new("5.0.0")
9
+
6
10
  def lowlevel_error(e, env, status=500)
7
- result = super
11
+ result =
12
+ if PUMA_4_AND_PRIOR
13
+ super(e, env)
14
+ else
15
+ super
16
+ end
8
17
 
9
18
  begin
10
19
  Sentry.capture_exception(e) do |scope|
@@ -20,6 +29,4 @@ module Sentry
20
29
  end
21
30
  end
22
31
 
23
- if defined?(Puma::Server)
24
- Sentry.register_patch(Sentry::Puma::Server, Puma::Server)
25
- end
32
+ Sentry.register_patch(:puma, Sentry::Puma::Server, Puma::Server)
data/lib/sentry/redis.rb CHANGED
@@ -99,8 +99,10 @@ end
99
99
 
100
100
  if defined?(::Redis::Client)
101
101
  if Gem::Version.new(::Redis::VERSION) < Gem::Version.new("5.0")
102
- Sentry.register_patch(Sentry::Redis::OldClientPatch, ::Redis::Client)
102
+ Sentry.register_patch(:redis, Sentry::Redis::OldClientPatch, ::Redis::Client)
103
103
  elsif defined?(RedisClient)
104
- RedisClient.register(Sentry::Redis::GlobalRedisInstrumentation)
104
+ Sentry.register_patch(:redis) do
105
+ RedisClient.register(Sentry::Redis::GlobalRedisInstrumentation)
106
+ end
105
107
  end
106
108
  end
data/lib/sentry/span.rb CHANGED
@@ -11,7 +11,7 @@ module Sentry
11
11
  URL = "url"
12
12
  HTTP_STATUS_CODE = "http.response.status_code"
13
13
  HTTP_QUERY = "http.query"
14
- HTTP_METHOD = "http.method"
14
+ HTTP_METHOD = "http.request.method"
15
15
 
16
16
  # An identifier for the database management system (DBMS) product being used.
17
17
  # Example: postgresql
@@ -14,24 +14,28 @@ module Sentry
14
14
  # @return [void]
15
15
  def setup_sentry_test(&block)
16
16
  raise "please make sure the SDK is initialized for testing" unless Sentry.initialized?
17
- copied_config = Sentry.configuration.dup
17
+ dummy_config = Sentry.configuration.dup
18
18
  # configure dummy DSN, so the events will not be sent to the actual service
19
- copied_config.dsn = DUMMY_DSN
19
+ dummy_config.dsn = DUMMY_DSN
20
20
  # set transport to DummyTransport, so we can easily intercept the captured events
21
- copied_config.transport.transport_class = Sentry::DummyTransport
21
+ dummy_config.transport.transport_class = Sentry::DummyTransport
22
22
  # make sure SDK allows sending under the current environment
23
- copied_config.enabled_environments << copied_config.environment unless copied_config.enabled_environments.include?(copied_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
- copied_config.background_worker_threads = 0
25
+ dummy_config.background_worker_threads = 0
26
26
 
27
27
  # user can overwrite some of the configs, with a few exceptions like:
28
28
  # - include_local_variables
29
29
  # - auto_session_tracking
30
- block&.call(copied_config)
30
+ block&.call(dummy_config)
31
31
 
32
- test_client = Sentry::Client.new(copied_config)
32
+ # the base layer's client should already use the dummy config so nothing will be sent by accident
33
+ base_client = Sentry::Client.new(dummy_config)
34
+ Sentry.get_current_hub.bind_client(base_client)
35
+ # create a new layer so mutations made to the testing scope or configuration could be simply popped later
36
+ Sentry.get_current_hub.push_scope
37
+ test_client = Sentry::Client.new(dummy_config.dup)
33
38
  Sentry.get_current_hub.bind_client(test_client)
34
- Sentry.get_current_scope.clear
35
39
  end
36
40
 
37
41
  # Clears all stored events and envelopes.
@@ -40,9 +44,12 @@ module Sentry
40
44
  def teardown_sentry_test
41
45
  return unless Sentry.initialized?
42
46
 
43
- sentry_transport.events = []
44
- sentry_transport.envelopes = []
45
- Sentry.get_current_scope.clear
47
+ # pop testing layer created by `setup_sentry_test`
48
+ # but keep the base layer to avoid nil-pointer errors
49
+ # TODO: find a way to notify users if they somehow popped the test layer before calling this method
50
+ if Sentry.get_current_hub.instance_variable_get(:@stack).size > 1
51
+ Sentry.get_current_hub.pop_scope
52
+ end
46
53
  end
47
54
 
48
55
  # @return [Transport]
@@ -75,4 +82,3 @@ module Sentry
75
82
  end
76
83
  end
77
84
  end
78
-
@@ -18,7 +18,8 @@ module Sentry
18
18
  :network_error,
19
19
  :sample_rate,
20
20
  :before_send,
21
- :event_processor
21
+ :event_processor,
22
+ :insufficient_data
22
23
  ]
23
24
 
24
25
  include LoggingHelper
@@ -143,7 +144,7 @@ module Sentry
143
144
  sent_at: Sentry.utc_now.iso8601
144
145
  }
145
146
 
146
- if event.is_a?(TransactionEvent) && event.dynamic_sampling_context
147
+ if event.is_a?(Event) && event.dynamic_sampling_context
147
148
  envelope_headers[:trace] = event.dynamic_sampling_context
148
149
  end
149
150
 
@@ -185,7 +186,7 @@ module Sentry
185
186
  reason, type = key
186
187
 
187
188
  # 'event' has to be mapped to 'error'
188
- category = type == 'transaction' ? 'transaction' : 'error'
189
+ category = type == 'event' ? 'error' : type
189
190
 
190
191
  { reason: reason, category: category, quantity: val }
191
192
  end
@@ -9,5 +9,11 @@ module Sentry
9
9
  raise ArgumentError, "expect the argument to be a #{expected_types.join(' or ')}, got #{argument.class} (#{argument.inspect})"
10
10
  end
11
11
  end
12
+
13
+ def check_argument_includes!(argument, values)
14
+ unless values.include?(argument)
15
+ raise ArgumentError, "expect the argument to be one of #{values.map(&:inspect).join(' or ')}, got #{argument.inspect}"
16
+ end
17
+ end
12
18
  end
13
19
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Sentry
4
- VERSION = "5.11.0"
4
+ VERSION = "5.13.0"
5
5
  end
data/lib/sentry-ruby.rb CHANGED
@@ -15,11 +15,13 @@ require "sentry/logger"
15
15
  require "sentry/event"
16
16
  require "sentry/error_event"
17
17
  require "sentry/transaction_event"
18
+ require "sentry/check_in_event"
18
19
  require "sentry/span"
19
20
  require "sentry/transaction"
20
21
  require "sentry/hub"
21
22
  require "sentry/background_worker"
22
23
  require "sentry/session_flusher"
24
+ require "sentry/cron/monitor_check_ins"
23
25
 
24
26
  [
25
27
  "sentry/rake",
@@ -73,15 +75,15 @@ module Sentry
73
75
  ##### Patch Registration #####
74
76
 
75
77
  # @!visibility private
76
- def register_patch(patch = nil, target = nil, &block)
78
+ def register_patch(key, patch = nil, target = nil, &block)
77
79
  if patch && block
78
80
  raise ArgumentError.new("Please provide either a patch and its target OR a block, but not both")
79
81
  end
80
82
 
81
83
  if block
82
- registered_patches << block
84
+ registered_patches[key] = block
83
85
  else
84
- registered_patches << proc do
86
+ registered_patches[key] = proc do
85
87
  target.send(:prepend, patch) unless target.ancestors.include?(patch)
86
88
  end
87
89
  end
@@ -89,14 +91,14 @@ module Sentry
89
91
 
90
92
  # @!visibility private
91
93
  def apply_patches(config)
92
- registered_patches.each do |patch|
93
- patch.call(config)
94
+ registered_patches.each do |key, patch|
95
+ patch.call(config) if config.enabled_patches.include?(key)
94
96
  end
95
97
  end
96
98
 
97
99
  # @!visibility private
98
100
  def registered_patches
99
- @registered_patches ||= []
101
+ @registered_patches ||= {}
100
102
  end
101
103
 
102
104
  ##### Integrations #####
@@ -430,6 +432,24 @@ module Sentry
430
432
  get_current_hub.capture_event(event)
431
433
  end
432
434
 
435
+ # Captures a check-in and sends it to Sentry via the currently active hub.
436
+ #
437
+ # @param slug [String] identifier of this monitor
438
+ # @param status [Symbol] status of this check-in, one of {CheckInEvent::VALID_STATUSES}
439
+ #
440
+ # @param [Hash] options extra check-in options
441
+ # @option options [String] check_in_id for updating the status of an existing monitor
442
+ # @option options [Integer] duration seconds elapsed since this monitor started
443
+ # @option options [Cron::MonitorConfig] monitor_config configuration for this monitor
444
+ #
445
+ # @yieldparam scope [Scope]
446
+ #
447
+ # @return [String, nil] The {CheckInEvent#check_in_id} to use for later updates on the same slug
448
+ def capture_check_in(slug, status, **options, &block)
449
+ return unless initialized?
450
+ get_current_hub.capture_check_in(slug, status, **options, &block)
451
+ end
452
+
433
453
  # Takes or initializes a new Sentry::Transaction and makes a sampling decision for it.
434
454
  #
435
455
  # @return [Transaction, nil]
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: sentry-ruby
3
3
  version: !ruby/object:Gem::Version
4
- version: 5.11.0
4
+ version: 5.13.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Sentry Team
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2023-09-06 00:00:00.000000000 Z
11
+ date: 2023-11-09 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: concurrent-ruby
@@ -56,10 +56,14 @@ files:
56
56
  - lib/sentry/breadcrumb.rb
57
57
  - lib/sentry/breadcrumb/sentry_logger.rb
58
58
  - lib/sentry/breadcrumb_buffer.rb
59
+ - lib/sentry/check_in_event.rb
59
60
  - lib/sentry/client.rb
60
61
  - lib/sentry/configuration.rb
61
62
  - lib/sentry/core_ext/object/deep_dup.rb
62
63
  - lib/sentry/core_ext/object/duplicable.rb
64
+ - lib/sentry/cron/monitor_check_ins.rb
65
+ - lib/sentry/cron/monitor_config.rb
66
+ - lib/sentry/cron/monitor_schedule.rb
63
67
  - lib/sentry/dsn.rb
64
68
  - lib/sentry/envelope.rb
65
69
  - lib/sentry/error_event.rb