sentry-ruby 5.10.0 → 5.12.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 2373a9b8990fd62763c4d1b26001f953f95efdd8b51f803e5d59bdb1ba984e3e
4
- data.tar.gz: d09371eb4bf3aaff6e7735f74cd5887b2e99f636a60f1dca3cf653bbb8ba9bb4
3
+ metadata.gz: 857c6d99b1c5d9378a2522ffa1e466f91869500ae425f9d1add4aae399f1852c
4
+ data.tar.gz: 726793c0ae09ce3b137cdc7f48f6745c55176a83c8f651b8b493ee5d1290b522
5
5
  SHA512:
6
- metadata.gz: 4799b727ddaab0622be00e73b58a2e3f723779e1b46c7d5007d5711a8897022190ff9f87cc90df7411f32fd6b59d4f4637bcf26cc7eba8484cb04429871e9242
7
- data.tar.gz: d97d9272ee815447ebdcbb72adb4e2255584475547e7878ed6c7af4f312d170bd37f3029acaaa6866eb04f2d89c1581c4854b849c2d57a1a51eb4b67e4ebeb0e
6
+ metadata.gz: 13ccb78050835c829d738550bc9838347c71abc2139089c29ab466796059259740207d21504bee7cc6f4ca07dbcc982e12be05ebc0b955a4274a4a30bd0470ee
7
+ data.tar.gz: d6b4edd6c562b787b9fe652b6ba6ddb3bf16bca937bacbd22fcc44be746643a77e3595460263f72e3e76e73a7b92ff09577d7c665cfac2520886c3a62a62a2ab
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
 
@@ -0,0 +1,60 @@
1
+ # frozen_string_literal
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]
@@ -148,6 +179,8 @@ module Sentry
148
179
  raise
149
180
  end
150
181
 
182
+ # @deprecated use Sentry.get_traceparent instead.
183
+ #
151
184
  # Generates a Sentry trace for distribted tracing from the given Span.
152
185
  # Returns `nil` if `config.propagate_traces` is `false`.
153
186
  # @param span [Span] the span to generate trace from.
@@ -160,7 +193,9 @@ module Sentry
160
193
  trace
161
194
  end
162
195
 
163
- # Generates a W3C Baggage header for distribted tracing from the given Span.
196
+ # @deprecated Use Sentry.get_baggage instead.
197
+ #
198
+ # Generates a W3C Baggage header for distributed tracing from the given Span.
164
199
  # Returns `nil` if `config.propagate_traces` is `false`.
165
200
  # @param span [Span] the span to generate trace from.
166
201
  # @return [String, nil]
@@ -243,6 +243,11 @@ module Sentry
243
243
  # @return [Boolean]
244
244
  attr_accessor :auto_session_tracking
245
245
 
246
+ # Allowlist of outgoing request targets to which sentry-trace and baggage headers are attached.
247
+ # Default is all (/.*/)
248
+ # @return [Array<String, Regexp>]
249
+ attr_accessor :trace_propagation_targets
250
+
246
251
  # The instrumenter to use, :sentry or :otel
247
252
  # @return [Symbol]
248
253
  attr_reader :instrumenter
@@ -290,6 +295,8 @@ module Sentry
290
295
 
291
296
  INSTRUMENTERS = [:sentry, :otel]
292
297
 
298
+ PROPAGATION_TARGETS_MATCH_ALL = /.*/.freeze
299
+
293
300
  class << self
294
301
  # Post initialization callbacks are called at the end of initialization process
295
302
  # allowing extending the configuration of sentry-ruby by multiple extensions
@@ -332,6 +339,7 @@ module Sentry
332
339
  self.dsn = ENV['SENTRY_DSN']
333
340
  self.server_name = server_name_from_env
334
341
  self.instrumenter = :sentry
342
+ self.trace_propagation_targets = [PROPAGATION_TARGETS_MATCH_ALL]
335
343
 
336
344
  self.before_send = nil
