sentry-ruby 5.28.1 → 6.3.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 +25 -1
- data/README.md +2 -2
- 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 +57 -135
- data/lib/sentry/configuration.rb +117 -75
- 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/envelope/item.rb +3 -3
- data/lib/sentry/error_event.rb +3 -3
- data/lib/sentry/event.rb +4 -10
- data/lib/sentry/hub.rb +26 -4
- 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 +4 -4
- 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 +24 -142
- data/lib/sentry/log_event_buffer.rb +13 -60
- data/lib/sentry/metric_event.rb +49 -0
- data/lib/sentry/metric_event_buffer.rb +28 -0
- data/lib/sentry/metrics.rb +47 -54
- data/lib/sentry/profiler.rb +4 -5
- data/lib/sentry/rack/capture_exceptions.rb +5 -1
- data/lib/sentry/rspec.rb +1 -1
- data/lib/sentry/scope.rb +50 -18
- data/lib/sentry/sequel.rb +35 -0
- data/lib/sentry/span.rb +2 -17
- data/lib/sentry/std_lib_logger.rb +4 -0
- data/lib/sentry/telemetry_event_buffer.rb +130 -0
- data/lib/sentry/test_helper.rb +8 -0
- data/lib/sentry/transaction.rb +52 -103
- data/lib/sentry/transaction_event.rb +4 -9
- data/lib/sentry/transport.rb +2 -5
- data/lib/sentry/utils/encoding_helper.rb +6 -0
- data/lib/sentry/utils/logging_helper.rb +25 -9
- data/lib/sentry/utils/telemetry_attributes.rb +30 -0
- data/lib/sentry/vernier/profiler.rb +4 -3
- data/lib/sentry/version.rb +1 -1
- data/lib/sentry-ruby.rb +53 -30
- data/sentry-ruby-core.gemspec +1 -1
- data/sentry-ruby.gemspec +2 -1
- metadata +27 -16
- data/lib/sentry/metrics/aggregator.rb +0 -248
- data/lib/sentry/metrics/configuration.rb +0 -57
- 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/span.rb
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
require "securerandom"
|
|
4
|
-
require "sentry/metrics/local_aggregator"
|
|
5
4
|
require "sentry/utils/uuid"
|
|
6
5
|
|
|
7
6
|
module Sentry
|
|
@@ -173,8 +172,8 @@ module Sentry
|
|
|
173
172
|
end
|
|
174
173
|
|
|
175
174
|
# @return [Hash]
|
|
176
|
-
def
|
|
177
|
-
|
|
175
|
+
def to_h
|
|
176
|
+
{
|
|
178
177
|
trace_id: @trace_id,
|
|
179
178
|
span_id: @span_id,
|
|
180
179
|
parent_span_id: @parent_span_id,
|
|
@@ -187,11 +186,6 @@ module Sentry
|
|
|
187
186
|
data: @data,
|
|
188
187
|
origin: @origin
|
|
189
188
|
}
|
|
190
|
-
|
|
191
|
-
summary = metrics_summary
|
|
192
|
-
hash[:_metrics_summary] = summary if summary
|
|
193
|
-
|
|
194
|
-
hash
|
|
195
189
|
end
|
|
196
190
|
|
|
197
191
|
# Returns the span's context that can be used to embed in an Event.
|
|
@@ -307,14 +301,5 @@ module Sentry
|
|
|
307
301
|
def set_origin(origin)
|
|
308
302
|
@origin = origin
|
|
309
303
|
end
|
|
310
|
-
|
|
311
|
-
# Collects gauge metrics on the span for metric summaries.
|
|
312
|
-
def metrics_local_aggregator
|
|
313
|
-
@metrics_local_aggregator ||= Sentry::Metrics::LocalAggregator.new
|
|
314
|
-
end
|
|
315
|
-
|
|
316
|
-
def metrics_summary
|
|
317
|
-
@metrics_local_aggregator&.to_hash
|
|
318
|
-
end
|
|
319
304
|
end
|
|
320
305
|
end
|
|
@@ -37,6 +37,10 @@ module Sentry
|
|
|
37
37
|
message = message.to_s.strip
|
|
38
38
|
|
|
39
39
|
if !message.nil? && message != Sentry::Logger::PROGNAME && method = SEVERITY_MAP[severity]
|
|
40
|
+
if (filter = Sentry.configuration.std_lib_logger_filter) && !filter.call(self, message, method)
|
|
41
|
+
return result
|
|
42
|
+
end
|
|
43
|
+
|
|
40
44
|
Sentry.logger.send(method, message, origin: ORIGIN)
|
|
41
45
|
end
|
|
42
46
|
end
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "sentry/threaded_periodic_worker"
|
|
4
|
+
require "sentry/envelope"
|
|
5
|
+
|
|
6
|
+
module Sentry
|
|
7
|
+
# TelemetryEventBuffer is a base class for buffering telemetry events (logs, metrics, etc.)
|
|
8
|
+
# and sending them to Sentry in a single envelope.
|
|
9
|
+
#
|
|
10
|
+
# This is used internally by the `Sentry::Client`.
|
|
11
|
+
#
|
|
12
|
+
# @!visibility private
|
|
13
|
+
class TelemetryEventBuffer < ThreadedPeriodicWorker
|
|
14
|
+
FLUSH_INTERVAL = 5 # seconds
|
|
15
|
+
|
|
16
|
+
# @!visibility private
|
|
17
|
+
attr_reader :pending_items, :envelope_type, :data_category, :thread
|
|
18
|
+
|
|
19
|
+
def initialize(configuration, client, event_class:, max_items:, max_items_before_drop:, envelope_type:, envelope_content_type:, before_send:)
|
|
20
|
+
super(configuration.sdk_logger, FLUSH_INTERVAL)
|
|
21
|
+
|
|
22
|
+
@client = client
|
|
23
|
+
@dsn = configuration.dsn
|
|
24
|
+
@debug = configuration.debug
|
|
25
|
+
@event_class = event_class
|
|
26
|
+
@max_items = max_items
|
|
27
|
+
@max_items_before_drop = max_items_before_drop
|
|
28
|
+
@envelope_type = envelope_type
|
|
29
|
+
@data_category = Sentry::Envelope::Item.data_category(@envelope_type)
|
|
30
|
+
@envelope_content_type = envelope_content_type
|
|
31
|
+
@before_send = before_send
|
|
32
|
+
|
|
33
|
+
@pending_items = []
|
|
34
|
+
@mutex = Mutex.new
|
|
35
|
+
|
|
36
|
+
log_debug("[#{self.class}] Initialized buffer with max_items=#{@max_items}, flush_interval=#{FLUSH_INTERVAL}s")
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def flush
|
|
40
|
+
@mutex.synchronize do
|
|
41
|
+
return if empty?
|
|
42
|
+
|
|
43
|
+
log_debug("[#{self.class}] flushing #{size} #{@event_class}")
|
|
44
|
+
|
|
45
|
+
send_items
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
self
|
|
49
|
+
end
|
|
50
|
+
alias_method :run, :flush
|
|
51
|
+
|
|
52
|
+
def add_item(item)
|
|
53
|
+
@mutex.synchronize do
|
|
54
|
+
return unless ensure_thread
|
|
55
|
+
|
|
56
|
+
if size >= @max_items_before_drop
|
|
57
|
+
log_debug("[#{self.class}] exceeded max capacity, dropping event")
|
|
58
|
+
@client.transport.record_lost_event(:queue_overflow, @data_category)
|
|
59
|
+
else
|
|
60
|
+
@pending_items << item
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
send_items if size >= @max_items
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
self
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
def empty?
|
|
70
|
+
@pending_items.empty?
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
def size
|
|
74
|
+
@pending_items.size
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
def clear!
|
|
78
|
+
@pending_items.clear
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
private
|
|
82
|
+
|
|
83
|
+
def send_items
|
|
84
|
+
envelope = Envelope.new(
|
|
85
|
+
event_id: Sentry::Utils.uuid,
|
|
86
|
+
sent_at: Sentry.utc_now.iso8601,
|
|
87
|
+
dsn: @dsn,
|
|
88
|
+
sdk: Sentry.sdk_meta
|
|
89
|
+
)
|
|
90
|
+
|
|
91
|
+
discarded_count = 0
|
|
92
|
+
envelope_items = []
|
|
93
|
+
|
|
94
|
+
if @before_send
|
|
95
|
+
@pending_items.each do |item|
|
|
96
|
+
processed_item = @before_send.call(item)
|
|
97
|
+
|
|
98
|
+
if processed_item
|
|
99
|
+
envelope_items << processed_item.to_h
|
|
100
|
+
else
|
|
101
|
+
discarded_count += 1
|
|
102
|
+
end
|
|
103
|
+
end
|
|
104
|
+
else
|
|
105
|
+
envelope_items = @pending_items.map(&:to_h)
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
unless discarded_count.zero?
|
|
109
|
+
@client.transport.record_lost_event(:before_send, @data_category, num: discarded_count)
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
return if envelope_items.empty?
|
|
113
|
+
|
|
114
|
+
envelope.add_item(
|
|
115
|
+
{
|
|
116
|
+
type: @envelope_type,
|
|
117
|
+
item_count: envelope_items.size,
|
|
118
|
+
content_type: @envelope_content_type
|
|
119
|
+
},
|
|
120
|
+
{ items: envelope_items }
|
|
121
|
+
)
|
|
122
|
+
|
|
123
|
+
@client.send_envelope(envelope)
|
|
124
|
+
rescue => e
|
|
125
|
+
log_error("[#{self.class}] Failed to send #{@event_class}", e, debug: @debug)
|
|
126
|
+
ensure
|
|
127
|
+
clear!
|
|
128
|
+
end
|
|
129
|
+
end
|
|
130
|
+
end
|
data/lib/sentry/test_helper.rb
CHANGED
|
@@ -27,6 +27,7 @@ module Sentry
|
|
|
27
27
|
# set transport to DummyTransport, so we can easily intercept the captured events
|
|
28
28
|
dummy_config.transport.transport_class = Sentry::DummyTransport
|
|
29
29
|
# make sure SDK allows sending under the current environment
|
|
30
|
+
dummy_config.enabled_environments ||= []
|
|
30
31
|
dummy_config.enabled_environments += [dummy_config.environment] unless dummy_config.enabled_environments.include?(dummy_config.environment)
|
|
31
32
|
# disble async event sending
|
|
32
33
|
dummy_config.background_worker_threads = 0
|
|
@@ -101,6 +102,13 @@ module Sentry
|
|
|
101
102
|
.flat_map { |item| item.payload[:items] }
|
|
102
103
|
end
|
|
103
104
|
|
|
105
|
+
def sentry_metrics
|
|
106
|
+
sentry_envelopes
|
|
107
|
+
.flat_map(&:items)
|
|
108
|
+
.select { |item| item.headers[:type] == "trace_metric" }
|
|
109
|
+
.flat_map { |item| item.payload[:items] }
|
|
110
|
+
end
|
|
111
|
+
|
|
104
112
|
# Returns the last captured event object.
|
|
105
113
|
# @return [Event, nil]
|
|
106
114
|
def last_sentry_event
|
data/lib/sentry/transaction.rb
CHANGED
|
@@ -7,9 +7,6 @@ require "sentry/propagation_context"
|
|
|
7
7
|
|
|
8
8
|
module Sentry
|
|
9
9
|
class Transaction < Span
|
|
10
|
-
# @deprecated Use Sentry::PropagationContext::SENTRY_TRACE_REGEXP instead.
|
|
11
|
-
SENTRY_TRACE_REGEXP = PropagationContext::SENTRY_TRACE_REGEXP
|
|
12
|
-
|
|
13
10
|
UNLABELD_NAME = "<unlabeled transaction>"
|
|
14
11
|
MESSAGE_PREFIX = "[Tracing]"
|
|
15
12
|
|
|
@@ -40,12 +37,6 @@ module Sentry
|
|
|
40
37
|
# @return [Hash]
|
|
41
38
|
attr_reader :measurements
|
|
42
39
|
|
|
43
|
-
# @deprecated Use Sentry.get_current_hub instead.
|
|
44
|
-
attr_reader :hub
|
|
45
|
-
|
|
46
|
-
# @deprecated Use Sentry.configuration instead.
|
|
47
|
-
attr_reader :configuration
|
|
48
|
-
|
|
49
40
|
# The effective sample rate at which this transaction was sampled.
|
|
50
41
|
# @return [Float, nil]
|
|
51
42
|
attr_reader :effective_sample_rate
|
|
@@ -63,7 +54,6 @@ module Sentry
|
|
|
63
54
|
attr_reader :sample_rand
|
|
64
55
|
|
|
65
56
|
def initialize(
|
|
66
|
-
hub:,
|
|
67
57
|
name: nil,
|
|
68
58
|
source: :custom,
|
|
69
59
|
parent_sampled: nil,
|
|
@@ -75,26 +65,14 @@ module Sentry
|
|
|
75
65
|
|
|
76
66
|
set_name(name, source: source)
|
|
77
67
|
@parent_sampled = parent_sampled
|
|
78
|
-
@hub = hub
|
|
79
68
|
@baggage = baggage
|
|
80
|
-
@configuration = hub.configuration # to be removed
|
|
81
|
-
@tracing_enabled = hub.configuration.tracing_enabled?
|
|
82
|
-
@traces_sampler = hub.configuration.traces_sampler
|
|
83
|
-
@traces_sample_rate = hub.configuration.traces_sample_rate
|
|
84
|
-
@sdk_logger = hub.configuration.sdk_logger
|
|
85
|
-
@release = hub.configuration.release
|
|
86
|
-
@environment = hub.configuration.environment
|
|
87
|
-
@dsn = hub.configuration.dsn
|
|
88
69
|
@effective_sample_rate = nil
|
|
89
70
|
@contexts = {}
|
|
90
71
|
@measurements = {}
|
|
91
72
|
@sample_rand = sample_rand
|
|
92
73
|
|
|
93
|
-
unless @hub.profiler_running?
|
|
94
|
-
@profiler = @configuration.profiler_class.new(@configuration)
|
|
95
|
-
end
|
|
96
|
-
|
|
97
74
|
init_span_recorder
|
|
75
|
+
init_profiler
|
|
98
76
|
|
|
99
77
|
unless @sample_rand
|
|
100
78
|
generator = Utils::SampleRand.new(trace_id: @trace_id)
|
|
@@ -102,65 +80,8 @@ module Sentry
|
|
|
102
80
|
end
|
|
103
81
|
end
|
|
104
82
|
|
|
105
|
-
# @deprecated use Sentry.continue_trace instead.
|
|
106
|
-
#
|
|
107
|
-
# Initalizes a Transaction instance with a Sentry trace string from another transaction (usually from an external request).
|
|
108
|
-
#
|
|
109
|
-
# The original transaction will become the parent of the new Transaction instance. And they will share the same `trace_id`.
|
|
110
|
-
#
|
|
111
|
-
# The child transaction will also store the parent's sampling decision in its `parent_sampled` attribute.
|
|
112
|
-
# @param sentry_trace [String] the trace string from the previous transaction.
|
|
113
|
-
# @param baggage [String, nil] the incoming baggage header string.
|
|
114
|
-
# @param hub [Hub] the hub that'll be responsible for sending this transaction when it's finished.
|
|
115
|
-
# @param options [Hash] the options you want to use to initialize a Transaction instance.
|
|
116
|
-
# @return [Transaction, nil]
|
|
117
|
-
def self.from_sentry_trace(sentry_trace, baggage: nil, hub: Sentry.get_current_hub, **options)
|
|
118
|
-
return unless hub.configuration.tracing_enabled?
|
|
119
|
-
return unless sentry_trace
|
|
120
|
-
|
|
121
|
-
sentry_trace_data = extract_sentry_trace(sentry_trace)
|
|
122
|
-
return unless sentry_trace_data
|
|
123
|
-
|
|
124
|
-
trace_id, parent_span_id, parent_sampled = sentry_trace_data
|
|
125
|
-
|
|
126
|
-
baggage =
|
|
127
|
-
if baggage && !baggage.empty?
|
|
128
|
-
Baggage.from_incoming_header(baggage)
|
|
129
|
-
else
|
|
130
|
-
# If there's an incoming sentry-trace but no incoming baggage header,
|
|
131
|
-
# for instance in traces coming from older SDKs,
|
|
132
|
-
# baggage will be empty and frozen and won't be populated as head SDK.
|
|
133
|
-
Baggage.new({})
|
|
134
|
-
end
|
|
135
|
-
|
|
136
|
-
baggage.freeze!
|
|
137
|
-
|
|
138
|
-
sample_rand = extract_sample_rand_from_baggage(baggage, trace_id, parent_sampled)
|
|
139
|
-
|
|
140
|
-
new(
|
|
141
|
-
trace_id: trace_id,
|
|
142
|
-
parent_span_id: parent_span_id,
|
|
143
|
-
parent_sampled: parent_sampled,
|
|
144
|
-
hub: hub,
|
|
145
|
-
baggage: baggage,
|
|
146
|
-
sample_rand: sample_rand,
|
|
147
|
-
**options
|
|
148
|
-
)
|
|
149
|
-
end
|
|
150
|
-
|
|
151
|
-
# @deprecated Use Sentry::PropagationContext.extract_sentry_trace instead.
|
|
152
|
-
# @return [Array, nil]
|
|
153
|
-
def self.extract_sentry_trace(sentry_trace)
|
|
154
|
-
PropagationContext.extract_sentry_trace(sentry_trace)
|
|
155
|
-
end
|
|
156
|
-
|
|
157
|
-
def self.extract_sample_rand_from_baggage(baggage, trace_id, parent_sampled)
|
|
158
|
-
PropagationContext.extract_sample_rand_from_baggage(baggage, trace_id) ||
|
|
159
|
-
PropagationContext.generate_sample_rand(baggage, trace_id, parent_sampled)
|
|
160
|
-
end
|
|
161
|
-
|
|
162
83
|
# @return [Hash]
|
|
163
|
-
def
|
|
84
|
+
def to_h
|
|
164
85
|
hash = super
|
|
165
86
|
|
|
166
87
|
hash.merge!(
|
|
@@ -207,7 +128,9 @@ module Sentry
|
|
|
207
128
|
# @param sampling_context [Hash] a context Hash that'll be passed to `traces_sampler` (if provided).
|
|
208
129
|
# @return [void]
|
|
209
130
|
def set_initial_sample_decision(sampling_context:)
|
|
210
|
-
|
|
131
|
+
configuration = Sentry.configuration
|
|
132
|
+
|
|
133
|
+
unless configuration && configuration.tracing_enabled?
|
|
211
134
|
@sampled = false
|
|
212
135
|
return
|
|
213
136
|
end
|
|
@@ -218,12 +141,12 @@ module Sentry
|
|
|
218
141
|
end
|
|
219
142
|
|
|
220
143
|
sample_rate =
|
|
221
|
-
if
|
|
222
|
-
|
|
144
|
+
if configuration.traces_sampler.is_a?(Proc)
|
|
145
|
+
configuration.traces_sampler.call(sampling_context)
|
|
223
146
|
elsif !sampling_context[:parent_sampled].nil?
|
|
224
147
|
sampling_context[:parent_sampled]
|
|
225
148
|
else
|
|
226
|
-
|
|
149
|
+
configuration.traces_sample_rate
|
|
227
150
|
end
|
|
228
151
|
|
|
229
152
|
transaction_description = generate_transaction_description
|
|
@@ -265,29 +188,28 @@ module Sentry
|
|
|
265
188
|
end
|
|
266
189
|
|
|
267
190
|
# Finishes the transaction's recording and send it to Sentry.
|
|
268
|
-
# @param hub [Hub] the hub that'll send this transaction. (Deprecated)
|
|
269
191
|
# @return [TransactionEvent]
|
|
270
|
-
def finish(
|
|
271
|
-
if hub
|
|
272
|
-
log_warn(
|
|
273
|
-
<<~MSG
|
|
274
|
-
Specifying a different hub in `Transaction#finish` will be deprecated in version 5.0.
|
|
275
|
-
Please use `Hub#start_transaction` with the designated hub.
|
|
276
|
-
MSG
|
|
277
|
-
)
|
|
278
|
-
end
|
|
279
|
-
|
|
280
|
-
hub ||= @hub
|
|
281
|
-
|
|
192
|
+
def finish(end_timestamp: nil)
|
|
282
193
|
super(end_timestamp: end_timestamp)
|
|
283
194
|
|
|
284
195
|
if @name.nil?
|
|
285
196
|
@name = UNLABELD_NAME
|
|
286
197
|
end
|
|
287
198
|
|
|
288
|
-
|
|
199
|
+
hub = Sentry.get_current_hub
|
|
200
|
+
return unless hub
|
|
289
201
|
|
|
290
|
-
|
|
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
|
|
291
213
|
event = hub.current_client.event_from_transaction(self)
|
|
292
214
|
hub.capture_event(event)
|
|
293
215
|
else
|
|
@@ -345,6 +267,15 @@ module Sentry
|
|
|
345
267
|
@span_recorder.add(self)
|
|
346
268
|
end
|
|
347
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
|
+
|
|
348
279
|
private
|
|
349
280
|
|
|
350
281
|
def generate_transaction_description
|
|
@@ -355,14 +286,16 @@ module Sentry
|
|
|
355
286
|
end
|
|
356
287
|
|
|
357
288
|
def populate_head_baggage
|
|
289
|
+
configuration = Sentry.configuration
|
|
290
|
+
|
|
358
291
|
items = {
|
|
359
292
|
"trace_id" => trace_id,
|
|
360
293
|
"sample_rate" => effective_sample_rate&.to_s,
|
|
361
294
|
"sample_rand" => Utils::SampleRand.format(@sample_rand),
|
|
362
295
|
"sampled" => sampled&.to_s,
|
|
363
|
-
"environment" =>
|
|
364
|
-
"release" =>
|
|
365
|
-
"public_key" =>
|
|
296
|
+
"environment" => configuration&.environment,
|
|
297
|
+
"release" => configuration&.release,
|
|
298
|
+
"public_key" => configuration&.dsn&.public_key
|
|
366
299
|
}
|
|
367
300
|
|
|
368
301
|
items["transaction"] = name unless source_low_quality?
|
|
@@ -371,6 +304,22 @@ module Sentry
|
|
|
371
304
|
@baggage = Baggage.new(items, mutable: false)
|
|
372
305
|
end
|
|
373
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
|
+
|
|
374
323
|
class SpanRecorder
|
|
375
324
|
attr_reader :max_length, :spans
|
|
376
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
|
|
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)
|
|
@@ -3,6 +3,8 @@
|
|
|
3
3
|
module Sentry
|
|
4
4
|
module Utils
|
|
5
5
|
module EncodingHelper
|
|
6
|
+
MALFORMED_STRING = "<malformed-string>"
|
|
7
|
+
|
|
6
8
|
def self.encode_to_utf_8(value)
|
|
7
9
|
if value.encoding != Encoding::UTF_8 && value.respond_to?(:force_encoding)
|
|
8
10
|
value = value.dup.force_encoding(Encoding::UTF_8)
|
|
@@ -17,6 +19,10 @@ module Sentry
|
|
|
17
19
|
|
|
18
20
|
value.dup.force_encoding(Encoding::UTF_8).valid_encoding?
|
|
19
21
|
end
|
|
22
|
+
|
|
23
|
+
def self.safe_utf_8_string(value)
|
|
24
|
+
valid_utf_8?(value) ? value : MALFORMED_STRING
|
|
25
|
+
end
|
|
20
26
|
end
|
|
21
27
|
end
|
|
22
28
|
end
|
|
@@ -3,27 +3,43 @@
|
|
|
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
|
-
message += "\n#{exception.backtrace.join("\n")}" if debug
|
|
9
|
+
message += "\n#{exception.backtrace.join("\n")}" if debug && exception.backtrace
|
|
13
10
|
|
|
14
|
-
sdk_logger
|
|
15
|
-
|
|
16
|
-
|
|
11
|
+
sdk_logger&.error(LOGGER_PROGNAME) { message }
|
|
12
|
+
rescue StandardError => e
|
|
13
|
+
log_to_stderr(e, message)
|
|
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 }
|
|
19
|
+
rescue StandardError => e
|
|
20
|
+
log_to_stderr(e, message)
|
|
22
21
|
end
|
|
23
22
|
|
|
24
23
|
# @!visibility private
|
|
25
24
|
def log_warn(message)
|
|
26
|
-
sdk_logger
|
|
25
|
+
sdk_logger&.warn(LOGGER_PROGNAME) { message }
|
|
26
|
+
rescue StandardError => e
|
|
27
|
+
log_to_stderr(e, message)
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
# @!visibility private
|
|
31
|
+
def sdk_logger
|
|
32
|
+
@sdk_logger ||= Sentry.sdk_logger
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
# @!visibility private
|
|
36
|
+
def log_to_stderr(error, message)
|
|
37
|
+
error_msg = "Sentry SDK logging failed (#{error.class}: #{error.message}): #{message}".scrub(%q(<?>))
|
|
38
|
+
error_msg += "\n#{error.backtrace.map { |line| line.scrub(%q(<?>)) }.join("\n")}" if error.backtrace
|
|
39
|
+
|
|
40
|
+
$stderr.puts(error_msg)
|
|
41
|
+
rescue StandardError
|
|
42
|
+
# swallow everything – logging must never crash the app
|
|
27
43
|
end
|
|
28
44
|
end
|
|
29
45
|
end
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "json"
|
|
4
|
+
|
|
5
|
+
module Sentry
|
|
6
|
+
module Utils
|
|
7
|
+
module TelemetryAttributes
|
|
8
|
+
private
|
|
9
|
+
|
|
10
|
+
def attribute_hash(value)
|
|
11
|
+
case value
|
|
12
|
+
when String
|
|
13
|
+
{ value: value, type: "string" }
|
|
14
|
+
when TrueClass, FalseClass
|
|
15
|
+
{ value: value, type: "boolean" }
|
|
16
|
+
when Integer
|
|
17
|
+
{ value: value, type: "integer" }
|
|
18
|
+
when Float
|
|
19
|
+
{ value: value, type: "double" }
|
|
20
|
+
else
|
|
21
|
+
begin
|
|
22
|
+
{ value: JSON.generate(value), type: "string" }
|
|
23
|
+
rescue
|
|
24
|
+
{ value: value, type: "string" }
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
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