sentry-ruby 5.4.2 → 5.9.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.rspec +0 -1
- data/Gemfile +12 -5
- data/README.md +3 -0
- data/Rakefile +8 -1
- data/lib/sentry/backtrace.rb +1 -1
- data/lib/sentry/baggage.rb +70 -0
- data/lib/sentry/client.rb +31 -11
- data/lib/sentry/configuration.rb +96 -20
- data/lib/sentry/envelope.rb +1 -4
- data/lib/sentry/event.rb +1 -1
- data/lib/sentry/hub.rb +26 -2
- data/lib/sentry/interfaces/request.rb +6 -16
- data/lib/sentry/interfaces/single_exception.rb +9 -1
- data/lib/sentry/net/http.rb +20 -35
- data/lib/sentry/profiler.rb +222 -0
- data/lib/sentry/puma.rb +25 -0
- data/lib/sentry/rack/capture_exceptions.rb +6 -4
- data/lib/sentry/rake.rb +1 -1
- data/lib/sentry/redis.rb +35 -23
- data/lib/sentry/scope.rb +55 -6
- data/lib/sentry/session.rb +5 -7
- data/lib/sentry/span.rb +19 -9
- data/lib/sentry/test_helper.rb +3 -1
- data/lib/sentry/transaction.rb +173 -19
- data/lib/sentry/transaction_event.rb +54 -0
- data/lib/sentry/transport.rb +19 -8
- data/lib/sentry/utils/encoding_helper.rb +22 -0
- data/lib/sentry/version.rb +1 -1
- data/lib/sentry-ruby.rb +38 -21
- metadata +6 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 7a114a391fe058601b40369376152bee8ba9b1b96ab94710344283cfe1a4490b
|
4
|
+
data.tar.gz: 7a17648c7a5d06f22d2d643f6ff1cb25d3fed2348609e372fc5762043ebd538a
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: dab19469a8e68201380e2be405332bb7c8caef7bf46fd4dae70f4dc3beaac2902ce51b2b2c902b39ca048e1bc5ff47f85b1e7d3a824b5f20ff002dcbdca2c354
|
7
|
+
data.tar.gz: 6bf0cba7b42dd9bfd5078e4353ad7f92d932b959a87c748cb30e505ffd8d205899c819ea4f72e5f3099f0b861139864062b870219678abdeda0a0629604963d3
|
data/.rspec
CHANGED
data/Gemfile
CHANGED
@@ -3,17 +3,23 @@ git_source(:github) { |name| "https://github.com/#{name}.git" }
|
|
3
3
|
|
4
4
|
gem "sentry-ruby", path: "./"
|
5
5
|
|
6
|
-
|
6
|
+
rack_version = ENV["RACK_VERSION"]
|
7
|
+
rack_version = "3.0.0" if rack_version.nil?
|
8
|
+
gem "rack", "~> #{Gem::Version.new(rack_version)}" unless rack_version == "0"
|
9
|
+
|
10
|
+
redis_rb_version = ENV.fetch("REDIS_RB_VERSION", "5.0")
|
11
|
+
gem "redis", "~> #{redis_rb_version}"
|
12
|
+
|
13
|
+
gem "puma"
|
7
14
|
|
8
15
|
gem "rake", "~> 12.0"
|
9
16
|
gem "rspec", "~> 3.0"
|
10
17
|
gem "rspec-retry"
|
11
|
-
gem "webmock"
|
12
|
-
gem "fakeredis"
|
13
18
|
gem "timecop"
|
14
|
-
gem
|
19
|
+
gem "simplecov"
|
15
20
|
gem "simplecov-cobertura", "~> 1.4"
|
16
21
|
gem "rexml"
|
22
|
+
gem "stackprof" unless RUBY_PLATFORM == "java"
|
17
23
|
|
18
24
|
gem "object_tracer"
|
19
25
|
gem "debug", github: "ruby/debug", platform: :ruby if RUBY_VERSION.to_f >= 2.6
|
@@ -24,4 +30,5 @@ gem "benchmark_driver"
|
|
24
30
|
gem "benchmark-ipsa"
|
25
31
|
gem "benchmark-memory"
|
26
32
|
|
27
|
-
gem "yard",
|
33
|
+
gem "yard", github: "lsegal/yard"
|
34
|
+
gem "webrick"
|
data/README.md
CHANGED
@@ -20,6 +20,7 @@ Sentry SDK for Ruby
|
|
20
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
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
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/) |
|
23
24
|
|
24
25
|
|
25
26
|
|
@@ -51,6 +52,7 @@ gem "sentry-rails"
|
|
51
52
|
gem "sentry-sidekiq"
|
52
53
|
gem "sentry-delayed_job"
|
53
54
|
gem "sentry-resque"
|
55
|
+
gem "sentry-opentelemetry"
|
54
56
|
```
|
55
57
|
|
56
58
|
### Configuration
|
@@ -88,6 +90,7 @@ To learn more about sampling transactions, please visit the [official documentat
|
|
88
90
|
- [Sidekiq](https://docs.sentry.io/platforms/ruby/guides/sidekiq/)
|
89
91
|
- [DelayedJob](https://docs.sentry.io/platforms/ruby/guides/delayed_job/)
|
90
92
|
- [Resque](https://docs.sentry.io/platforms/ruby/guides/resque/)
|
93
|
+
- [OpenTemeletry](https://docs.sentry.io/platforms/ruby/performance/instrumentation/opentelemetry/)
|
91
94
|
|
92
95
|
### Enriching Events
|
93
96
|
|
data/Rakefile
CHANGED
@@ -8,6 +8,13 @@ require "rspec/core/rake_task"
|
|
8
8
|
|
9
9
|
RSpec::Core::RakeTask.new(:spec).tap do |task|
|
10
10
|
task.rspec_opts = "--order rand"
|
11
|
+
task.exclude_pattern = "spec/isolated/**/*_spec.rb"
|
11
12
|
end
|
12
13
|
|
13
|
-
task :
|
14
|
+
task :isolated_specs do
|
15
|
+
Dir["spec/isolated/*"].each do |file|
|
16
|
+
sh "bundle exec rspec #{file}"
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
task :default => [:spec, :isolated_specs]
|
data/lib/sentry/backtrace.rb
CHANGED
@@ -0,0 +1,70 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'cgi'
|
4
|
+
|
5
|
+
module Sentry
|
6
|
+
# A {https://www.w3.org/TR/baggage W3C Baggage Header} implementation.
|
7
|
+
class Baggage
|
8
|
+
SENTRY_PREFIX = 'sentry-'
|
9
|
+
SENTRY_PREFIX_REGEX = /^sentry-/.freeze
|
10
|
+
|
11
|
+
# @return [Hash]
|
12
|
+
attr_reader :items
|
13
|
+
|
14
|
+
# @return [Boolean]
|
15
|
+
attr_reader :mutable
|
16
|
+
|
17
|
+
def initialize(items, mutable: true)
|
18
|
+
@items = items
|
19
|
+
@mutable = mutable
|
20
|
+
end
|
21
|
+
|
22
|
+
# Creates a Baggage object from an incoming W3C Baggage header string.
|
23
|
+
#
|
24
|
+
# Sentry items are identified with the 'sentry-' prefix and stored in a hash.
|
25
|
+
# The presence of a Sentry item makes the baggage object immutable.
|
26
|
+
#
|
27
|
+
# @param header [String] The incoming Baggage header string.
|
28
|
+
# @return [Baggage, nil]
|
29
|
+
def self.from_incoming_header(header)
|
30
|
+
items = {}
|
31
|
+
mutable = true
|
32
|
+
|
33
|
+
header.split(',').each do |item|
|
34
|
+
item = item.strip
|
35
|
+
key, val = item.split('=')
|
36
|
+
|
37
|
+
next unless key && val
|
38
|
+
next unless key =~ SENTRY_PREFIX_REGEX
|
39
|
+
|
40
|
+
baggage_key = key.split('-')[1]
|
41
|
+
next unless baggage_key
|
42
|
+
|
43
|
+
items[CGI.unescape(baggage_key)] = CGI.unescape(val)
|
44
|
+
mutable = false
|
45
|
+
end
|
46
|
+
|
47
|
+
new(items, mutable: mutable)
|
48
|
+
end
|
49
|
+
|
50
|
+
# Make the Baggage immutable.
|
51
|
+
# @return [void]
|
52
|
+
def freeze!
|
53
|
+
@mutable = false
|
54
|
+
end
|
55
|
+
|
56
|
+
# A {https://develop.sentry.dev/sdk/performance/dynamic-sampling-context/#envelope-header Dynamic Sampling Context}
|
57
|
+
# hash to be used in the trace envelope header.
|
58
|
+
# @return [Hash]
|
59
|
+
def dynamic_sampling_context
|
60
|
+
@items
|
61
|
+
end
|
62
|
+
|
63
|
+
# Serialize the Baggage object back to a string.
|
64
|
+
# @return [String]
|
65
|
+
def serialize
|
66
|
+
items = @items.map { |k, v| "#{SENTRY_PREFIX}#{CGI.escape(k)}=#{CGI.escape(v)}" }
|
67
|
+
items.join(',')
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
data/lib/sentry/client.rb
CHANGED
@@ -76,7 +76,10 @@ module Sentry
|
|
76
76
|
# @param hint [Hash] the hint data that'll be passed to `before_send` callback and the scope's event processors.
|
77
77
|
# @return [Event, nil]
|
78
78
|
def event_from_exception(exception, hint = {})
|
79
|
-
return unless @configuration.sending_allowed?
|
79
|
+
return unless @configuration.sending_allowed?
|
80
|
+
|
81
|
+
ignore_exclusions = hint.delete(:ignore_exclusions) { false }
|
82
|
+
return if !ignore_exclusions && !@configuration.exception_class_allowed?(exception)
|
80
83
|
|
81
84
|
integration_meta = Sentry.integrations[hint[:integration]]
|
82
85
|
|
@@ -105,16 +108,7 @@ module Sentry
|
|
105
108
|
# @param transaction [Transaction] the transaction to be recorded.
|
106
109
|
# @return [TransactionEvent]
|
107
110
|
def event_from_transaction(transaction)
|
108
|
-
TransactionEvent.new(configuration: configuration
|
109
|
-
event.transaction = transaction.name
|
110
|
-
event.contexts.merge!(trace: transaction.get_trace_context)
|
111
|
-
event.timestamp = transaction.timestamp
|
112
|
-
event.start_timestamp = transaction.start_timestamp
|
113
|
-
event.tags = transaction.tags
|
114
|
-
|
115
|
-
finished_spans = transaction.span_recorder.spans.select { |span| span.timestamp && span != transaction }
|
116
|
-
event.spans = finished_spans.map(&:to_hash)
|
117
|
-
end
|
111
|
+
TransactionEvent.new(configuration: configuration, transaction: transaction)
|
118
112
|
end
|
119
113
|
|
120
114
|
# @!macro send_event
|
@@ -131,6 +125,16 @@ module Sentry
|
|
131
125
|
end
|
132
126
|
end
|
133
127
|
|
128
|
+
if event_type == TransactionEvent::TYPE && configuration.before_send_transaction
|
129
|
+
event = configuration.before_send_transaction.call(event, hint)
|
130
|
+
|
131
|
+
if event.nil?
|
132
|
+
log_info("Discarded event because before_send_transaction returned nil")
|
133
|
+
transport.record_lost_event(:before_send, 'transaction')
|
134
|
+
return
|
135
|
+
end
|
136
|
+
end
|
137
|
+
|
134
138
|
transport.send_event(event)
|
135
139
|
|
136
140
|
event
|
@@ -156,6 +160,22 @@ module Sentry
|
|
156
160
|
trace
|
157
161
|
end
|
158
162
|
|
163
|
+
# Generates a W3C Baggage header for distribted tracing from the given Span.
|
164
|
+
# Returns `nil` if `config.propagate_traces` is `false`.
|
165
|
+
# @param span [Span] the span to generate trace from.
|
166
|
+
# @return [String, nil]
|
167
|
+
def generate_baggage(span)
|
168
|
+
return unless configuration.propagate_traces
|
169
|
+
|
170
|
+
baggage = span.to_baggage
|
171
|
+
|
172
|
+
if baggage && !baggage.empty?
|
173
|
+
log_debug("[Tracing] Adding #{BAGGAGE_HEADER_NAME} header to outgoing request: #{baggage}")
|
174
|
+
end
|
175
|
+
|
176
|
+
baggage
|
177
|
+
end
|
178
|
+
|
159
179
|
private
|
160
180
|
|
161
181
|
def dispatch_background_event(event, hint)
|
data/lib/sentry/configuration.rb
CHANGED
@@ -72,6 +72,19 @@ module Sentry
|
|
72
72
|
# @return [Proc]
|
73
73
|
attr_reader :before_send
|
74
74
|
|
75
|
+
# Optional Proc, called before sending an event to the server
|
76
|
+
# @example
|
77
|
+
# config.before_send_transaction = lambda do |event, hint|
|
78
|
+
# # skip unimportant transactions or strip sensitive data
|
79
|
+
# if event.transaction == "/healthcheck/route"
|
80
|
+
# nil
|
81
|
+
# else
|
82
|
+
# event
|
83
|
+
# end
|
84
|
+
# end
|
85
|
+
# @return [Proc]
|
86
|
+
attr_reader :before_send_transaction
|
87
|
+
|
75
88
|
# An array of breadcrumbs loggers to be used. Available options are:
|
76
89
|
# - :sentry_logger
|
77
90
|
# - :http_logger
|
@@ -84,10 +97,6 @@ module Sentry
|
|
84
97
|
# @return [Array<Symbol>]
|
85
98
|
attr_reader :breadcrumbs_logger
|
86
99
|
|
87
|
-
# Whether to capture local variables from the raised exception's frame. Default is false.
|
88
|
-
# @return [Boolean]
|
89
|
-
attr_accessor :capture_exception_frame_locals
|
90
|
-
|
91
100
|
# Max number of breadcrumbs a breadcrumb buffer can hold
|
92
101
|
# @return [Integer]
|
93
102
|
attr_accessor :max_breadcrumbs
|
@@ -127,6 +136,22 @@ module Sentry
|
|
127
136
|
attr_accessor :inspect_exception_causes_for_exclusion
|
128
137
|
alias inspect_exception_causes_for_exclusion? inspect_exception_causes_for_exclusion
|
129
138
|
|
139
|
+
# Whether to capture local variables from the raised exception's frame. Default is false.
|
140
|
+
# @return [Boolean]
|
141
|
+
attr_accessor :include_local_variables
|
142
|
+
|
143
|
+
# @deprecated Use {#include_local_variables} instead.
|
144
|
+
alias_method :capture_exception_frame_locals, :include_local_variables
|
145
|
+
|
146
|
+
# @deprecated Use {#include_local_variables=} instead.
|
147
|
+
def capture_exception_frame_locals=(value)
|
148
|
+
log_warn <<~MSG
|
149
|
+
`capture_exception_frame_locals` is now deprecated in favor of `include_local_variables`.
|
150
|
+
MSG
|
151
|
+
|
152
|
+
self.include_local_variables = value
|
153
|
+
end
|
154
|
+
|
130
155
|
# You may provide your own LineCache for matching paths with source files.
|
131
156
|
# This may be useful if you need to get source code from places other than the disk.
|
132
157
|
# @see LineCache
|
@@ -202,6 +227,11 @@ module Sentry
|
|
202
227
|
# @return [Proc]
|
203
228
|
attr_accessor :traces_sampler
|
204
229
|
|
230
|
+
# Easier way to use performance tracing
|
231
|
+
# If set to true, will set traces_sample_rate to 1.0
|
232
|
+
# @return [Boolean, nil]
|
233
|
+
attr_reader :enable_tracing
|
234
|
+
|
205
235
|
# Send diagnostic client reports about dropped events, true by default
|
206
236
|
# tries to attach to an existing envelope max once every 30s
|
207
237
|
# @return [Boolean]
|
@@ -211,6 +241,16 @@ module Sentry
|
|
211
241
|
# @return [Boolean]
|
212
242
|
attr_accessor :auto_session_tracking
|
213
243
|
|
244
|
+
# The instrumenter to use, :sentry or :otel
|
245
|
+
# @return [Symbol]
|
246
|
+
attr_reader :instrumenter
|
247
|
+
|
248
|
+
# Take a float between 0.0 and 1.0 as the sample rate for capturing profiles.
|
249
|
+
# Note that this rate is relative to traces_sample_rate / traces_sampler,
|
250
|
+
# i.e. the profile is sampled by this rate after the transaction is sampled.
|
251
|
+
# @return [Float, nil]
|
252
|
+
attr_reader :profiles_sample_rate
|
253
|
+
|
214
254
|
# these are not config options
|
215
255
|
# @!visibility private
|
216
256
|
attr_reader :errors, :gem_specs
|
@@ -237,9 +277,20 @@ module Sentry
|
|
237
277
|
MODULE_SEPARATOR = "::".freeze
|
238
278
|
SKIP_INSPECTION_ATTRIBUTES = [:@linecache, :@stacktrace_builder]
|
239
279
|
|
240
|
-
|
241
|
-
|
242
|
-
|
280
|
+
INSTRUMENTERS = [:sentry, :otel]
|
281
|
+
|
282
|
+
class << self
|
283
|
+
# Post initialization callbacks are called at the end of initialization process
|
284
|
+
# allowing extending the configuration of sentry-ruby by multiple extensions
|
285
|
+
def post_initialization_callbacks
|
286
|
+
@post_initialization_callbacks ||= []
|
287
|
+
end
|
288
|
+
|
289
|
+
# allow extensions to add their hooks to the Configuration class
|
290
|
+
def add_post_initialization_callback(&block)
|
291
|
+
post_initialization_callbacks << block
|
292
|
+
end
|
293
|
+
end
|
243
294
|
|
244
295
|
def initialize
|
245
296
|
self.app_dirs_pattern = nil
|
@@ -249,7 +300,7 @@ module Sentry
|
|
249
300
|
self.max_breadcrumbs = BreadcrumbBuffer::DEFAULT_SIZE
|
250
301
|
self.breadcrumbs_logger = []
|
251
302
|
self.context_lines = 3
|
252
|
-
self.
|
303
|
+
self.include_local_variables = false
|
253
304
|
self.environment = environment_from_env
|
254
305
|
self.enabled_environments = []
|
255
306
|
self.exclude_loggers = []
|
@@ -269,11 +320,14 @@ module Sentry
|
|
269
320
|
self.trusted_proxies = []
|
270
321
|
self.dsn = ENV['SENTRY_DSN']
|
271
322
|
self.server_name = server_name_from_env
|
323
|
+
self.instrumenter = :sentry
|
272
324
|
|
273
325
|
self.before_send = nil
|
326
|
+
self.before_send_transaction = nil
|
274
327
|
self.rack_env_whitelist = RACK_ENV_WHITELIST_DEFAULT
|
275
328
|
self.traces_sample_rate = nil
|
276
329
|
self.traces_sampler = nil
|
330
|
+
self.enable_tracing = nil
|
277
331
|
|
278
332
|
@transport = Transport::Configuration.new
|
279
333
|
@gem_specs = Hash[Gem::Specification.map { |spec| [spec.name, spec.version.to_s] }] if Gem::Specification.respond_to?(:map)
|
@@ -322,6 +376,12 @@ module Sentry
|
|
322
376
|
@before_send = value
|
323
377
|
end
|
324
378
|
|
379
|
+
def before_send_transaction=(value)
|
380
|
+
check_callable!("before_send_transaction", value)
|
381
|
+
|
382
|
+
@before_send_transaction = value
|
383
|
+
end
|
384
|
+
|
325
385
|
def before_breadcrumb=(value)
|
326
386
|
check_callable!("before_breadcrumb", value)
|
327
387
|
|
@@ -332,6 +392,20 @@ module Sentry
|
|
332
392
|
@environment = environment.to_s
|
333
393
|
end
|
334
394
|
|
395
|
+
def instrumenter=(instrumenter)
|
396
|
+
@instrumenter = INSTRUMENTERS.include?(instrumenter) ? instrumenter : :sentry
|
397
|
+
end
|
398
|
+
|
399
|
+
def enable_tracing=(enable_tracing)
|
400
|
+
@enable_tracing = enable_tracing
|
401
|
+
@traces_sample_rate ||= 1.0 if enable_tracing
|
402
|
+
end
|
403
|
+
|
404
|
+
def profiles_sample_rate=(profiles_sample_rate)
|
405
|
+
log_info("Please make sure to include the 'stackprof' gem in your Gemfile to use Profiling with Sentry.") unless defined?(StackProf)
|
406
|
+
@profiles_sample_rate = profiles_sample_rate
|
407
|
+
end
|
408
|
+
|
335
409
|
def sending_allowed?
|
336
410
|
@errors = []
|
337
411
|
|
@@ -362,7 +436,20 @@ module Sentry
|
|
362
436
|
end
|
363
437
|
|
364
438
|
def tracing_enabled?
|
365
|
-
!!((@traces_sample_rate &&
|
439
|
+
valid_sampler = !!((@traces_sample_rate &&
|
440
|
+
@traces_sample_rate >= 0.0 &&
|
441
|
+
@traces_sample_rate <= 1.0) ||
|
442
|
+
@traces_sampler)
|
443
|
+
|
444
|
+
(@enable_tracing != false) && valid_sampler && sending_allowed?
|
445
|
+
end
|
446
|
+
|
447
|
+
def profiling_enabled?
|
448
|
+
valid_sampler = !!(@profiles_sample_rate &&
|
449
|
+
@profiles_sample_rate >= 0.0 &&
|
450
|
+
@profiles_sample_rate <= 1.0)
|
451
|
+
|
452
|
+
tracing_enabled? && valid_sampler && sending_allowed?
|
366
453
|
end
|
367
454
|
|
368
455
|
# @return [String, nil]
|
@@ -487,16 +574,5 @@ module Sentry
|
|
487
574
|
instance_eval(&hook)
|
488
575
|
end
|
489
576
|
end
|
490
|
-
|
491
|
-
# allow extensions to add their hooks to the Configuration class
|
492
|
-
def self.add_post_initialization_callback(&block)
|
493
|
-
self.post_initialization_callbacks << block
|
494
|
-
end
|
495
|
-
|
496
|
-
protected
|
497
|
-
|
498
|
-
def self.post_initialization_callbacks
|
499
|
-
@@post_initialization_callbacks
|
500
|
-
end
|
501
577
|
end
|
502
578
|
end
|
data/lib/sentry/envelope.rb
CHANGED
data/lib/sentry/event.rb
CHANGED
data/lib/sentry/hub.rb
CHANGED
@@ -76,8 +76,9 @@ module Sentry
|
|
76
76
|
@stack.pop
|
77
77
|
end
|
78
78
|
|
79
|
-
def start_transaction(transaction: nil, custom_sampling_context: {}, **options)
|
79
|
+
def start_transaction(transaction: nil, custom_sampling_context: {}, instrumenter: :sentry, **options)
|
80
80
|
return unless configuration.tracing_enabled?
|
81
|
+
return unless instrumenter == configuration.instrumenter
|
81
82
|
|
82
83
|
transaction ||= Transaction.new(**options.merge(hub: self))
|
83
84
|
|
@@ -87,11 +88,33 @@ module Sentry
|
|
87
88
|
}
|
88
89
|
|
89
90
|
sampling_context.merge!(custom_sampling_context)
|
90
|
-
|
91
91
|
transaction.set_initial_sample_decision(sampling_context: sampling_context)
|
92
|
+
|
93
|
+
transaction.start_profiler!
|
94
|
+
|
92
95
|
transaction
|
93
96
|
end
|
94
97
|
|
98
|
+
def with_child_span(instrumenter: :sentry, **attributes, &block)
|
99
|
+
return yield(nil) unless instrumenter == configuration.instrumenter
|
100
|
+
|
101
|
+
current_span = current_scope.get_span
|
102
|
+
return yield(nil) unless current_span
|
103
|
+
|
104
|
+
result = nil
|
105
|
+
|
106
|
+
begin
|
107
|
+
current_span.with_child_span(**attributes) do |child_span|
|
108
|
+
current_scope.set_span(child_span)
|
109
|
+
result = yield(child_span)
|
110
|
+
end
|
111
|
+
ensure
|
112
|
+
current_scope.set_span(current_span)
|
113
|
+
end
|
114
|
+
|
115
|
+
result
|
116
|
+
end
|
117
|
+
|
95
118
|
def capture_exception(exception, **options, &block)
|
96
119
|
check_argument_type!(exception, ::Exception)
|
97
120
|
|
@@ -101,6 +124,7 @@ module Sentry
|
|
101
124
|
|
102
125
|
options[:hint] ||= {}
|
103
126
|
options[:hint][:exception] = exception
|
127
|
+
|
104
128
|
event = current_client.event_from_exception(exception, options[:hint])
|
105
129
|
|
106
130
|
return unless event
|
@@ -73,7 +73,7 @@ module Sentry
|
|
73
73
|
request.POST
|
74
74
|
elsif request.body # JSON requests, etc
|
75
75
|
data = request.body.read(MAX_BODY_LIMIT)
|
76
|
-
data = encode_to_utf_8(data.to_s)
|
76
|
+
data = Utils::EncodingHelper.encode_to_utf_8(data.to_s)
|
77
77
|
request.body.rewind
|
78
78
|
data
|
79
79
|
end
|
@@ -94,7 +94,7 @@ module Sentry
|
|
94
94
|
key = key.sub(/^HTTP_/, "")
|
95
95
|
key = key.split('_').map(&:capitalize).join('-')
|
96
96
|
|
97
|
-
memo[key] = encode_to_utf_8(value.to_s)
|
97
|
+
memo[key] = Utils::EncodingHelper.encode_to_utf_8(value.to_s)
|
98
98
|
rescue StandardError => e
|
99
99
|
# Rails adds objects to the Rack env that can sometimes raise exceptions
|
100
100
|
# when `to_s` is called.
|
@@ -105,31 +105,21 @@ module Sentry
|
|
105
105
|
end
|
106
106
|
end
|
107
107
|
|
108
|
-
def encode_to_utf_8(value)
|
109
|
-
if value.encoding != Encoding::UTF_8 && value.respond_to?(:force_encoding)
|
110
|
-
value = value.dup.force_encoding(Encoding::UTF_8)
|
111
|
-
end
|
112
|
-
|
113
|
-
if !value.valid_encoding?
|
114
|
-
value = value.scrub
|
115
|
-
end
|
116
|
-
|
117
|
-
value
|
118
|
-
end
|
119
|
-
|
120
108
|
def is_skippable_header?(key)
|
121
109
|
key.upcase != key || # lower-case envs aren't real http headers
|
122
110
|
key == "HTTP_COOKIE" || # Cookies don't go here, they go somewhere else
|
123
111
|
!(key.start_with?('HTTP_') || CONTENT_HEADERS.include?(key))
|
124
112
|
end
|
125
113
|
|
126
|
-
# Rack adds in an incorrect HTTP_VERSION key, which causes downstream
|
114
|
+
# In versions < 3, Rack adds in an incorrect HTTP_VERSION key, which causes downstream
|
127
115
|
# to think this is a Version header. Instead, this is mapped to
|
128
116
|
# env['SERVER_PROTOCOL']. But we don't want to ignore a valid header
|
129
117
|
# if the request has legitimately sent a Version header themselves.
|
130
118
|
# See: https://github.com/rack/rack/blob/028438f/lib/rack/handler/cgi.rb#L29
|
131
|
-
# NOTE: This will be removed in version 3.0+
|
132
119
|
def is_server_protocol?(key, value, protocol_version)
|
120
|
+
rack_version = Gem::Version.new(::Rack.release)
|
121
|
+
return false if rack_version >= Gem::Version.new("3.0")
|
122
|
+
|
133
123
|
key == 'HTTP_VERSION' && value == protocol_version
|
134
124
|
end
|
135
125
|
|
@@ -15,7 +15,15 @@ module Sentry
|
|
15
15
|
|
16
16
|
def initialize(exception:, stacktrace: nil)
|
17
17
|
@type = exception.class.to_s
|
18
|
-
|
18
|
+
exception_message =
|
19
|
+
if exception.respond_to?(:detailed_message)
|
20
|
+
exception.detailed_message(highlight: false)
|
21
|
+
else
|
22
|
+
exception.message || ""
|
23
|
+
end
|
24
|
+
|
25
|
+
@value = exception_message.byteslice(0..Event::MAX_MESSAGE_SIZE_IN_BYTES)
|
26
|
+
|
19
27
|
@module = exception.class.to_s.split('::')[0...-1].join('::')
|
20
28
|
@thread_id = Thread.current.object_id
|
21
29
|
@stacktrace = stacktrace
|
data/lib/sentry/net/http.rb
CHANGED
@@ -26,14 +26,21 @@ module Sentry
|
|
26
26
|
#
|
27
27
|
# So we're only instrumenting request when `Net::HTTP` is already started
|
28
28
|
def request(req, body = nil, &block)
|
29
|
-
return super unless started?
|
29
|
+
return super unless started? && Sentry.initialized?
|
30
|
+
return super if from_sentry_sdk?
|
30
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
34
|
|
34
|
-
|
35
|
-
|
36
|
-
|
35
|
+
super.tap do |res|
|
36
|
+
record_sentry_breadcrumb(req, res)
|
37
|
+
|
38
|
+
if sentry_span
|
39
|
+
request_info = extract_request_info(req)
|
40
|
+
sentry_span.set_description("#{request_info[:method]} #{request_info[:url]}")
|
41
|
+
sentry_span.set_data(:status, res.code.to_i)
|
42
|
+
end
|
43
|
+
end
|
37
44
|
end
|
38
45
|
end
|
39
46
|
|
@@ -42,13 +49,17 @@ module Sentry
|
|
42
49
|
def set_sentry_trace_header(req, sentry_span)
|
43
50
|
return unless sentry_span
|
44
51
|
|
45
|
-
|
52
|
+
client = Sentry.get_current_client
|
53
|
+
|
54
|
+
trace = client.generate_sentry_trace(sentry_span)
|
46
55
|
req[SENTRY_TRACE_HEADER_NAME] = trace if trace
|
56
|
+
|
57
|
+
baggage = client.generate_baggage(sentry_span)
|
58
|
+
req[BAGGAGE_HEADER_NAME] = baggage if baggage && !baggage.empty?
|
47
59
|
end
|
48
60
|
|
49
61
|
def record_sentry_breadcrumb(req, res)
|
50
62
|
return unless Sentry.initialized? && Sentry.configuration.breadcrumbs_logger.include?(:http_logger)
|
51
|
-
return if from_sentry_sdk?
|
52
63
|
|
53
64
|
request_info = extract_request_info(req)
|
54
65
|
|
@@ -64,29 +75,6 @@ module Sentry
|
|
64
75
|
Sentry.add_breadcrumb(crumb)
|
65
76
|
end
|
66
77
|
|
67
|
-
def record_sentry_span(req, res, sentry_span)
|
68
|
-
return unless Sentry.initialized? && sentry_span
|
69
|
-
|
70
|
-
request_info = extract_request_info(req)
|
71
|
-
sentry_span.set_description("#{request_info[:method]} #{request_info[:url]}")
|
72
|
-
sentry_span.set_data(:status, res.code.to_i)
|
73
|
-
finish_sentry_span(sentry_span)
|
74
|
-
end
|
75
|
-
|
76
|
-
def start_sentry_span
|
77
|
-
return unless Sentry.initialized? && span = Sentry.get_current_scope.get_span
|
78
|
-
return if from_sentry_sdk?
|
79
|
-
return if span.sampled == false
|
80
|
-
|
81
|
-
span.start_child(op: OP_NAME, start_timestamp: Sentry.utc_now.to_f)
|
82
|
-
end
|
83
|
-
|
84
|
-
def finish_sentry_span(sentry_span)
|
85
|
-
return unless Sentry.initialized? && sentry_span
|
86
|
-
|
87
|
-
sentry_span.set_timestamp(Sentry.utc_now.to_f)
|
88
|
-
end
|
89
|
-
|
90
78
|
def from_sentry_sdk?
|
91
79
|
dsn = Sentry.configuration.dsn
|
92
80
|
dsn && dsn.host == self.address
|
@@ -109,7 +97,4 @@ module Sentry
|
|
109
97
|
end
|
110
98
|
end
|
111
99
|
|
112
|
-
Sentry.register_patch
|
113
|
-
patch = Sentry::Net::HTTP
|
114
|
-
Net::HTTP.send(:prepend, patch) unless Net::HTTP.ancestors.include?(patch)
|
115
|
-
end
|
100
|
+
Sentry.register_patch(Sentry::Net::HTTP, Net::HTTP)
|