sentry-ruby 5.4.2 → 5.10.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.rspec +0 -1
- data/Gemfile +17 -7
- data/README.md +3 -0
- data/Rakefile +8 -1
- data/lib/sentry/backtrace.rb +1 -1
- data/lib/sentry/baggage.rb +70 -0
- data/lib/sentry/client.rb +31 -11
- data/lib/sentry/configuration.rb +128 -26
- data/lib/sentry/envelope.rb +1 -4
- data/lib/sentry/event.rb +1 -1
- data/lib/sentry/hub.rb +31 -3
- data/lib/sentry/interfaces/request.rb +6 -16
- data/lib/sentry/interfaces/single_exception.rb +9 -1
- data/lib/sentry/net/http.rb +26 -38
- data/lib/sentry/profiler.rb +222 -0
- data/lib/sentry/puma.rb +25 -0
- data/lib/sentry/rack/capture_exceptions.rb +6 -4
- data/lib/sentry/rake.rb +1 -1
- data/lib/sentry/redis.rb +36 -23
- data/lib/sentry/scope.rb +55 -6
- data/lib/sentry/session.rb +5 -7
- data/lib/sentry/span.rb +19 -9
- data/lib/sentry/test_helper.rb +3 -1
- data/lib/sentry/transaction.rb +173 -19
- data/lib/sentry/transaction_event.rb +54 -0
- data/lib/sentry/transport.rb +19 -8
- data/lib/sentry/utils/argument_checking_helper.rb +3 -3
- data/lib/sentry/utils/encoding_helper.rb +22 -0
- data/lib/sentry/version.rb +1 -1
- data/lib/sentry-ruby.rb +38 -21
- metadata +6 -3
- data/CODE_OF_CONDUCT.md +0 -74
data/lib/sentry/span.rb
CHANGED
@@ -60,25 +60,28 @@ module Sentry
|
|
60
60
|
# The Transaction object the Span belongs to.
|
61
61
|
# Every span needs to be attached to a Transaction and their child spans will also inherit the same transaction.
|
62
62
|
# @return [Transaction]
|
63
|
-
|
63
|
+
attr_reader :transaction
|
64
64
|
|
65
65
|
def initialize(
|
66
|
+
transaction:,
|
66
67
|
description: nil,
|
67
68
|
op: nil,
|
68
69
|
status: nil,
|
69
70
|
trace_id: nil,
|
71
|
+
span_id: nil,
|
70
72
|
parent_span_id: nil,
|
71
73
|
sampled: nil,
|
72
74
|
start_timestamp: nil,
|
73
75
|
timestamp: nil
|
74
76
|
)
|
75
77
|
@trace_id = trace_id || SecureRandom.uuid.delete("-")
|
76
|
-
@span_id = SecureRandom.hex(8)
|
78
|
+
@span_id = span_id || SecureRandom.hex(8)
|
77
79
|
@parent_span_id = parent_span_id
|
78
80
|
@sampled = sampled
|
79
81
|
@start_timestamp = start_timestamp || Sentry.utc_now.to_f
|
80
82
|
@timestamp = timestamp
|
81
83
|
@description = description
|
84
|
+
@transaction = transaction
|
82
85
|
@op = op
|
83
86
|
@status = status
|
84
87
|
@data = {}
|
@@ -87,11 +90,8 @@ module Sentry
|
|
87
90
|
|
88
91
|
# Finishes the span by adding a timestamp.
|
89
92
|
# @return [self]
|
90
|
-
def finish
|
91
|
-
|
92
|
-
return if @timestamp
|
93
|
-
|
94
|
-
@timestamp = Sentry.utc_now.to_f
|
93
|
+
def finish(end_timestamp: nil)
|
94
|
+
@timestamp = end_timestamp || @timestamp || Sentry.utc_now.to_f
|
95
95
|
self
|
96
96
|
end
|
97
97
|
|
@@ -104,6 +104,13 @@ module Sentry
|
|
104
104
|
"#{@trace_id}-#{@span_id}-#{sampled_flag}"
|
105
105
|
end
|
106
106
|
|
107
|
+
# Generates a W3C Baggage header string for distributed tracing
|
108
|
+
# from the incoming baggage stored on the transaction.
|
109
|
+
# @return [String, nil]
|
110
|
+
def to_baggage
|
111
|
+
transaction.get_baggage&.serialize
|
112
|
+
end
|
113
|
+
|
107
114
|
# @return [Hash]
|
108
115
|
def to_hash
|
109
116
|
{
|
@@ -136,9 +143,8 @@ module Sentry
|
|
136
143
|
# Starts a child span with given attributes.
|
137
144
|
# @param attributes [Hash] the attributes for the child span.
|
138
145
|
def start_child(**attributes)
|
139
|
-
attributes = attributes.dup.merge(trace_id: @trace_id, parent_span_id: @span_id, sampled: @sampled)
|
146
|
+
attributes = attributes.dup.merge(transaction: @transaction, trace_id: @trace_id, parent_span_id: @span_id, sampled: @sampled)
|
140
147
|
new_span = Span.new(**attributes)
|
141
|
-
new_span.transaction = transaction
|
142
148
|
new_span.span_recorder = span_recorder
|
143
149
|
|
144
150
|
if span_recorder
|
@@ -163,6 +169,10 @@ module Sentry
|
|
163
169
|
yield(child_span)
|
164
170
|
|
165
171
|
child_span.finish
|
172
|
+
rescue
|
173
|
+
child_span.set_http_status(500)
|
174
|
+
child_span.finish
|
175
|
+
raise
|
166
176
|
end
|
167
177
|
|
168
178
|
def deep_dup
|
data/lib/sentry/test_helper.rb
CHANGED
@@ -25,12 +25,13 @@ module Sentry
|
|
25
25
|
copied_config.background_worker_threads = 0
|
26
26
|
|
27
27
|
# user can overwrite some of the configs, with a few exceptions like:
|
28
|
-
# -
|
28
|
+
# - include_local_variables
|
29
29
|
# - auto_session_tracking
|
30
30
|
block&.call(copied_config)
|
31
31
|
|
32
32
|
test_client = Sentry::Client.new(copied_config)
|
33
33
|
Sentry.get_current_hub.bind_client(test_client)
|
34
|
+
Sentry.get_current_scope.clear
|
34
35
|
end
|
35
36
|
|
36
37
|
# Clears all stored events and envelopes.
|
@@ -41,6 +42,7 @@ module Sentry
|
|
41
42
|
|
42
43
|
sentry_transport.events = []
|
43
44
|
sentry_transport.envelopes = []
|
45
|
+
Sentry.get_current_scope.clear
|
44
46
|
end
|
45
47
|
|
46
48
|
# @return [Transport]
|
data/lib/sentry/transaction.rb
CHANGED
@@ -1,5 +1,8 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require "sentry/baggage"
|
4
|
+
require "sentry/profiler"
|
5
|
+
|
3
6
|
module Sentry
|
4
7
|
class Transaction < Span
|
5
8
|
SENTRY_TRACE_REGEXP = Regexp.new(
|
@@ -12,16 +15,33 @@ module Sentry
|
|
12
15
|
UNLABELD_NAME = "<unlabeled transaction>".freeze
|
13
16
|
MESSAGE_PREFIX = "[Tracing]"
|
14
17
|
|
18
|
+
# https://develop.sentry.dev/sdk/event-payloads/transaction/#transaction-annotations
|
19
|
+
SOURCES = %i(custom url route view component task)
|
20
|
+
|
15
21
|
include LoggingHelper
|
16
22
|
|
17
23
|
# The name of the transaction.
|
18
24
|
# @return [String]
|
19
25
|
attr_reader :name
|
20
26
|
|
27
|
+
# The source of the transaction name.
|
28
|
+
# @return [Symbol]
|
29
|
+
attr_reader :source
|
30
|
+
|
21
31
|
# The sampling decision of the parent transaction, which will be considered when making the current transaction's sampling decision.
|
22
32
|
# @return [String]
|
23
33
|
attr_reader :parent_sampled
|
24
34
|
|
35
|
+
# The parsed incoming W3C baggage header.
|
36
|
+
# This is only for accessing the current baggage variable.
|
37
|
+
# Please use the #get_baggage method for interfacing outside this class.
|
38
|
+
# @return [Baggage, nil]
|
39
|
+
attr_reader :baggage
|
40
|
+
|
41
|
+
# The measurements added to the transaction.
|
42
|
+
# @return [Hash]
|
43
|
+
attr_reader :measurements
|
44
|
+
|
25
45
|
# @deprecated Use Sentry.get_current_hub instead.
|
26
46
|
attr_reader :hub
|
27
47
|
|
@@ -31,18 +51,44 @@ module Sentry
|
|
31
51
|
# @deprecated Use Sentry.logger instead.
|
32
52
|
attr_reader :logger
|
33
53
|
|
34
|
-
|
35
|
-
|
54
|
+
# The effective sample rate at which this transaction was sampled.
|
55
|
+
# @return [Float, nil]
|
56
|
+
attr_reader :effective_sample_rate
|
36
57
|
|
37
|
-
|
58
|
+
# Additional contexts stored directly on the transaction object.
|
59
|
+
# @return [Hash]
|
60
|
+
attr_reader :contexts
|
61
|
+
|
62
|
+
# The Profiler instance for this transaction.
|
63
|
+
# @return [Profiler]
|
64
|
+
attr_reader :profiler
|
65
|
+
|
66
|
+
def initialize(
|
67
|
+
hub:,
|
68
|
+
name: nil,
|
69
|
+
source: :custom,
|
70
|
+
parent_sampled: nil,
|
71
|
+
baggage: nil,
|
72
|
+
**options
|
73
|
+
)
|
74
|
+
super(transaction: self, **options)
|
75
|
+
|
76
|
+
set_name(name, source: source)
|
38
77
|
@parent_sampled = parent_sampled
|
39
|
-
@transaction = self
|
40
78
|
@hub = hub
|
79
|
+
@baggage = baggage
|
41
80
|
@configuration = hub.configuration # to be removed
|
42
81
|
@tracing_enabled = hub.configuration.tracing_enabled?
|
43
82
|
@traces_sampler = hub.configuration.traces_sampler
|
44
83
|
@traces_sample_rate = hub.configuration.traces_sample_rate
|
45
84
|
@logger = hub.configuration.logger
|
85
|
+
@release = hub.configuration.release
|
86
|
+
@environment = hub.configuration.environment
|
87
|
+
@dsn = hub.configuration.dsn
|
88
|
+
@effective_sample_rate = nil
|
89
|
+
@contexts = {}
|
90
|
+
@measurements = {}
|
91
|
+
@profiler = Profiler.new(@configuration)
|
46
92
|
init_span_recorder
|
47
93
|
end
|
48
94
|
|
@@ -52,31 +98,65 @@ module Sentry
|
|
52
98
|
#
|
53
99
|
# The child transaction will also store the parent's sampling decision in its `parent_sampled` attribute.
|
54
100
|
# @param sentry_trace [String] the trace string from the previous transaction.
|
101
|
+
# @param baggage [String, nil] the incoming baggage header string.
|
55
102
|
# @param hub [Hub] the hub that'll be responsible for sending this transaction when it's finished.
|
56
103
|
# @param options [Hash] the options you want to use to initialize a Transaction instance.
|
57
104
|
# @return [Transaction, nil]
|
58
|
-
def self.from_sentry_trace(sentry_trace, hub: Sentry.get_current_hub, **options)
|
105
|
+
def self.from_sentry_trace(sentry_trace, baggage: nil, hub: Sentry.get_current_hub, **options)
|
59
106
|
return unless hub.configuration.tracing_enabled?
|
60
107
|
return unless sentry_trace
|
61
108
|
|
109
|
+
sentry_trace_data = extract_sentry_trace(sentry_trace)
|
110
|
+
return unless sentry_trace_data
|
111
|
+
|
112
|
+
trace_id, parent_span_id, parent_sampled = sentry_trace_data
|
113
|
+
|
114
|
+
baggage = if baggage && !baggage.empty?
|
115
|
+
Baggage.from_incoming_header(baggage)
|
116
|
+
else
|
117
|
+
# If there's an incoming sentry-trace but no incoming baggage header,
|
118
|
+
# for instance in traces coming from older SDKs,
|
119
|
+
# baggage will be empty and frozen and won't be populated as head SDK.
|
120
|
+
Baggage.new({})
|
121
|
+
end
|
122
|
+
|
123
|
+
baggage.freeze!
|
124
|
+
|
125
|
+
new(
|
126
|
+
trace_id: trace_id,
|
127
|
+
parent_span_id: parent_span_id,
|
128
|
+
parent_sampled: parent_sampled,
|
129
|
+
hub: hub,
|
130
|
+
baggage: baggage,
|
131
|
+
**options
|
132
|
+
)
|
133
|
+
end
|
134
|
+
|
135
|
+
# Extract the trace_id, parent_span_id and parent_sampled values from a sentry-trace header.
|
136
|
+
#
|
137
|
+
# @param sentry_trace [String] the sentry-trace header value from the previous transaction.
|
138
|
+
# @return [Array, nil]
|
139
|
+
def self.extract_sentry_trace(sentry_trace)
|
62
140
|
match = SENTRY_TRACE_REGEXP.match(sentry_trace)
|
63
|
-
return if match.nil?
|
64
|
-
trace_id, parent_span_id, sampled_flag = match[1..3]
|
141
|
+
return nil if match.nil?
|
65
142
|
|
66
|
-
|
67
|
-
|
68
|
-
nil
|
69
|
-
else
|
70
|
-
sampled_flag != "0"
|
71
|
-
end
|
143
|
+
trace_id, parent_span_id, sampled_flag = match[1..3]
|
144
|
+
parent_sampled = sampled_flag.nil? ? nil : sampled_flag != "0"
|
72
145
|
|
73
|
-
|
146
|
+
[trace_id, parent_span_id, parent_sampled]
|
74
147
|
end
|
75
148
|
|
76
149
|
# @return [Hash]
|
77
150
|
def to_hash
|
78
151
|
hash = super
|
79
|
-
|
152
|
+
|
153
|
+
hash.merge!(
|
154
|
+
name: @name,
|
155
|
+
source: @source,
|
156
|
+
sampled: @sampled,
|
157
|
+
parent_sampled: @parent_sampled
|
158
|
+
)
|
159
|
+
|
80
160
|
hash
|
81
161
|
end
|
82
162
|
|
@@ -94,6 +174,15 @@ module Sentry
|
|
94
174
|
copy
|
95
175
|
end
|
96
176
|
|
177
|
+
# Sets a custom measurement on the transaction.
|
178
|
+
# @param name [String] name of the measurement
|
179
|
+
# @param value [Float] value of the measurement
|
180
|
+
# @param unit [String] unit of the measurement
|
181
|
+
# @return [void]
|
182
|
+
def set_measurement(name, value, unit = "")
|
183
|
+
@measurements[name] = { value: value, unit: unit }
|
184
|
+
end
|
185
|
+
|
97
186
|
# Sets initial sampling decision of the transaction.
|
98
187
|
# @param sampling_context [Hash] a context Hash that'll be passed to `traces_sampler` (if provided).
|
99
188
|
# @return [void]
|
@@ -103,7 +192,10 @@ module Sentry
|
|
103
192
|
return
|
104
193
|
end
|
105
194
|
|
106
|
-
|
195
|
+
unless @sampled.nil?
|
196
|
+
@effective_sample_rate = @sampled ? 1.0 : 0.0
|
197
|
+
return
|
198
|
+
end
|
107
199
|
|
108
200
|
sample_rate =
|
109
201
|
if @traces_sampler.is_a?(Proc)
|
@@ -116,7 +208,11 @@ module Sentry
|
|
116
208
|
|
117
209
|
transaction_description = generate_transaction_description
|
118
210
|
|
119
|
-
|
211
|
+
if [true, false].include?(sample_rate)
|
212
|
+
@effective_sample_rate = sample_rate ? 1.0 : 0.0
|
213
|
+
elsif sample_rate.is_a?(Numeric) && sample_rate >= 0.0 && sample_rate <= 1.0
|
214
|
+
@effective_sample_rate = sample_rate.to_f
|
215
|
+
else
|
120
216
|
@sampled = false
|
121
217
|
log_warn("#{MESSAGE_PREFIX} Discarding #{transaction_description} because of invalid sample_rate: #{sample_rate}")
|
122
218
|
return
|
@@ -146,7 +242,7 @@ module Sentry
|
|
146
242
|
# Finishes the transaction's recording and send it to Sentry.
|
147
243
|
# @param hub [Hub] the hub that'll send this transaction. (Deprecated)
|
148
244
|
# @return [TransactionEvent]
|
149
|
-
def finish(hub: nil)
|
245
|
+
def finish(hub: nil, end_timestamp: nil)
|
150
246
|
if hub
|
151
247
|
log_warn(
|
152
248
|
<<~MSG
|
@@ -158,12 +254,14 @@ module Sentry
|
|
158
254
|
|
159
255
|
hub ||= @hub
|
160
256
|
|
161
|
-
super()
|
257
|
+
super(end_timestamp: end_timestamp)
|
162
258
|
|
163
259
|
if @name.nil?
|
164
260
|
@name = UNLABELD_NAME
|
165
261
|
end
|
166
262
|
|
263
|
+
@profiler.stop
|
264
|
+
|
167
265
|
if @sampled
|
168
266
|
event = hub.current_client.event_from_transaction(self)
|
169
267
|
hub.capture_event(event)
|
@@ -172,6 +270,39 @@ module Sentry
|
|
172
270
|
end
|
173
271
|
end
|
174
272
|
|
273
|
+
# Get the existing frozen incoming baggage
|
274
|
+
# or populate one with sentry- items as the head SDK.
|
275
|
+
# @return [Baggage]
|
276
|
+
def get_baggage
|
277
|
+
populate_head_baggage if @baggage.nil? || @baggage.mutable
|
278
|
+
@baggage
|
279
|
+
end
|
280
|
+
|
281
|
+
# Set the transaction name directly.
|
282
|
+
# Considered internal api since it bypasses the usual scope logic.
|
283
|
+
# @param name [String]
|
284
|
+
# @param source [Symbol]
|
285
|
+
# @return [void]
|
286
|
+
def set_name(name, source: :custom)
|
287
|
+
@name = name
|
288
|
+
@source = SOURCES.include?(source) ? source.to_sym : :custom
|
289
|
+
end
|
290
|
+
|
291
|
+
# Set contexts directly on the transaction.
|
292
|
+
# @param key [String, Symbol]
|
293
|
+
# @param value [Object]
|
294
|
+
# @return [void]
|
295
|
+
def set_context(key, value)
|
296
|
+
@contexts[key] = value
|
297
|
+
end
|
298
|
+
|
299
|
+
# Start the profiler.
|
300
|
+
# @return [void]
|
301
|
+
def start_profiler!
|
302
|
+
profiler.set_initial_sample_decision(sampled)
|
303
|
+
profiler.start
|
304
|
+
end
|
305
|
+
|
175
306
|
protected
|
176
307
|
|
177
308
|
def init_span_recorder(limit = 1000)
|
@@ -188,6 +319,29 @@ module Sentry
|
|
188
319
|
result
|
189
320
|
end
|
190
321
|
|
322
|
+
def populate_head_baggage
|
323
|
+
items = {
|
324
|
+
"trace_id" => trace_id,
|
325
|
+
"sample_rate" => effective_sample_rate&.to_s,
|
326
|
+
"environment" => @environment,
|
327
|
+
"release" => @release,
|
328
|
+
"public_key" => @dsn&.public_key
|
329
|
+
}
|
330
|
+
|
331
|
+
items["transaction"] = name unless source_low_quality?
|
332
|
+
|
333
|
+
user = @hub.current_scope&.user
|
334
|
+
items["user_segment"] = user["segment"] if user && user["segment"]
|
335
|
+
|
336
|
+
items.compact!
|
337
|
+
@baggage = Baggage.new(items, mutable: false)
|
338
|
+
end
|
339
|
+
|
340
|
+
# These are high cardinality and thus bad
|
341
|
+
def source_low_quality?
|
342
|
+
source == :url
|
343
|
+
end
|
344
|
+
|
191
345
|
class SpanRecorder
|
192
346
|
attr_reader :max_length, :spans
|
193
347
|
|
@@ -8,9 +8,37 @@ module Sentry
|
|
8
8
|
# @return [<Array[Span]>]
|
9
9
|
attr_accessor :spans
|
10
10
|
|
11
|
+
# @return [Hash, nil]
|
12
|
+
attr_accessor :dynamic_sampling_context
|
13
|
+
|
14
|
+
# @return [Hash]
|
15
|
+
attr_accessor :measurements
|
16
|
+
|
11
17
|
# @return [Float, nil]
|
12
18
|
attr_reader :start_timestamp
|
13
19
|
|
20
|
+
# @return [Hash, nil]
|
21
|
+
attr_accessor :profile
|
22
|
+
|
23
|
+
def initialize(transaction:, **options)
|
24
|
+
super(**options)
|
25
|
+
|
26
|
+
self.transaction = transaction.name
|
27
|
+
self.transaction_info = { source: transaction.source }
|
28
|
+
self.contexts.merge!(transaction.contexts)
|
29
|
+
self.contexts.merge!(trace: transaction.get_trace_context)
|
30
|
+
self.timestamp = transaction.timestamp
|
31
|
+
self.start_timestamp = transaction.start_timestamp
|
32
|
+
self.tags = transaction.tags
|
33
|
+
self.dynamic_sampling_context = transaction.get_baggage.dynamic_sampling_context
|
34
|
+
self.measurements = transaction.measurements
|
35
|
+
|
36
|
+
finished_spans = transaction.span_recorder.spans.select { |span| span.timestamp && span != transaction }
|
37
|
+
self.spans = finished_spans.map(&:to_hash)
|
38
|
+
|
39
|
+
populate_profile(transaction)
|
40
|
+
end
|
41
|
+
|
14
42
|
# Sets the event's start_timestamp.
|
15
43
|
# @param time [Time, Float]
|
16
44
|
# @return [void]
|
@@ -23,7 +51,33 @@ module Sentry
|
|
23
51
|
data = super
|
24
52
|
data[:spans] = @spans.map(&:to_hash) if @spans
|
25
53
|
data[:start_timestamp] = @start_timestamp
|
54
|
+
data[:measurements] = @measurements
|
26
55
|
data
|
27
56
|
end
|
57
|
+
|
58
|
+
private
|
59
|
+
|
60
|
+
def populate_profile(transaction)
|
61
|
+
profile_hash = transaction.profiler.to_hash
|
62
|
+
return if profile_hash.empty?
|
63
|
+
|
64
|
+
profile_hash.merge!(
|
65
|
+
environment: environment,
|
66
|
+
release: release,
|
67
|
+
timestamp: Time.at(start_timestamp).iso8601,
|
68
|
+
device: { architecture: Scope.os_context[:machine] },
|
69
|
+
os: { name: Scope.os_context[:name], version: Scope.os_context[:version] },
|
70
|
+
runtime: Scope.runtime_context,
|
71
|
+
transaction: {
|
72
|
+
id: event_id,
|
73
|
+
name: transaction.name,
|
74
|
+
trace_id: transaction.trace_id,
|
75
|
+
# TODO-neel-profiler stubbed for now, see thread_id note in profiler.rb
|
76
|
+
active_thead_id: '0'
|
77
|
+
}
|
78
|
+
)
|
79
|
+
|
80
|
+
self.profile = profile_hash
|
81
|
+
end
|
28
82
|
end
|
29
83
|
end
|
data/lib/sentry/transport.rb
CHANGED
@@ -136,20 +136,31 @@ module Sentry
|
|
136
136
|
event_id = event_payload[:event_id] || event_payload["event_id"]
|
137
137
|
item_type = event_payload[:type] || event_payload["type"]
|
138
138
|
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
)
|
139
|
+
envelope_headers = {
|
140
|
+
event_id: event_id,
|
141
|
+
dsn: @dsn.to_s,
|
142
|
+
sdk: Sentry.sdk_meta,
|
143
|
+
sent_at: Sentry.utc_now.iso8601
|
144
|
+
}
|
145
|
+
|
146
|
+
if event.is_a?(TransactionEvent) && event.dynamic_sampling_context
|
147
|
+
envelope_headers[:trace] = event.dynamic_sampling_context
|
148
|
+
end
|
149
|
+
|
150
|
+
envelope = Envelope.new(envelope_headers)
|
147
151
|
|
148
152
|
envelope.add_item(
|
149
153
|
{ type: item_type, content_type: 'application/json' },
|
150
154
|
event_payload
|
151
155
|
)
|
152
156
|
|
157
|
+
if event.is_a?(TransactionEvent) && event.profile
|
158
|
+
envelope.add_item(
|
159
|
+
{ type: 'profile', content_type: 'application/json' },
|
160
|
+
event.profile
|
161
|
+
)
|
162
|
+
end
|
163
|
+
|
153
164
|
client_report_headers, client_report_payload = fetch_pending_client_report
|
154
165
|
envelope.add_item(client_report_headers, client_report_payload) if client_report_headers
|
155
166
|
|
@@ -4,9 +4,9 @@ module Sentry
|
|
4
4
|
module ArgumentCheckingHelper
|
5
5
|
private
|
6
6
|
|
7
|
-
def check_argument_type!(argument,
|
8
|
-
unless argument.is_a?(
|
9
|
-
raise ArgumentError, "expect the argument to be a #{
|
7
|
+
def check_argument_type!(argument, *expected_types)
|
8
|
+
unless expected_types.any? { |t| argument.is_a?(t) }
|
9
|
+
raise ArgumentError, "expect the argument to be a #{expected_types.join(' or ')}, got #{argument.class} (#{argument.inspect})"
|
10
10
|
end
|
11
11
|
end
|
12
12
|
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Sentry
|
4
|
+
module Utils
|
5
|
+
module EncodingHelper
|
6
|
+
def self.encode_to_utf_8(value)
|
7
|
+
if value.encoding != Encoding::UTF_8 && value.respond_to?(:force_encoding)
|
8
|
+
value = value.dup.force_encoding(Encoding::UTF_8)
|
9
|
+
end
|
10
|
+
|
11
|
+
value = value.scrub unless value.valid_encoding?
|
12
|
+
value
|
13
|
+
end
|
14
|
+
|
15
|
+
def self.valid_utf_8?(value)
|
16
|
+
return true unless value.respond_to?(:force_encoding)
|
17
|
+
|
18
|
+
value.dup.force_encoding(Encoding::UTF_8).valid_encoding?
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
data/lib/sentry/version.rb
CHANGED
data/lib/sentry-ruby.rb
CHANGED
@@ -8,6 +8,7 @@ require "sentry/version"
|
|
8
8
|
require "sentry/exceptions"
|
9
9
|
require "sentry/core_ext/object/deep_dup"
|
10
10
|
require "sentry/utils/argument_checking_helper"
|
11
|
+
require "sentry/utils/encoding_helper"
|
11
12
|
require "sentry/utils/logging_helper"
|
12
13
|
require "sentry/configuration"
|
13
14
|
require "sentry/logger"
|
@@ -39,6 +40,8 @@ module Sentry
|
|
39
40
|
|
40
41
|
SENTRY_TRACE_HEADER_NAME = "sentry-trace".freeze
|
41
42
|
|
43
|
+
BAGGAGE_HEADER_NAME = "baggage".freeze
|
44
|
+
|
42
45
|
THREAD_LOCAL = :sentry_hub
|
43
46
|
|
44
47
|
class << self
|
@@ -70,8 +73,18 @@ module Sentry
|
|
70
73
|
##### Patch Registration #####
|
71
74
|
|
72
75
|
# @!visibility private
|
73
|
-
def register_patch(&block)
|
74
|
-
|
76
|
+
def register_patch(patch = nil, target = nil, &block)
|
77
|
+
if patch && block
|
78
|
+
raise ArgumentError.new("Please provide either a patch and its target OR a block, but not both")
|
79
|
+
end
|
80
|
+
|
81
|
+
if block
|
82
|
+
registered_patches << block
|
83
|
+
else
|
84
|
+
registered_patches << proc do
|
85
|
+
target.send(:prepend, patch) unless target.ancestors.include?(patch)
|
86
|
+
end
|
87
|
+
end
|
75
88
|
end
|
76
89
|
|
77
90
|
# @!visibility private
|
@@ -209,7 +222,7 @@ module Sentry
|
|
209
222
|
nil
|
210
223
|
end
|
211
224
|
|
212
|
-
if config.
|
225
|
+
if config.include_local_variables
|
213
226
|
exception_locals_tp.enable
|
214
227
|
end
|
215
228
|
|
@@ -231,7 +244,7 @@ module Sentry
|
|
231
244
|
@session_flusher = nil
|
232
245
|
end
|
233
246
|
|
234
|
-
if configuration&.
|
247
|
+
if configuration&.include_local_variables
|
235
248
|
exception_locals_tp.disable
|
236
249
|
end
|
237
250
|
|
@@ -348,7 +361,7 @@ module Sentry
|
|
348
361
|
# @yieldparam scope [Scope]
|
349
362
|
# @return [void]
|
350
363
|
def with_scope(&block)
|
351
|
-
return unless initialized?
|
364
|
+
return yield unless initialized?
|
352
365
|
get_current_hub.with_scope(&block)
|
353
366
|
end
|
354
367
|
|
@@ -439,22 +452,8 @@ module Sentry
|
|
439
452
|
# end
|
440
453
|
#
|
441
454
|
def with_child_span(**attributes, &block)
|
442
|
-
|
443
|
-
|
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
|
455
|
+
return yield(nil) unless Sentry.initialized?
|
456
|
+
get_current_hub.with_child_span(**attributes, &block)
|
458
457
|
end
|
459
458
|
|
460
459
|
# Returns the id of the lastly reported Sentry::Event.
|
@@ -473,6 +472,23 @@ module Sentry
|
|
473
472
|
!!exc.instance_variable_get(CAPTURED_SIGNATURE)
|
474
473
|
end
|
475
474
|
|
475
|
+
# Add a global event processor [Proc].
|
476
|
+
# These run before scope event processors.
|
477
|
+
#
|
478
|
+
# @yieldparam event [Event]
|
479
|
+
# @yieldparam hint [Hash, nil]
|
480
|
+
# @return [void]
|
481
|
+
#
|
482
|
+
# @example
|
483
|
+
# Sentry.add_global_event_processor do |event, hint|
|
484
|
+
# event.tags = { foo: 42 }
|
485
|
+
# event
|
486
|
+
# end
|
487
|
+
#
|
488
|
+
def add_global_event_processor(&block)
|
489
|
+
Scope.add_global_event_processor(&block)
|
490
|
+
end
|
491
|
+
|
476
492
|
##### Helpers #####
|
477
493
|
|
478
494
|
# @!visibility private
|
@@ -503,3 +519,4 @@ end
|
|
503
519
|
# patches
|
504
520
|
require "sentry/net/http"
|
505
521
|
require "sentry/redis"
|
522
|
+
require "sentry/puma"
|