sentry-ruby-core 5.1.0 → 5.2.1
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/lib/sentry/configuration.rb +5 -0
- data/lib/sentry/envelope.rb +29 -10
- data/lib/sentry/event.rb +1 -0
- data/lib/sentry/hub.rb +30 -2
- data/lib/sentry/interfaces/exception.rb +1 -0
- data/lib/sentry/rack/capture_exceptions.rb +26 -24
- data/lib/sentry/redis.rb +6 -4
- data/lib/sentry/scope.rb +11 -2
- data/lib/sentry/session.rb +35 -0
- data/lib/sentry/session_flusher.rb +79 -0
- data/lib/sentry/transaction_event.rb +1 -0
- data/lib/sentry/transport/dummy_transport.rb +6 -1
- data/lib/sentry/transport.rb +63 -14
- data/lib/sentry/version.rb +1 -1
- data/lib/sentry-ruby.rb +49 -0
- metadata +4 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 896f183ea604d1e6857591eff87175f8bb8a83dd1e40fc3e80982a71dd3abe0e
|
4
|
+
data.tar.gz: ffc931b4d436be3c5b1858260a5e2a0508d75455c23d0971bd0f1d2a02f0e178
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 467c2493df9a8ba98148b1d467d313bfc23defd9b34a9355242b01ed7ac4f092932b19607ad79e16883f83ba9b1fa53b736872f920f4fb0590761fcdbfc12a25
|
7
|
+
data.tar.gz: 074c4ac8eff86f456646ceef087433abf38a81ad1e8a3d0cec465caddf5a4e96459ed9c641854f7dba37ac9e851fde8f7e09798f35420f4d12586b89039282d4
|
data/Gemfile
CHANGED
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
@@ -3,24 +3,43 @@
|
|
3
3
|
module Sentry
|
4
4
|
# @api private
|
5
5
|
class Envelope
|
6
|
-
|
6
|
+
class Item
|
7
|
+
attr_accessor :headers, :payload
|
8
|
+
|
9
|
+
def initialize(headers, payload)
|
10
|
+
@headers = headers
|
11
|
+
@payload = payload
|
12
|
+
end
|
13
|
+
|
14
|
+
def type
|
15
|
+
@headers[:type] || 'event'
|
16
|
+
end
|
17
|
+
|
18
|
+
def to_s
|
19
|
+
<<~ITEM
|
20
|
+
#{JSON.generate(@headers)}
|
21
|
+
#{JSON.generate(@payload)}
|
22
|
+
ITEM
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
attr_accessor :headers, :items
|
27
|
+
|
28
|
+
def initialize(headers = {})
|
7
29
|
@headers = headers
|
8
30
|
@items = []
|
9
31
|
end
|
10
32
|
|
11
33
|
def add_item(headers, payload)
|
12
|
-
@items <<
|
34
|
+
@items << Item.new(headers, payload)
|
13
35
|
end
|
14
36
|
|
15
|
-
def
|
16
|
-
|
17
|
-
|
18
|
-
#{JSON.generate(item_headers)}
|
19
|
-
#{JSON.generate(item_payload)}
|
20
|
-
ENVELOPE
|
21
|
-
end.join("\n")
|
37
|
+
def item_types
|
38
|
+
@items.map(&:type)
|
39
|
+
end
|
22
40
|
|
23
|
-
|
41
|
+
def event_id
|
42
|
+
@headers[:event_id]
|
24
43
|
end
|
25
44
|
end
|
26
45
|
end
|
data/lib/sentry/event.rb
CHANGED
@@ -23,6 +23,7 @@ module Sentry
|
|
23
23
|
WRITER_ATTRIBUTES = SERIALIZEABLE_ATTRIBUTES - %i(type timestamp level)
|
24
24
|
|
25
25
|
MAX_MESSAGE_SIZE_IN_BYTES = 1024 * 8
|
26
|
+
MAX_SERIALIZED_PAYLOAD_SIZE = 1024 * 200
|
26
27
|
|
27
28
|
SKIP_INSPECTION_ATTRIBUTES = [:@modules, :@stacktrace_builder, :@send_default_pii, :@trusted_proxies, :@rack_env_whitelist]
|
28
29
|
|
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
|
@@ -94,6 +95,8 @@ module Sentry
|
|
94
95
|
def capture_exception(exception, **options, &block)
|
95
96
|
check_argument_type!(exception, ::Exception)
|
96
97
|
|
98
|
+
return if Sentry.exception_captured?(exception)
|
99
|
+
|
97
100
|
return unless current_client
|
98
101
|
|
99
102
|
options[:hint] ||= {}
|
@@ -102,9 +105,11 @@ module Sentry
|
|
102
105
|
|
103
106
|
return unless event
|
104
107
|
|
108
|
+
current_scope.session&.update_from_exception(event.exception)
|
109
|
+
|
105
110
|
capture_event(event, **options, &block).tap do
|
106
111
|
# mark the exception as captured so we can use this information to avoid duplicated capturing
|
107
|
-
exception.instance_variable_set(
|
112
|
+
exception.instance_variable_set(Sentry::CAPTURED_SIGNATURE, true)
|
108
113
|
end
|
109
114
|
end
|
110
115
|
|
@@ -141,7 +146,6 @@ module Sentry
|
|
141
146
|
|
142
147
|
event = current_client.capture_event(event, scope, hint)
|
143
148
|
|
144
|
-
|
145
149
|
if event && configuration.debug
|
146
150
|
configuration.log_debug(event.to_json_compatible)
|
147
151
|
end
|
@@ -173,6 +177,30 @@ module Sentry
|
|
173
177
|
configuration.background_worker_threads = original_background_worker_threads
|
174
178
|
end
|
175
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
|
+
|
176
204
|
private
|
177
205
|
|
178
206
|
def current_layer
|
@@ -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
|
|
@@ -173,7 +174,7 @@ module Sentry
|
|
173
174
|
def set_contexts(contexts_hash)
|
174
175
|
check_argument_type!(contexts_hash, Hash)
|
175
176
|
@contexts.merge!(contexts_hash) do |key, old, new|
|
176
|
-
|
177
|
+
old.merge(new)
|
177
178
|
end
|
178
179
|
end
|
179
180
|
|
@@ -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,79 @@
|
|
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
|
+
@thread&.kill
|
43
|
+
end
|
44
|
+
|
45
|
+
private
|
46
|
+
|
47
|
+
def init_aggregates(aggregation_key)
|
48
|
+
aggregates = { started: aggregation_key.iso8601 }
|
49
|
+
Session::AGGREGATE_STATUSES.each { |k| aggregates[k] = 0 }
|
50
|
+
aggregates
|
51
|
+
end
|
52
|
+
|
53
|
+
def pending_envelope
|
54
|
+
envelope = Envelope.new
|
55
|
+
|
56
|
+
header = { type: 'sessions' }
|
57
|
+
payload = { attrs: attrs, aggregates: @pending_aggregates.values }
|
58
|
+
|
59
|
+
envelope.add_item(header, payload)
|
60
|
+
envelope
|
61
|
+
end
|
62
|
+
|
63
|
+
def attrs
|
64
|
+
{ release: @release, environment: @environment }
|
65
|
+
end
|
66
|
+
|
67
|
+
def ensure_thread
|
68
|
+
return if @thread&.alive?
|
69
|
+
|
70
|
+
@thread = Thread.new do
|
71
|
+
loop do
|
72
|
+
sleep(FLUSH_INTERVAL)
|
73
|
+
flush
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
end
|
79
|
+
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
|
data/lib/sentry/transport.rb
CHANGED
@@ -46,23 +46,59 @@ module Sentry
|
|
46
46
|
end
|
47
47
|
|
48
48
|
def send_event(event)
|
49
|
-
|
50
|
-
|
49
|
+
envelope = envelope_from_event(event)
|
50
|
+
send_envelope(envelope)
|
51
51
|
|
52
|
-
|
53
|
-
|
54
|
-
|
52
|
+
event
|
53
|
+
end
|
54
|
+
|
55
|
+
def send_envelope(envelope)
|
56
|
+
reject_rate_limited_items(envelope)
|
55
57
|
|
56
|
-
|
58
|
+
return if envelope.items.empty?
|
59
|
+
|
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)
|
57
65
|
end
|
66
|
+
end
|
58
67
|
|
59
|
-
|
68
|
+
def serialize_envelope(envelope)
|
69
|
+
serialized_items = []
|
70
|
+
serialized_results = []
|
60
71
|
|
61
|
-
|
72
|
+
envelope.items.each do |item|
|
73
|
+
result = item.to_s
|
62
74
|
|
63
|
-
|
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
|
64
81
|
|
65
|
-
|
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]
|
66
102
|
end
|
67
103
|
|
68
104
|
def is_rate_limited?(item_type)
|
@@ -71,6 +107,8 @@ module Sentry
|
|
71
107
|
case item_type
|
72
108
|
when "transaction"
|
73
109
|
@rate_limits["transaction"]
|
110
|
+
when "sessions"
|
111
|
+
@rate_limits["session"]
|
74
112
|
else
|
75
113
|
@rate_limits["error"]
|
76
114
|
end
|
@@ -106,7 +144,7 @@ module Sentry
|
|
106
144
|
'Sentry ' + fields.map { |key, value| "#{key}=#{value}" }.join(', ')
|
107
145
|
end
|
108
146
|
|
109
|
-
def
|
147
|
+
def envelope_from_event(event)
|
110
148
|
# Convert to hash
|
111
149
|
event_payload = event.to_hash
|
112
150
|
event_id = event_payload[:event_id] || event_payload["event_id"]
|
@@ -129,9 +167,7 @@ module Sentry
|
|
129
167
|
client_report_headers, client_report_payload = fetch_pending_client_report
|
130
168
|
envelope.add_item(client_report_headers, client_report_payload) if client_report_headers
|
131
169
|
|
132
|
-
|
133
|
-
|
134
|
-
envelope.to_s
|
170
|
+
envelope
|
135
171
|
end
|
136
172
|
|
137
173
|
def record_lost_event(reason, item_type)
|
@@ -173,6 +209,19 @@ module Sentry
|
|
173
209
|
|
174
210
|
[item_header, item_payload]
|
175
211
|
end
|
212
|
+
|
213
|
+
def reject_rate_limited_items(envelope)
|
214
|
+
envelope.items.reject! do |item|
|
215
|
+
if is_rate_limited?(item.type)
|
216
|
+
log_info("[Transport] Envelope item [#{item.type}] not sent: rate limiting")
|
217
|
+
record_lost_event(:ratelimit_backoff, item.type)
|
218
|
+
|
219
|
+
true
|
220
|
+
else
|
221
|
+
false
|
222
|
+
end
|
223
|
+
end
|
224
|
+
end
|
176
225
|
end
|
177
226
|
end
|
178
227
|
|
data/lib/sentry/version.rb
CHANGED
data/lib/sentry-ruby.rb
CHANGED
@@ -17,6 +17,7 @@ require "sentry/span"
|
|
17
17
|
require "sentry/transaction"
|
18
18
|
require "sentry/hub"
|
19
19
|
require "sentry/background_worker"
|
20
|
+
require "sentry/session_flusher"
|
20
21
|
|
21
22
|
[
|
22
23
|
"sentry/rake",
|
@@ -31,6 +32,8 @@ end
|
|
31
32
|
module Sentry
|
32
33
|
META = { "name" => "sentry.ruby", "version" => Sentry::VERSION }.freeze
|
33
34
|
|
35
|
+
CAPTURED_SIGNATURE = :@__sentry_captured
|
36
|
+
|
34
37
|
LOGGER_PROGNAME = "sentry".freeze
|
35
38
|
|
36
39
|
SENTRY_TRACE_HEADER_NAME = "sentry-trace".freeze
|
@@ -59,6 +62,10 @@ module Sentry
|
|
59
62
|
# @return [BackgroundWorker]
|
60
63
|
attr_accessor :background_worker
|
61
64
|
|
65
|
+
# @!attribute [r] session_flusher
|
66
|
+
# @return [SessionFlusher]
|
67
|
+
attr_reader :session_flusher
|
68
|
+
|
62
69
|
##### Patch Registration #####
|
63
70
|
|
64
71
|
# @!visibility private
|
@@ -92,6 +99,14 @@ module Sentry
|
|
92
99
|
# @param name [String] name of the integration
|
93
100
|
# @param version [String] version of the integration
|
94
101
|
def register_integration(name, version)
|
102
|
+
if initialized?
|
103
|
+
logger.warn(LOGGER_PROGNAME) do
|
104
|
+
<<~MSG
|
105
|
+
Integration '#{name}' is loaded after the SDK is initialized, which can cause expected behavior. Please make sure all integrations are loaded before SDK initialization.
|
106
|
+
MSG
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
95
110
|
meta = { name: "sentry.ruby.#{name}", version: version }.freeze
|
96
111
|
integrations[name.to_s] = meta
|
97
112
|
end
|
@@ -187,11 +202,18 @@ module Sentry
|
|
187
202
|
@main_hub = hub
|
188
203
|
@background_worker = Sentry::BackgroundWorker.new(config)
|
189
204
|
|
205
|
+
@session_flusher = if config.auto_session_tracking
|
206
|
+
Sentry::SessionFlusher.new(config, client)
|
207
|
+
else
|
208
|
+
nil
|
209
|
+
end
|
210
|
+
|
190
211
|
if config.capture_exception_frame_locals
|
191
212
|
exception_locals_tp.enable
|
192
213
|
end
|
193
214
|
|
194
215
|
at_exit do
|
216
|
+
@session_flusher&.kill
|
195
217
|
@background_worker.shutdown
|
196
218
|
end
|
197
219
|
end
|
@@ -308,6 +330,26 @@ module Sentry
|
|
308
330
|
get_current_hub.with_scope(&block)
|
309
331
|
end
|
310
332
|
|
333
|
+
# Wrap a given block with session tracking.
|
334
|
+
# Aggregate sessions in minutely buckets will be recorded
|
335
|
+
# around this block and flushed every minute.
|
336
|
+
#
|
337
|
+
# @example
|
338
|
+
# Sentry.with_session_tracking do
|
339
|
+
# a = 1 + 1 # new session recorded with :exited status
|
340
|
+
# end
|
341
|
+
#
|
342
|
+
# Sentry.with_session_tracking do
|
343
|
+
# 1 / 0
|
344
|
+
# rescue => e
|
345
|
+
# Sentry.capture_exception(e) # new session recorded with :errored status
|
346
|
+
# end
|
347
|
+
# @return [void]
|
348
|
+
def with_session_tracking(&block)
|
349
|
+
return yield unless initialized?
|
350
|
+
get_current_hub.with_session_tracking(&block)
|
351
|
+
end
|
352
|
+
|
311
353
|
# Takes an exception and reports it to Sentry via the currently active hub.
|
312
354
|
#
|
313
355
|
# @yieldparam scope [Scope]
|
@@ -350,6 +392,13 @@ module Sentry
|
|
350
392
|
get_current_hub.last_event_id
|
351
393
|
end
|
352
394
|
|
395
|
+
# Checks if the exception object has been captured by the SDK.
|
396
|
+
#
|
397
|
+
# @return [Boolean]
|
398
|
+
def exception_captured?(exc)
|
399
|
+
return false unless initialized?
|
400
|
+
!!exc.instance_variable_get(CAPTURED_SIGNATURE)
|
401
|
+
end
|
353
402
|
|
354
403
|
##### Helpers #####
|
355
404
|
|
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.1
|
4
|
+
version: 5.2.1
|
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-03-18 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: concurrent-ruby
|
@@ -76,6 +76,8 @@ files:
|
|
76
76
|
- lib/sentry/redis.rb
|
77
77
|
- lib/sentry/release_detector.rb
|
78
78
|
- lib/sentry/scope.rb
|
79
|
+
- lib/sentry/session.rb
|
80
|
+
- lib/sentry/session_flusher.rb
|
79
81
|
- lib/sentry/span.rb
|
80
82
|
- lib/sentry/transaction.rb
|
81
83
|
- lib/sentry/transaction_event.rb
|