337
345
  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
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
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/event.rb CHANGED
@@ -37,6 +37,11 @@ module Sentry
37
37
  # @return [RequestInterface]
38
38
  attr_reader :request
39
39
 
40
+ # Dynamic Sampling Context (DSC) that gets attached
41
+ # as the trace envelope header in the transport.
42
+ # @return [Hash, nil]
43
+ attr_accessor :dynamic_sampling_context
44
+
40
45
  # @param configuration [Configuration]
41
46
  # @param integration_meta [Hash, nil]
42
47
  # @param message [String, nil]
@@ -54,6 +59,7 @@ module Sentry
54
59
  @tags = {}
55
60
 
56
61
  @fingerprint = []
62
+ @dynamic_sampling_context = nil
57
63
 
58
64
  # configuration data that's directly used by events
59
65
  @server_name = configuration.server_name
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
 
@@ -229,6 +253,50 @@ module Sentry
229
253
  end_session
230
254
  end
231
255
 
256
+ def get_traceparent
257
+ return nil unless current_scope
258
+
259
+ current_scope.get_span&.to_sentry_trace ||
260
+ current_scope.propagation_context.get_traceparent
261
+ end
262
+
263
+ def get_baggage
264
+ return nil unless current_scope
265
+
266
+ current_scope.get_span&.to_baggage ||
267
+ current_scope.propagation_context.get_baggage&.serialize
268
+ end
269
+
270
+ def get_trace_propagation_headers
271
+ headers = {}
272
+
273
+ traceparent = get_traceparent
274
+ headers[SENTRY_TRACE_HEADER_NAME] = traceparent if traceparent
275
+
276
+ baggage = get_baggage
277
+ headers[BAGGAGE_HEADER_NAME] = baggage if baggage && !baggage.empty?
278
+
279
+ headers
280
+ end
281
+
282
+ def continue_trace(env, **options)
283
+ configure_scope { |s| s.generate_propagation_context(env) }
284
+
285
+ return nil unless configuration.tracing_enabled?
286
+
287
+ propagation_context = current_scope.propagation_context
288
+ return nil unless propagation_context.incoming_trace
289
+
290
+ Transaction.new(
291
+ hub: self,
292
+ trace_id: propagation_context.trace_id,
293
+ parent_span_id: propagation_context.parent_span_id,
294
+ parent_sampled: propagation_context.parent_sampled,
295
+ baggage: propagation_context.baggage,
296
+ **options
297
+ )
298
+ end
299
+
232
300
  private
233
301
 
234
302
  def current_layer
@@ -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
@@ -11,7 +11,8 @@ module Sentry
11
11
  OMISSION_MARK = "...".freeze
12
12
  MAX_LOCAL_BYTES = 1024
13
13
 
14
- attr_reader :type, :value, :module, :thread_id, :stacktrace
14
+ attr_reader :type, :module, :thread_id, :stacktrace
15
+ attr_accessor :value
15
16
 
16
17
  def initialize(exception:, stacktrace: nil)
17
18
  @type = exception.class.to_s
@@ -22,7 +23,7 @@ module Sentry
22
23
  exception.message || ""
23
24
  end
24
25
 
25
- @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))
26
27
 
27
28
  @module = exception.class.to_s.split('::')[0...-1].join('::')
28
29
  @thread_id = Thread.current.object_id
@@ -50,7 +51,7 @@ module Sentry
50
51
  v = v.byteslice(0..MAX_LOCAL_BYTES - 1) + OMISSION_MARK
51
52
  end
52
53
 
53
- v
54
+ Utils::EncodingHelper.encode_to_utf_8(v)
54
55
  rescue StandardError
55
56
  PROBLEMATIC_LOCAL_VALUE_REPLACEMENT
56
57
  end
@@ -30,18 +30,21 @@ module Sentry
30
30
  return super if from_sentry_sdk?
31
31
 
