sentry-ruby 5.19.0 → 5.21.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (54) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile +3 -0
  3. data/Rakefile +2 -0
  4. data/bin/console +1 -0
  5. data/lib/sentry/attachment.rb +3 -5
  6. data/lib/sentry/backtrace.rb +3 -5
  7. data/lib/sentry/baggage.rb +7 -7
  8. data/lib/sentry/breadcrumb/sentry_logger.rb +6 -6
  9. data/lib/sentry/check_in_event.rb +4 -4
  10. data/lib/sentry/client.rb +9 -9
  11. data/lib/sentry/configuration.rb +52 -19
  12. data/lib/sentry/core_ext/object/deep_dup.rb +1 -1
  13. data/lib/sentry/cron/monitor_check_ins.rb +3 -1
  14. data/lib/sentry/cron/monitor_config.rb +1 -1
  15. data/lib/sentry/dsn.rb +3 -3
  16. data/lib/sentry/envelope/item.rb +88 -0
  17. data/lib/sentry/envelope.rb +2 -85
  18. data/lib/sentry/event.rb +7 -7
  19. data/lib/sentry/graphql.rb +1 -1
  20. data/lib/sentry/hub.rb +8 -1
  21. data/lib/sentry/interfaces/mechanism.rb +1 -1
  22. data/lib/sentry/interfaces/request.rb +5 -5
  23. data/lib/sentry/interfaces/single_exception.rb +3 -3
  24. data/lib/sentry/interfaces/stacktrace.rb +3 -1
  25. data/lib/sentry/interfaces/stacktrace_builder.rb +15 -2
  26. data/lib/sentry/logger.rb +1 -1
  27. data/lib/sentry/metrics/aggregator.rb +12 -12
  28. data/lib/sentry/metrics/set_metric.rb +2 -2
  29. data/lib/sentry/metrics.rb +15 -15
  30. data/lib/sentry/net/http.rb +1 -1
  31. data/lib/sentry/profiler/helpers.rb +46 -0
  32. data/lib/sentry/profiler.rb +25 -56
  33. data/lib/sentry/propagation_context.rb +1 -1
  34. data/lib/sentry/rack/capture_exceptions.rb +2 -2
  35. data/lib/sentry/rack.rb +2 -2
  36. data/lib/sentry/rake.rb +2 -2
  37. data/lib/sentry/release_detector.rb +4 -4
  38. data/lib/sentry/scope.rb +1 -0
  39. data/lib/sentry/session_flusher.rb +1 -1
  40. data/lib/sentry/span.rb +6 -0
  41. data/lib/sentry/test_helper.rb +3 -1
  42. data/lib/sentry/transaction.rb +4 -4
  43. data/lib/sentry/transaction_event.rb +1 -2
  44. data/lib/sentry/transport/http_transport.rb +12 -12
  45. data/lib/sentry/transport.rb +4 -4
  46. data/lib/sentry/utils/env_helper.rb +21 -0
  47. data/lib/sentry/utils/real_ip.rb +1 -1
  48. data/lib/sentry/vernier/output.rb +89 -0
  49. data/lib/sentry/vernier/profiler.rb +125 -0
  50. data/lib/sentry/version.rb +1 -1
  51. data/lib/sentry-ruby.rb +5 -4
  52. data/sentry-ruby-core.gemspec +3 -1
  53. data/sentry-ruby.gemspec +3 -1
  54. metadata +13 -8
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 03cdc73e6585f6e0e058539db55db290752a040c0535676687b2a5bce3a4a6a4
4
- data.tar.gz: 9480a3870fce66342cffae3f818f220658d8f8368c46d481a27a9bbba9f3c8a1
3
+ metadata.gz: cf62f77a0a968d4080fac612bf67f996bee72b51e050e17bf8d40c04c9c895ea
4
+ data.tar.gz: 348af55b8f56829cbf5fc5569ddc8308d4cd6ae508480de206443349e1221235
5
5
  SHA512:
