sentry-ruby 5.10.0 → 5.17.3

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 (63) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile +2 -13
  3. data/README.md +10 -10
  4. data/Rakefile +1 -1
  5. data/lib/sentry/background_worker.rb +9 -2
  6. data/lib/sentry/backpressure_monitor.rb +75 -0
  7. data/lib/sentry/backtrace.rb +7 -3
  8. data/lib/sentry/breadcrumb.rb +8 -2
  9. data/lib/sentry/check_in_event.rb +60 -0
  10. data/lib/sentry/client.rb +88 -17
  11. data/lib/sentry/configuration.rb +66 -12
  12. data/lib/sentry/cron/configuration.rb +23 -0
  13. data/lib/sentry/cron/monitor_check_ins.rb +75 -0
  14. data/lib/sentry/cron/monitor_config.rb +53 -0
  15. data/lib/sentry/cron/monitor_schedule.rb +42 -0
  16. data/lib/sentry/dsn.rb +1 -1
  17. data/lib/sentry/envelope.rb +19 -2
  18. data/lib/sentry/error_event.rb +2 -2
  19. data/lib/sentry/event.rb +14 -36
  20. data/lib/sentry/hub.rb +70 -2
  21. data/lib/sentry/integrable.rb +10 -0
  22. data/lib/sentry/interface.rb +1 -0
  23. data/lib/sentry/interfaces/exception.rb +5 -3
  24. data/lib/sentry/interfaces/mechanism.rb +20 -0
  25. data/lib/sentry/interfaces/request.rb +2 -2
  26. data/lib/sentry/interfaces/single_exception.rb +10 -6
  27. data/lib/sentry/interfaces/stacktrace_builder.rb +8 -0
  28. data/lib/sentry/metrics/aggregator.rb +276 -0
  29. data/lib/sentry/metrics/configuration.rb +47 -0
  30. data/lib/sentry/metrics/counter_metric.rb +25 -0
  31. data/lib/sentry/metrics/distribution_metric.rb +25 -0
  32. data/lib/sentry/metrics/gauge_metric.rb +35 -0
  33. data/lib/sentry/metrics/local_aggregator.rb +53 -0
  34. data/lib/sentry/metrics/metric.rb +19 -0
  35. data/lib/sentry/metrics/set_metric.rb +28 -0
  36. data/lib/sentry/metrics/timing.rb +43 -0
  37. data/lib/sentry/metrics.rb +55 -0
  38. data/lib/sentry/net/http.rb +25 -22
  39. data/lib/sentry/profiler.rb +18 -7
  40. data/lib/sentry/propagation_context.rb +135 -0
  41. data/lib/sentry/puma.rb +12 -5
  42. data/lib/sentry/rack/capture_exceptions.rb +7 -5
  43. data/lib/sentry/rake.rb +3 -14
  44. data/lib/sentry/redis.rb +8 -3
  45. data/lib/sentry/release_detector.rb +1 -1
  46. data/lib/sentry/scope.rb +36 -15
  47. data/lib/sentry/session.rb +2 -2
  48. data/lib/sentry/session_flusher.rb +1 -6
  49. data/lib/sentry/span.rb +54 -3
  50. data/lib/sentry/test_helper.rb +18 -12
  51. data/lib/sentry/transaction.rb +33 -33
  52. data/lib/sentry/transaction_event.rb +5 -3
  53. data/lib/sentry/transport/configuration.rb +73 -1
  54. data/lib/sentry/transport/http_transport.rb +68 -37
  55. data/lib/sentry/transport/spotlight_transport.rb +50 -0
  56. data/lib/sentry/transport.rb +27 -37
  57. data/lib/sentry/utils/argument_checking_helper.rb +12 -0
  58. data/lib/sentry/utils/real_ip.rb +1 -1
  59. data/lib/sentry/utils/request_id.rb +1 -1
  60. data/lib/sentry/version.rb +1 -1
  61. data/lib/sentry-ruby.rb +96 -26
  62. data/sentry-ruby.gemspec +1 -0
  63. metadata +35 -2
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 2373a9b8990fd62763c4d1b26001f953f95efdd8b51f803e5d59bdb1ba984e3e
4
- data.tar.gz: d09371eb4bf3aaff6e7735f74cd5887b2e99f636a60f1dca3cf653bbb8ba9bb4
3
+ metadata.gz: 917e0ed267815d8daeeb5f731a14642be3b901a1a09a9341cc6ca58750094496
4
+ data.tar.gz: db84b44742d0e847b6fd66dac5a3338e63d4497723979b7c71fbbea46eb50480
5
5
  SHA512:
6
- metadata.gz: 4799b727ddaab0622be00e73b58a2e3f723779e1b46c7d5007d5711a8897022190ff9f87cc90df7411f32fd6b59d4f4637bcf26cc7eba8484cb04429871e9242
7
- data.tar.gz: d97d9272ee815447ebdcbb72adb4e2255584475547e7878ed6c7af4f312d170bd37f3029acaaa6866eb04f2d89c1581c4854b849c2d57a1a51eb4b67e4ebeb0e
6
+ metadata.gz: 75e7822da3cf46b8faf9fd3a2528cd56ad8f3d8d38b7e21d178f257695409d07c2e7d643dbc48b09f818d6324c3f96d083d03b8ac3faa00e4fff0a8ea2dcdfc3
7
+ data.tar.gz: 1348bd6636f8283af3d85934ffe414606072758939cc0027c70730f8711f933ab72d9ee23d49cd921ca3562dcd4a034f93255fe5f3eb257a0e4288815be840e5
data/Gemfile CHANGED
@@ -12,22 +12,9 @@ gem "redis", "~> #{redis_rb_version}"
12
12
 
13
13
  gem "puma"
14
14
 
15
- gem "rake", "~> 12.0"
16
- gem "rspec", "~> 3.0"
17
- gem "rspec-retry"
18
15
  gem "timecop"
19
- gem "simplecov"
20
- gem "simplecov-cobertura", "~> 1.4"
21
- gem "rexml"
22
16
  gem "stackprof" unless RUBY_PLATFORM == "java"
23
17
 
24
- if RUBY_VERSION.to_f >= 2.6
25
- gem "debug", github: "ruby/debug", platform: :ruby
26
- gem "irb"
27
- end
28
-
29
- gem "pry"
30
-
31
18
  gem "benchmark-ips"
32
19
  gem "benchmark_driver"
33
20
  gem "benchmark-ipsa"
@@ -35,3 +22,5 @@ gem "benchmark-memory"
35
22
 
36
23
  gem "yard", github: "lsegal/yard"
37
24
  gem "webrick"
25
+
26
+ eval_gemfile File.expand_path("../Gemfile", __dir__)
data/README.md CHANGED
@@ -13,14 +13,14 @@ _Bad software is everywhere, and we're tired of it. Sentry is on a mission to he
13
13
  Sentry SDK for Ruby
14
14
  ===========
15
15
 