32
32
  Sentry.with_child_span(op: OP_NAME, start_timestamp: Sentry.utc_now.to_f) do |sentry_span|
33
- set_sentry_trace_header(req, sentry_span)
33
+ request_info = extract_request_info(req)
34
+
35
+ if propagate_trace?(request_info[:url], Sentry.configuration)
36
+ set_propagation_headers(req)
37
+ end
34
38
 
35
39
  super.tap do |res|
36
- record_sentry_breadcrumb(req, res)
40
+ record_sentry_breadcrumb(request_info, res)
37
41
 
38
42
  if sentry_span
39
- request_info = extract_request_info(req)
40
43
  sentry_span.set_description("#{request_info[:method]} #{request_info[:url]}")
41
- sentry_span.set_data('url', request_info[:url])
42
- sentry_span.set_data('http.method', request_info[:method])
43
- sentry_span.set_data('http.query', request_info[:query]) if request_info[:query]
44
- sentry_span.set_data('status', res.code.to_i)
44
+ sentry_span.set_data(Span::DataConventions::URL, request_info[:url])
45
+ sentry_span.set_data(Span::DataConventions::HTTP_METHOD, request_info[:method])
46
+ sentry_span.set_data(Span::DataConventions::HTTP_QUERY, request_info[:query]) if request_info[:query]
47
+ sentry_span.set_data(Span::DataConventions::HTTP_STATUS_CODE, res.code.to_i)
45
48
  end
46
49
  end
47
50
  end
@@ -49,23 +52,13 @@ module Sentry
49
52
 
50
53
  private
51
54
 
52
- def set_sentry_trace_header(req, sentry_span)
53
- return unless sentry_span
54
-
55
- client = Sentry.get_current_client
56
-
57
- trace = client.generate_sentry_trace(sentry_span)
58
- req[SENTRY_TRACE_HEADER_NAME] = trace if trace
59
-
60
- baggage = client.generate_baggage(sentry_span)
61
- req[BAGGAGE_HEADER_NAME] = baggage if baggage && !baggage.empty?
55
+ def set_propagation_headers(req)
56
+ Sentry.get_trace_propagation_headers&.each { |k, v| req[k] = v }
62
57
  end
63
58
 
64
- def record_sentry_breadcrumb(req, res)
59
+ def record_sentry_breadcrumb(request_info, res)
65
60
  return unless Sentry.initialized? && Sentry.configuration.breadcrumbs_logger.include?(:http_logger)
66
61
 
