sentry-ruby 5.19.0 → 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.
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"