16
- | current version | build | coverage | downloads |
17
- | --- | ----- | -------- | --------- |
18
- | [![Gem Version](https://img.shields.io/gem/v/sentry-ruby?label=sentry-ruby)](https://rubygems.org/gems/sentry-ruby) | [![Build Status](https://github.com/getsentry/sentry-ruby/workflows/sentry-ruby%20Test/badge.svg)](https://github.com/getsentry/sentry-ruby/actions/workflows/sentry_ruby_test.yml) | [![Coverage Status](https://img.shields.io/codecov/c/github/getsentry/sentry-ruby/master?logo=codecov)](https://codecov.io/gh/getsentry/sentry-ruby/branch/master) | [![Downloads](https://img.shields.io/gem/dt/sentry-ruby.svg)](https://rubygems.org/gems/sentry-ruby/) |
19
- | [![Gem Version](https://img.shields.io/gem/v/sentry-rails?label=sentry-rails)](https://rubygems.org/gems/sentry-rails) | [![Build Status](https://github.com/getsentry/sentry-ruby/workflows/sentry-rails%20Test/badge.svg)](https://github.com/getsentry/sentry-ruby/actions/workflows/sentry_rails_test.yml) | [![Coverage Status](https://img.shields.io/codecov/c/github/getsentry/sentry-ruby/master?logo=codecov)](https://codecov.io/gh/getsentry/sentry-ruby/branch/master) | [![Downloads](https://img.shields.io/gem/dt/sentry-rails.svg)](https://rubygems.org/gems/sentry-rails/) |
20
- | [![Gem Version](https://img.shields.io/gem/v/sentry-sidekiq?label=sentry-sidekiq)](https://rubygems.org/gems/sentry-sidekiq) | [![Build Status](https://github.com/getsentry/sentry-ruby/workflows/sentry-sidekiq%20Test/badge.svg)](https://github.com/getsentry/sentry-ruby/actions/workflows/sentry_sidekiq_test.yml) | [![Coverage Status](https://img.shields.io/codecov/c/github/getsentry/sentry-ruby/master?logo=codecov)](https://codecov.io/gh/getsentry/sentry-ruby/branch/master) | [![Downloads](https://img.shields.io/gem/dt/sentry-sidekiq.svg)](https://rubygems.org/gems/sentry-sidekiq/) |
21
- | [![Gem Version](https://img.shields.io/gem/v/sentry-delayed_job?label=sentry-delayed_job)](https://rubygems.org/gems/sentry-delayed_job) | [![Build Status](https://github.com/getsentry/sentry-ruby/workflows/sentry-delayed_job%20Test/badge.svg)](https://github.com/getsentry/sentry-ruby/actions/workflows/sentry_delayed_job_test.yml) | [![Coverage Status](https://img.shields.io/codecov/c/github/getsentry/sentry-ruby/master?logo=codecov)](https://codecov.io/gh/getsentry/sentry-ruby/branch/master) | [![Downloads](https://img.shields.io/gem/dt/sentry-delayed_job.svg)](https://rubygems.org/gems/sentry-delayed_job/) |
22
- | [![Gem Version](https://img.shields.io/gem/v/sentry-resque?label=sentry-resque)](https://rubygems.org/gems/sentry-resque) | [![Build Status](https://github.com/getsentry/sentry-ruby/workflows/sentry-resque%20Test/badge.svg)](https://github.com/getsentry/sentry-ruby/actions/workflows/sentry_resque_test.yml) | [![Coverage Status](https://img.shields.io/codecov/c/github/getsentry/sentry-ruby/master?logo=codecov)](https://codecov.io/gh/getsentry/sentry-ruby/branch/master) | [![Downloads](https://img.shields.io/gem/dt/sentry-resque.svg)](https://rubygems.org/gems/sentry-resque/) |
23
- | [![Gem Version](https://img.shields.io/gem/v/sentry-opentelemetry?label=sentry-opentelemetry)](https://rubygems.org/gems/sentry-opentelemetry) | [![Build Status](https://github.com/getsentry/sentry-ruby/workflows/sentry-opentelemetry%20Test/badge.svg)](https://github.com/getsentry/sentry-ruby/actions/workflows/sentry_opentelemetry_test.yml) | [![Coverage Status](https://img.shields.io/codecov/c/github/getsentry/sentry-ruby/master?logo=codecov)](https://codecov.io/gh/getsentry/sentry-ruby/branch/master) | [![Downloads](https://img.shields.io/gem/dt/sentry-opentelemetry.svg)](https://rubygems.org/gems/sentry-opentelemetry/) |
16
+ | current version | build | coverage | downloads |
17
+ | --- | ----- | -------- | --------- |
18
+ | [![Gem Version](https://img.shields.io/gem/v/sentry-ruby?label=sentry-ruby)](https://rubygems.org/gems/sentry-ruby) | [![Build Status](https://github.com/getsentry/sentry-ruby/actions/workflows/sentry_ruby_test.yml/badge.svg)](https://github.com/getsentry/sentry-ruby/actions/workflows/sentry_ruby_test.yml) | [![Coverage Status](https://img.shields.io/codecov/c/github/getsentry/sentry-ruby/master?logo=codecov)](https://codecov.io/gh/getsentry/sentry-ruby/branch/master) | [![Downloads](https://img.shields.io/gem/dt/sentry-ruby.svg)](https://rubygems.org/gems/sentry-ruby/) |
19
+ | [![Gem Version](https://img.shields.io/gem/v/sentry-rails?label=sentry-rails)](https://rubygems.org/gems/sentry-rails) | [![Build Status](https://github.com/getsentry/sentry-ruby/actions/workflows/sentry_rails_test.yml/badge.svg)](https://github.com/getsentry/sentry-ruby/actions/workflows/sentry_rails_test.yml) | [![Coverage Status](https://img.shields.io/codecov/c/github/getsentry/sentry-ruby/master?logo=codecov)](https://codecov.io/gh/getsentry/sentry-ruby/branch/master) | [![Downloads](https://img.shields.io/gem/dt/sentry-rails.svg)](https://rubygems.org/gems/sentry-rails/) |
20
+ | [![Gem Version](https://img.shields.io/gem/v/sentry-sidekiq?label=sentry-sidekiq)](https://rubygems.org/gems/sentry-sidekiq) | [![Build Status](https://github.com/getsentry/sentry-ruby/actions/workflows/sentry_sidekiq_test.yml/badge.svg)](https://github.com/getsentry/sentry-ruby/actions/workflows/sentry_sidekiq_test.yml) | [![Coverage Status](https://img.shields.io/codecov/c/github/getsentry/sentry-ruby/master?logo=codecov)](https://codecov.io/gh/getsentry/sentry-ruby/branch/master) | [![Downloads](https://img.shields.io/gem/dt/sentry-sidekiq.svg)](https://rubygems.org/gems/sentry-sidekiq/) |
21
+ | [![Gem Version](https://img.shields.io/gem/v/sentry-delayed_job?label=sentry-delayed_job)](https://rubygems.org/gems/sentry-delayed_job) | [![Build Status](https://github.com/getsentry/sentry-ruby/actions/workflows/sentry_delayed_job_test.yml/badge.svg)](https://github.com/getsentry/sentry-ruby/actions/workflows/sentry_delayed_job_test.yml) | [![Coverage Status](https://img.shields.io/codecov/c/github/getsentry/sentry-ruby/master?logo=codecov)](https://codecov.io/gh/getsentry/sentry-ruby/branch/master) | [![Downloads](https://img.shields.io/gem/dt/sentry-delayed_job.svg)](https://rubygems.org/gems/sentry-delayed_job/) |
22
+ | [![Gem Version](https://img.shields.io/gem/v/sentry-resque?label=sentry-resque)](https://rubygems.org/gems/sentry-resque) | [![Build Status](https://github.com/getsentry/sentry-ruby/actions/workflows/sentry_resque_test.yml/badge.svg)](https://github.com/getsentry/sentry-ruby/actions/workflows/sentry_resque_test.yml) | [![Coverage Status](https://img.shields.io/codecov/c/github/getsentry/sentry-ruby/master?logo=codecov)](https://codecov.io/gh/getsentry/sentry-ruby/branch/master) | [![Downloads](https://img.shields.io/gem/dt/sentry-resque.svg)](https://rubygems.org/gems/sentry-resque/) |
23
+ | [![Gem Version](https://img.shields.io/gem/v/sentry-opentelemetry?label=sentry-opentelemetry)](https://rubygems.org/gems/sentry-opentelemetry) | [![Build Status](https://github.com/getsentry/sentry-ruby/actions/workflows/sentry_opentelemetry_test.yml/badge.svg)](https://github.com/getsentry/sentry-ruby/actions/workflows/sentry_opentelemetry_test.yml) | [![Coverage Status](https://img.shields.io/codecov/c/github/getsentry/sentry-ruby/master?logo=codecov)](https://codecov.io/gh/getsentry/sentry-ruby/branch/master) | [![Downloads](https://img.shields.io/gem/dt/sentry-opentelemetry.svg)](https://rubygems.org/gems/sentry-opentelemetry/) |
24
24
 
25
25
 
26
26
 
@@ -33,7 +33,7 @@ If you're using `sentry-raven`, we recommend you to migrate to this new SDK. You
33
33
 
34
34
  ## Requirements
35
35
 
36
- We test on Ruby 2.4, 2.5, 2.6, 2.7, 3.0, and 3.1 at the latest patchlevel/teeny version. We also support JRuby 9.0.
36
+ We test from Ruby 2.4 to Ruby 3.2 at the latest patchlevel/teeny version. We also support JRuby 9.0.
37
37
 
38
38
  If you use self-hosted Sentry, please also make sure its version is above `20.6.0`.
39
39
 
@@ -90,7 +90,7 @@ To learn more about sampling transactions, please visit the [official documentat
90
90
  - [Sidekiq](https://docs.sentry.io/platforms/ruby/guides/sidekiq/)
91
91
  - [DelayedJob](https://docs.sentry.io/platforms/ruby/guides/delayed_job/)
92
92
  - [Resque](https://docs.sentry.io/platforms/ruby/guides/resque/)
93
- - [OpenTemeletry](https://docs.sentry.io/platforms/ruby/performance/instrumentation/opentelemetry/)
93
+ - [OpenTelemetry](https://docs.sentry.io/platforms/ruby/performance/instrumentation/opentelemetry/)
94
94
 
95
95
  ### Enriching Events
96
96
 
data/Rakefile CHANGED
@@ -17,4 +17,4 @@ task :isolated_specs do
17
17
  end
18
18
  end
19
19
 
20
- task :default => [:spec, :isolated_specs]
20
+ task default: [:spec, :isolated_specs]
@@ -13,10 +13,12 @@ module Sentry
13
13
  attr_reader :logger
14
14
  attr_accessor :shutdown_timeout
15
15
 
16
+ DEFAULT_MAX_QUEUE = 30
17
+
16
18
  def initialize(configuration)
17
- @max_queue = 30
18
19
  @shutdown_timeout = 1
19
20
  @number_of_threads = configuration.background_worker_threads
21
+ @max_queue = configuration.background_worker_max_queue
20
22
  @logger = configuration.logger
21
23
  @debug = configuration.debug
22
24
  @shutdown_callback = nil
@@ -29,7 +31,7 @@ module Sentry
29
31
  log_debug("config.background_worker_threads is set to 0, all events will be sent synchronously")
30
32
  Concurrent::ImmediateExecutor.new
31
33
  else
32
- log_debug("Initializing the background worker with #{@number_of_threads} threads")
34
+ log_debug("Initializing the Sentry background worker with #{@number_of_threads} threads")
33
35
 
34
36
  executor = Concurrent::ThreadPoolExecutor.new(
35
37
  min_threads: 0,
@@ -63,6 +65,11 @@ module Sentry
63
65
  @shutdown_callback&.call
64
66
  end
65
67
 
68
+ def full?
69
+ @executor.is_a?(Concurrent::ThreadPoolExecutor) &&
70
+ @executor.remaining_capacity == 0
71
+ end
72
+
66
73
  private
67
74
 
68
75
  def _perform(&block)
@@ -0,0 +1,75 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Sentry
4
+ class BackpressureMonitor
5
+ include LoggingHelper
6
+
7
+ DEFAULT_INTERVAL = 10
8
+ MAX_DOWNSAMPLE_FACTOR = 10
9
+
10
+ def initialize(configuration, client, interval: DEFAULT_INTERVAL)
11
+ @interval = interval
12
+ @client = client
13
+ @logger = configuration.logger
14
+
15
+ @thread = nil
16
+ @exited = false
17
+
18
+ @healthy = true
19
+ @downsample_factor = 0
20
+ end
21
+
22
+ def healthy?
23
+ ensure_thread
24
+ @healthy
25
+ end
26
+
27
+ def downsample_factor
28
+ ensure_thread
29
+ @downsample_factor
30
+ end
31
+
32
+ def run
33
+ check_health
34
+ set_downsample_factor
35
+ end
36
+
37
+ def check_health
38
+ @healthy = !(@client.transport.any_rate_limited? || Sentry.background_worker&.full?)
39
+ end
40
+
41
+ def set_downsample_factor
42
+ if @healthy
43
+ log_debug("[BackpressureMonitor] health check positive, reverting to normal sampling") if @downsample_factor.positive?
44
+ @downsample_factor = 0
45
+ else
46
+ @downsample_factor += 1 if @downsample_factor < MAX_DOWNSAMPLE_FACTOR
47
+ log_debug("[BackpressureMonitor] health check negative, downsampling with a factor of #{@downsample_factor}")
48
+ end
49
+ end
50
+
51
+ def kill
52
+ log_debug("[BackpressureMonitor] killing monitor")
53
+
54
+ @exited = true
55
+ @thread&.kill
56
+ end
57
+
58
+ private
59
+
60
+ def ensure_thread
61
+ return if @exited
62
+ return if @thread&.alive?
63
+
64
+ @thread = Thread.new do
65
+ loop do
66
+ sleep(@interval)
67
+ run
68
+ end
69
+ end
70
+ rescue ThreadError
71
+ log_debug("[BackpressureMonitor] Thread creation failed")
72
+ @exited = true
73
+ end
74
+ end
75
+ end
@@ -1,5 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require "rubygems"
4
+
3
5
  module Sentry
4
6
  # @api private
5
7
  class Backtrace
@@ -10,7 +12,7 @@ module Sentry
10
12
  RUBY_INPUT_FORMAT = /
11
13
  ^ \s* (?: [a-zA-Z]: | uri:classloader: )? ([^:]+ | <.*>):
12
14
  (\d+)
13
- (?: :in \s `([^']+)')?$
15
+ (?: :in\s('|`)([^']+)')?$
14
16
  /x.freeze
15
17
 
16
18
  # org.jruby.runtime.callsite.CachingCallSite.call(CachingCallSite.java:170)
@@ -33,10 +35,10 @@ module Sentry
33
35
  # Parses a single line of a given backtrace
34
36
  # @param [String] unparsed_line The raw line from +caller+ or some backtrace
35
37
  # @return [Line] The parsed backtrace line
36
- def self.parse(unparsed_line, in_app_pattern)
38
+ def self.parse(unparsed_line, in_app_pattern = nil)
37
39
  ruby_match = unparsed_line.match(RUBY_INPUT_FORMAT)
38
40
  if ruby_match
39
- _, file, number, method = ruby_match.to_a
41
+ _, file, number, _, method = ruby_match.to_a
40
42
  file.sub!(/\.class$/, RB_EXTENSION)
41
43
  module_name = nil
42
44
  else
@@ -55,6 +57,8 @@ module Sentry
55
57
  end
56
58
 
57
59
  def in_app
60
+ return false unless in_app_pattern
61
+
58
62
  if file =~ in_app_pattern
59
63
  true
60
64
  else
@@ -9,7 +9,7 @@ module Sentry
9
9
  # @return [Hash, nil]
10
10
  attr_accessor :data
11
11
  # @return [String, nil]
12
- attr_accessor :level
12
+ attr_reader :level
13
13
  # @return [Time, Integer, nil]
14
14
  attr_accessor :timestamp
15
15
  # @return [String, nil]
@@ -26,10 +26,10 @@ module Sentry
26
26
  def initialize(category: nil, data: nil, message: nil, timestamp: nil, level: nil, type: nil)
27
27
  @category = category
28
28
  @data = data || {}
29
- @level = level
30
29
  @timestamp = timestamp || Sentry.utc_now.to_i
31
30
  @type = type
32
31
  self.message = message
32
+ self.level = level
33
33
  end
34
34
 
35
35
  # @return [Hash]
@@ -50,6 +50,12 @@ module Sentry
50
50
  @message = (message || "").byteslice(0..Event::MAX_MESSAGE_SIZE_IN_BYTES)
51
51
  end
52
52
 
53
+ # @param level [String]
54
+ # @return [void]
55
+ def level=(level) # needed to meet the Sentry spec
56
+ @level = level == "warn" ? "warning" : level
57
+ end
58
+
53
59
  private
54
60
 
55
61
  def serialized_data
@@ -0,0 +1,60 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'securerandom'
4
+ require 'sentry/cron/monitor_config'
5
+
6
+ module Sentry
7
+ class CheckInEvent < Event
8
+ TYPE = 'check_in'
9
+
10
+ # uuid to identify this check-in.
11
+ # @return [String]
12
+ attr_accessor :check_in_id
13
+
14
+ # Identifier of the monitor for this check-in.
15
+ # @return [String]
16
+ attr_accessor :monitor_slug
17
+
18
+ # Duration of this check since it has started in seconds.
19
+ # @return [Integer, nil]
20
+ attr_accessor :duration
21
+
22
+ # Monitor configuration to support upserts.
23
+ # @return [Cron::MonitorConfig, nil]
24
+ attr_accessor :monitor_config
25
+
26
+ # Status of this check-in.
27
+ # @return [Symbol]
28
+ attr_accessor :status
29
+
30
+ VALID_STATUSES = %i[ok in_progress error]
31
+
32
+ def initialize(
33
+ slug:,
34
+ status:,
35
+ duration: nil,
36
+ monitor_config: nil,
37
+ check_in_id: nil,
38
+ **options
39
+ )
40
+ super(**options)
41
+
42
+ self.monitor_slug = slug
43
+ self.status = status
44
+ self.duration = duration
45
+ self.monitor_config = monitor_config
46
+ self.check_in_id = check_in_id || SecureRandom.uuid.delete('-')
47
+ end
48
+
49
+ # @return [Hash]
50
+ def to_hash
51
+ data = super
52
+ data[:check_in_id] = check_in_id
53
+ data[:monitor_slug] = monitor_slug
54
+ data[:status] = status
55
+ data[:duration] = duration if duration
56
+ data[:monitor_config] = monitor_config.to_hash if monitor_config
57
+ data
58
+ end
59
+ end
60
+ end
data/lib/sentry/client.rb CHANGED
@@ -10,6 +10,10 @@ module Sentry
10
10
  # @return [Transport]
11
11
  attr_reader :transport
12
12
 
13
+ # The Transport object that'll send events for the client.
14
+ # @return [SpotlightTransport, nil]
15
+ attr_reader :spotlight_transport
16
+
13
17
  # @!macro configuration
14
18
  attr_reader :configuration
15
19
 
@@ -32,6 +36,8 @@ module Sentry
32
36
  DummyTransport.new(configuration)
33
37
  end
34
38
  end
39
+
40
+ @spotlight_transport = SpotlightTransport.new(configuration) if configuration.spotlight
35
41
  end
36
42
 
37
43
  # Applies the given scope's data to the event and sends it to Sentry.
@@ -42,17 +48,18 @@ module Sentry
42
48
  def capture_event(event, scope, hint = {})
43
49
  return unless configuration.sending_allowed?
44
50
 
45
- unless event.is_a?(TransactionEvent) || configuration.sample_allowed?
46
- transport.record_lost_event(:sample_rate, 'event')
51
+ if event.is_a?(ErrorEvent) && !configuration.sample_allowed?
52
+ transport.record_lost_event(:sample_rate, 'error')
47
53
  return
48
54
  end
49
55
 
50
56
  event_type = event.is_a?(Event) ? event.type : event["type"]
57
+ data_category = Envelope::Item.data_category(event_type)
51
58
  event = scope.apply_to_event(event, hint)
52
59
 
53
60
  if event.nil?
54
- log_info("Discarded event because one of the event processors returned nil")
55
- transport.record_lost_event(:event_processor, event_type)
61
+ log_debug("Discarded event because one of the event processors returned nil")
62
+ transport.record_lost_event(:event_processor, data_category)
56
63
  return
57
64
  end
58
65
 
@@ -60,7 +67,7 @@ module Sentry
60
67
  dispatch_async_event(async_block, event, hint)
61
68
  elsif configuration.background_worker_threads != 0 && hint.fetch(:background, true)
62
69
  queued = dispatch_background_event(event, hint)
63
- transport.record_lost_event(:queue_overflow, event_type) unless queued
70
+ transport.record_lost_event(:queue_overflow, data_category) unless queued
64
71
  else
65
72
  send_event(event, hint)
66
73
  end
@@ -71,6 +78,20 @@ module Sentry
71
78
  nil
72
79
  end
73
80
 
81
+ # Capture an envelope directly.
82
+ # @param envelope [Envelope] the envelope to be captured.
83
+ # @return [void]
84
+ def capture_envelope(envelope)
85
+ Sentry.background_worker.perform { send_envelope(envelope) }
86
+ end
87
+
88
+ # Flush pending events to Sentry.
89
+ # @return [void]
90
+ def flush
91
+ transport.flush if configuration.sending_to_dsn_allowed?
92
+ spotlight_transport.flush if spotlight_transport
93
+ end
94
+
74
95
  # Initializes an Event object with the given exception. Returns `nil` if the exception's class is excluded from reporting.
75
96
  # @param exception [Exception] the exception to be reported.
76
97
  # @param hint [Hash] the hint data that'll be passed to `before_send` callback and the scope's event processors.
@@ -82,9 +103,10 @@ module Sentry
82
103
  return if !ignore_exclusions && !@configuration.exception_class_allowed?(exception)
83
104
 
84
105
  integration_meta = Sentry.integrations[hint[:integration]]
106
+ mechanism = hint.delete(:mechanism) { Mechanism.new }
85
107
 
86
108
  ErrorEvent.new(configuration: configuration, integration_meta: integration_meta).tap do |event|
87
- event.add_exception_interface(exception)
109
+ event.add_exception_interface(exception, mechanism: mechanism)
88
110
  event.add_threads_interface(crashed: true)
89
111
  event.level = :error
90
112
  end
@@ -104,6 +126,37 @@ module Sentry
104
126
  event
105
127
  end
106
128
 
129
+ # Initializes a CheckInEvent object with the given options.
130
+ #
131
+ # @param slug [String] identifier of this monitor
132
+ # @param status [Symbol] status of this check-in, one of {CheckInEvent::VALID_STATUSES}
133
+ # @param hint [Hash] the hint data that'll be passed to `before_send` callback and the scope's event processors.
134
+ # @param duration [Integer, nil] seconds elapsed since this monitor started
135
+ # @param monitor_config [Cron::MonitorConfig, nil] configuration for this monitor
136
+ # @param check_in_id [String, nil] for updating the status of an existing monitor
137
+ #
138
+ # @return [Event]
139
+ def event_from_check_in(
140
+ slug,
141
+ status,
142
+ hint = {},
143
+ duration: nil,
144
+ monitor_config: nil,
145
+ check_in_id: nil
146
+ )
147
+ return unless configuration.sending_allowed?
148
+
149
+ CheckInEvent.new(
150
+ configuration: configuration,
151
+ integration_meta: Sentry.integrations[hint[:integration]],
152
+ slug: slug,
153
+ status: status,
154
+ duration: duration,
155
+ monitor_config: monitor_config,
156
+ check_in_id: check_in_id
157
+ )
158
+ end
159
+
107
160
  # Initializes an Event object with the given Transaction object.
108
161
  # @param transaction [Transaction] the transaction to be recorded.
109
162
  # @return [TransactionEvent]
@@ -114,13 +167,14 @@ module Sentry
114
167
  # @!macro send_event
115
168
  def send_event(event, hint = nil)
116
169
  event_type = event.is_a?(Event) ? event.type : event["type"]
170
+ data_category = Envelope::Item.data_category(event_type)
117
171
 
118
172
  if event_type != TransactionEvent::TYPE && configuration.before_send
119
173
  event = configuration.before_send.call(event, hint)
120
174
 
121
175
  if event.nil?
122
- log_info("Discarded event because before_send returned nil")
123
- transport.record_lost_event(:before_send, 'event')
176
+ log_debug("Discarded event because before_send returned nil")
177
+ transport.record_lost_event(:before_send, data_category)
124
178
  return
125
179
  end
126
180
  end
@@ -129,25 +183,40 @@ module Sentry
129
183
  event = configuration.before_send_transaction.call(event, hint)
130
184
 
131
185
  if event.nil?
132
- log_info("Discarded event because before_send_transaction returned nil")
133
- transport.record_lost_event(:before_send, 'transaction')
186
+ log_debug("Discarded event because before_send_transaction returned nil")
187
+ transport.record_lost_event(:before_send, data_category)
134
188
  return
135
189
  end
136
190
  end
137
191
 
138
- transport.send_event(event)
192
+ transport.send_event(event) if configuration.sending_to_dsn_allowed?
193
+ spotlight_transport.send_event(event) if spotlight_transport
139
194
 
140
195
  event
141
196
  rescue => e
142
- loggable_event_type = event_type.capitalize
143
- log_error("#{loggable_event_type} sending failed", e, debug: configuration.debug)
197
+ log_error("Event sending failed", e, debug: configuration.debug)
198
+ transport.record_lost_event(:network_error, data_category)
199
+ raise
200
+ end
201
+
202
+ # Send an envelope directly to Sentry.
203
+ # @param envelope [Envelope] the envelope to be sent.
204
+ # @return [void]
205
+ def send_envelope(envelope)
206
+ transport.send_envelope(envelope) if configuration.sending_to_dsn_allowed?
207
+ spotlight_transport.send_envelope(envelope) if spotlight_transport
208
+ rescue => e
209
+ log_error("Envelope sending failed", e, debug: configuration.debug)
210
+
211
+ envelope.items.map(&:data_category).each do |data_category|
212
+ transport.record_lost_event(:network_error, data_category)
213
+ end
144
214
 
145
- event_info = Event.get_log_message(event.to_hash)
146
- log_info("Unreported #{loggable_event_type}: #{event_info}")
147
- transport.record_lost_event(:network_error, event_type)
148
215
  raise
149
216
  end
150
217
 
218
+ # @deprecated use Sentry.get_traceparent instead.
219
+ #
151
220
  # Generates a Sentry trace for distribted tracing from the given Span.
152
221
  # Returns `nil` if `config.propagate_traces` is `false`.
153
222
  # @param span [Span] the span to generate trace from.
@@ -160,7 +229,9 @@ module Sentry
160
229
  trace
161
230
  end
162
231
 
163
- # Generates a W3C Baggage header for distribted tracing from the given Span.
232
+ # @deprecated Use Sentry.get_baggage instead.
233
+ #
234
+ # Generates a W3C Baggage header for distributed tracing from the given Span.
164
235
  # Returns `nil` if `config.propagate_traces` is `false`.
165
236
  # @param span [Span] the span to generate trace from.
166
237
  # @return [String, nil]