sentry-ruby 5.16.1 → 5.21.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 +6 -0
- data/README.md +20 -10
- data/Rakefile +3 -1
- data/bin/console +2 -0
- data/lib/sentry/attachment.rb +40 -0
- data/lib/sentry/background_worker.rb +1 -1
- data/lib/sentry/backpressure_monitor.rb +2 -32
- data/lib/sentry/backtrace.rb +10 -8
- data/lib/sentry/baggage.rb +7 -7
- data/lib/sentry/breadcrumb/sentry_logger.rb +6 -6
- data/lib/sentry/check_in_event.rb +5 -5
- data/lib/sentry/client.rb +61 -11
- data/lib/sentry/configuration.rb +77 -31
- data/lib/sentry/core_ext/object/deep_dup.rb +1 -1
- data/lib/sentry/cron/monitor_check_ins.rb +3 -1
- data/lib/sentry/cron/monitor_config.rb +1 -1
- data/lib/sentry/cron/monitor_schedule.rb +1 -1
- 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 +20 -18
- data/lib/sentry/faraday.rb +77 -0
- data/lib/sentry/graphql.rb +9 -0
- data/lib/sentry/hub.rb +23 -3
- data/lib/sentry/integrable.rb +4 -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 +7 -7
- data/lib/sentry/interfaces/single_exception.rb +9 -7
- data/lib/sentry/interfaces/stacktrace.rb +3 -1
- data/lib/sentry/interfaces/stacktrace_builder.rb +23 -2
- 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 +43 -0
- data/lib/sentry/metrics.rb +56 -0
- data/lib/sentry/net/http.rb +18 -39
- data/lib/sentry/profiler/helpers.rb +46 -0
- data/lib/sentry/profiler.rb +25 -56
- data/lib/sentry/propagation_context.rb +10 -9
- data/lib/sentry/puma.rb +1 -1
- data/lib/sentry/rack/capture_exceptions.rb +16 -4
- data/lib/sentry/rack.rb +2 -2
- data/lib/sentry/rake.rb +4 -2
- data/lib/sentry/redis.rb +2 -1
- data/lib/sentry/release_detector.rb +4 -4
- data/lib/sentry/scope.rb +36 -26
- data/lib/sentry/session.rb +2 -2
- data/lib/sentry/session_flusher.rb +7 -39
- data/lib/sentry/span.rb +46 -5
- data/lib/sentry/test_helper.rb +5 -2
- data/lib/sentry/threaded_periodic_worker.rb +39 -0
- data/lib/sentry/transaction.rb +19 -17
- data/lib/sentry/transaction_event.rb +6 -2
- data/lib/sentry/transport/configuration.rb +0 -1
- data/lib/sentry/transport/http_transport.rb +12 -12
- data/lib/sentry/transport.rb +18 -26
- data/lib/sentry/utils/argument_checking_helper.rb +6 -0
- data/lib/sentry/utils/env_helper.rb +21 -0
- data/lib/sentry/utils/http_tracing.rb +41 -0
- data/lib/sentry/utils/logging_helper.rb +0 -4
- data/lib/sentry/utils/real_ip.rb +2 -2
- data/lib/sentry/utils/request_id.rb +1 -1
- data/lib/sentry/vernier/output.rb +89 -0
- data/lib/sentry/vernier/profiler.rb +125 -0
- data/lib/sentry/version.rb +1 -1
- data/lib/sentry-ruby.rb +38 -6
- data/sentry-ruby-core.gemspec +3 -1
- data/sentry-ruby.gemspec +15 -6
- metadata +44 -7
data/lib/sentry/configuration.rb
CHANGED
@@ -3,11 +3,13 @@
|
|
3
3
|
require "concurrent/utility/processor_counter"
|
4
4
|
|
5
5
|
require "sentry/utils/exception_cause_chain"
|
6
|
-
require
|
6
|
+
require "sentry/utils/custom_inspection"
|
7
|
+
require "sentry/utils/env_helper"
|
7
8
|
require "sentry/dsn"
|
8
9
|
require "sentry/release_detector"
|
9
10
|
require "sentry/transport/configuration"
|
10
11
|
require "sentry/cron/configuration"
|
12
|
+
require "sentry/metrics/configuration"
|
11
13
|
require "sentry/linecache"
|
12
14
|
require "sentry/interfaces/stacktrace_builder"
|
13
15
|
|
@@ -21,6 +23,8 @@ module Sentry
|
|
21
23
|
# have an `engines` dir at the root of your project, you may want
|
22
24
|
# to set this to something like /(app|config|engines|lib)/
|
23
25
|
#
|
26
|
+
# The default is value is /(bin|exe|app|config|lib|test|spec)/
|
27
|
+
#
|
24
28
|
# @return [Regexp, nil]
|
25
29
|
attr_accessor :app_dirs_pattern
|
26
30
|
|
@@ -186,6 +190,11 @@ module Sentry
|
|
186
190
|
# @return [String]
|
187
191
|
attr_accessor :project_root
|
188
192
|
|
193
|
+
# Whether to strip the load path while constructing the backtrace frame filename.
|
194
|
+
# Defaults to true.
|
195
|
+
# @return [Boolean]
|
196
|
+
attr_accessor :strip_backtrace_load_path
|
197
|
+
|
189
198
|
# Insert sentry-trace to outgoing requests' headers
|
190
199
|
# @return [Boolean]
|
191
200
|
attr_accessor :propagate_traces
|
@@ -235,6 +244,10 @@ module Sentry
|
|
235
244
|
# @return [Cron::Configuration]
|
236
245
|
attr_reader :cron
|
237
246
|
|
247
|
+
# Metrics related configuration.
|
248
|
+
# @return [Metrics::Configuration]
|
249
|
+
attr_reader :metrics
|
250
|
+
|
238
251
|
# Take a float between 0.0 and 1.0 as the sample rate for tracing events (transactions).
|
239
252
|
# @return [Float, nil]
|
240
253
|
attr_reader :traces_sample_rate
|
@@ -278,6 +291,10 @@ module Sentry
|
|
278
291
|
# @return [Symbol]
|
279
292
|
attr_reader :instrumenter
|
280
293
|
|
294
|
+
# The profiler class
|
295
|
+
# @return [Class]
|
296
|
+
attr_reader :profiler_class
|
297
|
+
|
281
298
|
# Take a float between 0.0 and 1.0 as the sample rate for capturing profiles.
|
282
299
|
# Note that this rate is relative to traces_sample_rate / traces_sampler,
|
283
300
|
# i.e. the profile is sampled by this rate after the transaction is sampled.
|
@@ -297,38 +314,40 @@ module Sentry
|
|
297
314
|
# But they are mostly considered as noise and should be ignored by default
|
298
315
|
# Please see https://github.com/getsentry/sentry-ruby/pull/2026 for more information
|
299
316
|
PUMA_IGNORE_DEFAULT = [
|
300
|
-
|
301
|
-
|
302
|
-
|
317
|
+
"Puma::MiniSSL::SSLError",
|
318
|
+
"Puma::HttpParserError",
|
319
|
+
"Puma::HttpParserError501"
|
303
320
|
].freeze
|
304
321
|
|
305
322
|
# Most of these errors generate 4XX responses. In general, Sentry clients
|
306
323
|
# only automatically report 5xx responses.
|
307
324
|
IGNORE_DEFAULT = [
|
308
|
-
|
309
|
-
|
310
|
-
|
311
|
-
|
325
|
+
"Mongoid::Errors::DocumentNotFound",
|
326
|
+
"Rack::QueryParser::InvalidParameterError",
|
327
|
+
"Rack::QueryParser::ParameterTypeError",
|
328
|
+
"Sinatra::NotFound"
|
312
329
|
].freeze
|
313
330
|
|
314
|
-
RACK_ENV_WHITELIST_DEFAULT = %w
|
331
|
+
RACK_ENV_WHITELIST_DEFAULT = %w[
|
315
332
|
REMOTE_ADDR
|
316
333
|
SERVER_NAME
|
317
334
|
SERVER_PORT
|
318
|
-
|
335
|
+
].freeze
|
319
336
|
|
320
337
|
HEROKU_DYNO_METADATA_MESSAGE = "You are running on Heroku but haven't enabled Dyno Metadata. For Sentry's "\
|
321
|
-
"release detection to work correctly, please run `heroku labs:enable runtime-dyno-metadata`"
|
338
|
+
"release detection to work correctly, please run `heroku labs:enable runtime-dyno-metadata`"
|
322
339
|
|
323
|
-
LOG_PREFIX = "** [Sentry] "
|
324
|
-
MODULE_SEPARATOR = "::"
|
340
|
+
LOG_PREFIX = "** [Sentry] "
|
341
|
+
MODULE_SEPARATOR = "::"
|
325
342
|
SKIP_INSPECTION_ATTRIBUTES = [:@linecache, :@stacktrace_builder]
|
326
343
|
|
327
344
|
INSTRUMENTERS = [:sentry, :otel]
|
328
345
|
|
329
|
-
PROPAGATION_TARGETS_MATCH_ALL =
|
346
|
+
PROPAGATION_TARGETS_MATCH_ALL = /.*/
|
330
347
|
|
331
|
-
DEFAULT_PATCHES = %i
|
348
|
+
DEFAULT_PATCHES = %i[redis puma http].freeze
|
349
|
+
|
350
|
+
APP_DIRS_PATTERN = /(bin|exe|app|config|lib|test|spec)/
|
332
351
|
|
333
352
|
class << self
|
334
353
|
# Post initialization callbacks are called at the end of initialization process
|
@@ -337,18 +356,19 @@ module Sentry
|
|
337
356
|
@post_initialization_callbacks ||= []
|
338
357
|
end
|
339
358
|
|
340
|
-
|
359
|
+
# allow extensions to add their hooks to the Configuration class
|
341
360
|
def add_post_initialization_callback(&block)
|
342
361
|
post_initialization_callbacks << block
|
343
362
|
end
|
344
363
|
end
|
345
364
|
|
346
365
|
def initialize
|
347
|
-
self.app_dirs_pattern =
|
348
|
-
self.debug =
|
349
|
-
self.background_worker_threads =
|
366
|
+
self.app_dirs_pattern = APP_DIRS_PATTERN
|
367
|
+
self.debug = Sentry::Utils::EnvHelper.env_to_bool(ENV["SENTRY_DEBUG"])
|
368
|
+
self.background_worker_threads = (processor_count / 2.0).ceil
|
350
369
|
self.background_worker_max_queue = BackgroundWorker::DEFAULT_MAX_QUEUE
|
351
370
|
self.backtrace_cleanup_callback = nil
|
371
|
+
self.strip_backtrace_load_path = true
|
352
372
|
self.max_breadcrumbs = BreadcrumbBuffer::DEFAULT_SIZE
|
353
373
|
self.breadcrumbs_logger = []
|
354
374
|
self.context_lines = 3
|
@@ -371,8 +391,11 @@ module Sentry
|
|
371
391
|
self.auto_session_tracking = true
|
372
392
|
self.enable_backpressure_handling = false
|
373
393
|
self.trusted_proxies = []
|
374
|
-
self.dsn = ENV[
|
375
|
-
|
394
|
+
self.dsn = ENV["SENTRY_DSN"]
|
395
|
+
|
396
|
+
spotlight_env = ENV["SENTRY_SPOTLIGHT"]
|
397
|
+
spotlight_bool = Sentry::Utils::EnvHelper.env_to_bool(spotlight_env, strict: true)
|
398
|
+
self.spotlight = spotlight_bool.nil? ? (spotlight_env || false) : spotlight_bool
|
376
399
|
self.server_name = server_name_from_env
|
377
400
|
self.instrumenter = :sentry
|
378
401
|
self.trace_propagation_targets = [PROPAGATION_TARGETS_MATCH_ALL]
|
@@ -384,8 +407,11 @@ module Sentry
|
|
384
407
|
self.traces_sampler = nil
|
385
408
|
self.enable_tracing = nil
|
386
409
|
|
410
|
+
self.profiler_class = Sentry::Profiler
|
411
|
+
|
387
412
|
@transport = Transport::Configuration.new
|
388
413
|
@cron = Cron::Configuration.new
|
414
|
+
@metrics = Metrics::Configuration.new
|
389
415
|
@gem_specs = Hash[Gem::Specification.map { |spec| [spec.name, spec.version.to_s] }] if Gem::Specification.respond_to?(:map)
|
390
416
|
|
391
417
|
run_post_initialization_callbacks
|
@@ -478,10 +504,26 @@ module Sentry
|
|
478
504
|
@profiles_sample_rate = profiles_sample_rate
|
479
505
|
end
|
480
506
|
|
507
|
+
def profiler_class=(profiler_class)
|
508
|
+
if profiler_class == Sentry::Vernier::Profiler
|
509
|
+
begin
|
510
|
+
require "vernier"
|
511
|
+
rescue LoadError
|
512
|
+
raise ArgumentError, "Please add the 'vernier' gem to your Gemfile to use the Vernier profiler with Sentry."
|
513
|
+
end
|
514
|
+
end
|
515
|
+
|
516
|
+
@profiler_class = profiler_class
|
517
|
+
end
|
518
|
+
|
481
519
|
def sending_allowed?
|
520
|
+
spotlight || sending_to_dsn_allowed?
|
521
|
+
end
|
522
|
+
|
523
|
+
def sending_to_dsn_allowed?
|
482
524
|
@errors = []
|
483
525
|
|
484
|
-
|
526
|
+
valid? && capture_in_environment?
|
485
527
|
end
|
486
528
|
|
487
529
|
def sample_allowed?
|
@@ -490,6 +532,10 @@ module Sentry
|
|
490
532
|
Random.rand < sample_rate
|
491
533
|
end
|
492
534
|
|
535
|
+
def session_tracking?
|
536
|
+
auto_session_tracking && enabled_in_current_env?
|
537
|
+
end
|
538
|
+
|
493
539
|
def exception_class_allowed?(exc)
|
494
540
|
if exc.is_a?(Sentry::Error)
|
495
541
|
# Try to prevent error reporting loops
|
@@ -541,7 +587,8 @@ module Sentry
|
|
541
587
|
app_dirs_pattern: @app_dirs_pattern,
|
542
588
|
linecache: @linecache,
|
543
589
|
context_lines: @context_lines,
|
544
|
-
backtrace_cleanup_callback: @backtrace_cleanup_callback
|
590
|
+
backtrace_cleanup_callback: @backtrace_cleanup_callback,
|
591
|
+
strip_backtrace_load_path: @strip_backtrace_load_path
|
545
592
|
)
|
546
593
|
end
|
547
594
|
|
@@ -566,12 +613,6 @@ module Sentry
|
|
566
613
|
|
567
614
|
private
|
568
615
|
|
569
|
-
def check_callable!(name, value)
|
570
|
-
unless value == nil || value.respond_to?(:call)
|
571
|
-
raise ArgumentError, "#{name} must be callable (or nil to disable)"
|
572
|
-
end
|
573
|
-
end
|
574
|
-
|
575
616
|
def init_dsn(dsn_string)
|
576
617
|
return if dsn_string.nil? || dsn_string.empty?
|
577
618
|
|
@@ -624,12 +665,12 @@ module Sentry
|
|
624
665
|
end
|
625
666
|
|
626
667
|
def environment_from_env
|
627
|
-
ENV[
|
668
|
+
ENV["SENTRY_CURRENT_ENV"] || ENV["SENTRY_ENVIRONMENT"] || ENV["RAILS_ENV"] || ENV["RACK_ENV"] || "development"
|
628
669
|
end
|
629
670
|
|
630
671
|
def server_name_from_env
|
631
672
|
if running_on_heroku?
|
632
|
-
ENV[
|
673
|
+
ENV["DYNO"]
|
633
674
|
else
|
634
675
|
# Try to resolve the hostname to an FQDN, but fall back to whatever
|
635
676
|
# the load name is.
|
@@ -646,5 +687,10 @@ module Sentry
|
|
646
687
|
instance_eval(&hook)
|
647
688
|
end
|
648
689
|
end
|
690
|
+
|
691
|
+
def processor_count
|
692
|
+
available_processor_count = Concurrent.available_processor_count if Concurrent.respond_to?(:available_processor_count)
|
693
|
+
available_processor_count || Concurrent.processor_count
|
694
|
+
end
|
649
695
|
end
|
650
696
|
end
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module Sentry
|
2
4
|
module Cron
|
3
5
|
module MonitorCheckIns
|
@@ -57,7 +59,7 @@ module Sentry
|
|
57
59
|
|
58
60
|
def sentry_monitor_slug(name: self.name)
|
59
61
|
@sentry_monitor_slug ||= begin
|
60
|
-
slug = name.gsub(
|
62
|
+
slug = name.gsub("::", "-").downcase
|
61
63
|
slug[-MAX_SLUG_LENGTH..-1] || slug
|
62
64
|
end
|
63
65
|
end
|
data/lib/sentry/dsn.rb
CHANGED
@@ -4,8 +4,8 @@ require "uri"
|
|
4
4
|
|
5
5
|
module Sentry
|
6
6
|
class DSN
|
7
|
-
PORT_MAP = {
|
8
|
-
REQUIRED_ATTRIBUTES = %w
|
7
|
+
PORT_MAP = { "http" => 80, "https" => 443 }.freeze
|
8
|
+
REQUIRED_ATTRIBUTES = %w[host path public_key project_id].freeze
|
9
9
|
|
10
10
|
attr_reader :scheme, :secret_key, :port, *REQUIRED_ATTRIBUTES
|
11
11
|
|
@@ -13,7 +13,7 @@ module Sentry
|
|
13
13
|
@raw_value = dsn_string
|
14
14
|
|
15
15
|
uri = URI.parse(dsn_string)
|
16
|
-
uri_path = uri.path.split(
|
16
|
+
uri_path = uri.path.split("/")
|
17
17
|
|
18
18
|
if uri.user
|
19
19
|
# DSN-style string
|
@@ -25,7 +25,7 @@ module Sentry
|
|
25
25
|
@scheme = uri.scheme
|
26
26
|
@host = uri.host
|
27
27
|
@port = uri.port if uri.port
|
28
|
-
@path = uri_path.join(
|
28
|
+
@path = uri_path.join("/")
|
29
29
|
end
|
30
30
|
|
31
31
|
def valid?
|
@@ -0,0 +1,88 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Sentry
|
4
|
+
# @api private
|
5
|
+
class Envelope::Item
|
6
|
+
STACKTRACE_FRAME_LIMIT_ON_OVERSIZED_PAYLOAD = 500
|
7
|
+
MAX_SERIALIZED_PAYLOAD_SIZE = 1024 * 1000
|
8
|
+
|
9
|
+
SIZE_LIMITS = Hash.new(MAX_SERIALIZED_PAYLOAD_SIZE).update(
|
10
|
+
"profile" => 1024 * 1000 * 50
|
11
|
+
)
|
12
|
+
|
13
|
+
attr_reader :size_limit, :headers, :payload, :type, :data_category
|
14
|
+
|
15
|
+
# rate limits and client reports use the data_category rather than envelope item type
|
16
|
+
def self.data_category(type)
|
17
|
+
case type
|
18
|
+
when "session", "attachment", "transaction", "profile", "span" then type
|
19
|
+
when "sessions" then "session"
|
20
|
+
when "check_in" then "monitor"
|
21
|
+
when "statsd", "metric_meta" then "metric_bucket"
|
22
|
+
when "event" then "error"
|
23
|
+
when "client_report" then "internal"
|
24
|
+
else "default"
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
def initialize(headers, payload)
|
29
|
+
@headers = headers
|
30
|
+
@payload = payload
|
31
|
+
@type = headers[:type] || "event"
|
32
|
+
@data_category = self.class.data_category(type)
|
33
|
+
@size_limit = SIZE_LIMITS[type]
|
34
|
+
end
|
35
|
+
|
36
|
+
def to_s
|
37
|
+
[JSON.generate(@headers), @payload.is_a?(String) ? @payload : JSON.generate(@payload)].join("\n")
|
38
|
+
end
|
39
|
+
|
40
|
+
def serialize
|
41
|
+
result = to_s
|
42
|
+
|
43
|
+
if result.bytesize > size_limit
|
44
|
+
remove_breadcrumbs!
|
45
|
+
result = to_s
|
46
|
+
end
|
47
|
+
|
48
|
+
if result.bytesize > size_limit
|
49
|
+
reduce_stacktrace!
|
50
|
+
result = to_s
|
51
|
+
end
|
52
|
+
|
53
|
+
[result, result.bytesize > size_limit]
|
54
|
+
end
|
55
|
+
|
56
|
+
def size_breakdown
|
57
|
+
payload.map do |key, value|
|
58
|
+
"#{key}: #{JSON.generate(value).bytesize}"
|
59
|
+
end.join(", ")
|
60
|
+
end
|
61
|
+
|
62
|
+
private
|
63
|
+
|
64
|
+
def remove_breadcrumbs!
|
65
|
+
if payload.key?(:breadcrumbs)
|
66
|
+
payload.delete(:breadcrumbs)
|
67
|
+
elsif payload.key?("breadcrumbs")
|
68
|
+
payload.delete("breadcrumbs")
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
def reduce_stacktrace!
|
73
|
+
if exceptions = payload.dig(:exception, :values) || payload.dig("exception", "values")
|
74
|
+
exceptions.each do |exception|
|
75
|
+
# in most cases there is only one exception (2 or 3 when have multiple causes), so we won't loop through this double condition much
|
76
|
+
traces = exception.dig(:stacktrace, :frames) || exception.dig("stacktrace", "frames")
|
77
|
+
|
78
|
+
if traces && traces.size > STACKTRACE_FRAME_LIMIT_ON_OVERSIZED_PAYLOAD
|
79
|
+
size_on_both_ends = STACKTRACE_FRAME_LIMIT_ON_OVERSIZED_PAYLOAD / 2
|
80
|
+
traces.replace(
|
81
|
+
traces[0..(size_on_both_ends - 1)] + traces[-size_on_both_ends..-1],
|
82
|
+
)
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
data/lib/sentry/envelope.rb
CHANGED
@@ -3,74 +3,6 @@
|
|
3
3
|
module Sentry
|
4
4
|
# @api private
|
5
5
|
class Envelope
|
6
|
-
class Item
|
7
|
-
STACKTRACE_FRAME_LIMIT_ON_OVERSIZED_PAYLOAD = 500
|
8
|
-
MAX_SERIALIZED_PAYLOAD_SIZE = 1024 * 1000
|
9
|
-
|
10
|
-
attr_accessor :headers, :payload
|
11
|
-
|
12
|
-
def initialize(headers, payload)
|
13
|
-
@headers = headers
|
14
|
-
@payload = payload
|
15
|
-
end
|
16
|
-
|
17
|
-
def type
|
18
|
-
@headers[:type] || 'event'
|
19
|
-
end
|
20
|
-
|
21
|
-
def to_s
|
22
|
-
[JSON.generate(@headers), JSON.generate(@payload)].join("\n")
|
23
|
-
end
|
24
|
-
|
25
|
-
def serialize
|
26
|
-
result = to_s
|
27
|
-
|
28
|
-
if result.bytesize > MAX_SERIALIZED_PAYLOAD_SIZE
|
29
|
-
remove_breadcrumbs!
|
30
|
-
result = to_s
|
31
|
-
end
|
32
|
-
|
33
|
-
if result.bytesize > MAX_SERIALIZED_PAYLOAD_SIZE
|
34
|
-
reduce_stacktrace!
|
35
|
-
result = to_s
|
36
|
-
end
|
37
|
-
|
38
|
-
[result, result.bytesize > MAX_SERIALIZED_PAYLOAD_SIZE]
|
39
|
-
end
|
40
|
-
|
41
|
-
def size_breakdown
|
42
|
-
payload.map do |key, value|
|
43
|
-
"#{key}: #{JSON.generate(value).bytesize}"
|
44
|
-
end.join(", ")
|
45
|
-
end
|
46
|
-
|
47
|
-
private
|
48
|
-
|
49
|
-
def remove_breadcrumbs!
|
50
|
-
if payload.key?(:breadcrumbs)
|
51
|
-
payload.delete(:breadcrumbs)
|
52
|
-
elsif payload.key?("breadcrumbs")
|
53
|
-
payload.delete("breadcrumbs")
|
54
|
-
end
|
55
|
-
end
|
56
|
-
|
57
|
-
def reduce_stacktrace!
|
58
|
-
if exceptions = payload.dig(:exception, :values) || payload.dig("exception", "values")
|
59
|
-
exceptions.each do |exception|
|
60
|
-
# in most cases there is only one exception (2 or 3 when have multiple causes), so we won't loop through this double condition much
|
61
|
-
traces = exception.dig(:stacktrace, :frames) || exception.dig("stacktrace", "frames")
|
62
|
-
|
63
|
-
if traces && traces.size > STACKTRACE_FRAME_LIMIT_ON_OVERSIZED_PAYLOAD
|
64
|
-
size_on_both_ends = STACKTRACE_FRAME_LIMIT_ON_OVERSIZED_PAYLOAD / 2
|
65
|
-
traces.replace(
|
66
|
-
traces[0..(size_on_both_ends - 1)] + traces[-size_on_both_ends..-1],
|
67
|
-
)
|
68
|
-
end
|
69
|
-
end
|
70
|
-
end
|
71
|
-
end
|
72
|
-
end
|
73
|
-
|
74
6
|
attr_accessor :headers, :items
|
75
7
|
|
76
8
|
def initialize(headers = {})
|
@@ -91,3 +23,5 @@ module Sentry
|
|
91
23
|
end
|
92
24
|
end
|
93
25
|
end
|
26
|
+
|
27
|
+
require_relative "envelope/item"
|
data/lib/sentry/error_event.rb
CHANGED
@@ -27,12 +27,12 @@ module Sentry
|
|
27
27
|
end
|
28
28
|
|
29
29
|
# @!visibility private
|
30
|
-
def add_exception_interface(exception)
|
30
|
+
def add_exception_interface(exception, mechanism:)
|
31
31
|
if exception.respond_to?(:sentry_context)
|
32
32
|
@extra.merge!(exception.sentry_context)
|
33
33
|
end
|
34
34
|
|
35
|
-
@exception = Sentry::ExceptionInterface.build(exception: exception, stacktrace_builder: @stacktrace_builder)
|
35
|
+
@exception = Sentry::ExceptionInterface.build(exception: exception, stacktrace_builder: @stacktrace_builder, mechanism: mechanism)
|
36
36
|
end
|
37
37
|
end
|
38
38
|
end
|
data/lib/sentry/event.rb
CHANGED
@@ -1,12 +1,12 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require
|
4
|
-
require
|
5
|
-
require
|
6
|
-
require
|
7
|
-
require
|
8
|
-
require
|
9
|
-
require
|
3
|
+
require "socket"
|
4
|
+
require "securerandom"
|
5
|
+
require "sentry/interface"
|
6
|
+
require "sentry/backtrace"
|
7
|
+
require "sentry/utils/real_ip"
|
8
|
+
require "sentry/utils/request_id"
|
9
|
+
require "sentry/utils/custom_inspection"
|
10
10
|
|
11
11
|
module Sentry
|
12
12
|
# This is an abstract class that defines the shared attributes of an event.
|
@@ -14,16 +14,16 @@ module Sentry
|
|
14
14
|
class Event
|
15
15
|
TYPE = "event"
|
16
16
|
# These are readable attributes.
|
17
|
-
SERIALIZEABLE_ATTRIBUTES = %i
|
17
|
+
SERIALIZEABLE_ATTRIBUTES = %i[
|
18
18
|
event_id level timestamp
|
19
19
|
release environment server_name modules
|
20
20
|
message user tags contexts extra
|
21
21
|
fingerprint breadcrumbs transaction transaction_info
|
22
22
|
platform sdk type
|
23
|
-
|
23
|
+
]
|
24
24
|
|
25
25
|
# These are writable attributes.
|
26
|
-
WRITER_ATTRIBUTES = SERIALIZEABLE_ATTRIBUTES - %i
|
26
|
+
WRITER_ATTRIBUTES = SERIALIZEABLE_ATTRIBUTES - %i[type timestamp level]
|
27
27
|
|
28
28
|
MAX_MESSAGE_SIZE_IN_BYTES = 1024 * 8
|
29
29
|
|
@@ -42,6 +42,9 @@ module Sentry
|
|
42
42
|
# @return [Hash, nil]
|
43
43
|
attr_accessor :dynamic_sampling_context
|
44
44
|
|
45
|
+
# @return [Array<Attachment>]
|
46
|
+
attr_accessor :attachments
|
47
|
+
|
45
48
|
# @param configuration [Configuration]
|
46
49
|
# @param integration_meta [Hash, nil]
|
47
50
|
# @param message [String, nil]
|
@@ -57,6 +60,7 @@ module Sentry
|
|
57
60
|
@extra = {}
|
58
61
|
@contexts = {}
|
59
62
|
@tags = {}
|
63
|
+
@attachments = []
|
60
64
|
|
61
65
|
@fingerprint = []
|
62
66
|
@dynamic_sampling_context = nil
|
@@ -104,9 +108,7 @@ module Sentry
|
|
104
108
|
unless request || env.empty?
|
105
109
|
add_request_interface(env)
|
106
110
|
|
107
|
-
if @send_default_pii
|
108
|
-
user[:ip_address] = calculate_real_ip_from_rack(env)
|
109
|
-
end
|
111
|
+
user[:ip_address] ||= calculate_real_ip_from_rack(env) if @send_default_pii
|
110
112
|
|
111
113
|
if request_id = Utils::RequestId.read_from(env)
|
112
114
|
tags[:request_id] = request_id
|
@@ -145,11 +147,11 @@ module Sentry
|
|
145
147
|
# REMOTE_ADDR to determine the Event IP, and must use other headers instead.
|
146
148
|
def calculate_real_ip_from_rack(env)
|
147
149
|
Utils::RealIp.new(
|
148
|
-
:
|
149
|
-
:
|
150
|
-
:
|
151
|
-
:
|
152
|
-
:
|
150
|
+
remote_addr: env["REMOTE_ADDR"],
|
151
|
+
client_ip: env["HTTP_CLIENT_IP"],
|
152
|
+
real_ip: env["HTTP_X_REAL_IP"],
|
153
|
+
forwarded_for: env["HTTP_X_FORWARDED_FOR"],
|
154
|
+
trusted_proxies: @trusted_proxies
|
153
155
|
).calculate_ip
|
154
156
|
end
|
155
157
|
end
|
@@ -0,0 +1,77 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Sentry
|
4
|
+
module Faraday
|
5
|
+
OP_NAME = "http.client"
|
6
|
+
|
7
|
+
module Connection
|
8
|
+
# Since there's no way to preconfigure Faraday connections and add our instrumentation
|
9
|
+
# by default, we need to extend the connection constructor and do it there
|
10
|
+
#
|
11
|
+
# @see https://lostisland.github.io/faraday/#/customization/index?id=configuration
|
12
|
+
def initialize(url = nil, options = nil)
|
13
|
+
super
|
14
|
+
|
15
|
+
# Ensure that we attach instrumentation only if the adapter is not net/http
|
16
|
+
# because if is is, then the net/http instrumentation will take care of it
|
17
|
+
if builder.adapter.name != "Faraday::Adapter::NetHttp"
|
18
|
+
# Make sure that it's going to be the first middleware so that it can capture
|
19
|
+
# the entire request processing involving other middlewares
|
20
|
+
builder.insert(0, ::Faraday::Request::Instrumentation, name: OP_NAME, instrumenter: Instrumenter.new)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
class Instrumenter
|
26
|
+
SPAN_ORIGIN = "auto.http.faraday"
|
27
|
+
BREADCRUMB_CATEGORY = "http"
|
28
|
+
|
29
|
+
include Utils::HttpTracing
|
30
|
+
|
31
|
+
def instrument(op_name, env, &block)
|
32
|
+
return block.call unless Sentry.initialized?
|
33
|
+
|
34
|
+
Sentry.with_child_span(op: op_name, start_timestamp: Sentry.utc_now.to_f, origin: SPAN_ORIGIN) do |sentry_span|
|
35
|
+
request_info = extract_request_info(env)
|
36
|
+
|
37
|
+
if propagate_trace?(request_info[:url])
|
38
|
+
set_propagation_headers(env[:request_headers])
|
39
|
+
end
|
40
|
+
|
41
|
+
res = block.call
|
42
|
+
response_status = res.status
|
43
|
+
|
44
|
+
if record_sentry_breadcrumb?
|
45
|
+
record_sentry_breadcrumb(request_info, response_status)
|
46
|
+
end
|
47
|
+
|
48
|
+
if sentry_span
|
49
|
+
set_span_info(sentry_span, request_info, response_status)
|
50
|
+
end
|
51
|
+
|
52
|
+
res
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
private
|
57
|
+
|
58
|
+
def extract_request_info(env)
|
59
|
+
url = env[:url].scheme + "://" + env[:url].host + env[:url].path
|
60
|
+
result = { method: env[:method].to_s.upcase, url: url }
|
61
|
+
|
62
|
+
if Sentry.configuration.send_default_pii
|
63
|
+
result[:query] = env[:url].query
|
64
|
+
result[:body] = env[:body]
|
65
|
+
end
|
66
|
+
|
67
|
+
result
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
Sentry.register_patch(:faraday) do
|
74
|
+
if defined?(::Faraday)
|
75
|
+
::Faraday::Connection.prepend(Sentry::Faraday::Connection)
|
76
|
+
end
|
77
|
+
end
|
@@ -0,0 +1,9 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
Sentry.register_patch(:graphql) do |config|
|
4
|
+
if defined?(::GraphQL::Schema) && defined?(::GraphQL::Tracing::SentryTrace) && ::GraphQL::Schema.respond_to?(:trace_with)
|
5
|
+
::GraphQL::Schema.trace_with(::GraphQL::Tracing::SentryTrace, set_transaction_name: true)
|
6
|
+
else
|
7
|
+
config.logger.warn(Sentry::LOGGER_PROGNAME) { "You tried to enable the GraphQL integration but no GraphQL gem was detected. Make sure you have the `graphql` gem (>= 2.2.6) in your Gemfile." }
|
8
|
+
end
|
9
|
+
end
|