sentry-ruby-core 5.2.1 → 5.4.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 +0 -1
- data/README.md +8 -7
- data/lib/sentry/background_worker.rb +2 -1
- data/lib/sentry/client.rb +14 -6
- data/lib/sentry/configuration.rb +10 -0
- data/lib/sentry/envelope.rb +51 -0
- data/lib/sentry/error_event.rb +38 -0
- data/lib/sentry/event.rb +4 -30
- data/lib/sentry/hub.rb +1 -1
- data/lib/sentry/interfaces/exception.rb +3 -0
- data/lib/sentry/net/http.rb +3 -3
- data/lib/sentry/rack/capture_exceptions.rb +8 -4
- data/lib/sentry/session_flusher.rb +12 -1
- data/lib/sentry/test_helper.rb +76 -0
- data/lib/sentry/transaction_event.rb +4 -20
- data/lib/sentry/transport/http_transport.rb +15 -1
- data/lib/sentry/transport.rb +4 -23
- data/lib/sentry/version.rb +1 -1
- data/lib/sentry-ruby.rb +78 -5
- data/sentry-ruby-core.gemspec +1 -4
- data/sentry-ruby.gemspec +2 -1
- metadata +19 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: '05368ec66748ebeba74ec529e6395ce1ad5eb36f1beff0f7014e041a507099fe'
|
4
|
+
data.tar.gz: 150e09181157a7f36a21a46aeffdc37f47db942d967dbf206167ddb6208e6b51
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: eec3eb3c33f43854ff89fdf19c5b0b53726c9f79e8e9325969d5241f51490c1096234cbc27aa090bf408207142a265ec9c4eebdf06d159771a5e22a44d405d8b
|
7
|
+
data.tar.gz: e2e50b8e694d3eea9606a41dfcca942ebc41a5e64f128bc455ad34ee9ad5806ae1794993306706039270439bfa26cf7f46614ccf6525d8ecc0a46db6dad3d60a
|
data/Gemfile
CHANGED
data/README.md
CHANGED
@@ -1,8 +1,11 @@
|
|
1
1
|
<p align="center">
|
2
|
-
<a href="https://sentry.io" target="_blank"
|
3
|
-
<
|
2
|
+
<a href="https://sentry.io/?utm_source=github&utm_medium=logo" target="_blank">
|
3
|
+
<picture>
|
4
|
+
<source srcset="https://sentry-brand.storage.googleapis.com/sentry-logo-white.png" media="(prefers-color-scheme: dark)" />
|
5
|
+
<source srcset="https://sentry-brand.storage.googleapis.com/sentry-logo-black.png" media="(prefers-color-scheme: light), (prefers-color-scheme: no-preference)" />
|
6
|
+
<img src="https://sentry-brand.storage.googleapis.com/sentry-logo-black.png" alt="Sentry" width="280">
|
7
|
+
</picture>
|
4
8
|
</a>
|
5
|
-
<br />
|
6
9
|
</p>
|
7
10
|
|
8
11
|
_Bad software is everywhere, and we're tired of it. Sentry is on a mission to help developers write better software faster, so we can get back to enjoying technology. If you want to join us [<kbd>**Check out our open positions**</kbd>](https://sentry.io/careers/)_
|
@@ -29,7 +32,7 @@ If you're using `sentry-raven`, we recommend you to migrate to this new SDK. You
|
|
29
32
|
|
30
33
|
## Requirements
|
31
34
|
|
32
|
-
We test on Ruby 2.4, 2.5, 2.6, 2.7, and 3.
|
35
|
+
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.
|
33
36
|
|
34
37
|
If you use self-hosted Sentry, please also make sure its version is above `20.6.0`.
|
35
38
|
|
@@ -52,13 +55,11 @@ gem "sentry-resque"
|
|
52
55
|
|
53
56
|
### Configuration
|
54
57
|
|
55
|
-
You
|
56
|
-
|
58
|
+
You need to use Sentry.init to initialize and configure your SDK:
|
57
59
|
```ruby
|
58
60
|
Sentry.init do |config|
|
59
61
|
config.dsn = "MY_DSN"
|
60
62
|
end
|
61
|
-
|
62
63
|
```
|
63
64
|
|
64
65
|
To learn more about available configuration options, please visit the [official documentation](https://docs.sentry.io/platforms/ruby/configuration/options/).
|
@@ -29,7 +29,7 @@ module Sentry
|
|
29
29
|
log_debug("config.background_worker_threads is set to 0, all events will be sent synchronously")
|
30
30
|
Concurrent::ImmediateExecutor.new
|
31
31
|
else
|
32
|
-
log_debug("
|
32
|
+
log_debug("Initializing the background worker with #{@number_of_threads} threads")
|
33
33
|
|
34
34
|
executor = Concurrent::ThreadPoolExecutor.new(
|
35
35
|
min_threads: 0,
|
@@ -59,6 +59,7 @@ module Sentry
|
|
59
59
|
end
|
60
60
|
|
61
61
|
def shutdown
|
62
|
+
log_debug("Shutting down background worker")
|
62
63
|
@shutdown_callback&.call
|
63
64
|
end
|
64
65
|
|
data/lib/sentry/client.rb
CHANGED
@@ -80,9 +80,10 @@ module Sentry
|
|
80
80
|
|
81
81
|
integration_meta = Sentry.integrations[hint[:integration]]
|
82
82
|
|
83
|
-
|
83
|
+
ErrorEvent.new(configuration: configuration, integration_meta: integration_meta).tap do |event|
|
84
84
|
event.add_exception_interface(exception)
|
85
85
|
event.add_threads_interface(crashed: true)
|
86
|
+
event.level = :error
|
86
87
|
end
|
87
88
|
end
|
88
89
|
|
@@ -94,8 +95,9 @@ module Sentry
|
|
94
95
|
return unless @configuration.sending_allowed?
|
95
96
|
|
96
97
|
integration_meta = Sentry.integrations[hint[:integration]]
|
97
|
-
event =
|
98
|
+
event = ErrorEvent.new(configuration: configuration, integration_meta: integration_meta, message: message)
|
98
99
|
event.add_threads_interface(backtrace: backtrace || caller)
|
100
|
+
event.level = :error
|
99
101
|
event
|
100
102
|
end
|
101
103
|
|
@@ -133,7 +135,7 @@ module Sentry
|
|
133
135
|
|
134
136
|
event
|
135
137
|
rescue => e
|
136
|
-
loggable_event_type =
|
138
|
+
loggable_event_type = event_type.capitalize
|
137
139
|
log_error("#{loggable_event_type} sending failed", e, debug: configuration.debug)
|
138
140
|
|
139
141
|
event_info = Event.get_log_message(event.to_hash)
|
@@ -165,7 +167,14 @@ module Sentry
|
|
165
167
|
def dispatch_async_event(async_block, event, hint)
|
166
168
|
# We have to convert to a JSON-like hash, because background job
|
167
169
|
# processors (esp ActiveJob) may not like weird types in the event hash
|
168
|
-
|
170
|
+
|
171
|
+
event_hash =
|
172
|
+
begin
|
173
|
+
event.to_json_compatible
|
174
|
+
rescue => e
|
175
|
+
log_error("Converting #{event.type} (#{event.event_id}) to JSON compatible hash failed", e, debug: configuration.debug)
|
176
|
+
return
|
177
|
+
end
|
169
178
|
|
170
179
|
if async_block.arity == 2
|
171
180
|
hint = JSON.parse(JSON.generate(hint))
|
@@ -174,8 +183,7 @@ module Sentry
|
|
174
183
|
async_block.call(event_hash)
|
175
184
|
end
|
176
185
|
rescue => e
|
177
|
-
|
178
|
-
log_error("Async #{loggable_event_type} sending failed", e, debug: configuration.debug)
|
186
|
+
log_error("Async #{event_hash["type"]} sending failed", e, debug: configuration.debug)
|
179
187
|
send_event(event, hint)
|
180
188
|
end
|
181
189
|
end
|
data/lib/sentry/configuration.rb
CHANGED
@@ -290,6 +290,16 @@ module Sentry
|
|
290
290
|
def async=(value)
|
291
291
|
check_callable!("async", value)
|
292
292
|
|
293
|
+
log_warn <<~MSG
|
294
|
+
|
295
|
+
sentry-ruby now sends events asynchronously by default with its background worker (supported since 4.1.0).
|
296
|
+
The `config.async` callback has become redundant while continuing to cause issues.
|
297
|
+
(The problems of `async` are detailed in https://github.com/getsentry/sentry-ruby/issues/1522)
|
298
|
+
|
299
|
+
Therefore, we encourage you to remove it and let the background worker take care of async job sending.
|
300
|
+
It's deprecation is planned in the next major release (6.0), which is scheduled around the 3rd quarter of 2022.
|
301
|
+
MSG
|
302
|
+
|
293
303
|
@async = value
|
294
304
|
end
|
295
305
|
|
data/lib/sentry/envelope.rb
CHANGED
@@ -4,6 +4,9 @@ module Sentry
|
|
4
4
|
# @api private
|
5
5
|
class Envelope
|
6
6
|
class Item
|
7
|
+
STACKTRACE_FRAME_LIMIT_ON_OVERSIZED_PAYLOAD = 500
|
8
|
+
MAX_SERIALIZED_PAYLOAD_SIZE = 1024 * 200
|
9
|
+
|
7
10
|
attr_accessor :headers, :payload
|
8
11
|
|
9
12
|
def initialize(headers, payload)
|
@@ -21,6 +24,54 @@ module Sentry
|
|
21
24
|
#{JSON.generate(@payload)}
|
22
25
|
ITEM
|
23
26
|
end
|
27
|
+
|
28
|
+
def serialize
|
29
|
+
result = to_s
|
30
|
+
|
31
|
+
if result.bytesize > MAX_SERIALIZED_PAYLOAD_SIZE
|
32
|
+
remove_breadcrumbs!
|
33
|
+
result = to_s
|
34
|
+
end
|
35
|
+
|
36
|
+
if result.bytesize > MAX_SERIALIZED_PAYLOAD_SIZE
|
37
|
+
reduce_stacktrace!
|
38
|
+
result = to_s
|
39
|
+
end
|
40
|
+
|
41
|
+
[result, result.bytesize > MAX_SERIALIZED_PAYLOAD_SIZE]
|
42
|
+
end
|
43
|
+
|
44
|
+
def size_breakdown
|
45
|
+
payload.map do |key, value|
|
46
|
+
"#{key}: #{JSON.generate(value).bytesize}"
|
47
|
+
end.join(", ")
|
48
|
+
end
|
49
|
+
|
50
|
+
private
|
51
|
+
|
52
|
+
def remove_breadcrumbs!
|
53
|
+
if payload.key?(:breadcrumbs)
|
54
|
+
payload.delete(:breadcrumbs)
|
55
|
+
elsif payload.key?("breadcrumbs")
|
56
|
+
payload.delete("breadcrumbs")
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
def reduce_stacktrace!
|
61
|
+
if exceptions = payload.dig(:exception, :values) || payload.dig("exception", "values")
|
62
|
+
exceptions.each do |exception|
|
63
|
+
# 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
|
64
|
+
traces = exception.dig(:stacktrace, :frames) || exception.dig("stacktrace", "frames")
|
65
|
+
|
66
|
+
if traces && traces.size > STACKTRACE_FRAME_LIMIT_ON_OVERSIZED_PAYLOAD
|
67
|
+
size_on_both_ends = STACKTRACE_FRAME_LIMIT_ON_OVERSIZED_PAYLOAD / 2
|
68
|
+
traces.replace(
|
69
|
+
traces[0..(size_on_both_ends - 1)] + traces[-size_on_both_ends..-1],
|
70
|
+
)
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
24
75
|
end
|
25
76
|
|
26
77
|
attr_accessor :headers, :items
|
@@ -0,0 +1,38 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Sentry
|
4
|
+
# ErrorEvent represents error or normal message events.
|
5
|
+
class ErrorEvent < Event
|
6
|
+
# @return [ExceptionInterface]
|
7
|
+
attr_reader :exception
|
8
|
+
|
9
|
+
# @return [ThreadsInterface]
|
10
|
+
attr_reader :threads
|
11
|
+
|
12
|
+
# @return [Hash]
|
13
|
+
def to_hash
|
14
|
+
data = super
|
15
|
+
data[:threads] = threads.to_hash if threads
|
16
|
+
data[:exception] = exception.to_hash if exception
|
17
|
+
data
|
18
|
+
end
|
19
|
+
|
20
|
+
# @!visibility private
|
21
|
+
def add_threads_interface(backtrace: nil, **options)
|
22
|
+
@threads = ThreadsInterface.build(
|
23
|
+
backtrace: backtrace,
|
24
|
+
stacktrace_builder: @stacktrace_builder,
|
25
|
+
**options
|
26
|
+
)
|
27
|
+
end
|
28
|
+
|
29
|
+
# @!visibility private
|
30
|
+
def add_exception_interface(exception)
|
31
|
+
if exception.respond_to?(:sentry_context)
|
32
|
+
@extra.merge!(exception.sentry_context)
|
33
|
+
end
|
34
|
+
|
35
|
+
@exception = Sentry::ExceptionInterface.build(exception: exception, stacktrace_builder: @stacktrace_builder)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
data/lib/sentry/event.rb
CHANGED
@@ -9,7 +9,10 @@ require 'sentry/utils/request_id'
|
|
9
9
|
require 'sentry/utils/custom_inspection'
|
10
10
|
|
11
11
|
module Sentry
|
12
|
+
# This is an abstract class that defines the shared attributes of an event.
|
13
|
+
# Please don't use it directly. The user-facing classes are its child classes.
|
12
14
|
class Event
|
15
|
+
TYPE = "event"
|
13
16
|
# These are readable attributes.
|
14
17
|
SERIALIZEABLE_ATTRIBUTES = %i(
|
15
18
|
event_id level timestamp
|
@@ -23,7 +26,6 @@ module Sentry
|
|
23
26
|
WRITER_ATTRIBUTES = SERIALIZEABLE_ATTRIBUTES - %i(type timestamp level)
|
24
27
|
|
25
28
|
MAX_MESSAGE_SIZE_IN_BYTES = 1024 * 8
|
26
|
-
MAX_SERIALIZED_PAYLOAD_SIZE = 1024 * 200
|
27
29
|
|
28
30
|
SKIP_INSPECTION_ATTRIBUTES = [:@modules, :@stacktrace_builder, :@send_default_pii, :@trusted_proxies, :@rack_env_whitelist]
|
29
31
|
|
@@ -35,12 +37,6 @@ module Sentry
|
|
35
37
|
# @return [RequestInterface]
|
36
38
|
attr_reader :request
|
37
39
|
|
38
|
-
# @return [ExceptionInterface]
|
39
|
-
attr_reader :exception
|
40
|
-
|
41
|
-
# @return [ThreadsInterface]
|
42
|
-
attr_reader :threads
|
43
|
-
|
44
40
|
# @param configuration [Configuration]
|
45
41
|
# @param integration_meta [Hash, nil]
|
46
42
|
# @param message [String, nil]
|
@@ -49,6 +45,7 @@ module Sentry
|
|
49
45
|
@event_id = SecureRandom.uuid.delete("-")
|
50
46
|
@timestamp = Sentry.utc_now.iso8601
|
51
47
|
@platform = :ruby
|
48
|
+
@type = self.class::TYPE
|
52
49
|
@sdk = integration_meta || Sentry.sdk_meta
|
53
50
|
|
54
51
|
@user = {}
|
@@ -71,8 +68,6 @@ module Sentry
|
|
71
68
|
@rack_env_whitelist = configuration.rack_env_whitelist
|
72
69
|
|
73
70
|
@message = (message || "").byteslice(0..MAX_MESSAGE_SIZE_IN_BYTES)
|
74
|
-
|
75
|
-
self.level = :error
|
76
71
|
end
|
77
72
|
|
78
73
|
class << self
|
@@ -146,9 +141,6 @@ module Sentry
|
|
146
141
|
data = serialize_attributes
|
147
142
|
data[:breadcrumbs] = breadcrumbs.to_hash if breadcrumbs
|
148
143
|
data[:request] = request.to_hash if request
|
149
|
-
data[:exception] = exception.to_hash if exception
|
150
|
-
data[:threads] = threads.to_hash if threads
|
151
|
-
|
152
144
|
data
|
153
145
|
end
|
154
146
|
|
@@ -157,24 +149,6 @@ module Sentry
|
|
157
149
|
JSON.parse(JSON.generate(to_hash))
|
158
150
|
end
|
159
151
|
|
160
|
-
# @!visibility private
|
161
|
-
def add_threads_interface(backtrace: nil, **options)
|
162
|
-
@threads = ThreadsInterface.build(
|
163
|
-
backtrace: backtrace,
|
164
|
-
stacktrace_builder: @stacktrace_builder,
|
165
|
-
**options
|
166
|
-
)
|
167
|
-
end
|
168
|
-
|
169
|
-
# @!visibility private
|
170
|
-
def add_exception_interface(exception)
|
171
|
-
if exception.respond_to?(:sentry_context)
|
172
|
-
@extra.merge!(exception.sentry_context)
|
173
|
-
end
|
174
|
-
|
175
|
-
@exception = Sentry::ExceptionInterface.build(exception: exception, stacktrace_builder: @stacktrace_builder)
|
176
|
-
end
|
177
|
-
|
178
152
|
private
|
179
153
|
|
180
154
|
def add_request_interface(env)
|
data/lib/sentry/hub.rb
CHANGED
data/lib/sentry/net/http.rb
CHANGED
@@ -74,11 +74,11 @@ module Sentry
|
|
74
74
|
end
|
75
75
|
|
76
76
|
def start_sentry_span
|
77
|
-
return unless Sentry.initialized? &&
|
77
|
+
return unless Sentry.initialized? && span = Sentry.get_current_scope.get_span
|
78
78
|
return if from_sentry_sdk?
|
79
|
-
return if
|
79
|
+
return if span.sampled == false
|
80
80
|
|
81
|
-
|
81
|
+
span.start_child(op: OP_NAME, start_timestamp: Sentry.utc_now.to_f)
|
82
82
|
end
|
83
83
|
|
84
84
|
def finish_sentry_span(sentry_span)
|
@@ -3,6 +3,8 @@
|
|
3
3
|
module Sentry
|
4
4
|
module Rack
|
5
5
|
class CaptureExceptions
|
6
|
+
ERROR_EVENT_ID_KEY = "sentry.error_event_id"
|
7
|
+
|
6
8
|
def initialize(app)
|
7
9
|
@app = app
|
8
10
|
end
|
@@ -28,13 +30,13 @@ module Sentry
|
|
28
30
|
finish_transaction(transaction, 500)
|
29
31
|
raise # Don't capture Sentry errors
|
30
32
|
rescue Exception => e
|
31
|
-
capture_exception(e)
|
33
|
+
capture_exception(e, env)
|
32
34
|
finish_transaction(transaction, 500)
|
33
35
|
raise
|
34
36
|
end
|
35
37
|
|
36
38
|
exception = collect_exception(env)
|
37
|
-
capture_exception(exception) if exception
|
39
|
+
capture_exception(exception, env) if exception
|
38
40
|
|
39
41
|
finish_transaction(transaction, response[0])
|
40
42
|
|
@@ -53,8 +55,10 @@ module Sentry
|
|
53
55
|
"rack.request".freeze
|
54
56
|
end
|
55
57
|
|
56
|
-
def capture_exception(exception)
|
57
|
-
Sentry.capture_exception(exception)
|
58
|
+
def capture_exception(exception, env)
|
59
|
+
Sentry.capture_exception(exception).tap do |event|
|
60
|
+
env[ERROR_EVENT_ID_KEY] = event.event_id if event
|
61
|
+
end
|
58
62
|
end
|
59
63
|
|
60
64
|
def start_transaction(env, scope)
|
@@ -8,6 +8,7 @@ module Sentry
|
|
8
8
|
|
9
9
|
def initialize(configuration, client)
|
10
10
|
@thread = nil
|
11
|
+
@exited = false
|
11
12
|
@client = client
|
12
13
|
@pending_aggregates = {}
|
13
14
|
@release = configuration.release
|
@@ -29,9 +30,16 @@ module Sentry
|
|
29
30
|
end
|
30
31
|
|
31
32
|
def add_session(session)
|
33
|
+
return if @exited
|
32
34
|
return unless @release
|
33
35
|
|
34
|
-
|
36
|
+
begin
|
37
|
+
ensure_thread
|
38
|
+
rescue ThreadError
|
39
|
+
log_debug("Session flusher thread creation failed")
|
40
|
+
@exited = true
|
41
|
+
return
|
42
|
+
end
|
35
43
|
|
36
44
|
return unless Session::AGGREGATE_STATUSES.include?(session.status)
|
37
45
|
@pending_aggregates[session.aggregation_key] ||= init_aggregates(session.aggregation_key)
|
@@ -39,6 +47,9 @@ module Sentry
|
|
39
47
|
end
|
40
48
|
|
41
49
|
def kill
|
50
|
+
log_debug("Killing session flusher")
|
51
|
+
|
52
|
+
@exited = true
|
42
53
|
@thread&.kill
|
43
54
|
end
|
44
55
|
|
@@ -0,0 +1,76 @@
|
|
1
|
+
module Sentry
|
2
|
+
module TestHelper
|
3
|
+
DUMMY_DSN = 'http://12345:67890@sentry.localdomain/sentry/42'
|
4
|
+
|
5
|
+
# Alters the existing SDK configuration with test-suitable options. Mainly:
|
6
|
+
# - Sets a dummy DSN instead of `nil` or an actual DSN.
|
7
|
+
# - Sets the transport to DummyTransport, which allows easy access to the captured events.
|
8
|
+
# - Disables background worker.
|
9
|
+
# - Makes sure the SDK is enabled under the current environment ("test" in most cases).
|
10
|
+
#
|
11
|
+
# It should be called **before** every test case.
|
12
|
+
#
|
13
|
+
# @yieldparam config [Configuration]
|
14
|
+
# @return [void]
|
15
|
+
def setup_sentry_test(&block)
|
16
|
+
raise "please make sure the SDK is initialized for testing" unless Sentry.initialized?
|
17
|
+
copied_config = Sentry.configuration.dup
|
18
|
+
# configure dummy DSN, so the events will not be sent to the actual service
|
19
|
+
copied_config.dsn = DUMMY_DSN
|
20
|
+
# set transport to DummyTransport, so we can easily intercept the captured events
|
21
|
+
copied_config.transport.transport_class = Sentry::DummyTransport
|
22
|
+
# make sure SDK allows sending under the current environment
|
23
|
+
copied_config.enabled_environments << copied_config.environment unless copied_config.enabled_environments.include?(copied_config.environment)
|
24
|
+
# disble async event sending
|
25
|
+
copied_config.background_worker_threads = 0
|
26
|
+
|
27
|
+
# user can overwrite some of the configs, with a few exceptions like:
|
28
|
+
# - capture_exception_frame_locals
|
29
|
+
# - auto_session_tracking
|
30
|
+
block&.call(copied_config)
|
31
|
+
|
32
|
+
test_client = Sentry::Client.new(copied_config)
|
33
|
+
Sentry.get_current_hub.bind_client(test_client)
|
34
|
+
end
|
35
|
+
|
36
|
+
# Clears all stored events and envelopes.
|
37
|
+
# It should be called **after** every test case.
|
38
|
+
# @return [void]
|
39
|
+
def teardown_sentry_test
|
40
|
+
return unless Sentry.initialized?
|
41
|
+
|
42
|
+
sentry_transport.events = []
|
43
|
+
sentry_transport.envelopes = []
|
44
|
+
end
|
45
|
+
|
46
|
+
# @return [Transport]
|
47
|
+
def sentry_transport
|
48
|
+
Sentry.get_current_client.transport
|
49
|
+
end
|
50
|
+
|
51
|
+
# Returns the captured event objects.
|
52
|
+
# @return [Array<Event>]
|
53
|
+
def sentry_events
|
54
|
+
sentry_transport.events
|
55
|
+
end
|
56
|
+
|
57
|
+
# Returns the captured envelope objects.
|
58
|
+
# @return [Array<Envelope>]
|
59
|
+
def sentry_envelopes
|
60
|
+
sentry_transport.envelopes
|
61
|
+
end
|
62
|
+
|
63
|
+
# Returns the last captured event object.
|
64
|
+
# @return [Event, nil]
|
65
|
+
def last_sentry_event
|
66
|
+
sentry_events.last
|
67
|
+
end
|
68
|
+
|
69
|
+
# Extracts SDK's internal exception container (not actual exception objects) from an given event.
|
70
|
+
# @return [Array<Sentry::SingleExceptionInterface>]
|
71
|
+
def extract_sentry_exceptions(event)
|
72
|
+
event&.exception&.values || []
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
@@ -1,32 +1,15 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
module Sentry
|
4
|
+
# TransactionEvent represents events that carry transaction data (type: "transaction").
|
4
5
|
class TransactionEvent < Event
|
5
6
|
TYPE = "transaction"
|
6
7
|
|
7
|
-
SERIALIZEABLE_ATTRIBUTES = %i(
|
8
|
-
event_id level timestamp start_timestamp
|
9
|
-
release environment server_name modules
|
10
|
-
user tags contexts extra
|
11
|
-
transaction platform sdk type
|
12
|
-
)
|
13
|
-
|
14
|
-
WRITER_ATTRIBUTES = SERIALIZEABLE_ATTRIBUTES - %i(type timestamp start_timestamp level)
|
15
|
-
|
16
|
-
attr_writer(*WRITER_ATTRIBUTES)
|
17
|
-
attr_reader(*SERIALIZEABLE_ATTRIBUTES)
|
18
|
-
|
19
8
|
# @return [<Array[Span]>]
|
20
9
|
attr_accessor :spans
|
21
10
|
|
22
|
-
# @
|
23
|
-
|
24
|
-
# @param message [String, nil]
|
25
|
-
def initialize(configuration:, integration_meta: nil, message: nil)
|
26
|
-
super
|
27
|
-
@type = TYPE
|
28
|
-
self.level = nil
|
29
|
-
end
|
11
|
+
# @return [Float, nil]
|
12
|
+
attr_reader :start_timestamp
|
30
13
|
|
31
14
|
# Sets the event's start_timestamp.
|
32
15
|
# @param time [Time, Float]
|
@@ -39,6 +22,7 @@ module Sentry
|
|
39
22
|
def to_hash
|
40
23
|
data = super
|
41
24
|
data[:spans] = @spans.map(&:to_hash) if @spans
|
25
|
+
data[:start_timestamp] = @start_timestamp
|
42
26
|
data
|
43
27
|
end
|
44
28
|
end
|
@@ -130,7 +130,7 @@ module Sentry
|
|
130
130
|
server = URI(@dsn.server)
|
131
131
|
|
132
132
|
connection =
|
133
|
-
if proxy = @transport_configuration.proxy
|
133
|
+
if proxy = normalize_proxy(@transport_configuration.proxy)
|
134
134
|
::Net::HTTP.new(server.hostname, server.port, proxy[:uri].hostname, proxy[:uri].port, proxy[:user], proxy[:password])
|
135
135
|
else
|
136
136
|
::Net::HTTP.new(server.hostname, server.port, nil)
|
@@ -148,6 +148,20 @@ module Sentry
|
|
148
148
|
connection
|
149
149
|
end
|
150
150
|
|
151
|
+
def normalize_proxy(proxy)
|
152
|
+
return proxy unless proxy
|
153
|
+
|
154
|
+
case proxy
|
155
|
+
when String
|
156
|
+
uri = URI(proxy)
|
157
|
+
{ uri: uri, user: uri.user, password: uri.password }
|
158
|
+
when URI
|
159
|
+
{ uri: proxy, user: proxy.user, password: proxy.password }
|
160
|
+
when Hash
|
161
|
+
proxy
|
162
|
+
end
|
163
|
+
end
|
164
|
+
|
151
165
|
def ssl_configuration
|
152
166
|
configuration = {
|
153
167
|
verify: @transport_configuration.ssl_verification,
|
data/lib/sentry/transport.rb
CHANGED
@@ -70,24 +70,10 @@ module Sentry
|
|
70
70
|
serialized_results = []
|
71
71
|
|
72
72
|
envelope.items.each do |item|
|
73
|
-
result = item.
|
73
|
+
result, oversized = item.serialize
|
74
74
|
|
75
|
-
if
|
76
|
-
|
77
|
-
item.payload.delete(:breadcrumbs)
|
78
|
-
elsif item.payload.key?("breadcrumbs")
|
79
|
-
item.payload.delete("breadcrumbs")
|
80
|
-
end
|
81
|
-
|
82
|
-
result = item.to_s
|
83
|
-
end
|
84
|
-
|
85
|
-
if result.bytesize > Event::MAX_SERIALIZED_PAYLOAD_SIZE
|
86
|
-
size_breakdown = item.payload.map do |key, value|
|
87
|
-
"#{key}: #{JSON.generate(value).bytesize}"
|
88
|
-
end.join(", ")
|
89
|
-
|
90
|
-
log_debug("Envelope item [#{item.type}] is still oversized without breadcrumbs: {#{size_breakdown}}")
|
75
|
+
if oversized
|
76
|
+
log_info("Envelope item [#{item.type}] is still oversized after size reduction: {#{item.size_breakdown}}")
|
91
77
|
|
92
78
|
next
|
93
79
|
end
|
@@ -148,7 +134,7 @@ module Sentry
|
|
148
134
|
# Convert to hash
|
149
135
|
event_payload = event.to_hash
|
150
136
|
event_id = event_payload[:event_id] || event_payload["event_id"]
|
151
|
-
item_type =
|
137
|
+
item_type = event_payload[:type] || event_payload["type"]
|
152
138
|
|
153
139
|
envelope = Envelope.new(
|
154
140
|
{
|
@@ -174,16 +160,11 @@ module Sentry
|
|
174
160
|
return unless @send_client_reports
|
175
161
|
return unless CLIENT_REPORT_REASONS.include?(reason)
|
176
162
|
|
177
|
-
item_type ||= 'event'
|
178
163
|
@discarded_events[[reason, item_type]] += 1
|
179
164
|
end
|
180
165
|
|
181
166
|
private
|
182
167
|
|
183
|
-
def get_item_type(event_hash)
|
184
|
-
event_hash[:type] || event_hash["type"] || "event"
|
185
|
-
end
|
186
|
-
|
187
168
|
def fetch_pending_client_report
|
188
169
|
return nil unless @send_client_reports
|
189
170
|
return nil if @last_client_report_sent > Time.now - CLIENT_REPORT_INTERVAL
|
data/lib/sentry/version.rb
CHANGED
data/lib/sentry-ruby.rb
CHANGED
@@ -12,6 +12,7 @@ require "sentry/utils/logging_helper"
|
|
12
12
|
require "sentry/configuration"
|
13
13
|
require "sentry/logger"
|
14
14
|
require "sentry/event"
|
15
|
+
require "sentry/error_event"
|
15
16
|
require "sentry/transaction_event"
|
16
17
|
require "sentry/span"
|
17
18
|
require "sentry/transaction"
|
@@ -59,11 +60,11 @@ module Sentry
|
|
59
60
|
end
|
60
61
|
|
61
62
|
# @!attribute [rw] background_worker
|
62
|
-
# @return [BackgroundWorker]
|
63
|
+
# @return [BackgroundWorker, nil]
|
63
64
|
attr_accessor :background_worker
|
64
65
|
|
65
66
|
# @!attribute [r] session_flusher
|
66
|
-
# @return [SessionFlusher]
|
67
|
+
# @return [SessionFlusher, nil]
|
67
68
|
attr_reader :session_flusher
|
68
69
|
|
69
70
|
##### Patch Registration #####
|
@@ -102,7 +103,7 @@ module Sentry
|
|
102
103
|
if initialized?
|
103
104
|
logger.warn(LOGGER_PROGNAME) do
|
104
105
|
<<~MSG
|
105
|
-
Integration '#{name}' is loaded after the SDK is initialized, which can cause
|
106
|
+
Integration '#{name}' is loaded after the SDK is initialized, which can cause unexpected behavior. Please make sure all integrations are loaded before SDK initialization.
|
106
107
|
MSG
|
107
108
|
end
|
108
109
|
end
|
@@ -212,10 +213,30 @@ module Sentry
|
|
212
213
|
exception_locals_tp.enable
|
213
214
|
end
|
214
215
|
|
215
|
-
at_exit
|
216
|
-
|
216
|
+
at_exit { close }
|
217
|
+
end
|
218
|
+
|
219
|
+
# Flushes pending events and cleans up SDK state.
|
220
|
+
# SDK will stop sending events and all top-level APIs will be no-ops after this.
|
221
|
+
#
|
222
|
+
# @return [void]
|
223
|
+
def close
|
224
|
+
if @background_worker
|
217
225
|
@background_worker.shutdown
|
226
|
+
@background_worker = nil
|
227
|
+
end
|
228
|
+
|
229
|
+
if @session_flusher
|
230
|
+
@session_flusher.kill
|
231
|
+
@session_flusher = nil
|
232
|
+
end
|
233
|
+
|
234
|
+
if configuration&.capture_exception_frame_locals
|
235
|
+
exception_locals_tp.disable
|
218
236
|
end
|
237
|
+
|
238
|
+
@main_hub = nil
|
239
|
+
Thread.current.thread_variable_set(THREAD_LOCAL, nil)
|
219
240
|
end
|
220
241
|
|
221
242
|
# Returns true if the SDK is initialized.
|
@@ -286,6 +307,7 @@ module Sentry
|
|
286
307
|
#
|
287
308
|
# @return [void]
|
288
309
|
def clone_hub_to_current_thread
|
310
|
+
return unless initialized?
|
289
311
|
Thread.current.thread_variable_set(THREAD_LOCAL, get_main_hub.clone)
|
290
312
|
end
|
291
313
|
|
@@ -359,6 +381,25 @@ module Sentry
|
|
359
381
|
get_current_hub.capture_exception(exception, **options, &block)
|
360
382
|
end
|
361
383
|
|
384
|
+
# Takes a block and evaluates it. If the block raised an exception, it reports the exception to Sentry and re-raises it.
|
385
|
+
# If the block ran without exception, it returns the evaluation result.
|
386
|
+
#
|
387
|
+
# @example
|
388
|
+
# Sentry.with_exception_captured do
|
389
|
+
# 1/1 #=> 1 will be returned
|
390
|
+
# end
|
391
|
+
#
|
392
|
+
# Sentry.with_exception_captured do
|
393
|
+
# 1/0 #=> ZeroDivisionError will be reported and re-raised
|
394
|
+
# end
|
395
|
+
#
|
396
|
+
def with_exception_captured(**options, &block)
|
397
|
+
yield
|
398
|
+
rescue Exception => e
|
399
|
+
capture_exception(e, **options)
|
400
|
+
raise
|
401
|
+
end
|
402
|
+
|
362
403
|
# Takes a message string and reports it to Sentry via the currently active hub.
|
363
404
|
#
|
364
405
|
# @yieldparam scope [Scope]
|
@@ -384,6 +425,38 @@ module Sentry
|
|
384
425
|
get_current_hub.start_transaction(**options)
|
385
426
|
end
|
386
427
|
|
428
|
+
# Records the block's execution as a child of the current span.
|
429
|
+
# If the current scope doesn't have a span, the block would still be executed but the yield param will be nil.
|
430
|
+
# @param attributes [Hash] attributes for the child span.
|
431
|
+
# @yieldparam child_span [Span, nil]
|
432
|
+
# @return yield result
|
433
|
+
#
|
434
|
+
# @example
|
435
|
+
# Sentry.with_child_span(op: "my operation") do |child_span|
|
436
|
+
# child_span.set_data(operation_data)
|
437
|
+
# child_span.set_description(operation_detail)
|
438
|
+
# # result will be returned
|
439
|
+
# end
|
440
|
+
#
|
441
|
+
def with_child_span(**attributes, &block)
|
442
|
+
if Sentry.initialized? && current_span = get_current_scope.get_span
|
443
|
+
result = nil
|
444
|
+
|
445
|
+
begin
|
446
|
+
current_span.with_child_span(**attributes) do |child_span|
|
447
|
+
get_current_scope.set_span(child_span)
|
448
|
+
result = yield(child_span)
|
449
|
+
end
|
450
|
+
ensure
|
451
|
+
get_current_scope.set_span(current_span)
|
452
|
+
end
|
453
|
+
|
454
|
+
result
|
455
|
+
else
|
456
|
+
yield(nil)
|
457
|
+
end
|
458
|
+
end
|
459
|
+
|
387
460
|
# Returns the id of the lastly reported Sentry::Event.
|
388
461
|
#
|
389
462
|
# @return [String, nil]
|
data/sentry-ruby-core.gemspec
CHANGED
@@ -18,9 +18,6 @@ Gem::Specification.new do |spec|
|
|
18
18
|
spec.metadata["source_code_uri"] = spec.homepage
|
19
19
|
spec.metadata["changelog_uri"] = "#{spec.homepage}/blob/master/CHANGELOG.md"
|
20
20
|
|
21
|
-
spec.
|
22
|
-
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
23
|
-
spec.require_paths = ["lib"]
|
24
|
-
|
21
|
+
spec.add_dependency "sentry-ruby", Sentry::VERSION
|
25
22
|
spec.add_dependency "concurrent-ruby"
|
26
23
|
end
|
data/sentry-ruby.gemspec
CHANGED
@@ -17,6 +17,7 @@ Gem::Specification.new do |spec|
|
|
17
17
|
spec.metadata["source_code_uri"] = spec.homepage
|
18
18
|
spec.metadata["changelog_uri"] = "#{spec.homepage}/blob/master/CHANGELOG.md"
|
19
19
|
|
20
|
-
spec.
|
20
|
+
spec.require_paths = ["lib"]
|
21
|
+
|
21
22
|
spec.add_dependency "concurrent-ruby", '~> 1.0', '>= 1.0.2'
|
22
23
|
end
|
metadata
CHANGED
@@ -1,15 +1,29 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: sentry-ruby-core
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 5.
|
4
|
+
version: 5.4.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Sentry Team
|
8
8
|
autorequire:
|
9
|
-
bindir:
|
9
|
+
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2022-
|
11
|
+
date: 2022-07-26 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: sentry-ruby
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - '='
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: 5.4.0
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - '='
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: 5.4.0
|
13
27
|
- !ruby/object:Gem::Dependency
|
14
28
|
name: concurrent-ruby
|
15
29
|
requirement: !ruby/object:Gem::Requirement
|
@@ -56,6 +70,7 @@ files:
|
|
56
70
|
- lib/sentry/core_ext/object/duplicable.rb
|
57
71
|
- lib/sentry/dsn.rb
|
58
72
|
- lib/sentry/envelope.rb
|
73
|
+
- lib/sentry/error_event.rb
|
59
74
|
- lib/sentry/event.rb
|
60
75
|
- lib/sentry/exceptions.rb
|
61
76
|
- lib/sentry/hub.rb
|
@@ -79,6 +94,7 @@ files:
|
|
79
94
|
- lib/sentry/session.rb
|
80
95
|
- lib/sentry/session_flusher.rb
|
81
96
|
- lib/sentry/span.rb
|
97
|
+
- lib/sentry/test_helper.rb
|
82
98
|
- lib/sentry/transaction.rb
|
83
99
|
- lib/sentry/transaction_event.rb
|
84
100
|
- lib/sentry/transport.rb
|