sentry-ruby-core 5.1.0 → 5.2.1
Sign up to get free protection for your applications and to get access to all the features.
- 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
|