6
- metadata.gz: 00a09f1d1913abc908247cceb99e813137fe45a9521daa429e385d8fd4d2a6e4c1c6d45c264717de5335dde0d4efc4f2aec9bda2cf5131967ba5878bcb596ee7
7
- data.tar.gz: 1f1236a73ed900dc9d38a5448c154dcf4a66ab55eb490528e214f1b3f393a99eccbaa1d303d9c8a3064060b0447c21d6d59f5fdbbcb843203c0e00a4c18adb89
6
+ metadata.gz: 5baaf6d507772d4a3882a99a43b3f4c88ae4ee56ddfd505433dc88b1e1f21430a2b8fa374932ec4b94478e054a9d1baf12789594bcfe9574db94f449b6526fc2
7
+ data.tar.gz: 8eacb9b0e326b529c743af650920b17e382b379032895d112a83689965a53fe42ae48eb9c070cd86085b1d7d5d9cc7300e0d9199b975edc25ee4aca1f093ecd6
data/Gemfile CHANGED
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  source "https://rubygems.org"
2
4
  git_source(:github) { |name| "https://github.com/#{name}.git" }
3
5
 
@@ -14,6 +16,7 @@ gem "puma"
14
16
 
15
17
  gem "timecop"
16
18
  gem "stackprof" unless RUBY_PLATFORM == "java"
19
+ gem "vernier", platforms: :ruby if RUBY_VERSION >= "3.2.1"
17
20
 
18
21
  gem "graphql", ">= 2.2.6" if RUBY_VERSION.to_f >= 2.7
19
22
 
data/Rakefile CHANGED
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require "rake/clean"
2
4
  CLOBBER.include "pkg"
3
5
 
data/bin/console CHANGED
@@ -1,4 +1,5 @@
1
1
  #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
2
3
 
3
4
  require "bundler/setup"
4
5
  require "debug"
@@ -8,13 +8,13 @@ module Sentry
8
8
 
9
9
  def initialize(bytes: nil, filename: nil, content_type: nil, path: nil)
10
10
  @bytes = bytes
11
- @filename = infer_filename(filename, path)
11
+ @filename = filename || infer_filename(path)
12
12
  @path = path
13
13
  @content_type = content_type
14
14
  end
15
15
 
16
16
  def to_envelope_headers
17
- { type: 'attachment', filename: filename, content_type: content_type, length: payload.bytesize }
17
+ { type: "attachment", filename: filename, content_type: content_type, length: payload.bytesize }
18
18
  end
19
19
 
20
20
  def payload
@@ -29,9 +29,7 @@ module Sentry
29
29
 
30
30
  private
31
31
 
32
- def infer_filename(filename, path)
33
- return filename if filename
34
-
32
+ def infer_filename(path)
35
33
  if path
36
34
  File.basename(path)
37
35
  else
@@ -13,10 +13,10 @@ module Sentry
13
13
  ^ \s* (?: [a-zA-Z]: | uri:classloader: )? ([^:]+ | <.*>):
14
14
  (\d+)
15
15
  (?: :in\s('|`)([^']+)')?$
16
- /x.freeze
16
+ /x
17
17
 
18
18
  # org.jruby.runtime.callsite.CachingCallSite.call(CachingCallSite.java:170)
19
- JAVA_INPUT_FORMAT = /^(.+)\.([^\.]+)\(([^\:]+)\:(\d+)\)$/.freeze
19
+ JAVA_INPUT_FORMAT = /^(.+)\.([^\.]+)\(([^\:]+)\:(\d+)\)$/
20
20
 
21
21
  # The file portion of the line (such as app/models/user.rb)
22
22
  attr_reader :file
@@ -80,8 +80,6 @@ module Sentry
80
80
  end
81
81
  end
82
82
 
83
- APP_DIRS_PATTERN = /(bin|exe|app|config|lib|test|spec)/.freeze
84
-
85
83
  # holder for an Array of Backtrace::Line instances
86
84
  attr_reader :lines
87
85
 
@@ -91,7 +89,7 @@ module Sentry
91
89
  ruby_lines = backtrace_cleanup_callback.call(ruby_lines) if backtrace_cleanup_callback
92
90
 
93
91
  in_app_pattern ||= begin
94
- Regexp.new("^(#{project_root}/)?#{app_dirs_pattern || APP_DIRS_PATTERN}")
92
+ Regexp.new("^(#{project_root}/)?#{app_dirs_pattern}")
95
93
  end
96
94
 
97
95
  lines = ruby_lines.to_a.map do |unparsed_line|
@@ -1,12 +1,12 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'cgi'
3
+ require "cgi"
4
4
 
5
5
  module Sentry
6
6
  # A {https://www.w3.org/TR/baggage W3C Baggage Header} implementation.
7
7
  class Baggage
8
- SENTRY_PREFIX = 'sentry-'
9
- SENTRY_PREFIX_REGEX = /^sentry-/.freeze
8
+ SENTRY_PREFIX = "sentry-"
9
+ SENTRY_PREFIX_REGEX = /^sentry-/
10
10
 