67
- request_info = extract_request_info(req)
68
-
69
62
  crumb = Sentry::Breadcrumb.new(
70
63
  level: :info,
71
64
  category: BREADCRUMB_CATEGORY,
@@ -96,6 +89,12 @@ module Sentry
96
89
 
97
90
  result
98
91
  end
92
+
93
+ def propagate_trace?(url, configuration)
94
+ url &&
95
+ configuration.propagate_traces &&
96
+ configuration.trace_propagation_targets.any? { |target| url.match?(target) }
97
+ end
99
98
  end
100
99
  end
101
100
  end
@@ -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
 
@@ -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
@@ -0,0 +1,134 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "securerandom"
4
+ require "sentry/baggage"
5
+
6
+ module Sentry
7
+ class PropagationContext
8
+ SENTRY_TRACE_REGEXP = Regexp.new(
9
+ "^[ \t]*" + # whitespace
10
+ "([0-9a-f]{32})?" + # trace_id
11
+ "-?([0-9a-f]{16})?" + # span_id
12
+ "-?([01])?" + # sampled
13
+ "[ \t]*$" # whitespace
14
+ )
15
+
16
+ # An uuid that can be used to identify a trace.
17
+ # @return [String]
18
+ attr_reader :trace_id
19
+ # An uuid that can be used to identify the span.
20
+ # @return [String]
21
+ attr_reader :span_id
22
+ # Span parent's span_id.
23
+ # @return [String, nil]
24
+ attr_reader :parent_span_id
25
+ # The sampling decision of the parent transaction.
26
+ # @return [Boolean, nil]
27
+ attr_reader :parent_sampled
28
+ # Is there an incoming trace or not?
29
+ # @return [Boolean]
30
+ attr_reader :incoming_trace
31
+ # This is only for accessing the current baggage variable.
32
+ # Please use the #get_baggage method for interfacing outside this class.
33
+ # @return [Baggage, nil]
34
+ attr_reader :baggage
35
+
36
+ def initialize(scope, env = nil)
37
+ @scope = scope
38
+ @parent_span_id = nil
39
+ @parent_sampled = nil
40
+ @baggage = nil
41
+ @incoming_trace = false
42
+
43
+ if env
44
+ sentry_trace_header = env["HTTP_SENTRY_TRACE"] || env[SENTRY_TRACE_HEADER_NAME]
45
+ baggage_header = env["HTTP_BAGGAGE"] || env[BAGGAGE_HEADER_NAME]
46
+
47
+ if sentry_trace_header
48
+ sentry_trace_data = self.class.extract_sentry_trace(sentry_trace_header)
49
+
50
+ if sentry_trace_data
51
+ @trace_id, @parent_span_id, @parent_sampled = sentry_trace_data
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
61
+
62
+ @baggage.freeze!
63
+ @incoming_trace = true
64
+ end
65
+ end
66
+ end
67
+
68
+ @trace_id ||= SecureRandom.uuid.delete("-")
69
+ @span_id = SecureRandom.uuid.delete("-").slice(0, 16)
70
+ end
71
+
72
+ # Extract the trace_id, parent_span_id and parent_sampled values from a sentry-trace header.
73
+ #
74
+ # @param sentry_trace [String] the sentry-trace header value from the previous transaction.
75
+ # @return [Array, nil]
76
+ def self.extract_sentry_trace(sentry_trace)
77
+ match = SENTRY_TRACE_REGEXP.match(sentry_trace)
78
+ return nil if match.nil?
79
+
80
+ trace_id, parent_span_id, sampled_flag = match[1..3]
81
+ parent_sampled = sampled_flag.nil? ? nil : sampled_flag != "0"
82
+
83
+ [trace_id, parent_span_id, parent_sampled]
84
+ end
85
+
86
+ # Returns the trace context that can be used to embed in an Event.
87
+ # @return [Hash]
88
+ def get_trace_context
89
+ {
90
+ trace_id: trace_id,
91
+ span_id: span_id,
92
+ parent_span_id: parent_span_id
93
+ }
94
+ end
95
+
96
+ # Returns the sentry-trace header from the propagation context.
97
+ # @return [String]
98
+ def get_traceparent
99
+ "#{trace_id}-#{span_id}"
100
+ end
101
+
102
+ # Returns the Baggage from the propagation context or populates as head SDK if empty.
103
+ # @return [Baggage, nil]
104
+ def get_baggage
105
+ populate_head_baggage if @baggage.nil? || @baggage.mutable
106
+ @baggage
107
+ end
108
+
109
+ # Returns the Dynamic Sampling Context from the baggage.
110
+ # @return [String, nil]
111
+ def get_dynamic_sampling_context
112
+ get_baggage&.dynamic_sampling_context
113
+ end
114
+
115
+ private
116
+
117
+ def populate_head_baggage
118
+ return unless Sentry.initialized?
119
+
120
+ configuration = Sentry.configuration
121
+
122
+ items = {
123
+ "trace_id" => trace_id,
124
+ "environment" => configuration.environment,
125
+ "release" => configuration.release,
126
+ "public_key" => configuration.dsn&.public_key,
127
+ "user_segment" => @scope.user && @scope.user["segment"]
128
+ }
129
+
130
+ items.compact!
131
+ @baggage = Baggage.new(items, mutable: false)
132
+ end
133
+ end
134
+ end
@@ -62,11 +62,8 @@ module Sentry
62
62
  end
63
63
 
64
64
  def start_transaction(env, scope)
65
- sentry_trace = env["HTTP_SENTRY_TRACE"]
66
- baggage = env["HTTP_BAGGAGE"]
67
-
68
65
  options = { name: scope.transaction_name, source: scope.transaction_source, op: transaction_op }
69
- transaction = Sentry::Transaction.from_sentry_trace(sentry_trace, baggage: baggage, **options) if sentry_trace
66
+ transaction = Sentry.continue_trace(env, **options)
70
67
  Sentry.start_transaction(transaction: transaction, custom_sampling_context: { env: env }, **options)
71
68
  end
72
69
 
data/lib/sentry/redis.rb CHANGED
@@ -19,7 +19,10 @@ module Sentry
19
19
 
20
20
  if span
21
21
  span.set_description(commands_description)
22
- span.set_data(:server, server_description)
22
+ span.set_data(Span::DataConventions::DB_SYSTEM, "redis")
23
+ span.set_data(Span::DataConventions::DB_NAME, db)
24
+ span.set_data(Span::DataConventions::SERVER_ADDRESS, host)
25
+ span.set_data(Span::DataConventions::SERVER_PORT, port)
23
26
  end
24
27
  end
25
28
  end
data/lib/sentry/scope.rb CHANGED
@@ -1,6 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "sentry/breadcrumb_buffer"
4
+ require "sentry/propagation_context"
4
5
  require "etc"
5
6
 
6
7
  module Sentry
@@ -20,7 +21,8 @@ module Sentry
20
21
  :event_processors,
21
22
  :rack_env,
22
23
  :span,
23
- :session
24
+ :session,
25
+ :propagation_context
24
26
  ]
