sentry-ruby 5.13.0 → 5.19.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 +4 -18
- data/README.md +20 -10
- data/Rakefile +1 -1
- data/bin/console +1 -0
- data/lib/sentry/attachment.rb +42 -0
- data/lib/sentry/background_worker.rb +9 -2
- data/lib/sentry/backpressure_monitor.rb +45 -0
- data/lib/sentry/backtrace.rb +7 -3
- data/lib/sentry/check_in_event.rb +1 -1
- data/lib/sentry/client.rb +69 -16
- data/lib/sentry/configuration.rb +57 -14
- data/lib/sentry/cron/configuration.rb +23 -0
- data/lib/sentry/cron/monitor_check_ins.rb +40 -26
- data/lib/sentry/cron/monitor_schedule.rb +1 -1
- data/lib/sentry/dsn.rb +1 -1
- data/lib/sentry/envelope.rb +18 -1
- data/lib/sentry/error_event.rb +2 -2
- data/lib/sentry/event.rb +13 -39
- data/lib/sentry/faraday.rb +77 -0
- data/lib/sentry/graphql.rb +9 -0
- data/lib/sentry/hub.rb +17 -4
- 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 +2 -2
- data/lib/sentry/interfaces/single_exception.rb +7 -4
- data/lib/sentry/interfaces/stacktrace_builder.rb +8 -0
- 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 +22 -39
- data/lib/sentry/propagation_context.rb +9 -8
- data/lib/sentry/puma.rb +1 -1
- data/lib/sentry/rack/capture_exceptions.rb +14 -2
- data/lib/sentry/rake.rb +3 -14
- data/lib/sentry/redis.rb +2 -1
- data/lib/sentry/release_detector.rb +1 -1
- data/lib/sentry/scope.rb +47 -37
- data/lib/sentry/session.rb +2 -2
- data/lib/sentry/session_flusher.rb +6 -38
- data/lib/sentry/span.rb +40 -5
- data/lib/sentry/test_helper.rb +2 -1
- data/lib/sentry/threaded_periodic_worker.rb +39 -0
- data/lib/sentry/transaction.rb +25 -16
- data/lib/sentry/transaction_event.rb +5 -0
- data/lib/sentry/transport/configuration.rb +73 -1
- data/lib/sentry/transport/http_transport.rb +68 -37
- data/lib/sentry/transport/spotlight_transport.rb +50 -0
- data/lib/sentry/transport.rb +32 -37
- data/lib/sentry/utils/argument_checking_helper.rb +6 -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 +1 -1
- data/lib/sentry/utils/request_id.rb +1 -1
- data/lib/sentry/version.rb +1 -1
- data/lib/sentry-ruby.rb +57 -24
- data/sentry-ruby.gemspec +12 -5
- metadata +42 -7
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 03cdc73e6585f6e0e058539db55db290752a040c0535676687b2a5bce3a4a6a4
|
4
|
+
data.tar.gz: 9480a3870fce66342cffae3f818f220658d8f8368c46d481a27a9bbba9f3c8a1
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 00a09f1d1913abc908247cceb99e813137fe45a9521daa429e385d8fd4d2a6e4c1c6d45c264717de5335dde0d4efc4f2aec9bda2cf5131967ba5878bcb596ee7
|
7
|
+
data.tar.gz: 1f1236a73ed900dc9d38a5448c154dcf4a66ab55eb490528e214f1b3f393a99eccbaa1d303d9c8a3064060b0447c21d6d59f5fdbbcb843203c0e00a4c18adb89
|
data/Gemfile
CHANGED
@@ -12,27 +12,10 @@ 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
|
-
|
25
|
-
|
26
|
-
if ruby_version >= Gem::Version.new("2.6.0")
|
27
|
-
gem "debug", github: "ruby/debug", platform: :ruby
|
28
|
-
gem "irb"
|
29
|
-
|
30
|
-
if ruby_version >= Gem::Version.new("3.0.0")
|
31
|
-
gem "ruby-lsp-rspec"
|
32
|
-
end
|
33
|
-
end
|
34
|
-
|
35
|
-
gem "pry"
|
18
|
+
gem "graphql", ">= 2.2.6" if RUBY_VERSION.to_f >= 2.7
|
36
19
|
|
37
20
|
gem "benchmark-ips"
|
38
21
|
gem "benchmark_driver"
|
@@ -41,3 +24,6 @@ gem "benchmark-memory"
|
|
41
24
|
|
42
25
|
gem "yard", github: "lsegal/yard"
|
43
26
|
gem "webrick"
|
27
|
+
gem "faraday"
|
28
|
+
|
29
|
+
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
|
-
|
|
17
|
-
|
|
18
|
-
| [](https://rubygems.org/gems/sentry-ruby) | [](https://github.com/getsentry/sentry-ruby/actions/workflows/sentry_ruby_test.yml) | [](https://codecov.io/gh/getsentry/sentry-ruby/branch/master) | [](https://rubygems.org/gems/sentry-rails) | [](https://github.com/getsentry/sentry-ruby/actions/workflows/sentry_rails_test.yml) | [](https://codecov.io/gh/getsentry/sentry-ruby/branch/master) | [](https://rubygems.org/gems/sentry-sidekiq) | [](https://github.com/getsentry/sentry-ruby/actions/workflows/sentry_sidekiq_test.yml) | [](https://codecov.io/gh/getsentry/sentry-ruby/branch/master) | [](https://rubygems.org/gems/sentry-delayed_job) | [](https://github.com/getsentry/sentry-ruby/actions/workflows/sentry_delayed_job_test.yml) | [](https://codecov.io/gh/getsentry/sentry-ruby/branch/master) | [](https://rubygems.org/gems/sentry-resque) | [](https://github.com/getsentry/sentry-ruby/actions/workflows/sentry_resque_test.yml) | [](https://codecov.io/gh/getsentry/sentry-ruby/branch/master) | [](https://rubygems.org/gems/sentry-opentelemetry) | [](https://github.com/getsentry/sentry-ruby/actions/workflows/sentry_opentelemetry_test.yml) | [](https://codecov.io/gh/getsentry/sentry-ruby/branch/master) | [](https://rubygems.org/gems/sentry-ruby) | [](https://github.com/getsentry/sentry-ruby/actions/workflows/sentry_ruby_test.yml) | [](https://codecov.io/gh/getsentry/sentry-ruby/branch/master) | [](https://www.rubydoc.info/gems/sentry-ruby) |
|
19
|
+
| [](https://rubygems.org/gems/sentry-rails) | [](https://github.com/getsentry/sentry-ruby/actions/workflows/sentry_rails_test.yml) | [](https://codecov.io/gh/getsentry/sentry-ruby/branch/master) | [](https://www.rubydoc.info/gems/sentry-rails) |
|
20
|
+
| [](https://rubygems.org/gems/sentry-sidekiq) | [](https://github.com/getsentry/sentry-ruby/actions/workflows/sentry_sidekiq_test.yml) | [](https://codecov.io/gh/getsentry/sentry-ruby/branch/master) | [](https://www.rubydoc.info/gems/sentry-sidekiq) |
|
21
|
+
| [](https://rubygems.org/gems/sentry-delayed_job) | [](https://github.com/getsentry/sentry-ruby/actions/workflows/sentry_delayed_job_test.yml) | [](https://codecov.io/gh/getsentry/sentry-ruby/branch/master) | [](https://www.rubydoc.info/gems/sentry-delayed_job) |
|
22
|
+
| [](https://rubygems.org/gems/sentry-resque) | [](https://github.com/getsentry/sentry-ruby/actions/workflows/sentry_resque_test.yml) | [](https://codecov.io/gh/getsentry/sentry-ruby/branch/master) | [](https://www.rubydoc.info/gems/sentry-resque) |
|
23
|
+
| [](https://rubygems.org/gems/sentry-opentelemetry) | [](https://github.com/getsentry/sentry-ruby/actions/workflows/sentry_opentelemetry_test.yml) | [](https://codecov.io/gh/getsentry/sentry-ruby/branch/master) | [](https://www.rubydoc.info/gems/sentry-opentelemetry) |
|
24
24
|
|
25
25
|
|
26
26
|
|
@@ -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
|
-
- [
|
93
|
+
- [OpenTelemetry](https://docs.sentry.io/platforms/ruby/performance/instrumentation/opentelemetry/)
|
94
94
|
|
95
95
|
### Enriching Events
|
96
96
|
|
@@ -103,6 +103,16 @@ To learn more about sampling transactions, please visit the [official documentat
|
|
103
103
|
|
104
104
|
* [](https://docs.sentry.io/platforms/ruby/)
|
105
105
|
* [](https://forum.sentry.io/c/sdks)
|
106
|
-
* [](https://discord.gg/PXa5Apfe7K)
|
106
|
+
* [](https://discord.gg/PXa5Apfe7K)
|
107
107
|
* [](https://stackoverflow.com/questions/tagged/sentry)
|
108
108
|
* [](https://twitter.com/intent/follow?screen_name=getsentry)
|
109
|
+
|
110
|
+
## Contributing to the SDK
|
111
|
+
|
112
|
+
Please make sure to read the [CONTRIBUTING.md](https://github.com/getsentry/sentry-ruby/blob/master/CONTRIBUTING.md) before making a pull request.
|
113
|
+
|
114
|
+
Thanks to everyone who has contributed to this project so far.
|
115
|
+
|
116
|
+
<a href="https://github.com/getsentry/sentry-ruby/graphs/contributors">
|
117
|
+
<img src="https://contributors-img.web.app/image?repo=getsentry/sentry-ruby" />
|
118
|
+
</a>
|
data/Rakefile
CHANGED
data/bin/console
CHANGED
@@ -0,0 +1,42 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Sentry
|
4
|
+
class Attachment
|
5
|
+
PathNotFoundError = Class.new(StandardError)
|
6
|
+
|
7
|
+
attr_reader :bytes, :filename, :path, :content_type
|
8
|
+
|
9
|
+
def initialize(bytes: nil, filename: nil, content_type: nil, path: nil)
|
10
|
+
@bytes = bytes
|
11
|
+
@filename = infer_filename(filename, path)
|
12
|
+
@path = path
|
13
|
+
@content_type = content_type
|
14
|
+
end
|
15
|
+
|
16
|
+
def to_envelope_headers
|
17
|
+
{ type: 'attachment', filename: filename, content_type: content_type, length: payload.bytesize }
|
18
|
+
end
|
19
|
+
|
20
|
+
def payload
|
21
|
+
@payload ||= if bytes
|
22
|
+
bytes
|
23
|
+
else
|
24
|
+
File.binread(path)
|
25
|
+
end
|
26
|
+
rescue Errno::ENOENT
|
27
|
+
raise PathNotFoundError, "Failed to read attachment file, file not found: #{path}"
|
28
|
+
end
|
29
|
+
|
30
|
+
private
|
31
|
+
|
32
|
+
def infer_filename(filename, path)
|
33
|
+
return filename if filename
|
34
|
+
|
35
|
+
if path
|
36
|
+
File.basename(path)
|
37
|
+
else
|
38
|
+
raise ArgumentError, "filename or path is required"
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
@@ -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,45 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Sentry
|
4
|
+
class BackpressureMonitor < ThreadedPeriodicWorker
|
5
|
+
DEFAULT_INTERVAL = 10
|
6
|
+
MAX_DOWNSAMPLE_FACTOR = 10
|
7
|
+
|
8
|
+
def initialize(configuration, client, interval: DEFAULT_INTERVAL)
|
9
|
+
super(configuration.logger, interval)
|
10
|
+
@client = client
|
11
|
+
|
12
|
+
@healthy = true
|
13
|
+
@downsample_factor = 0
|
14
|
+
end
|
15
|
+
|
16
|
+
def healthy?
|
17
|
+
ensure_thread
|
18
|
+
@healthy
|
19
|
+
end
|
20
|
+
|
21
|
+
def downsample_factor
|
22
|
+
ensure_thread
|
23
|
+
@downsample_factor
|
24
|
+
end
|
25
|
+
|
26
|
+
def run
|
27
|
+
check_health
|
28
|
+
set_downsample_factor
|
29
|
+
end
|
30
|
+
|
31
|
+
def check_health
|
32
|
+
@healthy = !(@client.transport.any_rate_limited? || Sentry.background_worker&.full?)
|
33
|
+
end
|
34
|
+
|
35
|
+
def set_downsample_factor
|
36
|
+
if @healthy
|
37
|
+
log_debug("[BackpressureMonitor] health check positive, reverting to normal sampling") if @downsample_factor.positive?
|
38
|
+
@downsample_factor = 0
|
39
|
+
else
|
40
|
+
@downsample_factor += 1 if @downsample_factor < MAX_DOWNSAMPLE_FACTOR
|
41
|
+
log_debug("[BackpressureMonitor] health check negative, downsampling with a factor of #{@downsample_factor}")
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
data/lib/sentry/backtrace.rb
CHANGED
@@ -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
|
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
|
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,25 +48,36 @@ module Sentry
|
|
42
48
|
def capture_event(event, scope, hint = {})
|
43
49
|
return unless configuration.sending_allowed?
|
44
50
|
|
45
|
-
|
46
|
-
transport.record_lost_event(:sample_rate, '
|
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)
|
58
|
+
|
59
|
+
is_transaction = event.is_a?(TransactionEvent)
|
60
|
+
spans_before = is_transaction ? event.spans.size : 0
|
61
|
+
|
51
62
|
event = scope.apply_to_event(event, hint)
|
52
63
|
|
53
64
|
if event.nil?
|
54
|
-
|
55
|
-
transport.record_lost_event(:event_processor,
|
65
|
+
log_debug("Discarded event because one of the event processors returned nil")
|
66
|
+
transport.record_lost_event(:event_processor, data_category)
|
67
|
+
transport.record_lost_event(:event_processor, 'span', num: spans_before + 1) if is_transaction
|
56
68
|
return
|
69
|
+
elsif is_transaction
|
70
|
+
spans_delta = spans_before - event.spans.size
|
71
|
+
transport.record_lost_event(:event_processor, 'span', num: spans_delta) if spans_delta > 0
|
57
72
|
end
|
58
73
|
|
59
74
|
if async_block = configuration.async
|
60
75
|
dispatch_async_event(async_block, event, hint)
|
61
76
|
elsif configuration.background_worker_threads != 0 && hint.fetch(:background, true)
|
62
|
-
|
63
|
-
|
77
|
+
unless dispatch_background_event(event, hint)
|
78
|
+
transport.record_lost_event(:queue_overflow, data_category)
|
79
|
+
transport.record_lost_event(:queue_overflow, 'span', num: spans_before + 1) if is_transaction
|
80
|
+
end
|
64
81
|
else
|
65
82
|
send_event(event, hint)
|
66
83
|
end
|
@@ -71,6 +88,20 @@ module Sentry
|
|
71
88
|
nil
|
72
89
|
end
|
73
90
|
|
91
|
+
# Capture an envelope directly.
|
92
|
+
# @param envelope [Envelope] the envelope to be captured.
|
93
|
+
# @return [void]
|
94
|
+
def capture_envelope(envelope)
|
95
|
+
Sentry.background_worker.perform { send_envelope(envelope) }
|
96
|
+
end
|
97
|
+
|
98
|
+
# Flush pending events to Sentry.
|
99
|
+
# @return [void]
|
100
|
+
def flush
|
101
|
+
transport.flush if configuration.sending_to_dsn_allowed?
|
102
|
+
spotlight_transport.flush if spotlight_transport
|
103
|
+
end
|
104
|
+
|
74
105
|
# Initializes an Event object with the given exception. Returns `nil` if the exception's class is excluded from reporting.
|
75
106
|
# @param exception [Exception] the exception to be reported.
|
76
107
|
# @param hint [Hash] the hint data that'll be passed to `before_send` callback and the scope's event processors.
|
@@ -82,9 +113,10 @@ module Sentry
|
|
82
113
|
return if !ignore_exclusions && !@configuration.exception_class_allowed?(exception)
|
83
114
|
|
84
115
|
integration_meta = Sentry.integrations[hint[:integration]]
|
116
|
+
mechanism = hint.delete(:mechanism) { Mechanism.new }
|
85
117
|
|
86
118
|
ErrorEvent.new(configuration: configuration, integration_meta: integration_meta).tap do |event|
|
87
|
-
event.add_exception_interface(exception)
|
119
|
+
event.add_exception_interface(exception, mechanism: mechanism)
|
88
120
|
event.add_threads_interface(crashed: true)
|
89
121
|
event.level = :error
|
90
122
|
end
|
@@ -145,13 +177,15 @@ module Sentry
|
|
145
177
|
# @!macro send_event
|
146
178
|
def send_event(event, hint = nil)
|
147
179
|
event_type = event.is_a?(Event) ? event.type : event["type"]
|
180
|
+
data_category = Envelope::Item.data_category(event_type)
|
181
|
+
spans_before = event.is_a?(TransactionEvent) ? event.spans.size : 0
|
148
182
|
|
149
183
|
if event_type != TransactionEvent::TYPE && configuration.before_send
|
150
184
|
event = configuration.before_send.call(event, hint)
|
151
185
|
|
152
186
|
if event.nil?
|
153
|
-
|
154
|
-
transport.record_lost_event(:before_send,
|
187
|
+
log_debug("Discarded event because before_send returned nil")
|
188
|
+
transport.record_lost_event(:before_send, data_category)
|
155
189
|
return
|
156
190
|
end
|
157
191
|
end
|
@@ -160,22 +194,41 @@ module Sentry
|
|
160
194
|
event = configuration.before_send_transaction.call(event, hint)
|
161
195
|
|
162
196
|
if event.nil?
|
163
|
-
|
197
|
+
log_debug("Discarded event because before_send_transaction returned nil")
|
164
198
|
transport.record_lost_event(:before_send, 'transaction')
|
199
|
+
transport.record_lost_event(:before_send, 'span', num: spans_before + 1)
|
165
200
|
return
|
201
|
+
else
|
202
|
+
spans_after = event.is_a?(TransactionEvent) ? event.spans.size : 0
|
203
|
+
spans_delta = spans_before - spans_after
|
204
|
+
transport.record_lost_event(:before_send, 'span', num: spans_delta) if spans_delta > 0
|
166
205
|
end
|
167
206
|
end
|
168
207
|
|
169
|
-
transport.send_event(event)
|
208
|
+
transport.send_event(event) if configuration.sending_to_dsn_allowed?
|
209
|
+
spotlight_transport.send_event(event) if spotlight_transport
|
170
210
|
|
171
211
|
event
|
172
212
|
rescue => e
|
173
|
-
|
174
|
-
|
213
|
+
log_error("Event sending failed", e, debug: configuration.debug)
|
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)
|
216
|
+
raise
|
217
|
+
end
|
218
|
+
|
219
|
+
# Send an envelope directly to Sentry.
|
220
|
+
# @param envelope [Envelope] the envelope to be sent.
|
221
|
+
# @return [void]
|
222
|
+
def send_envelope(envelope)
|
223
|
+
transport.send_envelope(envelope) if configuration.sending_to_dsn_allowed?
|
224
|
+
spotlight_transport.send_envelope(envelope) if spotlight_transport
|
225
|
+
rescue => e
|
226
|
+
log_error("Envelope sending failed", e, debug: configuration.debug)
|
227
|
+
|
228
|
+
envelope.items.map(&:data_category).each do |data_category|
|
229
|
+
transport.record_lost_event(:network_error, data_category)
|
230
|
+
end
|
175
231
|
|
176
|
-
event_info = Event.get_log_message(event.to_hash)
|
177
|
-
log_info("Unreported #{loggable_event_type}: #{event_info}")
|
178
|
-
transport.record_lost_event(:network_error, event_type)
|
179
232
|
raise
|
180
233
|
end
|
181
234
|
|
data/lib/sentry/configuration.rb
CHANGED
@@ -7,6 +7,8 @@ require 'sentry/utils/custom_inspection'
|
|
7
7
|
require "sentry/dsn"
|
8
8
|
require "sentry/release_detector"
|
9
9
|
require "sentry/transport/configuration"
|
10
|
+
require "sentry/cron/configuration"
|
11
|
+
require "sentry/metrics/configuration"
|
10
12
|
require "sentry/linecache"
|
11
13
|
require "sentry/interfaces/stacktrace_builder"
|
12
14
|
|
@@ -40,6 +42,13 @@ module Sentry
|
|
40
42
|
# @return [Integer]
|
41
43
|
attr_accessor :background_worker_threads
|
42
44
|
|
45
|
+
# The maximum queue size for the background worker.
|
46
|
+
# Jobs will be rejected above this limit.
|
47
|
+
#
|
48
|
+
# Default is {BackgroundWorker::DEFAULT_MAX_QUEUE}.
|
49
|
+
# @return [Integer]
|
50
|
+
attr_accessor :background_worker_max_queue
|
51
|
+
|
43
52
|
# a proc/lambda that takes an array of stack traces
|
44
53
|
# it'll be used to silence (reduce) backtrace of the exception
|
45
54
|
#
|
@@ -142,6 +151,14 @@ module Sentry
|
|
142
151
|
# @return [Boolean]
|
143
152
|
attr_accessor :include_local_variables
|
144
153
|
|
154
|
+
# Whether to capture events and traces into Spotlight. Default is false.
|
155
|
+
# If you set this to true, Sentry will send events and traces to the local
|
156
|
+
# Sidecar proxy at http://localhost:8969/stream.
|
157
|
+
# If you want to use a different Sidecar proxy address, set this to String
|
158
|
+
# with the proxy URL.
|
159
|
+
# @return [Boolean, String]
|
160
|
+
attr_accessor :spotlight
|
161
|
+
|
145
162
|
# @deprecated Use {#include_local_variables} instead.
|
146
163
|
alias_method :capture_exception_frame_locals, :include_local_variables
|
147
164
|
|
@@ -211,10 +228,18 @@ module Sentry
|
|
211
228
|
# @return [String]
|
212
229
|
attr_accessor :server_name
|
213
230
|
|
214
|
-
#
|
215
|
-
# @return [Transport]
|
231
|
+
# Transport related configuration.
|
232
|
+
# @return [Transport::Configuration]
|
216
233
|
attr_reader :transport
|
217
234
|
|
235
|
+
# Cron related configuration.
|
236
|
+
# @return [Cron::Configuration]
|
237
|
+
attr_reader :cron
|
238
|
+
|
239
|
+
# Metrics related configuration.
|
240
|
+
# @return [Metrics::Configuration]
|
241
|
+
attr_reader :metrics
|
242
|
+
|
218
243
|
# Take a float between 0.0 and 1.0 as the sample rate for tracing events (transactions).
|
219
244
|
# @return [Float, nil]
|
220
245
|
attr_reader :traces_sample_rate
|
@@ -243,6 +268,12 @@ module Sentry
|
|
243
268
|
# @return [Boolean]
|
244
269
|
attr_accessor :auto_session_tracking
|
245
270
|
|
271
|
+
# Whether to downsample transactions automatically because of backpressure.
|
272
|
+
# Starts a new monitor thread to check health of the SDK every 10 seconds.
|
273
|
+
# Default is false
|
274
|
+
# @return [Boolean]
|
275
|
+
attr_accessor :enable_backpressure_handling
|
276
|
+
|
246
277
|
# Allowlist of outgoing request targets to which sentry-trace and baggage headers are attached.
|
247
278
|
# Default is all (/.*/)
|
248
279
|
# @return [Array<String, Regexp>]
|
@@ -285,11 +316,11 @@ module Sentry
|
|
285
316
|
'Sinatra::NotFound'
|
286
317
|
].freeze
|
287
318
|
|
288
|
-
RACK_ENV_WHITELIST_DEFAULT = %w
|
319
|
+
RACK_ENV_WHITELIST_DEFAULT = %w[
|
289
320
|
REMOTE_ADDR
|
290
321
|
SERVER_NAME
|
291
322
|
SERVER_PORT
|
292
|
-
|
323
|
+
].freeze
|
293
324
|
|
294
325
|
HEROKU_DYNO_METADATA_MESSAGE = "You are running on Heroku but haven't enabled Dyno Metadata. For Sentry's "\
|
295
326
|
"release detection to work correctly, please run `heroku labs:enable runtime-dyno-metadata`".freeze
|
@@ -302,7 +333,7 @@ module Sentry
|
|
302
333
|
|
303
334
|
PROPAGATION_TARGETS_MATCH_ALL = /.*/.freeze
|
304
335
|
|
305
|
-
DEFAULT_PATCHES = %i
|
336
|
+
DEFAULT_PATCHES = %i[redis puma http].freeze
|
306
337
|
|
307
338
|
class << self
|
308
339
|
# Post initialization callbacks are called at the end of initialization process
|
@@ -311,7 +342,7 @@ module Sentry
|
|
311
342
|
@post_initialization_callbacks ||= []
|
312
343
|
end
|
313
344
|
|
314
|
-
|
345
|
+
# allow extensions to add their hooks to the Configuration class
|
315
346
|
def add_post_initialization_callback(&block)
|
316
347
|
post_initialization_callbacks << block
|
317
348
|
end
|
@@ -320,7 +351,8 @@ module Sentry
|
|
320
351
|
def initialize
|
321
352
|
self.app_dirs_pattern = nil
|
322
353
|
self.debug = false
|
323
|
-
self.background_worker_threads =
|
354
|
+
self.background_worker_threads = (processor_count / 2.0).ceil
|
355
|
+
self.background_worker_max_queue = BackgroundWorker::DEFAULT_MAX_QUEUE
|
324
356
|
self.backtrace_cleanup_callback = nil
|
325
357
|
self.max_breadcrumbs = BreadcrumbBuffer::DEFAULT_SIZE
|
326
358
|
self.breadcrumbs_logger = []
|
@@ -342,8 +374,10 @@ module Sentry
|
|
342
374
|
self.skip_rake_integration = false
|
343
375
|
self.send_client_reports = true
|
344
376
|
self.auto_session_tracking = true
|
377
|
+
self.enable_backpressure_handling = false
|
345
378
|
self.trusted_proxies = []
|
346
379
|
self.dsn = ENV['SENTRY_DSN']
|
380
|
+
self.spotlight = false
|
347
381
|
self.server_name = server_name_from_env
|
348
382
|
self.instrumenter = :sentry
|
349
383
|
self.trace_propagation_targets = [PROPAGATION_TARGETS_MATCH_ALL]
|
@@ -356,6 +390,8 @@ module Sentry
|
|
356
390
|
self.enable_tracing = nil
|
357
391
|
|
358
392
|
@transport = Transport::Configuration.new
|
393
|
+
@cron = Cron::Configuration.new
|
394
|
+
@metrics = Metrics::Configuration.new
|
359
395
|
@gem_specs = Hash[Gem::Specification.map { |spec| [spec.name, spec.version.to_s] }] if Gem::Specification.respond_to?(:map)
|
360
396
|
|
361
397
|
run_post_initialization_callbacks
|
@@ -444,11 +480,15 @@ module Sentry
|
|
444
480
|
|
445
481
|
def profiles_sample_rate=(profiles_sample_rate)
|
446
482
|
raise ArgumentError, "profiles_sample_rate must be a Numeric or nil" unless is_numeric_or_nil?(profiles_sample_rate)
|
447
|
-
|
483
|
+
log_warn("Please make sure to include the 'stackprof' gem in your Gemfile to use Profiling with Sentry.") unless defined?(StackProf)
|
448
484
|
@profiles_sample_rate = profiles_sample_rate
|
449
485
|
end
|
450
486
|
|
451
487
|
def sending_allowed?
|
488
|
+
spotlight || sending_to_dsn_allowed?
|
489
|
+
end
|
490
|
+
|
491
|
+
def sending_to_dsn_allowed?
|
452
492
|
@errors = []
|
453
493
|
|
454
494
|
valid? && capture_in_environment?
|
@@ -460,6 +500,10 @@ module Sentry
|
|
460
500
|
Random.rand < sample_rate
|
461
501
|
end
|
462
502
|
|
503
|
+
def session_tracking?
|
504
|
+
auto_session_tracking && enabled_in_current_env?
|
505
|
+
end
|
506
|
+
|
463
507
|
def exception_class_allowed?(exc)
|
464
508
|
if exc.is_a?(Sentry::Error)
|
465
509
|
# Try to prevent error reporting loops
|
@@ -536,12 +580,6 @@ module Sentry
|
|
536
580
|
|
537
581
|
private
|
538
582
|
|
539
|
-
def check_callable!(name, value)
|
540
|
-
unless value == nil || value.respond_to?(:call)
|
541
|
-
raise ArgumentError, "#{name} must be callable (or nil to disable)"
|
542
|
-
end
|
543
|
-
end
|
544
|
-
|
545
583
|
def init_dsn(dsn_string)
|
546
584
|
return if dsn_string.nil? || dsn_string.empty?
|
547
585
|
|
@@ -616,5 +654,10 @@ module Sentry
|
|
616
654
|
instance_eval(&hook)
|
617
655
|
end
|
618
656
|
end
|
657
|
+
|
658
|
+
def processor_count
|
659
|
+
available_processor_count = Concurrent.available_processor_count if Concurrent.respond_to?(:available_processor_count)
|
660
|
+
available_processor_count || Concurrent.processor_count
|
661
|
+
end
|
619
662
|
end
|
620
663
|
end
|