11
11
  # @return [Hash]
12
12
  attr_reader :items
@@ -30,14 +30,14 @@ module Sentry
30
30
  items = {}
31
31
  mutable = true
32
32
 
33
- header.split(',').each do |item|
33
+ header.split(",").each do |item|
34
34
  item = item.strip
35
- key, val = item.split('=')
35
+ key, val = item.split("=")
36
36
 
37
37
  next unless key && val
38
38
  next unless key =~ SENTRY_PREFIX_REGEX
39
39
 
40
- baggage_key = key.split('-')[1]
40
+ baggage_key = key.split("-")[1]
41
41
  next unless baggage_key
42
42
 
43
43
  items[CGI.unescape(baggage_key)] = CGI.unescape(val)
@@ -64,7 +64,7 @@ module Sentry
64
64
  # @return [String]
65
65
  def serialize
66
66
  items = @items.map { |k, v| "#{SENTRY_PREFIX}#{CGI.escape(k)}=#{CGI.escape(v)}" }
67
- items.join(',')
67
+ items.join(",")
68
68
  end
69
69
  end
70
70
  end
@@ -1,16 +1,16 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'logger'
3
+ require "logger"
4
4
 
5
5
  module Sentry
6
6
  class Breadcrumb
7
7
  module SentryLogger
8
8
  LEVELS = {
9
- ::Logger::DEBUG => 'debug',
10
- ::Logger::INFO => 'info',
11
- ::Logger::WARN => 'warn',
12
- ::Logger::ERROR => 'error',
13
- ::Logger::FATAL => 'fatal'
9
+ ::Logger::DEBUG => "debug",
10
+ ::Logger::INFO => "info",
11
+ ::Logger::WARN => "warn",
12
+ ::Logger::ERROR => "error",
13
+ ::Logger::FATAL => "fatal"
14
14
  }.freeze
15
15
 
16
16
  def add(*args, &block)
@@ -1,11 +1,11 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'securerandom'
4
- require 'sentry/cron/monitor_config'
3
+ require "securerandom"
4
+ require "sentry/cron/monitor_config"
5
5
 
6
6
  module Sentry
7
7
  class CheckInEvent < Event
8
- TYPE = 'check_in'
8
+ TYPE = "check_in"
9
9
 
10
10
  # uuid to identify this check-in.
11
11
  # @return [String]
@@ -43,7 +43,7 @@ module Sentry
43
43
  self.status = status
44
44
  self.duration = duration
45
45
  self.monitor_config = monitor_config
46
- self.check_in_id = check_in_id || SecureRandom.uuid.delete('-')
46
+ self.check_in_id = check_in_id || SecureRandom.uuid.delete("-")
47
47
  end
48
48
 
49
49
  # @return [Hash]
data/lib/sentry/client.rb CHANGED
@@ -30,7 +30,7 @@ module Sentry
30
30
  else
31
31
  @transport =
32
32
  case configuration.dsn&.scheme
33
- when 'http', 'https'
33
+ when "http", "https"
34
34
  HTTPTransport.new(configuration)
35
35
  else
36
36
  DummyTransport.new(configuration)
@@ -49,7 +49,7 @@ module Sentry
49
49
  return unless configuration.sending_allowed?
50
50
 
51
51
  if event.is_a?(ErrorEvent) && !configuration.sample_allowed?
52
- transport.record_lost_event(:sample_rate, 'error')
52
+ transport.record_lost_event(:sample_rate, "error")
53
53
  return
54
54
  end
55
55
 
@@ -64,11 +64,11 @@ module Sentry
64
64
  if event.nil?
65
65
  log_debug("Discarded event because one of the event processors returned nil")
66
66
  transport.record_lost_event(:event_processor, data_category)
67
- transport.record_lost_event(:event_processor, 'span', num: spans_before + 1) if is_transaction
67
+ transport.record_lost_event(:event_processor, "span", num: spans_before + 1) if is_transaction
68
68
  return
69
69
  elsif is_transaction
70
70
  spans_delta = spans_before - event.spans.size
71
- transport.record_lost_event(:event_processor, 'span', num: spans_delta) if spans_delta > 0
71
+ transport.record_lost_event(:event_processor, "span", num: spans_delta) if spans_delta > 0
72
72
  end
73
73
 
74
74
  if async_block = configuration.async
@@ -76,7 +76,7 @@ module Sentry
76
76
  elsif configuration.background_worker_threads != 0 && hint.fetch(:background, true)
