sentry-ruby-core 5.1.1 → 5.3.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 +1 -0
- data/README.md +1 -1
- data/lib/sentry/background_worker.rb +2 -1
- data/lib/sentry/client.rb +6 -5
- data/lib/sentry/configuration.rb +5 -0
- data/lib/sentry/envelope.rb +0 -4
- data/lib/sentry/error_event.rb +38 -0
- data/lib/sentry/event.rb +5 -29
- data/lib/sentry/hub.rb +28 -2
- data/lib/sentry/interfaces/exception.rb +1 -0
- data/lib/sentry/net/http.rb +3 -3
- data/lib/sentry/rack/capture_exceptions.rb +26 -24
- data/lib/sentry/redis.rb +6 -4
- data/lib/sentry/scope.rb +10 -1
- data/lib/sentry/session.rb +35 -0
- data/lib/sentry/session_flusher.rb +80 -0
- data/lib/sentry/transaction_event.rb +4 -19
- data/lib/sentry/transport/dummy_transport.rb +6 -1
- data/lib/sentry/transport/http_transport.rb +15 -1
- data/lib/sentry/transport.rb +45 -9
- data/lib/sentry/version.rb +1 -1
- data/lib/sentry-ruby.rb +75 -0
- metadata +5 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 03d233c6e1bd55ddc4044c7a23b6bc74047eec17a8c16d5f33f768065641259c
|
4
|
+
data.tar.gz: bec07f880bff192b6ef71aaefdf9dc9ff2ff072a539f94ed5c4fd0398ad1146a
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: c3d90a9b888db8a396e260dccf5df3a38d4f153c9bc2b31b92fc206009553fec2d3d0fe47bb1a871b001372fcba8013fee4e16b60405588db4ce3984f56d675e
|
7
|
+
data.tar.gz: 2ae1db7dee78487fec06b1cb1c6fb2dfa77852ae20b0c28b6c2f0e0b964f728fdea40a8261495439ef6e193182a0f7dc3196dc1bd75368abcf2d78852310361a
|
data/Gemfile
CHANGED
data/README.md
CHANGED
@@ -29,7 +29,7 @@ If you're using `sentry-raven`, we recommend you to migrate to this new SDK. You
|
|
29
29
|
|
30
30
|
## Requirements
|
31
31
|
|
32
|
-
We test on Ruby 2.4, 2.5, 2.6, 2.7, and 3.
|
32
|
+
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
33
|
|
34
34
|
If you use self-hosted Sentry, please also make sure its version is above `20.6.0`.
|
35
35
|
|
@@ -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)
|
@@ -174,8 +176,7 @@ module Sentry
|
|
174
176
|
async_block.call(event_hash)
|
175
177
|
end
|
176
178
|
rescue => e
|
177
|
-
|
178
|
-
log_error("Async #{loggable_event_type} sending failed", e, debug: configuration.debug)
|
179
|
+
log_error("Async #{event_hash["type"]} sending failed", e, debug: configuration.debug)
|
179
180
|
send_event(event, hint)
|
180
181
|
end
|
181
182
|
end
|
data/lib/sentry/configuration.rb
CHANGED
@@ -207,6 +207,10 @@ module Sentry
|
|
207
207
|
# @return [Boolean]
|
208
208
|
attr_accessor :send_client_reports
|
209
209
|
|
210
|
+
# Track sessions in request/response cycles automatically
|
211
|
+
# @return [Boolean]
|
212
|
+
attr_accessor :auto_session_tracking
|
213
|
+
|
210
214
|
# these are not config options
|
211
215
|
# @!visibility private
|
212
216
|
attr_reader :errors, :gem_specs
|
@@ -261,6 +265,7 @@ module Sentry
|
|
261
265
|
self.send_default_pii = false
|
262
266
|
self.skip_rake_integration = false
|
263
267
|
self.send_client_reports = true
|
268
|
+
self.auto_session_tracking = true
|
264
269
|
self.trusted_proxies = []
|
265
270
|
self.dsn = ENV['SENTRY_DSN']
|
266
271
|
self.server_name = server_name_from_env
|
data/lib/sentry/envelope.rb
CHANGED
@@ -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,6 +26,7 @@ module Sentry
|
|
23
26
|
WRITER_ATTRIBUTES = SERIALIZEABLE_ATTRIBUTES - %i(type timestamp level)
|
24
27
|
|
25
28
|
MAX_MESSAGE_SIZE_IN_BYTES = 1024 * 8
|
29
|
+
MAX_SERIALIZED_PAYLOAD_SIZE = 1024 * 200
|
26
30
|
|
27
31
|
SKIP_INSPECTION_ATTRIBUTES = [:@modules, :@stacktrace_builder, :@send_default_pii, :@trusted_proxies, :@rack_env_whitelist]
|
28
32
|
|
@@ -34,12 +38,6 @@ module Sentry
|
|
34
38
|
# @return [RequestInterface]
|
35
39
|
attr_reader :request
|
36
40
|
|
37
|
-
# @return [ExceptionInterface]
|
38
|
-
attr_reader :exception
|
39
|
-
|
40
|
-
# @return [ThreadsInterface]
|
41
|
-
attr_reader :threads
|
42
|
-
|
43
41
|
# @param configuration [Configuration]
|
44
42
|
# @param integration_meta [Hash, nil]
|
45
43
|
# @param message [String, nil]
|
@@ -48,6 +46,7 @@ module Sentry
|
|
48
46
|
@event_id = SecureRandom.uuid.delete("-")
|
49
47
|
@timestamp = Sentry.utc_now.iso8601
|
50
48
|
@platform = :ruby
|
49
|
+
@type = self.class::TYPE
|
51
50
|
@sdk = integration_meta || Sentry.sdk_meta
|
52
51
|
|
53
52
|
@user = {}
|
@@ -70,8 +69,6 @@ module Sentry
|
|
70
69
|
@rack_env_whitelist = configuration.rack_env_whitelist
|
71
70
|
|
72
71
|
@message = (message || "").byteslice(0..MAX_MESSAGE_SIZE_IN_BYTES)
|
73
|
-
|
74
|
-
self.level = :error
|
75
72
|
end
|
76
73
|
|
77
74
|
class << self
|
@@ -145,9 +142,6 @@ module Sentry
|
|
145
142
|
data = serialize_attributes
|
146
143
|
data[:breadcrumbs] = breadcrumbs.to_hash if breadcrumbs
|
147
144
|
data[:request] = request.to_hash if request
|
148
|
-
data[:exception] = exception.to_hash if exception
|
149
|
-
data[:threads] = threads.to_hash if threads
|
150
|
-
|
151
145
|
data
|
152
146
|
end
|
153
147
|
|
@@ -156,24 +150,6 @@ module Sentry
|
|
156
150
|
JSON.parse(JSON.generate(to_hash))
|
157
151
|
end
|
158
152
|
|
159
|
-
# @!visibility private
|
160
|
-
def add_threads_interface(backtrace: nil, **options)
|
161
|
-
@threads = ThreadsInterface.build(
|
162
|
-
backtrace: backtrace,
|
163
|
-
stacktrace_builder: @stacktrace_builder,
|
164
|
-
**options
|
165
|
-
)
|
166
|
-
end
|
167
|
-
|
168
|
-
# @!visibility private
|
169
|
-
def add_exception_interface(exception)
|
170
|
-
if exception.respond_to?(:sentry_context)
|
171
|
-
@extra.merge!(exception.sentry_context)
|
172
|
-
end
|
173
|
-
|
174
|
-
@exception = Sentry::ExceptionInterface.build(exception: exception, stacktrace_builder: @stacktrace_builder)
|
175
|
-
end
|
176
|
-
|
177
153
|
private
|
178
154
|
|
179
155
|
def add_request_interface(env)
|
data/lib/sentry/hub.rb
CHANGED
@@ -2,6 +2,7 @@
|
|
2
2
|
|
3
3
|
require "sentry/scope"
|
4
4
|
require "sentry/client"
|
5
|
+
require "sentry/session"
|
5
6
|
|
6
7
|
module Sentry
|
7
8
|
class Hub
|
@@ -104,6 +105,8 @@ module Sentry
|
|
104
105
|
|
105
106
|
return unless event
|
106
107
|
|
108
|
+
current_scope.session&.update_from_exception(event.exception)
|
109
|
+
|
107
110
|
capture_event(event, **options, &block).tap do
|
108
111
|
# mark the exception as captured so we can use this information to avoid duplicated capturing
|
109
112
|
exception.instance_variable_set(Sentry::CAPTURED_SIGNATURE, true)
|
@@ -143,12 +146,11 @@ module Sentry
|
|
143
146
|
|
144
147
|
event = current_client.capture_event(event, scope, hint)
|
145
148
|
|
146
|
-
|
147
149
|
if event && configuration.debug
|
148
150
|
configuration.log_debug(event.to_json_compatible)
|
149
151
|
end
|
150
152
|
|
151
|
-
@last_event_id = event&.event_id
|
153
|
+
@last_event_id = event&.event_id unless event.is_a?(Sentry::TransactionEvent)
|
152
154
|
event
|
153
155
|
end
|
154
156
|
|
@@ -175,6 +177,30 @@ module Sentry
|
|
175
177
|
configuration.background_worker_threads = original_background_worker_threads
|
176
178
|
end
|
177
179
|
|
180
|
+
def start_session
|
181
|
+
return unless current_scope
|
182
|
+
current_scope.set_session(Session.new)
|
183
|
+
end
|
184
|
+
|
185
|
+
def end_session
|
186
|
+
return unless current_scope
|
187
|
+
session = current_scope.session
|
188
|
+
current_scope.set_session(nil)
|
189
|
+
|
190
|
+
return unless session
|
191
|
+
session.close
|
192
|
+
Sentry.session_flusher.add_session(session)
|
193
|
+
end
|
194
|
+
|
195
|
+
def with_session_tracking(&block)
|
196
|
+
return yield unless configuration.auto_session_tracking
|
197
|
+
|
198
|
+
start_session
|
199
|
+
yield
|
200
|
+
ensure
|
201
|
+
end_session
|
202
|
+
end
|
203
|
+
|
178
204
|
private
|
179
205
|
|
180
206
|
def current_layer
|
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)
|
@@ -14,30 +14,32 @@ module Sentry
|
|
14
14
|
Sentry.clone_hub_to_current_thread
|
15
15
|
|
16
16
|
Sentry.with_scope do |scope|
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
17
|
+
Sentry.with_session_tracking do
|
18
|
+
scope.clear_breadcrumbs
|
19
|
+
scope.set_transaction_name(env["PATH_INFO"]) if env["PATH_INFO"]
|
20
|
+
scope.set_rack_env(env)
|
21
|
+
|
22
|
+
transaction = start_transaction(env, scope)
|
23
|
+
scope.set_span(transaction) if transaction
|
24
|
+
|
25
|
+
begin
|
26
|
+
response = @app.call(env)
|
27
|
+
rescue Sentry::Error
|
28
|
+
finish_transaction(transaction, 500)
|
29
|
+
raise # Don't capture Sentry errors
|
30
|
+
rescue Exception => e
|
31
|
+
capture_exception(e)
|
32
|
+
finish_transaction(transaction, 500)
|
33
|
+
raise
|
34
|
+
end
|
35
|
+
|
36
|
+
exception = collect_exception(env)
|
37
|
+
capture_exception(exception) if exception
|
38
|
+
|
39
|
+
finish_transaction(transaction, response[0])
|
40
|
+
|
41
|
+
response
|
33
42
|
end
|
34
|
-
|
35
|
-
exception = collect_exception(env)
|
36
|
-
capture_exception(exception) if exception
|
37
|
-
|
38
|
-
finish_transaction(transaction, response[0])
|
39
|
-
|
40
|
-
response
|
41
43
|
end
|
42
44
|
end
|
43
45
|
|
@@ -59,7 +61,7 @@ module Sentry
|
|
59
61
|
sentry_trace = env["HTTP_SENTRY_TRACE"]
|
60
62
|
options = { name: scope.transaction_name, op: transaction_op }
|
61
63
|
transaction = Sentry::Transaction.from_sentry_trace(sentry_trace, **options) if sentry_trace
|
62
|
-
Sentry.start_transaction(transaction: transaction, **options)
|
64
|
+
Sentry.start_transaction(transaction: transaction, custom_sampling_context: { env: env }, **options)
|
63
65
|
end
|
64
66
|
|
65
67
|
|
data/lib/sentry/redis.rb
CHANGED
@@ -3,8 +3,8 @@
|
|
3
3
|
module Sentry
|
4
4
|
# @api private
|
5
5
|
class Redis
|
6
|
-
OP_NAME
|
7
|
-
LOGGER_NAME
|
6
|
+
OP_NAME = "db.redis.command"
|
7
|
+
LOGGER_NAME = :redis_logger
|
8
8
|
|
9
9
|
def initialize(commands, host, port, db)
|
10
10
|
@commands, @host, @port, @db = commands, host, port, db
|
@@ -60,9 +60,11 @@ module Sentry
|
|
60
60
|
|
61
61
|
def parsed_commands
|
62
62
|
commands.map do |statement|
|
63
|
-
command, key, *
|
63
|
+
command, key, *arguments = statement
|
64
64
|
|
65
|
-
{ command: command.to_s.upcase, key: key }
|
65
|
+
{ command: command.to_s.upcase, key: key }.tap do |command_set|
|
66
|
+
command_set[:arguments] = arguments.join(" ") if Sentry.configuration.send_default_pii
|
67
|
+
end
|
66
68
|
end
|
67
69
|
end
|
68
70
|
|
data/lib/sentry/scope.rb
CHANGED
@@ -7,7 +7,7 @@ module Sentry
|
|
7
7
|
class Scope
|
8
8
|
include ArgumentCheckingHelper
|
9
9
|
|
10
|
-
ATTRIBUTES = [:transaction_names, :contexts, :extra, :tags, :user, :level, :breadcrumbs, :fingerprint, :event_processors, :rack_env, :span]
|
10
|
+
ATTRIBUTES = [:transaction_names, :contexts, :extra, :tags, :user, :level, :breadcrumbs, :fingerprint, :event_processors, :rack_env, :span, :session]
|
11
11
|
|
12
12
|
attr_reader(*ATTRIBUTES)
|
13
13
|
|
@@ -76,6 +76,7 @@ module Sentry
|
|
76
76
|
copy.transaction_names = transaction_names.deep_dup
|
77
77
|
copy.fingerprint = fingerprint.deep_dup
|
78
78
|
copy.span = span.deep_dup
|
79
|
+
copy.session = session.deep_dup
|
79
80
|
copy
|
80
81
|
end
|
81
82
|
|
@@ -198,6 +199,13 @@ module Sentry
|
|
198
199
|
@transaction_names << transaction_name
|
199
200
|
end
|
200
201
|
|
202
|
+
# Sets the currently active session on the scope.
|
203
|
+
# @param session [Session, nil]
|
204
|
+
# @return [void]
|
205
|
+
def set_session(session)
|
206
|
+
@session = session
|
207
|
+
end
|
208
|
+
|
201
209
|
# Returns current transaction name.
|
202
210
|
# The "transaction" here does not refer to `Transaction` objects.
|
203
211
|
# @return [String, nil]
|
@@ -251,6 +259,7 @@ module Sentry
|
|
251
259
|
@event_processors = []
|
252
260
|
@rack_env = {}
|
253
261
|
@span = nil
|
262
|
+
@session = nil
|
254
263
|
set_new_breadcrumb_buffer
|
255
264
|
end
|
256
265
|
|
@@ -0,0 +1,35 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Sentry
|
4
|
+
class Session
|
5
|
+
attr_reader :started, :status
|
6
|
+
|
7
|
+
# TODO-neel add :crashed after adding handled mechanism
|
8
|
+
STATUSES = %i(ok errored exited)
|
9
|
+
AGGREGATE_STATUSES = %i(errored exited)
|
10
|
+
|
11
|
+
def initialize
|
12
|
+
@started = Sentry.utc_now
|
13
|
+
@status = :ok
|
14
|
+
end
|
15
|
+
|
16
|
+
# TODO-neel add :crashed after adding handled mechanism
|
17
|
+
def update_from_exception(_exception = nil)
|
18
|
+
@status = :errored
|
19
|
+
end
|
20
|
+
|
21
|
+
def close
|
22
|
+
@status = :exited if @status == :ok
|
23
|
+
end
|
24
|
+
|
25
|
+
# truncate seconds from the timestamp since we only care about
|
26
|
+
# minute level granularity for aggregation
|
27
|
+
def aggregation_key
|
28
|
+
Time.utc(started.year, started.month, started.day, started.hour, started.min)
|
29
|
+
end
|
30
|
+
|
31
|
+
def deep_dup
|
32
|
+
dup
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,80 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Sentry
|
4
|
+
class SessionFlusher
|
5
|
+
include LoggingHelper
|
6
|
+
|
7
|
+
FLUSH_INTERVAL = 60
|
8
|
+
|
9
|
+
def initialize(configuration, client)
|
10
|
+
@thread = nil
|
11
|
+
@client = client
|
12
|
+
@pending_aggregates = {}
|
13
|
+
@release = configuration.release
|
14
|
+
@environment = configuration.environment
|
15
|
+
@logger = configuration.logger
|
16
|
+
|
17
|
+
log_debug("[Sessions] Sessions won't be captured without a valid release") unless @release
|
18
|
+
end
|
19
|
+
|
20
|
+
def flush
|
21
|
+
return if @pending_aggregates.empty?
|
22
|
+
envelope = pending_envelope
|
23
|
+
|
24
|
+
Sentry.background_worker.perform do
|
25
|
+
@client.transport.send_envelope(envelope)
|
26
|
+
end
|
27
|
+
|
28
|
+
@pending_aggregates = {}
|
29
|
+
end
|
30
|
+
|
31
|
+
def add_session(session)
|
32
|
+
return unless @release
|
33
|
+
|
34
|
+
ensure_thread
|
35
|
+
|
36
|
+
return unless Session::AGGREGATE_STATUSES.include?(session.status)
|
37
|
+
@pending_aggregates[session.aggregation_key] ||= init_aggregates(session.aggregation_key)
|
38
|
+
@pending_aggregates[session.aggregation_key][session.status] += 1
|
39
|
+
end
|
40
|
+
|
41
|
+
def kill
|
42
|
+
log_debug("Killing session flusher")
|
43
|
+
@thread&.kill
|
44
|
+
end
|
45
|
+
|
46
|
+
private
|
47
|
+
|
48
|
+
def init_aggregates(aggregation_key)
|
49
|
+
aggregates = { started: aggregation_key.iso8601 }
|
50
|
+
Session::AGGREGATE_STATUSES.each { |k| aggregates[k] = 0 }
|
51
|
+
aggregates
|
52
|
+
end
|
53
|
+
|
54
|
+
def pending_envelope
|
55
|
+
envelope = Envelope.new
|
56
|
+
|
57
|
+
header = { type: 'sessions' }
|
58
|
+
payload = { attrs: attrs, aggregates: @pending_aggregates.values }
|
59
|
+
|
60
|
+
envelope.add_item(header, payload)
|
61
|
+
envelope
|
62
|
+
end
|
63
|
+
|
64
|
+
def attrs
|
65
|
+
{ release: @release, environment: @environment }
|
66
|
+
end
|
67
|
+
|
68
|
+
def ensure_thread
|
69
|
+
return if @thread&.alive?
|
70
|
+
|
71
|
+
@thread = Thread.new do
|
72
|
+
loop do
|
73
|
+
sleep(FLUSH_INTERVAL)
|
74
|
+
flush
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
end
|
80
|
+
end
|
@@ -1,31 +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
|
-
end
|
11
|
+
# @return [Float, nil]
|
12
|
+
attr_reader :start_timestamp
|
29
13
|
|
30
14
|
# Sets the event's start_timestamp.
|
31
15
|
# @param time [Time, Float]
|
@@ -38,6 +22,7 @@ module Sentry
|
|
38
22
|
def to_hash
|
39
23
|
data = super
|
40
24
|
data[:spans] = @spans.map(&:to_hash) if @spans
|
25
|
+
data[:start_timestamp] = @start_timestamp
|
41
26
|
data
|
42
27
|
end
|
43
28
|
end
|
@@ -2,15 +2,20 @@
|
|
2
2
|
|
3
3
|
module Sentry
|
4
4
|
class DummyTransport < Transport
|
5
|
-
attr_accessor :events
|
5
|
+
attr_accessor :events, :envelopes
|
6
6
|
|
7
7
|
def initialize(*)
|
8
8
|
super
|
9
9
|
@events = []
|
10
|
+
@envelopes = []
|
10
11
|
end
|
11
12
|
|
12
13
|
def send_event(event)
|
13
14
|
@events << event
|
14
15
|
end
|
16
|
+
|
17
|
+
def send_envelope(envelope)
|
18
|
+
@envelopes << envelope
|
19
|
+
end
|
15
20
|
end
|
16
21
|
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
@@ -57,8 +57,48 @@ module Sentry
|
|
57
57
|
|
58
58
|
return if envelope.items.empty?
|
59
59
|
|
60
|
-
|
61
|
-
|
60
|
+
data, serialized_items = serialize_envelope(envelope)
|
61
|
+
|
62
|
+
if data
|
63
|
+
log_info("[Transport] Sending envelope with items [#{serialized_items.map(&:type).join(', ')}] #{envelope.event_id} to Sentry")
|
64
|
+
send_data(data)
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
def serialize_envelope(envelope)
|
69
|
+
serialized_items = []
|
70
|
+
serialized_results = []
|
71
|
+
|
72
|
+
envelope.items.each do |item|
|
73
|
+
result = item.to_s
|
74
|
+
|
75
|
+
if result.bytesize > Event::MAX_SERIALIZED_PAYLOAD_SIZE
|
76
|
+
if item.payload.key?(:breadcrumbs)
|
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}}")
|
91
|
+
|
92
|
+
next
|
93
|
+
end
|
94
|
+
|
95
|
+
serialized_results << result
|
96
|
+
serialized_items << item
|
97
|
+
end
|
98
|
+
|
99
|
+
data = [JSON.generate(envelope.headers), *serialized_results].join("\n") unless serialized_results.empty?
|
100
|
+
|
101
|
+
[data, serialized_items]
|
62
102
|
end
|
63
103
|
|
64
104
|
def is_rate_limited?(item_type)
|
@@ -67,6 +107,8 @@ module Sentry
|
|
67
107
|
case item_type
|
68
108
|
when "transaction"
|
69
109
|
@rate_limits["transaction"]
|
110
|
+
when "sessions"
|
111
|
+
@rate_limits["session"]
|
70
112
|
else
|
71
113
|
@rate_limits["error"]
|
72
114
|
end
|
@@ -106,7 +148,7 @@ module Sentry
|
|
106
148
|
# Convert to hash
|
107
149
|
event_payload = event.to_hash
|
108
150
|
event_id = event_payload[:event_id] || event_payload["event_id"]
|
109
|
-
item_type =
|
151
|
+
item_type = event_payload[:type] || event_payload["type"]
|
110
152
|
|
111
153
|
envelope = Envelope.new(
|
112
154
|
{
|
@@ -125,7 +167,6 @@ module Sentry
|
|
125
167
|
client_report_headers, client_report_payload = fetch_pending_client_report
|
126
168
|
envelope.add_item(client_report_headers, client_report_payload) if client_report_headers
|
127
169
|
|
128
|
-
|
129
170
|
envelope
|
130
171
|
end
|
131
172
|
|
@@ -133,16 +174,11 @@ module Sentry
|
|
133
174
|
return unless @send_client_reports
|
134
175
|
return unless CLIENT_REPORT_REASONS.include?(reason)
|
135
176
|
|
136
|
-
item_type ||= 'event'
|
137
177
|
@discarded_events[[reason, item_type]] += 1
|
138
178
|
end
|
139
179
|
|
140
180
|
private
|
141
181
|
|
142
|
-
def get_item_type(event_hash)
|
143
|
-
event_hash[:type] || event_hash["type"] || "event"
|
144
|
-
end
|
145
|
-
|
146
182
|
def fetch_pending_client_report
|
147
183
|
return nil unless @send_client_reports
|
148
184
|
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,11 +12,13 @@ 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"
|
18
19
|
require "sentry/hub"
|
19
20
|
require "sentry/background_worker"
|
21
|
+
require "sentry/session_flusher"
|
20
22
|
|
21
23
|
[
|
22
24
|
"sentry/rake",
|
@@ -61,6 +63,10 @@ module Sentry
|
|
61
63
|
# @return [BackgroundWorker]
|
62
64
|
attr_accessor :background_worker
|
63
65
|
|
66
|
+
# @!attribute [r] session_flusher
|
67
|
+
# @return [SessionFlusher]
|
68
|
+
attr_reader :session_flusher
|
69
|
+
|
64
70
|
##### Patch Registration #####
|
65
71
|
|
66
72
|
# @!visibility private
|
@@ -94,6 +100,14 @@ module Sentry
|
|
94
100
|
# @param name [String] name of the integration
|
95
101
|
# @param version [String] version of the integration
|
96
102
|
def register_integration(name, version)
|
103
|
+
if initialized?
|
104
|
+
logger.warn(LOGGER_PROGNAME) do
|
105
|
+
<<~MSG
|
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.
|
107
|
+
MSG
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
97
111
|
meta = { name: "sentry.ruby.#{name}", version: version }.freeze
|
98
112
|
integrations[name.to_s] = meta
|
99
113
|
end
|
@@ -189,11 +203,18 @@ module Sentry
|
|
189
203
|
@main_hub = hub
|
190
204
|
@background_worker = Sentry::BackgroundWorker.new(config)
|
191
205
|
|
206
|
+
@session_flusher = if config.auto_session_tracking
|
207
|
+
Sentry::SessionFlusher.new(config, client)
|
208
|
+
else
|
209
|
+
nil
|
210
|
+
end
|
211
|
+
|
192
212
|
if config.capture_exception_frame_locals
|
193
213
|
exception_locals_tp.enable
|
194
214
|
end
|
195
215
|
|
196
216
|
at_exit do
|
217
|
+
@session_flusher&.kill
|
197
218
|
@background_worker.shutdown
|
198
219
|
end
|
199
220
|
end
|
@@ -310,6 +331,26 @@ module Sentry
|
|
310
331
|
get_current_hub.with_scope(&block)
|
311
332
|
end
|
312
333
|
|
334
|
+
# Wrap a given block with session tracking.
|
335
|
+
# Aggregate sessions in minutely buckets will be recorded
|
336
|
+
# around this block and flushed every minute.
|
337
|
+
#
|
338
|
+
# @example
|
339
|
+
# Sentry.with_session_tracking do
|
340
|
+
# a = 1 + 1 # new session recorded with :exited status
|
341
|
+
# end
|
342
|
+
#
|
343
|
+
# Sentry.with_session_tracking do
|
344
|
+
# 1 / 0
|
345
|
+
# rescue => e
|
346
|
+
# Sentry.capture_exception(e) # new session recorded with :errored status
|
347
|
+
# end
|
348
|
+
# @return [void]
|
349
|
+
def with_session_tracking(&block)
|
350
|
+
return yield unless initialized?
|
351
|
+
get_current_hub.with_session_tracking(&block)
|
352
|
+
end
|
353
|
+
|
313
354
|
# Takes an exception and reports it to Sentry via the currently active hub.
|
314
355
|
#
|
315
356
|
# @yieldparam scope [Scope]
|
@@ -344,6 +385,40 @@ module Sentry
|
|
344
385
|
get_current_hub.start_transaction(**options)
|
345
386
|
end
|
346
387
|
|
388
|
+
# Records the block's execution as a child of the current span.
|
389
|
+
# If the current scope doesn't have a span, the block would still be executed but the yield param will be nil.
|
390
|
+
# @param attributes [Hash] attributes for the child span.
|
391
|
+
# @yieldparam child_span [Span, nil]
|
392
|
+
# @return yield result
|
393
|
+
#
|
394
|
+
# @example
|
395
|
+
# Sentry.with_child_span(op: "my operation") do |child_span|
|
396
|
+
# child_span.set_data(operation_data)
|
397
|
+
# child_span.set_description(operation_detail)
|
398
|
+
# # result will be returned
|
399
|
+
# end
|
400
|
+
#
|
401
|
+
def with_child_span(**attributes, &block)
|
402
|
+
current_span = get_current_scope.get_span
|
403
|
+
|
404
|
+
if current_span
|
405
|
+
result = nil
|
406
|
+
|
407
|
+
begin
|
408
|
+
current_span.with_child_span(**attributes) do |child_span|
|
409
|
+
get_current_scope.set_span(child_span)
|
410
|
+
result = yield(child_span)
|
411
|
+
end
|
412
|
+
ensure
|
413
|
+
get_current_scope.set_span(current_span)
|
414
|
+
end
|
415
|
+
|
416
|
+
result
|
417
|
+
else
|
418
|
+
yield(nil)
|
419
|
+
end
|
420
|
+
end
|
421
|
+
|
347
422
|
# Returns the id of the lastly reported Sentry::Event.
|
348
423
|
#
|
349
424
|
# @return [String, nil]
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
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.3.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Sentry Team
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2022-
|
11
|
+
date: 2022-04-27 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: concurrent-ruby
|
@@ -56,6 +56,7 @@ files:
|
|
56
56
|
- lib/sentry/core_ext/object/duplicable.rb
|
57
57
|
- lib/sentry/dsn.rb
|
58
58
|
- lib/sentry/envelope.rb
|
59
|
+
- lib/sentry/error_event.rb
|
59
60
|
- lib/sentry/event.rb
|
60
61
|
- lib/sentry/exceptions.rb
|
61
62
|
- lib/sentry/hub.rb
|
@@ -76,6 +77,8 @@ files:
|
|
76
77
|
- lib/sentry/redis.rb
|
77
78
|
- lib/sentry/release_detector.rb
|
78
79
|
- lib/sentry/scope.rb
|
80
|
+
- lib/sentry/session.rb
|
81
|
+
- lib/sentry/session_flusher.rb
|
79
82
|
- lib/sentry/span.rb
|
80
83
|
- lib/sentry/transaction.rb
|
81
84
|
- lib/sentry/transaction_event.rb
|