25
27
 
26
28
  attr_reader(*ATTRIBUTES)
@@ -50,7 +52,10 @@ module Sentry
50
52
  event.transaction_info = { source: transaction_source } if transaction_source
51
53
 
52
54
  if span
53
- event.contexts[:trace] = span.get_trace_context
55
+ event.contexts[:trace] ||= span.get_trace_context
56
+ else
57
+ event.contexts[:trace] ||= propagation_context.get_trace_context
58
+ event.dynamic_sampling_context ||= propagation_context.get_dynamic_sampling_context
54
59
  end
55
60
 
56
61
  event.fingerprint = fingerprint
@@ -95,6 +100,7 @@ module Sentry
95
100
  copy.fingerprint = fingerprint.deep_dup
96
101
  copy.span = span.deep_dup
97
102
  copy.session = session.deep_dup
103
+ copy.propagation_context = propagation_context.deep_dup
98
104
  copy
99
105
  end
100
106
 
@@ -111,6 +117,7 @@ module Sentry
111
117
  self.transaction_sources = scope.transaction_sources
112
118
  self.fingerprint = scope.fingerprint
113
119
  self.span = scope.span
120
+ self.propagation_context = scope.propagation_context
114
121
  end
115
122
 
116
123
  # Updates the scope's data from the given options.
@@ -272,6 +279,13 @@ module Sentry
272
279
  @event_processors << block
273
280
  end
274
281
 
282
+ # Generate a new propagation context either from the incoming env headers or from scratch.
283
+ # @param env [Hash, nil]
284
+ # @return [void]
285
+ def generate_propagation_context(env = nil)
286
+ @propagation_context = PropagationContext.new(self, env)
287
+ end
288
+
275
289
  protected
276
290
 
277
291
  # for duplicating scopes internally
@@ -292,6 +306,7 @@ module Sentry
292
306
  @rack_env = {}
293
307
  @span = nil
294
308
  @session = nil
309
+ generate_propagation_context
295
310
  set_new_breadcrumb_buffer
296
311
  end
297
312
 
data/lib/sentry/span.rb CHANGED
@@ -4,6 +4,43 @@ require "securerandom"
4
4
 