77
77
  unless dispatch_background_event(event, hint)
78
78
  transport.record_lost_event(:queue_overflow, data_category)
79
- transport.record_lost_event(:queue_overflow, 'span', num: spans_before + 1) if is_transaction
79
+ transport.record_lost_event(:queue_overflow, "span", num: spans_before + 1) if is_transaction
80
80
  end
81
81
  else
82
82
  send_event(event, hint)
@@ -195,13 +195,13 @@ module Sentry
195
195
 
196
196
  if event.nil?
197
197
  log_debug("Discarded event because before_send_transaction returned nil")
198
- transport.record_lost_event(:before_send, 'transaction')
199
- transport.record_lost_event(:before_send, 'span', num: spans_before + 1)
198
+ transport.record_lost_event(:before_send, "transaction")
199
+ transport.record_lost_event(:before_send, "span", num: spans_before + 1)
200
200
  return
201
201
  else
202
202
  spans_after = event.is_a?(TransactionEvent) ? event.spans.size : 0
203
203
  spans_delta = spans_before - spans_after
204
- transport.record_lost_event(:before_send, 'span', num: spans_delta) if spans_delta > 0
204
+ transport.record_lost_event(:before_send, "span", num: spans_delta) if spans_delta > 0
205
205
  end
206
206
  end
207
207
 
@@ -212,7 +212,7 @@ module Sentry
212
212
  rescue => e
213
213
  log_error("Event sending failed", e, debug: configuration.debug)
214
214
  transport.record_lost_event(:network_error, data_category)
215
- transport.record_lost_event(:network_error, 'span', num: spans_before + 1) if event.is_a?(TransactionEvent)
215
+ transport.record_lost_event(:network_error, "span", num: spans_before + 1) if event.is_a?(TransactionEvent)
216
216
  raise
217
217
  end
218
218
 
@@ -3,7 +3,8 @@
3
3
  require "concurrent/utility/processor_counter"
4
4
 
5
5
  require "sentry/utils/exception_cause_chain"
6
- require 'sentry/utils/custom_inspection'
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"
@@ -22,6 +23,8 @@ module Sentry
22
23
  # have an `engines` dir at the root of your project, you may want
23
24
  # to set this to something like /(app|config|engines|lib)/
24
25
  #
26
+ # The default is value is /(bin|exe|app|config|lib|test|spec)/
27
+ #
25
28
  # @return [Regexp, nil]
26
29
  attr_accessor :app_dirs_pattern
27
30
 
@@ -187,6 +190,11 @@ module Sentry
187
190
  # @return [String]
188
191
  attr_accessor :project_root
189
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
+
190
198
  # Insert sentry-trace to outgoing requests' headers
191
199
  # @return [Boolean]
192
200
  attr_accessor :propagate_traces
@@ -283,6 +291,10 @@ module Sentry
283
291
  # @return [Symbol]
284
292
  attr_reader :instrumenter
285
293
 
294
+ # The profiler class
295
+ # @return [Class]
296
+ attr_reader :profiler_class
297
+
286
298
  # Take a float between 0.0 and 1.0 as the sample rate for capturing profiles.
287
299
  # Note that this rate is relative to traces_sample_rate / traces_sampler,
288
300
  # i.e. the profile is sampled by this rate after the transaction is sampled.
@@ -302,18 +314,18 @@ module Sentry
302
314
  # But they are mostly considered as noise and should be ignored by default
303
315
  # Please see https://github.com/getsentry/sentry-ruby/pull/2026 for more information
304
316
  PUMA_IGNORE_DEFAULT = [
305
- 'Puma::MiniSSL::SSLError',
306
- 'Puma::HttpParserError',
307
- 'Puma::HttpParserError501'
317
+ "Puma::MiniSSL::SSLError",
318
+ "Puma::HttpParserError",
319
+ "Puma::HttpParserError501"
308
320
  ].freeze
309
321
 
310
322
  # Most of these errors generate 4XX responses. In general, Sentry clients
311
323
  # only automatically report 5xx responses.
312
324
  IGNORE_DEFAULT = [
313
- 'Mongoid::Errors::DocumentNotFound',
314
- 'Rack::QueryParser::InvalidParameterError',
315
- 'Rack::QueryParser::ParameterTypeError',
316
- 'Sinatra::NotFound'
325
+ "Mongoid::Errors::DocumentNotFound",
326
+ "Rack::QueryParser::InvalidParameterError",
327
+ "Rack::QueryParser::ParameterTypeError",
328
+ "Sinatra::NotFound"
317
329
  ].freeze
