sentry-ruby 5.10.0 → 5.12.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 +4 -4
- data/Gemfile +7 -1
- data/README.md +8 -8
- data/lib/sentry/check_in_event.rb +60 -0
- data/lib/sentry/client.rb +36 -1
- data/lib/sentry/configuration.rb +8 -0
- data/lib/sentry/cron/monitor_check_ins.rb +61 -0
- data/lib/sentry/cron/monitor_config.rb +53 -0
- data/lib/sentry/cron/monitor_schedule.rb +42 -0
- data/lib/sentry/envelope.rb +1 -1
- data/lib/sentry/event.rb +6 -0
- data/lib/sentry/hub.rb +69 -1
- data/lib/sentry/integrable.rb +6 -0
- data/lib/sentry/interfaces/single_exception.rb +4 -3
- data/lib/sentry/net/http.rb +19 -20
- data/lib/sentry/profiler.rb +17 -6
- data/lib/sentry/propagation_context.rb +134 -0
- data/lib/sentry/rack/capture_exceptions.rb +1 -4
- data/lib/sentry/redis.rb +4 -1
- data/lib/sentry/scope.rb +17 -2
- data/lib/sentry/span.rb +39 -2
- data/lib/sentry/test_helper.rb +18 -12
- data/lib/sentry/transaction.rb +9 -17
- data/lib/sentry/transaction_event.rb +0 -3
- data/lib/sentry/transport.rb +4 -3
- data/lib/sentry/utils/argument_checking_helper.rb +6 -0
- data/lib/sentry/version.rb +1 -1
- data/lib/sentry-ruby.rb +56 -0
- metadata +7 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 857c6d99b1c5d9378a2522ffa1e466f91869500ae425f9d1add4aae399f1852c
|
4
|
+
data.tar.gz: 726793c0ae09ce3b137cdc7f48f6745c55176a83c8f651b8b493ee5d1290b522
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
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
|
-
|
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
|
17
|
-
| ---
|
18
|
-
| [](https://rubygems.org/gems/sentry-ruby)
|
19
|
-
| [](https://rubygems.org/gems/sentry-rails)
|
20
|
-
| [](https://rubygems.org/gems/sentry-sidekiq)
|
21
|
-
| [](https://rubygems.org/gems/sentry-delayed_job)
|
22
|
-
| [](https://rubygems.org/gems/sentry-resque)
|
23
|
-
| [](https://rubygems.org/gems/sentry-opentelemetry) | [](https://rubygems.org/gems/sentry-ruby) | [](https://github.com/getsentry/sentry-ruby/actions/workflows/sentry_ruby_test.yml) | [](https://codecov.io/gh/getsentry/sentry-ruby/branch/master) | [](https://rubygems.org/gems/sentry-ruby/) |
|
19
|
+
| [](https://rubygems.org/gems/sentry-rails) | [](https://github.com/getsentry/sentry-ruby/actions/workflows/sentry_rails_test.yml) | [](https://codecov.io/gh/getsentry/sentry-ruby/branch/master) | [](https://rubygems.org/gems/sentry-rails/) |
|
20
|
+
| [](https://rubygems.org/gems/sentry-sidekiq) | [](https://github.com/getsentry/sentry-ruby/actions/workflows/sentry_sidekiq_test.yml) | [](https://codecov.io/gh/getsentry/sentry-ruby/branch/master) | [](https://rubygems.org/gems/sentry-sidekiq/) |
|
21
|
+
| [](https://rubygems.org/gems/sentry-delayed_job) | [](https://github.com/getsentry/sentry-ruby/actions/workflows/sentry_delayed_job_test.yml) | [](https://codecov.io/gh/getsentry/sentry-ruby/branch/master) | [](https://rubygems.org/gems/sentry-delayed_job/) |
|
22
|
+
| [](https://rubygems.org/gems/sentry-resque) | [](https://github.com/getsentry/sentry-ruby/actions/workflows/sentry_resque_test.yml) | [](https://codecov.io/gh/getsentry/sentry-ruby/branch/master) | [](https://rubygems.org/gems/sentry-resque/) |
|
23
|
+
| [](https://rubygems.org/gems/sentry-opentelemetry) | [](https://github.com/getsentry/sentry-ruby/actions/workflows/sentry_opentelemetry_test.yml) | [](https://codecov.io/gh/getsentry/sentry-ruby/branch/master) | [](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
|
-
#
|
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]
|
data/lib/sentry/configuration.rb
CHANGED
@@ -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
|
data/lib/sentry/envelope.rb
CHANGED
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
|
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
|
data/lib/sentry/integrable.rb
CHANGED
@@ -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, :
|
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
|
data/lib/sentry/net/http.rb
CHANGED
@@ -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
|
-
|
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(
|
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(
|
42
|
-
sentry_span.set_data(
|
43
|
-
sentry_span.set_data(
|
44
|
-
sentry_span.set_data(
|
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
|
53
|
-
|
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(
|
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
|
data/lib/sentry/profiler.rb
CHANGED
@@ -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
|
-
|
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
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
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 <=
|
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
|
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(
|
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]
|
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.
|
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(
|
248
|
+
set_data(DataConventions::HTTP_STATUS_CODE, status_code)
|
212
249
|
|
213
250
|
status =
|
214
251
|
if status_code >= 200 && status_code < 299
|
data/lib/sentry/test_helper.rb
CHANGED
@@ -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
|
-
|
17
|
+
dummy_config = Sentry.configuration.dup
|
18
18
|
# configure dummy DSN, so the events will not be sent to the actual service
|
19
|
-
|
19
|
+
dummy_config.dsn = DUMMY_DSN
|
20
20
|
# set transport to DummyTransport, so we can easily intercept the captured events
|
21
|
-
|
21
|
+
dummy_config.transport.transport_class = Sentry::DummyTransport
|
22
22
|
# make sure SDK allows sending under the current environment
|
23
|
-
|
23
|
+
dummy_config.enabled_environments << dummy_config.environment unless dummy_config.enabled_environments.include?(dummy_config.environment)
|
24
24
|
# disble async event sending
|
25
|
-
|
25
|
+
dummy_config.background_worker_threads = 0
|
26
26
|
|
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(
|
30
|
+
block&.call(dummy_config)
|
31
31
|
|
32
|
-
|
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
|
-
|
44
|
-
|
45
|
-
|
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
|
-
|
data/lib/sentry/transaction.rb
CHANGED
@@ -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
|
9
|
-
|
10
|
-
|
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
|
-
#
|
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
|
-
|
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
|
data/lib/sentry/transport.rb
CHANGED
@@ -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?(
|
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 == '
|
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
|
data/lib/sentry/version.rb
CHANGED
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.
|
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-
|
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
|