5
5
  module Sentry
6
6
  class Span
7
+
8
+ # We will try to be consistent with OpenTelemetry on this front going forward.
9
+ # https://develop.sentry.dev/sdk/performance/span-data-conventions/
10
+ module DataConventions
11
+ URL = "url"
12
+ HTTP_STATUS_CODE = "http.response.status_code"
13
+ HTTP_QUERY = "http.query"
14
+ HTTP_METHOD = "http.request.method"
15
+
16
+ # An identifier for the database management system (DBMS) product being used.
17
+ # Example: postgresql
18
+ DB_SYSTEM = "db.system"
19
+
20
+ # The name of the database being accessed.
21
+ # For commands that switch the database, this should be set to the target database
22
+ # (even if the command fails).
23
+ # Example: myDatabase
24
+ DB_NAME = "db.name"
25
+
26
+ # Name of the database host.
27
+ # Example: example.com
28
+ SERVER_ADDRESS = "server.address"
29
+
30
+ # Logical server port number
31
+ # Example: 80; 8080; 443
32
+ SERVER_PORT = "server.port"
33
+
34
+ # Physical server IP address or Unix socket address.
35
+ # Example: 10.5.3.2
36
+ SERVER_SOCKET_ADDRESS = "server.socket.address"
37
+
38
+ # Physical server port.
39
+ # Recommended: If different than server.port.
40
+ # Example: 16456
41
+ SERVER_SOCKET_PORT = "server.socket.port"
42
+ end
43
+
7
44
  STATUS_MAP = {
8
45
  400 => "invalid_argument",
9
46
  401 => "unauthenticated",
@@ -75,7 +112,7 @@ module Sentry
75
112
  timestamp: nil
76
113
  )
77
114
  @trace_id = trace_id || SecureRandom.uuid.delete("-")
78
- @span_id = span_id || SecureRandom.hex(8)
115
+ @span_id = span_id || SecureRandom.uuid.delete("-").slice(0, 16)
79
116
  @parent_span_id = parent_span_id
80
117
  @sampled = sampled
81
118
  @start_timestamp = start_timestamp || Sentry.utc_now.to_f
@@ -208,7 +245,7 @@ module Sentry
208
245
  # @param status_code [String] example: "500".
209
246
  def set_http_status(status_code)
210
247
  status_code = status_code.to_i
211
- set_data("status_code", status_code)
248
+ set_data(DataConventions::HTTP_STATUS_CODE, status_code)
212
249
 
213
250
  status =
214
251
  if status_code >= 200 && status_code < 299
@@ -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
-
@@ -2,16 +2,13 @@
2
2
 
3
3
  require "sentry/baggage"
4
4
  require "sentry/profiler"
5
+ require "sentry/propagation_context"
5
6
 
6
7
  module Sentry
7
8
  class Transaction < Span
8
- SENTRY_TRACE_REGEXP = Regexp.new(
9
- "^[ \t]*" + # whitespace
10
- "([0-9a-f]{32})?" + # trace_id
11
- "-?([0-9a-f]{16})?" + # span_id
12
- "-?([01])?" + # sampled
13
- "[ \t]*$" # whitespace
14
- )
9
+ # @deprecated Use Sentry::PropagationContext::SENTRY_TRACE_REGEXP instead.
10
+ SENTRY_TRACE_REGEXP = PropagationContext::SENTRY_TRACE_REGEXP
11
+
15
12
  UNLABELD_NAME = "<unlabeled transaction>".freeze
16
13
  MESSAGE_PREFIX = "[Tracing]"
17
14
 
@@ -92,6 +89,8 @@ module Sentry
92
89
  init_span_recorder
93
90
  end
94
91
 
92
+ # @deprecated use Sentry.continue_trace instead.
93
+ #
95
94
  # Initalizes a Transaction instance with a Sentry trace string from another transaction (usually from an external request).
96
95
  #