318
330
 
319
331
  RACK_ENV_WHITELIST_DEFAULT = %w[
@@ -323,18 +335,20 @@ module Sentry
323
335
  ].freeze
324
336
 
325
337
  HEROKU_DYNO_METADATA_MESSAGE = "You are running on Heroku but haven't enabled Dyno Metadata. For Sentry's "\
326
- "release detection to work correctly, please run `heroku labs:enable runtime-dyno-metadata`".freeze
338
+ "release detection to work correctly, please run `heroku labs:enable runtime-dyno-metadata`"
327
339
 
328
- LOG_PREFIX = "** [Sentry] ".freeze
329
- MODULE_SEPARATOR = "::".freeze
340
+ LOG_PREFIX = "** [Sentry] "
341
+ MODULE_SEPARATOR = "::"
330
342
  SKIP_INSPECTION_ATTRIBUTES = [:@linecache, :@stacktrace_builder]
331
343
 
332
344
  INSTRUMENTERS = [:sentry, :otel]
333
345
 
334
- PROPAGATION_TARGETS_MATCH_ALL = /.*/.freeze
346
+ PROPAGATION_TARGETS_MATCH_ALL = /.*/
335
347
 
336
348
  DEFAULT_PATCHES = %i[redis puma http].freeze
337
349
 
350
+ APP_DIRS_PATTERN = /(bin|exe|app|config|lib|test|spec)/
351
+
338
352
  class << self
339
353
  # Post initialization callbacks are called at the end of initialization process
340
354
  # allowing extending the configuration of sentry-ruby by multiple extensions
@@ -349,11 +363,12 @@ module Sentry
349
363
  end
350
364
 
351
365
  def initialize
352
- self.app_dirs_pattern = nil
353
- self.debug = false
366
+ self.app_dirs_pattern = APP_DIRS_PATTERN
367
+ self.debug = Sentry::Utils::EnvHelper.env_to_bool(ENV["SENTRY_DEBUG"])
354
368
  self.background_worker_threads = (processor_count / 2.0).ceil
355
369
  self.background_worker_max_queue = BackgroundWorker::DEFAULT_MAX_QUEUE
356
370
  self.backtrace_cleanup_callback = nil
371
+ self.strip_backtrace_load_path = true
357
372
  self.max_breadcrumbs = BreadcrumbBuffer::DEFAULT_SIZE
358
373
  self.breadcrumbs_logger = []
359
374
  self.context_lines = 3
@@ -376,8 +391,11 @@ module Sentry
376
391
  self.auto_session_tracking = true
377
392
  self.enable_backpressure_handling = false
378
393
  self.trusted_proxies = []
379
- self.dsn = ENV['SENTRY_DSN']
380
- self.spotlight = false
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
381
399
  self.server_name = server_name_from_env
382
400
  self.instrumenter = :sentry
383
401
  self.trace_propagation_targets = [PROPAGATION_TARGETS_MATCH_ALL]
@@ -389,6 +407,8 @@ module Sentry
389
407
  self.traces_sampler = nil
390
408
  self.enable_tracing = nil
391
409
 
410
+ self.profiler_class = Sentry::Profiler
411
+
392
412
  @transport = Transport::Configuration.new
393
413
  @cron = Cron::Configuration.new
394
414
  @metrics = Metrics::Configuration.new
@@ -484,6 +504,18 @@ module Sentry
484
504
  @profiles_sample_rate = profiles_sample_rate
485
505
  end
486
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
+
487
519
  def sending_allowed?
488
520
  spotlight || sending_to_dsn_allowed?
489
521
  end
@@ -555,7 +587,8 @@ module Sentry
555
587
  app_dirs_pattern: @app_dirs_pattern,
556
588
  linecache: @linecache,
557
589
  context_lines: @context_lines,
558
- backtrace_cleanup_callback: @backtrace_cleanup_callback
590
+ backtrace_cleanup_callback: @backtrace_cleanup_callback,
591
+ strip_backtrace_load_path: @strip_backtrace_load_path
559
592
  )
560
593
  end
561
594
 
@@ -632,12 +665,12 @@ module Sentry
632
665
  end
633
666
 
634
667
  def environment_from_env
635
- ENV['SENTRY_CURRENT_ENV'] || ENV['SENTRY_ENVIRONMENT'] || ENV['RAILS_ENV'] || ENV['RACK_ENV'] || 'development'
668
+ ENV["SENTRY_CURRENT_ENV"] || ENV["SENTRY_ENVIRONMENT"] || ENV["RAILS_ENV"] || ENV["RACK_ENV"] || "development"
636
669
  end
