sentry-ruby 5.9.0 → 5.26.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/.rspec +3 -1
- data/Gemfile +12 -10
- data/README.md +26 -11
- data/Rakefile +9 -11
- data/bin/console +2 -0
- data/lib/sentry/attachment.rb +40 -0
- data/lib/sentry/background_worker.rb +11 -5
- data/lib/sentry/backpressure_monitor.rb +45 -0
- data/lib/sentry/backtrace.rb +12 -9
- data/lib/sentry/baggage.rb +7 -7
- data/lib/sentry/breadcrumb/sentry_logger.rb +6 -6
- data/lib/sentry/breadcrumb.rb +13 -6
- data/lib/sentry/check_in_event.rb +61 -0
- data/lib/sentry/client.rb +214 -25
- data/lib/sentry/configuration.rb +251 -42
- data/lib/sentry/core_ext/object/deep_dup.rb +1 -1
- data/lib/sentry/cron/configuration.rb +23 -0
- data/lib/sentry/cron/monitor_check_ins.rb +77 -0
- data/lib/sentry/cron/monitor_config.rb +53 -0
- data/lib/sentry/cron/monitor_schedule.rb +42 -0
- data/lib/sentry/dsn.rb +4 -4
- data/lib/sentry/envelope/item.rb +88 -0
- data/lib/sentry/envelope.rb +2 -68
- data/lib/sentry/error_event.rb +2 -2
- data/lib/sentry/event.rb +28 -47
- data/lib/sentry/excon/middleware.rb +77 -0
- data/lib/sentry/excon.rb +10 -0
- data/lib/sentry/faraday.rb +77 -0
- data/lib/sentry/graphql.rb +9 -0
- data/lib/sentry/hub.rb +143 -7
- data/lib/sentry/integrable.rb +10 -0
- data/lib/sentry/interface.rb +1 -0
- data/lib/sentry/interfaces/exception.rb +5 -3
- data/lib/sentry/interfaces/mechanism.rb +20 -0
- data/lib/sentry/interfaces/request.rb +8 -8
- data/lib/sentry/interfaces/single_exception.rb +13 -9
- data/lib/sentry/interfaces/stacktrace.rb +3 -1
- data/lib/sentry/interfaces/stacktrace_builder.rb +23 -2
- data/lib/sentry/linecache.rb +3 -3
- data/lib/sentry/log_event.rb +206 -0
- data/lib/sentry/log_event_buffer.rb +75 -0
- data/lib/sentry/logger.rb +1 -1
- data/lib/sentry/metrics/aggregator.rb +248 -0
- data/lib/sentry/metrics/configuration.rb +47 -0
- data/lib/sentry/metrics/counter_metric.rb +25 -0
- data/lib/sentry/metrics/distribution_metric.rb +25 -0
- data/lib/sentry/metrics/gauge_metric.rb +35 -0
- data/lib/sentry/metrics/local_aggregator.rb +53 -0
- data/lib/sentry/metrics/metric.rb +19 -0
- data/lib/sentry/metrics/set_metric.rb +28 -0
- data/lib/sentry/metrics/timing.rb +51 -0
- data/lib/sentry/metrics.rb +56 -0
- data/lib/sentry/net/http.rb +28 -42
- data/lib/sentry/profiler/helpers.rb +46 -0
- data/lib/sentry/profiler.rb +41 -60
- data/lib/sentry/propagation_context.rb +135 -0
- data/lib/sentry/puma.rb +12 -5
- data/lib/sentry/rack/capture_exceptions.rb +17 -8
- data/lib/sentry/rack.rb +2 -2
- data/lib/sentry/rake.rb +4 -15
- data/lib/sentry/redis.rb +11 -4
- data/lib/sentry/release_detector.rb +5 -5
- data/lib/sentry/rspec.rb +91 -0
- data/lib/sentry/scope.rb +75 -39
- data/lib/sentry/session.rb +2 -2
- data/lib/sentry/session_flusher.rb +15 -43
- data/lib/sentry/span.rb +92 -8
- data/lib/sentry/std_lib_logger.rb +50 -0
- data/lib/sentry/structured_logger.rb +138 -0
- data/lib/sentry/test_helper.rb +42 -13
- data/lib/sentry/threaded_periodic_worker.rb +39 -0
- data/lib/sentry/transaction.rb +44 -43
- data/lib/sentry/transaction_event.rb +10 -6
- data/lib/sentry/transport/configuration.rb +73 -1
- data/lib/sentry/transport/http_transport.rb +71 -41
- data/lib/sentry/transport/spotlight_transport.rb +50 -0
- data/lib/sentry/transport.rb +53 -49
- data/lib/sentry/utils/argument_checking_helper.rb +15 -3
- data/lib/sentry/utils/env_helper.rb +21 -0
- data/lib/sentry/utils/http_tracing.rb +74 -0
- data/lib/sentry/utils/logging_helper.rb +10 -7
- data/lib/sentry/utils/real_ip.rb +2 -2
- data/lib/sentry/utils/request_id.rb +1 -1
- data/lib/sentry/utils/uuid.rb +13 -0
- data/lib/sentry/vernier/output.rb +89 -0
- data/lib/sentry/vernier/profiler.rb +132 -0
- data/lib/sentry/version.rb +1 -1
- data/lib/sentry-ruby.rb +206 -35
- data/sentry-ruby-core.gemspec +3 -1
- data/sentry-ruby.gemspec +15 -6
- metadata +61 -12
- data/CODE_OF_CONDUCT.md +0 -74
data/lib/sentry/hub.rb
CHANGED
@@ -8,12 +8,42 @@ module Sentry
|
|
8
8
|
class Hub
|
9
9
|
include ArgumentCheckingHelper
|
10
10
|
|
11
|
+
MUTEX = Mutex.new
|
12
|
+
|
11
13
|
attr_reader :last_event_id
|
12
14
|
|
15
|
+
attr_reader :current_profiler
|
16
|
+
|
13
17
|
def initialize(client, scope)
|
14
18
|
first_layer = Layer.new(client, scope)
|
15
19
|
@stack = [first_layer]
|
16
20
|
@last_event_id = nil
|
21
|
+
@current_profiler = {}
|
22
|
+
end
|
23
|
+
|
24
|
+
# This is an internal private method
|
25
|
+
# @api private
|
26
|
+
def start_profiler!(transaction)
|
27
|
+
MUTEX.synchronize do
|
28
|
+
transaction.start_profiler!
|
29
|
+
@current_profiler[transaction.__id__] = transaction.profiler
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
# This is an internal private method
|
34
|
+
# @api private
|
35
|
+
def stop_profiler!(transaction)
|
36
|
+
MUTEX.synchronize do
|
37
|
+
@current_profiler.delete(transaction.__id__)&.stop
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
# This is an internal private method
|
42
|
+
# @api private
|
43
|
+
def profiler_running?
|
44
|
+
MUTEX.synchronize do
|
45
|
+
!@current_profiler.empty?
|
46
|
+
end
|
17
47
|
end
|
18
48
|
|
19
49
|
def new_from_top
|
@@ -73,7 +103,13 @@ module Sentry
|
|
73
103
|
end
|
74
104
|
|
75
105
|
def pop_scope
|
76
|
-
@stack.
|
106
|
+
if @stack.size > 1
|
107
|
+
@stack.pop
|
108
|
+
else
|
109
|
+
# We never want to enter a situation where we have no scope and no client
|
110
|
+
client = current_client
|
111
|
+
@stack = [Layer.new(client, Scope.new)]
|
112
|
+
end
|
77
113
|
end
|
78
114
|
|
79
115
|
def start_transaction(transaction: nil, custom_sampling_context: {}, instrumenter: :sentry, **options)
|
@@ -90,7 +126,7 @@ module Sentry
|
|
90
126
|
sampling_context.merge!(custom_sampling_context)
|
91
127
|
transaction.set_initial_sample_decision(sampling_context: sampling_context)
|
92
128
|
|
93
|
-
|
129
|
+
start_profiler!(transaction)
|
94
130
|
|
95
131
|
transaction
|
96
132
|
end
|
@@ -116,7 +152,11 @@ module Sentry
|
|
116
152
|
end
|
117
153
|
|
118
154
|
def capture_exception(exception, **options, &block)
|
119
|
-
|
155
|
+
if RUBY_PLATFORM == "java"
|
156
|
+
check_argument_type!(exception, ::Exception, ::Java::JavaLang::Throwable)
|
157
|
+
else
|
158
|
+
check_argument_type!(exception, ::Exception)
|
159
|
+
end
|
120
160
|
|
121
161
|
return if Sentry.exception_captured?(exception)
|
122
162
|
|
@@ -152,6 +192,40 @@ module Sentry
|
|
152
192
|
capture_event(event, **options, &block)
|
153
193
|
end
|
154
194
|
|
195
|
+
def capture_check_in(slug, status, **options)
|
196
|
+
check_argument_type!(slug, ::String)
|
197
|
+
check_argument_includes!(status, Sentry::CheckInEvent::VALID_STATUSES)
|
198
|
+
|
199
|
+
return unless current_client
|
200
|
+
|
201
|
+
options[:hint] ||= {}
|
202
|
+
options[:hint][:slug] = slug
|
203
|
+
|
204
|
+
event = current_client.event_from_check_in(
|
205
|
+
slug,
|
206
|
+
status,
|
207
|
+
options[:hint],
|
208
|
+
duration: options.delete(:duration),
|
209
|
+
monitor_config: options.delete(:monitor_config),
|
210
|
+
check_in_id: options.delete(:check_in_id)
|
211
|
+
)
|
212
|
+
|
213
|
+
return unless event
|
214
|
+
|
215
|
+
capture_event(event, **options)
|
216
|
+
event.check_in_id
|
217
|
+
end
|
218
|
+
|
219
|
+
def capture_log_event(message, **options)
|
220
|
+
return unless current_client
|
221
|
+
|
222
|
+
event = current_client.event_from_log(message, **options)
|
223
|
+
|
224
|
+
return unless event
|
225
|
+
|
226
|
+
current_client.buffer_log_event(event, current_scope)
|
227
|
+
end
|
228
|
+
|
155
229
|
def capture_event(event, **options, &block)
|
156
230
|
check_argument_type!(event, Sentry::Event)
|
157
231
|
|
@@ -165,7 +239,14 @@ module Sentry
|
|
165
239
|
elsif custom_scope = options[:scope]
|
166
240
|
scope.update_from_scope(custom_scope)
|
167
241
|
elsif !options.empty?
|
168
|
-
scope.update_from_options(**options)
|
242
|
+
unsupported_option_keys = scope.update_from_options(**options)
|
243
|
+
|
244
|
+
unless unsupported_option_keys.empty?
|
245
|
+
configuration.log_debug <<~MSG
|
246
|
+
Options #{unsupported_option_keys} are not supported and will not be applied to the event.
|
247
|
+
You may want to set them under the `extra` option.
|
248
|
+
MSG
|
249
|
+
end
|
169
250
|
end
|
170
251
|
|
171
252
|
event = current_client.capture_event(event, scope, hint)
|
@@ -174,11 +255,12 @@ module Sentry
|
|
174
255
|
configuration.log_debug(event.to_json_compatible)
|
175
256
|
end
|
176
257
|
|
177
|
-
@last_event_id = event&.event_id
|
258
|
+
@last_event_id = event&.event_id if event.is_a?(Sentry::ErrorEvent)
|
178
259
|
event
|
179
260
|
end
|
180
261
|
|
181
262
|
def add_breadcrumb(breadcrumb, hint: {})
|
263
|
+
return unless current_client
|
182
264
|
return unless configuration.enabled_in_current_env?
|
183
265
|
|
184
266
|
if before_breadcrumb = current_client.configuration.before_breadcrumb
|
@@ -213,11 +295,15 @@ module Sentry
|
|
213
295
|
|
214
296
|
return unless session
|
215
297
|
session.close
|
216
|
-
|
298
|
+
|
299
|
+
# NOTE: Under some circumstances, session_flusher nilified out of sync
|
300
|
+
# See: https://github.com/getsentry/sentry-ruby/issues/2378
|
301
|
+
# See: https://github.com/getsentry/sentry-ruby/pull/2396
|
302
|
+
Sentry.session_flusher&.add_session(session)
|
217
303
|
end
|
218
304
|
|
219
305
|
def with_session_tracking(&block)
|
220
|
-
return yield unless configuration.
|
306
|
+
return yield unless configuration.session_tracking?
|
221
307
|
|
222
308
|
start_session
|
223
309
|
yield
|
@@ -225,6 +311,56 @@ module Sentry
|
|
225
311
|
end_session
|
226
312
|
end
|
227
313
|
|
314
|
+
def get_traceparent
|
315
|
+
return nil unless current_scope
|
316
|
+
|
317
|
+
current_scope.get_span&.to_sentry_trace ||
|
318
|
+
current_scope.propagation_context.get_traceparent
|
319
|
+
end
|
320
|
+
|
321
|
+
def get_baggage
|
322
|
+
return nil unless current_scope
|
323
|
+
|
324
|
+
current_scope.get_span&.to_baggage ||
|
325
|
+
current_scope.propagation_context.get_baggage&.serialize
|
326
|
+
end
|
327
|
+
|
328
|
+
def get_trace_propagation_headers
|
329
|
+
headers = {}
|
330
|
+
|
331
|
+
traceparent = get_traceparent
|
332
|
+
headers[SENTRY_TRACE_HEADER_NAME] = traceparent if traceparent
|
333
|
+
|
334
|
+
baggage = get_baggage
|
335
|
+
headers[BAGGAGE_HEADER_NAME] = baggage if baggage && !baggage.empty?
|
336
|
+
|
337
|
+
headers
|
338
|
+
end
|
339
|
+
|
340
|
+
def get_trace_propagation_meta
|
341
|
+
get_trace_propagation_headers.map do |k, v|
|
342
|
+
"<meta name=\"#{k}\" content=\"#{v}\">"
|
343
|
+
end.join("\n")
|
344
|
+
end
|
345
|
+
|
346
|
+
def continue_trace(env, **options)
|
347
|
+
configure_scope { |s| s.generate_propagation_context(env) }
|
348
|
+
|
349
|
+
return nil unless configuration.tracing_enabled?
|
350
|
+
|
351
|
+
propagation_context = current_scope.propagation_context
|
352
|
+
return nil unless propagation_context.incoming_trace
|
353
|
+
|
354
|
+
Transaction.new(
|
355
|
+
hub: self,
|
356
|
+
trace_id: propagation_context.trace_id,
|
357
|
+
parent_span_id: propagation_context.parent_span_id,
|
358
|
+
parent_sampled: propagation_context.parent_sampled,
|
359
|
+
baggage: propagation_context.baggage,
|
360
|
+
**options
|
361
|
+
)
|
362
|
+
end
|
363
|
+
|
228
364
|
private
|
229
365
|
|
230
366
|
def current_layer
|
data/lib/sentry/integrable.rb
CHANGED
@@ -14,6 +14,10 @@ module Sentry
|
|
14
14
|
def capture_exception(exception, **options, &block)
|
15
15
|
options[:hint] ||= {}
|
16
16
|
options[:hint][:integration] = integration_name
|
17
|
+
|
18
|
+
# within an integration, we usually intercept uncaught exceptions so we set handled to false.
|
19
|
+
options[:hint][:mechanism] ||= Sentry::Mechanism.new(type: integration_name, handled: false)
|
20
|
+
|
17
21
|
Sentry.capture_exception(exception, **options, &block)
|
18
22
|
end
|
19
23
|
|
@@ -22,5 +26,11 @@ module Sentry
|
|
22
26
|
options[:hint][:integration] = integration_name
|
23
27
|
Sentry.capture_message(message, **options, &block)
|
24
28
|
end
|
29
|
+
|
30
|
+
def capture_check_in(slug, status, **options, &block)
|
31
|
+
options[:hint] ||= {}
|
32
|
+
options[:hint][:integration] = integration_name
|
33
|
+
Sentry.capture_check_in(slug, status, **options, &block)
|
34
|
+
end
|
25
35
|
end
|
26
36
|
end
|
data/lib/sentry/interface.rb
CHANGED
@@ -1,4 +1,5 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
|
+
|
2
3
|
require "set"
|
3
4
|
|
4
5
|
module Sentry
|
@@ -23,17 +24,18 @@ module Sentry
|
|
23
24
|
# @param stacktrace_builder [StacktraceBuilder]
|
24
25
|
# @see SingleExceptionInterface#build_with_stacktrace
|
25
26
|
# @see SingleExceptionInterface#initialize
|
27
|
+
# @param mechanism [Mechanism]
|
26
28
|
# @return [ExceptionInterface]
|
27
|
-
def self.build(exception:, stacktrace_builder:)
|
29
|
+
def self.build(exception:, stacktrace_builder:, mechanism:)
|
28
30
|
exceptions = Sentry::Utils::ExceptionCauseChain.exception_to_array(exception).reverse
|
29
31
|
processed_backtrace_ids = Set.new
|
30
32
|
|
31
33
|
exceptions = exceptions.map do |e|
|
32
34
|
if e.backtrace && !processed_backtrace_ids.include?(e.backtrace.object_id)
|
33
35
|
processed_backtrace_ids << e.backtrace.object_id
|
34
|
-
SingleExceptionInterface.build_with_stacktrace(exception: e, stacktrace_builder: stacktrace_builder)
|
36
|
+
SingleExceptionInterface.build_with_stacktrace(exception: e, stacktrace_builder: stacktrace_builder, mechanism: mechanism)
|
35
37
|
else
|
36
|
-
SingleExceptionInterface.new(exception: exception)
|
38
|
+
SingleExceptionInterface.new(exception: exception, mechanism: mechanism)
|
37
39
|
end
|
38
40
|
end
|
39
41
|
|
@@ -0,0 +1,20 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Sentry
|
4
|
+
class Mechanism < Interface
|
5
|
+
# Generic identifier, mostly the source integration for this exception.
|
6
|
+
# @return [String]
|
7
|
+
attr_accessor :type
|
8
|
+
|
9
|
+
# A manually captured exception has handled set to true,
|
10
|
+
# false if coming from an integration where we intercept an uncaught exception.
|
11
|
+
# Defaults to true here and will be set to false explicitly in integrations.
|
12
|
+
# @return [Boolean]
|
13
|
+
attr_accessor :handled
|
14
|
+
|
15
|
+
def initialize(type: "generic", handled: true)
|
16
|
+
@type = type
|
17
|
+
@handled = handled
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -2,8 +2,8 @@
|
|
2
2
|
|
3
3
|
module Sentry
|
4
4
|
class RequestInterface < Interface
|
5
|
-
REQUEST_ID_HEADERS = %w
|
6
|
-
CONTENT_HEADERS = %w
|
5
|
+
REQUEST_ID_HEADERS = %w[action_dispatch.request_id HTTP_X_REQUEST_ID].freeze
|
6
|
+
CONTENT_HEADERS = %w[CONTENT_TYPE CONTENT_LENGTH].freeze
|
7
7
|
IP_HEADERS = [
|
8
8
|
"REMOTE_ADDR",
|
9
9
|
"HTTP_CLIENT_IP",
|
@@ -59,7 +59,7 @@ module Sentry
|
|
59
59
|
self.query_string = request.query_string
|
60
60
|
end
|
61
61
|
|
62
|
-
self.url = request.scheme && request.url.split(
|
62
|
+
self.url = request.scheme && request.url.split("?").first
|
63
63
|
self.method = request.request_method
|
64
64
|
|
65
65
|
self.headers = filter_and_format_headers(env, send_default_pii)
|
@@ -85,21 +85,21 @@ module Sentry
|
|
85
85
|
env.each_with_object({}) do |(key, value), memo|
|
86
86
|
begin
|
87
87
|
key = key.to_s # rack env can contain symbols
|
88
|
-
next memo[
|
88
|
+
next memo["X-Request-Id"] ||= Utils::RequestId.read_from(env) if Utils::RequestId::REQUEST_ID_HEADERS.include?(key)
|
89
89
|
next if is_server_protocol?(key, value, env["SERVER_PROTOCOL"])
|
90
90
|
next if is_skippable_header?(key)
|
91
91
|
next if key == "HTTP_AUTHORIZATION" && !send_default_pii
|
92
92
|
|
93
93
|
# Rack stores headers as HTTP_WHAT_EVER, we need What-Ever
|
94
94
|
key = key.sub(/^HTTP_/, "")
|
95
|
-
key = key.split(
|
95
|
+
key = key.split("_").map(&:capitalize).join("-")
|
96
96
|
|
97
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.
|
101
101
|
# See: https://github.com/rails/rails/blob/master/actionpack/lib/action_dispatch/middleware/remote_ip.rb#L134
|
102
|
-
Sentry.
|
102
|
+
Sentry.sdk_logger.warn(LOGGER_PROGNAME) { "Error raised while formatting headers: #{e.message}" }
|
103
103
|
next
|
104
104
|
end
|
105
105
|
end
|
@@ -108,7 +108,7 @@ module Sentry
|
|
108
108
|
def is_skippable_header?(key)
|
109
109
|
key.upcase != key || # lower-case envs aren't real http headers
|
110
110
|
key == "HTTP_COOKIE" || # Cookies don't go here, they go somewhere else
|
111
|
-
!(key.start_with?(
|
111
|
+
!(key.start_with?("HTTP_") || CONTENT_HEADERS.include?(key))
|
112
112
|
end
|
113
113
|
|
114
114
|
# In versions < 3, Rack adds in an incorrect HTTP_VERSION key, which causes downstream
|
@@ -120,7 +120,7 @@ module Sentry
|
|
120
120
|
rack_version = Gem::Version.new(::Rack.release)
|
121
121
|
return false if rack_version >= Gem::Version.new("3.0")
|
122
122
|
|
123
|
-
key ==
|
123
|
+
key == "HTTP_VERSION" && value == protocol_version
|
124
124
|
end
|
125
125
|
|
126
126
|
def filter_and_format_env(env, rack_env_whitelist)
|
@@ -7,13 +7,14 @@ module Sentry
|
|
7
7
|
include CustomInspection
|
8
8
|
|
9
9
|
SKIP_INSPECTION_ATTRIBUTES = [:@stacktrace]
|
10
|
-
PROBLEMATIC_LOCAL_VALUE_REPLACEMENT = "[ignored due to error]"
|
11
|
-
OMISSION_MARK = "..."
|
10
|
+
PROBLEMATIC_LOCAL_VALUE_REPLACEMENT = "[ignored due to error]"
|
11
|
+
OMISSION_MARK = "..."
|
12
12
|
MAX_LOCAL_BYTES = 1024
|
13
13
|
|
14
|
-
attr_reader :type, :
|
14
|
+
attr_reader :type, :module, :thread_id, :stacktrace, :mechanism
|
15
|
+
attr_accessor :value
|
15
16
|
|
16
|
-
def initialize(exception:, stacktrace: nil)
|
17
|
+
def initialize(exception:, mechanism:, stacktrace: nil)
|
17
18
|
@type = exception.class.to_s
|
18
19
|
exception_message =
|
19
20
|
if exception.respond_to?(:detailed_message)
|
@@ -21,23 +22,26 @@ module Sentry
|
|
21
22
|
else
|
22
23
|
exception.message || ""
|
23
24
|
end
|
25
|
+
exception_message = exception_message.inspect unless exception_message.is_a?(String)
|
24
26
|
|
25
|
-
@value = exception_message.byteslice(0..Event::MAX_MESSAGE_SIZE_IN_BYTES)
|
27
|
+
@value = Utils::EncodingHelper.encode_to_utf_8(exception_message.byteslice(0..Event::MAX_MESSAGE_SIZE_IN_BYTES))
|
26
28
|
|
27
|
-
@module = exception.class.to_s.split(
|
29
|
+
@module = exception.class.to_s.split("::")[0...-1].join("::")
|
28
30
|
@thread_id = Thread.current.object_id
|
29
31
|
@stacktrace = stacktrace
|
32
|
+
@mechanism = mechanism
|
30
33
|
end
|
31
34
|
|
32
35
|
def to_hash
|
33
36
|
data = super
|
34
37
|
data[:stacktrace] = data[:stacktrace].to_hash if data[:stacktrace]
|
38
|
+
data[:mechanism] = data[:mechanism].to_hash
|
35
39
|
data
|
36
40
|
end
|
37
41
|
|
38
42
|
# patch this method if you want to change an exception's stacktrace frames
|
39
43
|
# also see `StacktraceBuilder.build`.
|
40
|
-
def self.build_with_stacktrace(exception:, stacktrace_builder:)
|
44
|
+
def self.build_with_stacktrace(exception:, stacktrace_builder:, mechanism:)
|
41
45
|
stacktrace = stacktrace_builder.build(backtrace: exception.backtrace)
|
42
46
|
|
43
47
|
if locals = exception.instance_variable_get(:@sentry_locals)
|
@@ -50,7 +54,7 @@ module Sentry
|
|
50
54
|
v = v.byteslice(0..MAX_LOCAL_BYTES - 1) + OMISSION_MARK
|
51
55
|
end
|
52
56
|
|
53
|
-
v
|
57
|
+
Utils::EncodingHelper.encode_to_utf_8(v)
|
54
58
|
rescue StandardError
|
55
59
|
PROBLEMATIC_LOCAL_VALUE_REPLACEMENT
|
56
60
|
end
|
@@ -59,7 +63,7 @@ module Sentry
|
|
59
63
|
stacktrace.frames.last.vars = locals
|
60
64
|
end
|
61
65
|
|
62
|
-
new(exception: exception, stacktrace: stacktrace)
|
66
|
+
new(exception: exception, stacktrace: stacktrace, mechanism: mechanism)
|
63
67
|
end
|
64
68
|
end
|
65
69
|
end
|
@@ -27,8 +27,9 @@ module Sentry
|
|
27
27
|
attr_accessor :abs_path, :context_line, :function, :in_app, :filename,
|
28
28
|
:lineno, :module, :pre_context, :post_context, :vars
|
29
29
|
|
30
|
-
def initialize(project_root, line)
|
30
|
+
def initialize(project_root, line, strip_backtrace_load_path = true)
|
31
31
|
@project_root = project_root
|
32
|
+
@strip_backtrace_load_path = strip_backtrace_load_path
|
32
33
|
|
33
34
|
@abs_path = line.file
|
34
35
|
@function = line.method if line.method
|
@@ -44,6 +45,7 @@ module Sentry
|
|
44
45
|
|
45
46
|
def compute_filename
|
46
47
|
return if abs_path.nil?
|
48
|
+
return abs_path unless @strip_backtrace_load_path
|
47
49
|
|
48
50
|
prefix =
|
49
51
|
if under_project_root? && in_app
|
@@ -17,22 +17,35 @@ module Sentry
|
|
17
17
|
# @return [Proc, nil]
|
18
18
|
attr_reader :backtrace_cleanup_callback
|
19
19
|
|
20
|
+
# @return [Boolean]
|
21
|
+
attr_reader :strip_backtrace_load_path
|
22
|
+
|
20
23
|
# @param project_root [String]
|
21
24
|
# @param app_dirs_pattern [Regexp, nil]
|
22
25
|
# @param linecache [LineCache]
|
23
26
|
# @param context_lines [Integer, nil]
|
24
27
|
# @param backtrace_cleanup_callback [Proc, nil]
|
28
|
+
# @param strip_backtrace_load_path [Boolean]
|
25
29
|
# @see Configuration#project_root
|
26
30
|
# @see Configuration#app_dirs_pattern
|
27
31
|
# @see Configuration#linecache
|
28
32
|
# @see Configuration#context_lines
|
29
33
|
# @see Configuration#backtrace_cleanup_callback
|
30
|
-
|
34
|
+
# @see Configuration#strip_backtrace_load_path
|
35
|
+
def initialize(
|
36
|
+
project_root:,
|
37
|
+
app_dirs_pattern:,
|
38
|
+
linecache:,
|
39
|
+
context_lines:,
|
40
|
+
backtrace_cleanup_callback: nil,
|
41
|
+
strip_backtrace_load_path: true
|
42
|
+
)
|
31
43
|
@project_root = project_root
|
32
44
|
@app_dirs_pattern = app_dirs_pattern
|
33
45
|
@linecache = linecache
|
34
46
|
@context_lines = context_lines
|
35
47
|
@backtrace_cleanup_callback = backtrace_cleanup_callback
|
48
|
+
@strip_backtrace_load_path = strip_backtrace_load_path
|
36
49
|
end
|
37
50
|
|
38
51
|
# Generates a StacktraceInterface with the given backtrace.
|
@@ -62,10 +75,18 @@ module Sentry
|
|
62
75
|
StacktraceInterface.new(frames: frames)
|
63
76
|
end
|
64
77
|
|
78
|
+
# Get the code location hash for a single line for where metrics where added.
|
79
|
+
# @return [Hash]
|
80
|
+
def metrics_code_location(unparsed_line)
|
81
|
+
parsed_line = Backtrace::Line.parse(unparsed_line)
|
82
|
+
frame = convert_parsed_line_into_frame(parsed_line)
|
83
|
+
frame.to_hash.reject { |k, _| %i[project_root in_app].include?(k) }
|
84
|
+
end
|
85
|
+
|
65
86
|
private
|
66
87
|
|
67
88
|
def convert_parsed_line_into_frame(line)
|
68
|
-
frame = StacktraceInterface::Frame.new(project_root, line)
|
89
|
+
frame = StacktraceInterface::Frame.new(project_root, line, strip_backtrace_load_path)
|
69
90
|
frame.set_context(linecache, context_lines) if context_lines
|
70
91
|
frame
|
71
92
|
end
|