97
96
  # The original transaction will become the parent of the new Transaction instance. And they will share the same `trace_id`.
@@ -132,18 +131,10 @@ module Sentry
132
131
  )
133
132
  end
134
133
 
135
- # Extract the trace_id, parent_span_id and parent_sampled values from a sentry-trace header.
136
- #
137
- # @param sentry_trace [String] the sentry-trace header value from the previous transaction.
134
+ # @deprecated Use Sentry::PropagationContext.extract_sentry_trace instead.
138
135
  # @return [Array, nil]
139
136
  def self.extract_sentry_trace(sentry_trace)
140
- match = SENTRY_TRACE_REGEXP.match(sentry_trace)
141
- return nil if match.nil?
142
-
143
- trace_id, parent_span_id, sampled_flag = match[1..3]
144
- parent_sampled = sampled_flag.nil? ? nil : sampled_flag != "0"
145
-
146
- [trace_id, parent_span_id, parent_sampled]
137
+ PropagationContext.extract_sentry_trace(sentry_trace)
147
138
  end
148
139
 
149
140
  # @return [Hash]
@@ -323,6 +314,7 @@ module Sentry
323
314
  items = {
324
315
  "trace_id" => trace_id,
325
316
  "sample_rate" => effective_sample_rate&.to_s,
317
+ "sampled" => sampled&.to_s,
326
318
  "environment" => @environment,
327
319
  "release" => @release,
328
320
  "public_key" => @dsn&.public_key
@@ -8,9 +8,6 @@ module Sentry
8
8
  # @return [<Array[Span]>]
9
9
  attr_accessor :spans
10
10
 
11
- # @return [Hash, nil]
12
- attr_accessor :dynamic_sampling_context
13
-
14
11
  # @return [Hash]
15
12
  attr_accessor :measurements
16
13
 
@@ -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.10.0"
4
+ VERSION = "5.12.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",
@@ -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]
@@ -489,6 +509,42 @@ module Sentry
489
509
  Scope.add_global_event_processor(&block)
490
510
  end
491
511
 
512
+ # Returns the traceparent (sentry-trace) header for distributed tracing.
513
+ # Can be either from the currently active span or the propagation context.
514
+ #
515
+ # @return [String, nil]
516
+ def get_traceparent
517
+ return nil unless initialized?
518
+ get_current_hub.get_traceparent
519
+ end
520
+
521
+ # Returns the baggage header for distributed tracing.
522
+ # Can be either from the currently active span or the propagation context.
523
+ #
524
+ # @return [String, nil]
525
+ def get_baggage
526
+ return nil unless initialized?
527
+ get_current_hub.get_baggage
528
+ end
529
+
530
+ # Returns the a Hash containing sentry-trace and baggage.
531
+ # Can be either from the currently active span or the propagation context.
532
+ #
533
+ # @return [Hash, nil]
534
+ def get_trace_propagation_headers
535
+ return nil unless initialized?
536
+ get_current_hub.get_trace_propagation_headers
537
+ end
538
+
539
+ # Continue an incoming trace from a rack env like hash.
540
+ #
541
+ # @param env [Hash]
542
+ # @return [Transaction, nil]
543
+ def continue_trace(env, **options)
544
+ return nil unless initialized?
545
+ get_current_hub.continue_trace(env, **options)
546
+ end
547
+
492
548
  ##### Helpers #####
493
549
 
494
550
  # @!visibility private
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.10.0
4
+ version: 5.12.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-07-04 00:00:00.000000000 Z
11
+ date: 2023-10-10 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
@@ -78,6 +82,7 @@ files:
78
82
  - lib/sentry/logger.rb
79
83
  - lib/sentry/net/http.rb
80
84
  - lib/sentry/profiler.rb
85
+ - lib/sentry/propagation_context.rb
81
86
  - lib/sentry/puma.rb
82
87
  - lib/sentry/rack.rb
83
88
  - lib/sentry/rack/capture_exceptions.rb