637
670
 
638
671
  def server_name_from_env
639
672
  if running_on_heroku?
640
- ENV['DYNO']
673
+ ENV["DYNO"]
641
674
  else
642
675
  # Try to resolve the hostname to an FQDN, but fall back to whatever
643
676
  # the load name is.
@@ -2,7 +2,7 @@
2
2
 
3
3
  return if Object.method_defined?(:deep_dup)
4
4
 
5
- require 'sentry/core_ext/object/duplicable'
5
+ require "sentry/core_ext/object/duplicable"
6
6
 
7
7
  #########################################
8
8
  # This file was copied from Rails 5.2 #
@@ -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('::', '-').downcase
62
+ slug = name.gsub("::", "-").downcase
61
63
  slug[-MAX_SLUG_LENGTH..-1] || slug
62
64
  end
63
65
  end
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'sentry/cron/monitor_schedule'
3
+ require "sentry/cron/monitor_schedule"
4
4
 
5
5
  module Sentry
6
6
  module Cron
data/lib/sentry/dsn.rb CHANGED
@@ -4,7 +4,7 @@ require "uri"
4
4
 
5
5
  module Sentry
6
6
  class DSN
7
- PORT_MAP = { 'http' => 80, 'https' => 443 }.freeze
7
+ PORT_MAP = { "http" => 80, "https" => 443 }.freeze
8
8
  REQUIRED_ATTRIBUTES = %w[host path public_key project_id].freeze
9
9
 
10
10
  attr_reader :scheme, :secret_key, :port, *REQUIRED_ATTRIBUTES
@@ -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
@@ -3,91 +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
- # rate limits and client reports use the data_category rather than envelope item type
22
- def self.data_category(type)
23
- case type
24
- when 'session', 'attachment', 'transaction', 'profile', 'span' then type
25
- when 'sessions' then 'session'
26
- when 'check_in' then 'monitor'
27
- when 'statsd', 'metric_meta' then 'metric_bucket'
28
- when 'event' then 'error'
29
- when 'client_report' then 'internal'
30
- else 'default'
31
- end
32
- end
33
-
34
- def data_category
35
- self.class.data_category(type)
36
- end
37
-
38
- def to_s
39
- [JSON.generate(@headers), @payload.is_a?(String) ? @payload : JSON.generate(@payload)].join("\n")
40
- end
41
-
42
- def serialize
43
- result = to_s
44
-
45
- if result.bytesize > MAX_SERIALIZED_PAYLOAD_SIZE
46
- remove_breadcrumbs!
47
- result = to_s
48
- end
49
-
50
- if result.bytesize > MAX_SERIALIZED_PAYLOAD_SIZE
51
- reduce_stacktrace!
52
- result = to_s
53
- end
54
-
55
- [result, result.bytesize > MAX_SERIALIZED_PAYLOAD_SIZE]
56
- end
57
-
58
- def size_breakdown
59
- payload.map do |key, value|
60
- "#{key}: #{JSON.generate(value).bytesize}"
61
- end.join(", ")
62
- end
63
-
64
- private
65
-
66
- def remove_breadcrumbs!
67
- if payload.key?(:breadcrumbs)
68
- payload.delete(:breadcrumbs)
69
- elsif payload.key?("breadcrumbs")
70
- payload.delete("breadcrumbs")
71
- end
72
- end
73
-
74
- def reduce_stacktrace!
75
- if exceptions = payload.dig(:exception, :values) || payload.dig("exception", "values")
76
- exceptions.each do |exception|
77
- # 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
78
- traces = exception.dig(:stacktrace, :frames) || exception.dig("stacktrace", "frames")
79
-
80
- if traces && traces.size > STACKTRACE_FRAME_LIMIT_ON_OVERSIZED_PAYLOAD
81
- size_on_both_ends = STACKTRACE_FRAME_LIMIT_ON_OVERSIZED_PAYLOAD / 2
82
- traces.replace(
83
- traces[0..(size_on_both_ends - 1)] + traces[-size_on_both_ends..-1],
84
- )
85
- end
86
- end
87
- end
88
- end
89
- end
90
-
91
6
  attr_accessor :headers, :items
92
7
 
93
8
  def initialize(headers = {})
@@ -108,3 +23,5 @@ module Sentry
108
23
  end
109
24
  end
110
25
  end
26
+
27
+ require_relative "envelope/item"