sentry-ruby 5.26.0 → 6.1.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 +2 -4
- data/README.md +1 -1
- data/lib/sentry/background_worker.rb +1 -4
- data/lib/sentry/backtrace/line.rb +99 -0
- data/lib/sentry/backtrace.rb +44 -76
- data/lib/sentry/breadcrumb.rb +1 -1
- data/lib/sentry/breadcrumb_buffer.rb +2 -2
- data/lib/sentry/check_in_event.rb +2 -2
- data/lib/sentry/client.rb +39 -89
- data/lib/sentry/configuration.rb +125 -78
- data/lib/sentry/cron/monitor_check_ins.rb +3 -3
- data/lib/sentry/cron/monitor_config.rb +2 -2
- data/lib/sentry/cron/monitor_schedule.rb +2 -2
- data/lib/sentry/debug_structured_logger.rb +94 -0
- data/lib/sentry/dsn.rb +32 -0
- data/lib/sentry/envelope/item.rb +1 -2
- data/lib/sentry/error_event.rb +3 -3
- data/lib/sentry/event.rb +4 -10
- data/lib/sentry/graphql.rb +1 -1
- data/lib/sentry/hub.rb +6 -5
- data/lib/sentry/interface.rb +1 -1
- data/lib/sentry/interfaces/exception.rb +2 -2
- data/lib/sentry/interfaces/request.rb +2 -0
- data/lib/sentry/interfaces/single_exception.rb +3 -3
- data/lib/sentry/interfaces/stacktrace.rb +3 -3
- data/lib/sentry/interfaces/stacktrace_builder.rb +0 -8
- data/lib/sentry/interfaces/threads.rb +2 -2
- data/lib/sentry/log_event.rb +19 -6
- data/lib/sentry/profiler.rb +4 -5
- data/lib/sentry/propagation_context.rb +55 -18
- data/lib/sentry/rspec.rb +1 -1
- data/lib/sentry/span.rb +2 -17
- data/lib/sentry/std_lib_logger.rb +6 -1
- data/lib/sentry/test_helper.rb +23 -0
- data/lib/sentry/transaction.rb +72 -95
- data/lib/sentry/transaction_event.rb +4 -9
- data/lib/sentry/transport/debug_transport.rb +70 -0
- data/lib/sentry/transport/dummy_transport.rb +1 -0
- data/lib/sentry/transport/http_transport.rb +9 -5
- data/lib/sentry/transport.rb +3 -5
- data/lib/sentry/utils/encoding_helper.rb +7 -0
- data/lib/sentry/utils/logging_helper.rb +8 -6
- data/lib/sentry/utils/sample_rand.rb +97 -0
- data/lib/sentry/vernier/profiler.rb +4 -3
- data/lib/sentry/version.rb +1 -1
- data/lib/sentry-ruby.rb +6 -30
- data/sentry-ruby-core.gemspec +1 -1
- data/sentry-ruby.gemspec +1 -1
- metadata +12 -18
- data/lib/sentry/metrics/aggregator.rb +0 -248
- data/lib/sentry/metrics/configuration.rb +0 -47
- data/lib/sentry/metrics/counter_metric.rb +0 -25
- data/lib/sentry/metrics/distribution_metric.rb +0 -25
- data/lib/sentry/metrics/gauge_metric.rb +0 -35
- data/lib/sentry/metrics/local_aggregator.rb +0 -53
- data/lib/sentry/metrics/metric.rb +0 -19
- data/lib/sentry/metrics/set_metric.rb +0 -28
- data/lib/sentry/metrics/timing.rb +0 -51
- data/lib/sentry/metrics.rb +0 -56
data/lib/sentry/transaction.rb
CHANGED
|
@@ -2,13 +2,11 @@
|
|
|
2
2
|
|
|
3
3
|
require "sentry/baggage"
|
|
4
4
|
require "sentry/profiler"
|
|
5
|
+
require "sentry/utils/sample_rand"
|
|
5
6
|
require "sentry/propagation_context"
|
|
6
7
|
|
|
7
8
|
module Sentry
|
|
8
9
|
class Transaction < Span
|
|
9
|
-
# @deprecated Use Sentry::PropagationContext::SENTRY_TRACE_REGEXP instead.
|
|
10
|
-
SENTRY_TRACE_REGEXP = PropagationContext::SENTRY_TRACE_REGEXP
|
|
11
|
-
|
|
12
10
|
UNLABELD_NAME = "<unlabeled transaction>"
|
|
13
11
|
MESSAGE_PREFIX = "[Tracing]"
|
|
14
12
|
|
|
@@ -39,12 +37,6 @@ module Sentry
|
|
|
39
37
|
# @return [Hash]
|
|
40
38
|
attr_reader :measurements
|
|
41
39
|
|
|
42
|
-
# @deprecated Use Sentry.get_current_hub instead.
|
|
43
|
-
attr_reader :hub
|
|
44
|
-
|
|
45
|
-
# @deprecated Use Sentry.configuration instead.
|
|
46
|
-
attr_reader :configuration
|
|
47
|
-
|
|
48
40
|
# The effective sample rate at which this transaction was sampled.
|
|
49
41
|
# @return [Float, nil]
|
|
50
42
|
attr_reader :effective_sample_rate
|
|
@@ -57,90 +49,39 @@ module Sentry
|
|
|
57
49
|
# @return [Profiler]
|
|
58
50
|
attr_reader :profiler
|
|
59
51
|
|
|
52
|
+
# Sample rand value generated from trace_id
|
|
53
|
+
# @return [String]
|
|
54
|
+
attr_reader :sample_rand
|
|
55
|
+
|
|
60
56
|
def initialize(
|
|
61
|
-
hub:,
|
|
62
57
|
name: nil,
|
|
63
58
|
source: :custom,
|
|
64
59
|
parent_sampled: nil,
|
|
65
60
|
baggage: nil,
|
|
61
|
+
sample_rand: nil,
|
|
66
62
|
**options
|
|
67
63
|
)
|
|
68
64
|
super(transaction: self, **options)
|
|
69
65
|
|
|
70
66
|
set_name(name, source: source)
|
|
71
67
|
@parent_sampled = parent_sampled
|
|
72
|
-
@hub = hub
|
|
73
68
|
@baggage = baggage
|
|
74
|
-
@configuration = hub.configuration # to be removed
|
|
75
|
-
@tracing_enabled = hub.configuration.tracing_enabled?
|
|
76
|
-
@traces_sampler = hub.configuration.traces_sampler
|
|
77
|
-
@traces_sample_rate = hub.configuration.traces_sample_rate
|
|
78
|
-
@sdk_logger = hub.configuration.sdk_logger
|
|
79
|
-
@release = hub.configuration.release
|
|
80
|
-
@environment = hub.configuration.environment
|
|
81
|
-
@dsn = hub.configuration.dsn
|
|
82
69
|
@effective_sample_rate = nil
|
|
83
70
|
@contexts = {}
|
|
84
71
|
@measurements = {}
|
|
85
|
-
|
|
86
|
-
unless @hub.profiler_running?
|
|
87
|
-
@profiler = @configuration.profiler_class.new(@configuration)
|
|
88
|
-
end
|
|
72
|
+
@sample_rand = sample_rand
|
|
89
73
|
|
|
90
74
|
init_span_recorder
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
# @deprecated use Sentry.continue_trace instead.
|
|
94
|
-
#
|
|
95
|
-
# Initalizes a Transaction instance with a Sentry trace string from another transaction (usually from an external request).
|
|
96
|
-
#
|
|
97
|
-
# The original transaction will become the parent of the new Transaction instance. And they will share the same `trace_id`.
|
|
98
|
-
#
|
|
99
|
-
# The child transaction will also store the parent's sampling decision in its `parent_sampled` attribute.
|
|
100
|
-
# @param sentry_trace [String] the trace string from the previous transaction.
|
|
101
|
-
# @param baggage [String, nil] the incoming baggage header string.
|
|
102
|
-
# @param hub [Hub] the hub that'll be responsible for sending this transaction when it's finished.
|
|
103
|
-
# @param options [Hash] the options you want to use to initialize a Transaction instance.
|
|
104
|
-
# @return [Transaction, nil]
|
|
105
|
-
def self.from_sentry_trace(sentry_trace, baggage: nil, hub: Sentry.get_current_hub, **options)
|
|
106
|
-
return unless hub.configuration.tracing_enabled?
|
|
107
|
-
return unless sentry_trace
|
|
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 =
|
|
115
|
-
if baggage && !baggage.empty?
|
|
116
|
-
Baggage.from_incoming_header(baggage)
|
|
117
|
-
else
|
|
118
|
-
# If there's an incoming sentry-trace but no incoming baggage header,
|
|
119
|
-
# for instance in traces coming from older SDKs,
|
|
120
|
-
# baggage will be empty and frozen and won't be populated as head SDK.
|
|
121
|
-
Baggage.new({})
|
|
122
|
-
end
|
|
75
|
+
init_profiler
|
|
123
76
|
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
parent_span_id: parent_span_id,
|
|
129
|
-
parent_sampled: parent_sampled,
|
|
130
|
-
hub: hub,
|
|
131
|
-
baggage: baggage,
|
|
132
|
-
**options
|
|
133
|
-
)
|
|
134
|
-
end
|
|
135
|
-
|
|
136
|
-
# @deprecated Use Sentry::PropagationContext.extract_sentry_trace instead.
|
|
137
|
-
# @return [Array, nil]
|
|
138
|
-
def self.extract_sentry_trace(sentry_trace)
|
|
139
|
-
PropagationContext.extract_sentry_trace(sentry_trace)
|
|
77
|
+
unless @sample_rand
|
|
78
|
+
generator = Utils::SampleRand.new(trace_id: @trace_id)
|
|
79
|
+
@sample_rand = generator.generate_from_trace_id
|
|
80
|
+
end
|
|
140
81
|
end
|
|
141
82
|
|
|
142
83
|
# @return [Hash]
|
|
143
|
-
def
|
|
84
|
+
def to_h
|
|
144
85
|
hash = super
|
|
145
86
|
|
|
146
87
|
hash.merge!(
|
|
@@ -153,6 +94,13 @@ module Sentry
|
|
|
153
94
|
hash
|
|
154
95
|
end
|
|
155
96
|
|
|
97
|
+
def parent_sample_rate
|
|
98
|
+
return unless @baggage&.items
|
|
99
|
+
|
|
100
|
+
sample_rate_str = @baggage.items["sample_rate"]
|
|
101
|
+
sample_rate_str&.to_f
|
|
102
|
+
end
|
|
103
|
+
|
|
156
104
|
# @return [Transaction]
|
|
157
105
|
def deep_dup
|
|
158
106
|
copy = super
|
|
@@ -180,7 +128,9 @@ module Sentry
|
|
|
180
128
|
# @param sampling_context [Hash] a context Hash that'll be passed to `traces_sampler` (if provided).
|
|
181
129
|
# @return [void]
|
|
182
130
|
def set_initial_sample_decision(sampling_context:)
|
|
183
|
-
|
|
131
|
+
configuration = Sentry.configuration
|
|
132
|
+
|
|
133
|
+
unless configuration && configuration.tracing_enabled?
|
|
184
134
|
@sampled = false
|
|
185
135
|
return
|
|
186
136
|
end
|
|
@@ -191,12 +141,12 @@ module Sentry
|
|
|
191
141
|
end
|
|
192
142
|
|
|
193
143
|
sample_rate =
|
|
194
|
-
if
|
|
195
|
-
|
|
144
|
+
if configuration.traces_sampler.is_a?(Proc)
|
|
145
|
+
configuration.traces_sampler.call(sampling_context)
|
|
196
146
|
elsif !sampling_context[:parent_sampled].nil?
|
|
197
147
|
sampling_context[:parent_sampled]
|
|
198
148
|
else
|
|
199
|
-
|
|
149
|
+
configuration.traces_sample_rate
|
|
200
150
|
end
|
|
201
151
|
|
|
202
152
|
transaction_description = generate_transaction_description
|
|
@@ -225,7 +175,7 @@ module Sentry
|
|
|
225
175
|
@effective_sample_rate /= 2**factor
|
|
226
176
|
end
|
|
227
177
|
|
|
228
|
-
@sampled =
|
|
178
|
+
@sampled = @sample_rand < @effective_sample_rate
|
|
229
179
|
end
|
|
230
180
|
|
|
231
181
|
if @sampled
|
|
@@ -238,29 +188,28 @@ module Sentry
|
|
|
238
188
|
end
|
|
239
189
|
|
|
240
190
|
# Finishes the transaction's recording and send it to Sentry.
|
|
241
|
-
# @param hub [Hub] the hub that'll send this transaction. (Deprecated)
|
|
242
191
|
# @return [TransactionEvent]
|
|
243
|
-
def finish(
|
|
244
|
-
if hub
|
|
245
|
-
log_warn(
|
|
246
|
-
<<~MSG
|
|
247
|
-
Specifying a different hub in `Transaction#finish` will be deprecated in version 5.0.
|
|
248
|
-
Please use `Hub#start_transaction` with the designated hub.
|
|
249
|
-
MSG
|
|
250
|
-
)
|
|
251
|
-
end
|
|
252
|
-
|
|
253
|
-
hub ||= @hub
|
|
254
|
-
|
|
192
|
+
def finish(end_timestamp: nil)
|
|
255
193
|
super(end_timestamp: end_timestamp)
|
|
256
194
|
|
|
257
195
|
if @name.nil?
|
|
258
196
|
@name = UNLABELD_NAME
|
|
259
197
|
end
|
|
260
198
|
|
|
261
|
-
|
|
199
|
+
hub = Sentry.get_current_hub
|
|
200
|
+
return unless hub
|
|
262
201
|
|
|
263
|
-
|
|
202
|
+
hub.stop_profiler!(self)
|
|
203
|
+
|
|
204
|
+
if @sampled && ignore_status_code?
|
|
205
|
+
@sampled = false
|
|
206
|
+
|
|
207
|
+
status_code = get_http_status_code
|
|
208
|
+
log_debug("#{MESSAGE_PREFIX} Discarding #{generate_transaction_description} due to ignored HTTP status code: #{status_code}")
|
|
209
|
+
|
|
210
|
+
hub.current_client.transport.record_lost_event(:event_processor, "transaction")
|
|
211
|
+
hub.current_client.transport.record_lost_event(:event_processor, "span")
|
|
212
|
+
elsif @sampled
|
|
264
213
|
event = hub.current_client.event_from_transaction(self)
|
|
265
214
|
hub.capture_event(event)
|
|
266
215
|
else
|
|
@@ -318,6 +267,15 @@ module Sentry
|
|
|
318
267
|
@span_recorder.add(self)
|
|
319
268
|
end
|
|
320
269
|
|
|
270
|
+
def init_profiler
|
|
271
|
+
hub = Sentry.get_current_hub
|
|
272
|
+
return unless hub
|
|
273
|
+
|
|
274
|
+
unless hub.profiler_running?
|
|
275
|
+
@profiler = hub.configuration.profiler_class.new(hub.configuration)
|
|
276
|
+
end
|
|
277
|
+
end
|
|
278
|
+
|
|
321
279
|
private
|
|
322
280
|
|
|
323
281
|
def generate_transaction_description
|
|
@@ -328,13 +286,16 @@ module Sentry
|
|
|
328
286
|
end
|
|
329
287
|
|
|
330
288
|
def populate_head_baggage
|
|
289
|
+
configuration = Sentry.configuration
|
|
290
|
+
|
|
331
291
|
items = {
|
|
332
292
|
"trace_id" => trace_id,
|
|
333
293
|
"sample_rate" => effective_sample_rate&.to_s,
|
|
294
|
+
"sample_rand" => Utils::SampleRand.format(@sample_rand),
|
|
334
295
|
"sampled" => sampled&.to_s,
|
|
335
|
-
"environment" =>
|
|
336
|
-
"release" =>
|
|
337
|
-
"public_key" =>
|
|
296
|
+
"environment" => configuration&.environment,
|
|
297
|
+
"release" => configuration&.release,
|
|
298
|
+
"public_key" => configuration&.dsn&.public_key
|
|
338
299
|
}
|
|
339
300
|
|
|
340
301
|
items["transaction"] = name unless source_low_quality?
|
|
@@ -343,6 +304,22 @@ module Sentry
|
|
|
343
304
|
@baggage = Baggage.new(items, mutable: false)
|
|
344
305
|
end
|
|
345
306
|
|
|
307
|
+
def ignore_status_code?
|
|
308
|
+
trace_ignore_status_codes = Sentry.configuration&.trace_ignore_status_codes
|
|
309
|
+
return false unless trace_ignore_status_codes
|
|
310
|
+
|
|
311
|
+
status_code = get_http_status_code
|
|
312
|
+
return false unless status_code
|
|
313
|
+
|
|
314
|
+
trace_ignore_status_codes.any? do |ignored|
|
|
315
|
+
ignored.is_a?(Range) ? ignored.include?(status_code) : status_code == ignored
|
|
316
|
+
end
|
|
317
|
+
end
|
|
318
|
+
|
|
319
|
+
def get_http_status_code
|
|
320
|
+
@data && @data[Span::DataConventions::HTTP_STATUS_CODE]
|
|
321
|
+
end
|
|
322
|
+
|
|
346
323
|
class SpanRecorder
|
|
347
324
|
attr_reader :max_length, :spans
|
|
348
325
|
|
|
@@ -17,9 +17,6 @@ module Sentry
|
|
|
17
17
|
# @return [Hash, nil]
|
|
18
18
|
attr_accessor :profile
|
|
19
19
|
|
|
20
|
-
# @return [Hash, nil]
|
|
21
|
-
attr_accessor :metrics_summary
|
|
22
|
-
|
|
23
20
|
def initialize(transaction:, **options)
|
|
24
21
|
super(**options)
|
|
25
22
|
|
|
@@ -32,10 +29,9 @@ module Sentry
|
|
|
32
29
|
self.tags = transaction.tags
|
|
33
30
|
self.dynamic_sampling_context = transaction.get_baggage.dynamic_sampling_context
|
|
34
31
|
self.measurements = transaction.measurements
|
|
35
|
-
self.metrics_summary = transaction.metrics_summary
|
|
36
32
|
|
|
37
33
|
finished_spans = transaction.span_recorder.spans.select { |span| span.timestamp && span != transaction }
|
|
38
|
-
self.spans = finished_spans.map(&:
|
|
34
|
+
self.spans = finished_spans.map(&:to_h)
|
|
39
35
|
|
|
40
36
|
populate_profile(transaction)
|
|
41
37
|
end
|
|
@@ -48,12 +44,11 @@ module Sentry
|
|
|
48
44
|
end
|
|
49
45
|
|
|
50
46
|
# @return [Hash]
|
|
51
|
-
def
|
|
47
|
+
def to_h
|
|
52
48
|
data = super
|
|
53
|
-
data[:spans] = @spans.map(&:
|
|
49
|
+
data[:spans] = @spans.map(&:to_h) if @spans
|
|
54
50
|
data[:start_timestamp] = @start_timestamp
|
|
55
51
|
data[:measurements] = @measurements
|
|
56
|
-
data[:_metrics_summary] = @metrics_summary if @metrics_summary
|
|
57
52
|
data
|
|
58
53
|
end
|
|
59
54
|
|
|
@@ -62,7 +57,7 @@ module Sentry
|
|
|
62
57
|
EMPTY_PROFILE = {}.freeze
|
|
63
58
|
|
|
64
59
|
def populate_profile(transaction)
|
|
65
|
-
profile_hash = transaction.profiler&.
|
|
60
|
+
profile_hash = transaction.profiler&.to_h || EMPTY_PROFILE
|
|
66
61
|
|
|
67
62
|
return if profile_hash.empty?
|
|
68
63
|
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "json"
|
|
4
|
+
require "fileutils"
|
|
5
|
+
require "pathname"
|
|
6
|
+
require "delegate"
|
|
7
|
+
|
|
8
|
+
module Sentry
|
|
9
|
+
# DebugTransport is a transport that logs events to a file for debugging purposes.
|
|
10
|
+
#
|
|
11
|
+
# It can optionally also send events to Sentry via HTTP transport if a real DSN
|
|
12
|
+
# is provided.
|
|
13
|
+
class DebugTransport < SimpleDelegator
|
|
14
|
+
DEFAULT_LOG_FILE_PATH = File.join("log", "sentry_debug_events.log")
|
|
15
|
+
|
|
16
|
+
attr_reader :log_file, :backend
|
|
17
|
+
|
|
18
|
+
def initialize(configuration)
|
|
19
|
+
@log_file = initialize_log_file(configuration)
|
|
20
|
+
@backend = initialize_backend(configuration)
|
|
21
|
+
|
|
22
|
+
super(@backend)
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def send_event(event)
|
|
26
|
+
log_envelope(envelope_from_event(event))
|
|
27
|
+
backend.send_event(event)
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def log_envelope(envelope)
|
|
31
|
+
envelope_json = {
|
|
32
|
+
timestamp: Time.now.utc.iso8601,
|
|
33
|
+
envelope_headers: envelope.headers,
|
|
34
|
+
items: envelope.items.map do |item|
|
|
35
|
+
{ headers: item.headers, payload: item.payload }
|
|
36
|
+
end
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
File.open(log_file, "a") { |file| file << JSON.dump(envelope_json) << "\n" }
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def logged_envelopes
|
|
43
|
+
return [] unless File.exist?(log_file)
|
|
44
|
+
|
|
45
|
+
File.readlines(log_file).map do |line|
|
|
46
|
+
JSON.parse(line)
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def clear
|
|
51
|
+
File.write(log_file, "")
|
|
52
|
+
log_debug("DebugTransport: Cleared events from #{log_file}")
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
private
|
|
56
|
+
|
|
57
|
+
def initialize_backend(configuration)
|
|
58
|
+
backend = configuration.dsn.local? ? DummyTransport : HTTPTransport
|
|
59
|
+
backend.new(configuration)
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
def initialize_log_file(configuration)
|
|
63
|
+
log_file = Pathname(configuration.sdk_debug_transport_log_file || DEFAULT_LOG_FILE_PATH)
|
|
64
|
+
|
|
65
|
+
FileUtils.mkdir_p(log_file.dirname) unless log_file.dirname.exist?
|
|
66
|
+
|
|
67
|
+
log_file
|
|
68
|
+
end
|
|
69
|
+
end
|
|
70
|
+
end
|
|
@@ -45,11 +45,7 @@ module Sentry
|
|
|
45
45
|
auth_header = generate_auth_header
|
|
46
46
|
headers["X-Sentry-Auth"] = auth_header if auth_header
|
|
47
47
|
|
|
48
|
-
response =
|
|
49
|
-
request = ::Net::HTTP::Post.new(endpoint, headers)
|
|
50
|
-
request.body = data
|
|
51
|
-
http.request(request)
|
|
52
|
-
end
|
|
48
|
+
response = do_request(endpoint, headers, data)
|
|
53
49
|
|
|
54
50
|
if response.code.match?(/\A2\d{2}/)
|
|
55
51
|
handle_rate_limited_response(response) if has_rate_limited_header?(response)
|
|
@@ -111,6 +107,14 @@ module Sentry
|
|
|
111
107
|
connection
|
|
112
108
|
end
|
|
113
109
|
|
|
110
|
+
def do_request(endpoint, headers, body)
|
|
111
|
+
conn.start do |http|
|
|
112
|
+
request = ::Net::HTTP::Post.new(endpoint, headers)
|
|
113
|
+
request.body = body
|
|
114
|
+
http.request(request)
|
|
115
|
+
end
|
|
116
|
+
end
|
|
117
|
+
|
|
114
118
|
private
|
|
115
119
|
|
|
116
120
|
def has_rate_limited_header?(headers)
|
data/lib/sentry/transport.rb
CHANGED
|
@@ -113,7 +113,7 @@ module Sentry
|
|
|
113
113
|
|
|
114
114
|
def envelope_from_event(event)
|
|
115
115
|
# Convert to hash
|
|
116
|
-
event_payload = event.
|
|
116
|
+
event_payload = event.to_h
|
|
117
117
|
event_id = event_payload[:event_id] || event_payload["event_id"]
|
|
118
118
|
item_type = event_payload[:type] || event_payload["type"]
|
|
119
119
|
|
|
@@ -124,10 +124,7 @@ module Sentry
|
|
|
124
124
|
sent_at: Sentry.utc_now.iso8601
|
|
125
125
|
}
|
|
126
126
|
|
|
127
|
-
|
|
128
|
-
envelope_headers[:trace] = event.dynamic_sampling_context
|
|
129
|
-
end
|
|
130
|
-
|
|
127
|
+
envelope_headers[:trace] = event.dynamic_sampling_context if event.dynamic_sampling_context
|
|
131
128
|
envelope = Envelope.new(envelope_headers)
|
|
132
129
|
|
|
133
130
|
if event.is_a?(LogEvent)
|
|
@@ -223,3 +220,4 @@ end
|
|
|
223
220
|
require "sentry/transport/dummy_transport"
|
|
224
221
|
require "sentry/transport/http_transport"
|
|
225
222
|
require "sentry/transport/spotlight_transport"
|
|
223
|
+
require "sentry/transport/debug_transport"
|
|
@@ -3,6 +3,9 @@
|
|
|
3
3
|
module Sentry
|
|
4
4
|
module Utils
|
|
5
5
|
module EncodingHelper
|
|
6
|
+
EMPTY_STRING = ""
|
|
7
|
+
MALFORMED_STRING = "<malformed-string>"
|
|
8
|
+
|
|
6
9
|
def self.encode_to_utf_8(value)
|
|
7
10
|
if value.encoding != Encoding::UTF_8 && value.respond_to?(:force_encoding)
|
|
8
11
|
value = value.dup.force_encoding(Encoding::UTF_8)
|
|
@@ -17,6 +20,10 @@ module Sentry
|
|
|
17
20
|
|
|
18
21
|
value.dup.force_encoding(Encoding::UTF_8).valid_encoding?
|
|
19
22
|
end
|
|
23
|
+
|
|
24
|
+
def self.safe_utf_8_string(value)
|
|
25
|
+
valid_utf_8?(value) ? value : MALFORMED_STRING
|
|
26
|
+
end
|
|
20
27
|
end
|
|
21
28
|
end
|
|
22
29
|
end
|
|
@@ -3,27 +3,29 @@
|
|
|
3
3
|
module Sentry
|
|
4
4
|
# @private
|
|
5
5
|
module LoggingHelper
|
|
6
|
-
# @!visibility private
|
|
7
|
-
attr_reader :sdk_logger
|
|
8
|
-
|
|
9
6
|
# @!visibility private
|
|
10
7
|
def log_error(message, exception, debug: false)
|
|
11
8
|
message = "#{message}: #{exception.message}"
|
|
12
9
|
message += "\n#{exception.backtrace.join("\n")}" if debug
|
|
13
10
|
|
|
14
|
-
sdk_logger
|
|
11
|
+
sdk_logger&.error(LOGGER_PROGNAME) do
|
|
15
12
|
message
|
|
16
13
|
end
|
|
17
14
|
end
|
|
18
15
|
|
|
19
16
|
# @!visibility private
|
|
20
17
|
def log_debug(message)
|
|
21
|
-
sdk_logger
|
|
18
|
+
sdk_logger&.debug(LOGGER_PROGNAME) { message }
|
|
22
19
|
end
|
|
23
20
|
|
|
24
21
|
# @!visibility private
|
|
25
22
|
def log_warn(message)
|
|
26
|
-
sdk_logger
|
|
23
|
+
sdk_logger&.warn(LOGGER_PROGNAME) { message }
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
# @!visibility private
|
|
27
|
+
def sdk_logger
|
|
28
|
+
@sdk_logger ||= Sentry.sdk_logger
|
|
27
29
|
end
|
|
28
30
|
end
|
|
29
31
|
end
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Sentry
|
|
4
|
+
module Utils
|
|
5
|
+
class SampleRand
|
|
6
|
+
PRECISION = 1_000_000.0
|
|
7
|
+
FORMAT_PRECISION = 6
|
|
8
|
+
|
|
9
|
+
attr_reader :trace_id
|
|
10
|
+
|
|
11
|
+
def self.valid?(value)
|
|
12
|
+
return false unless value
|
|
13
|
+
value >= 0.0 && value < 1.0
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def self.format(value)
|
|
17
|
+
return unless value
|
|
18
|
+
|
|
19
|
+
truncated = (value * PRECISION).floor / PRECISION
|
|
20
|
+
"%.#{FORMAT_PRECISION}f" % truncated
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def initialize(trace_id: nil)
|
|
24
|
+
@trace_id = trace_id
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def generate_from_trace_id
|
|
28
|
+
(random_from_trace_id * PRECISION).floor / PRECISION
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def generate_from_sampling_decision(sampled, sample_rate)
|
|
32
|
+
if invalid_sample_rate?(sample_rate)
|
|
33
|
+
fallback_generation
|
|
34
|
+
else
|
|
35
|
+
generate_based_on_sampling(sampled, sample_rate)
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def generate_from_value(sample_rand_value)
|
|
40
|
+
parsed_value = parse_value(sample_rand_value)
|
|
41
|
+
|
|
42
|
+
if self.class.valid?(parsed_value)
|
|
43
|
+
parsed_value
|
|
44
|
+
else
|
|
45
|
+
fallback_generation
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
private
|
|
50
|
+
|
|
51
|
+
def random_from_trace_id
|
|
52
|
+
if @trace_id
|
|
53
|
+
Random.new(@trace_id[0, 16].to_i(16))
|
|
54
|
+
else
|
|
55
|
+
Random.new
|
|
56
|
+
end.rand(1.0)
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
def invalid_sample_rate?(sample_rate)
|
|
60
|
+
sample_rate.nil? || sample_rate <= 0.0 || sample_rate > 1.0
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
def fallback_generation
|
|
64
|
+
if @trace_id
|
|
65
|
+
(random_from_trace_id * PRECISION).floor / PRECISION
|
|
66
|
+
else
|
|
67
|
+
format_random(Random.rand(1.0))
|
|
68
|
+
end
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
def generate_based_on_sampling(sampled, sample_rate)
|
|
72
|
+
random = random_from_trace_id
|
|
73
|
+
|
|
74
|
+
result = if sampled
|
|
75
|
+
random * sample_rate
|
|
76
|
+
elsif sample_rate == 1.0
|
|
77
|
+
random
|
|
78
|
+
else
|
|
79
|
+
sample_rate + random * (1.0 - sample_rate)
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
format_random(result)
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
def format_random(value)
|
|
86
|
+
truncated = (value * PRECISION).floor / PRECISION
|
|
87
|
+
("%.#{FORMAT_PRECISION}f" % truncated).to_f
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
def parse_value(sample_rand_value)
|
|
91
|
+
Float(sample_rand_value)
|
|
92
|
+
rescue ArgumentError
|
|
93
|
+
nil
|
|
94
|
+
end
|
|
95
|
+
end
|
|
96
|
+
end
|
|
97
|
+
end
|
|
@@ -20,6 +20,7 @@ module Sentry
|
|
|
20
20
|
|
|
21
21
|
@profiling_enabled = defined?(Vernier) && configuration.profiling_enabled?
|
|
22
22
|
@profiles_sample_rate = configuration.profiles_sample_rate
|
|
23
|
+
@profiles_sample_interval = configuration.profiles_sample_interval
|
|
23
24
|
@project_root = configuration.project_root
|
|
24
25
|
@app_dirs_pattern = configuration.app_dirs_pattern
|
|
25
26
|
@in_app_pattern = Regexp.new("^(#{@project_root}/)?#{@app_dirs_pattern}")
|
|
@@ -56,7 +57,7 @@ module Sentry
|
|
|
56
57
|
return unless @sampled
|
|
57
58
|
return if @started
|
|
58
59
|
|
|
59
|
-
@started = ::Vernier.start_profile
|
|
60
|
+
@started = ::Vernier.start_profile(interval: @profiles_sample_interval)
|
|
60
61
|
|
|
61
62
|
log("Started")
|
|
62
63
|
|
|
@@ -90,9 +91,9 @@ module Sentry
|
|
|
90
91
|
Thread.current.object_id
|
|
91
92
|
end
|
|
92
93
|
|
|
93
|
-
def
|
|
94
|
+
def to_h
|
|
94
95
|
unless @sampled
|
|
95
|
-
record_lost_event(:sample_rate)
|
|
96
|
+
record_lost_event(:sample_rate) if @profiling_enabled
|
|
96
97
|
return EMPTY_RESULT
|
|
97
98
|
end
|
|
98
99
|
|
data/lib/sentry/version.rb
CHANGED