sentry-ruby 5.10.0 → 5.26.0
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/.rspec +3 -1
- data/Gemfile +12 -13
- data/README.md +26 -11
- data/Rakefile +9 -11
- data/bin/console +2 -0
- data/lib/sentry/attachment.rb +40 -0
- data/lib/sentry/background_worker.rb +11 -5
- data/lib/sentry/backpressure_monitor.rb +45 -0
- data/lib/sentry/backtrace.rb +12 -9
- data/lib/sentry/baggage.rb +7 -7
- data/lib/sentry/breadcrumb/sentry_logger.rb +6 -6
- data/lib/sentry/breadcrumb.rb +13 -6
- data/lib/sentry/check_in_event.rb +61 -0
- data/lib/sentry/client.rb +214 -25
- data/lib/sentry/configuration.rb +221 -38
- data/lib/sentry/core_ext/object/deep_dup.rb +1 -1
- data/lib/sentry/cron/configuration.rb +23 -0
- data/lib/sentry/cron/monitor_check_ins.rb +77 -0
- data/lib/sentry/cron/monitor_config.rb +53 -0
- data/lib/sentry/cron/monitor_schedule.rb +42 -0
- data/lib/sentry/dsn.rb +4 -4
- data/lib/sentry/envelope/item.rb +88 -0
- data/lib/sentry/envelope.rb +2 -68
- data/lib/sentry/error_event.rb +2 -2
- data/lib/sentry/event.rb +28 -47
- data/lib/sentry/excon/middleware.rb +77 -0
- data/lib/sentry/excon.rb +10 -0
- data/lib/sentry/faraday.rb +77 -0
- data/lib/sentry/graphql.rb +9 -0
- data/lib/sentry/hub.rb +138 -6
- data/lib/sentry/integrable.rb +10 -0
- data/lib/sentry/interface.rb +1 -0
- data/lib/sentry/interfaces/exception.rb +5 -3
- data/lib/sentry/interfaces/mechanism.rb +20 -0
- data/lib/sentry/interfaces/request.rb +8 -8
- data/lib/sentry/interfaces/single_exception.rb +13 -9
- data/lib/sentry/interfaces/stacktrace.rb +3 -1
- data/lib/sentry/interfaces/stacktrace_builder.rb +23 -2
- data/lib/sentry/linecache.rb +3 -3
- data/lib/sentry/log_event.rb +206 -0
- data/lib/sentry/log_event_buffer.rb +75 -0
- data/lib/sentry/logger.rb +1 -1
- data/lib/sentry/metrics/aggregator.rb +248 -0
- data/lib/sentry/metrics/configuration.rb +47 -0
- data/lib/sentry/metrics/counter_metric.rb +25 -0
- data/lib/sentry/metrics/distribution_metric.rb +25 -0
- data/lib/sentry/metrics/gauge_metric.rb +35 -0
- data/lib/sentry/metrics/local_aggregator.rb +53 -0
- data/lib/sentry/metrics/metric.rb +19 -0
- data/lib/sentry/metrics/set_metric.rb +28 -0
- data/lib/sentry/metrics/timing.rb +51 -0
- data/lib/sentry/metrics.rb +56 -0
- data/lib/sentry/net/http.rb +27 -44
- data/lib/sentry/profiler/helpers.rb +46 -0
- data/lib/sentry/profiler.rb +41 -60
- data/lib/sentry/propagation_context.rb +135 -0
- data/lib/sentry/puma.rb +12 -5
- data/lib/sentry/rack/capture_exceptions.rb +17 -8
- data/lib/sentry/rack.rb +2 -2
- data/lib/sentry/rake.rb +4 -15
- data/lib/sentry/redis.rb +10 -4
- data/lib/sentry/release_detector.rb +5 -5
- data/lib/sentry/rspec.rb +91 -0
- data/lib/sentry/scope.rb +75 -39
- data/lib/sentry/session.rb +2 -2
- data/lib/sentry/session_flusher.rb +15 -43
- data/lib/sentry/span.rb +92 -8
- data/lib/sentry/std_lib_logger.rb +50 -0
- data/lib/sentry/structured_logger.rb +138 -0
- data/lib/sentry/test_helper.rb +42 -13
- data/lib/sentry/threaded_periodic_worker.rb +39 -0
- data/lib/sentry/transaction.rb +44 -43
- data/lib/sentry/transaction_event.rb +10 -6
- data/lib/sentry/transport/configuration.rb +73 -1
- data/lib/sentry/transport/http_transport.rb +71 -41
- data/lib/sentry/transport/spotlight_transport.rb +50 -0
- data/lib/sentry/transport.rb +53 -49
- data/lib/sentry/utils/argument_checking_helper.rb +12 -0
- data/lib/sentry/utils/env_helper.rb +21 -0
- data/lib/sentry/utils/http_tracing.rb +74 -0
- data/lib/sentry/utils/logging_helper.rb +10 -7
- data/lib/sentry/utils/real_ip.rb +2 -2
- data/lib/sentry/utils/request_id.rb +1 -1
- data/lib/sentry/utils/uuid.rb +13 -0
- data/lib/sentry/vernier/output.rb +89 -0
- data/lib/sentry/vernier/profiler.rb +132 -0
- data/lib/sentry/version.rb +1 -1
- data/lib/sentry-ruby.rb +206 -35
- data/sentry-ruby-core.gemspec +3 -1
- data/sentry-ruby.gemspec +15 -6
- metadata +61 -11
data/lib/sentry/client.rb
CHANGED
@@ -1,6 +1,9 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require "sentry/transport"
|
4
|
+
require "sentry/log_event"
|
5
|
+
require "sentry/log_event_buffer"
|
6
|
+
require "sentry/utils/uuid"
|
4
7
|
|
5
8
|
module Sentry
|
6
9
|
class Client
|
@@ -10,28 +13,38 @@ module Sentry
|
|
10
13
|
# @return [Transport]
|
11
14
|
attr_reader :transport
|
12
15
|
|
16
|
+
# The Transport object that'll send events for the client.
|
17
|
+
# @return [SpotlightTransport, nil]
|
18
|
+
attr_reader :spotlight_transport
|
19
|
+
|
20
|
+
# @!visibility private
|
21
|
+
attr_reader :log_event_buffer
|
22
|
+
|
13
23
|
# @!macro configuration
|
14
24
|
attr_reader :configuration
|
15
25
|
|
16
|
-
# @deprecated Use Sentry.logger to retrieve the current logger instead.
|
17
|
-
attr_reader :logger
|
18
|
-
|
19
26
|
# @param configuration [Configuration]
|
20
27
|
def initialize(configuration)
|
21
28
|
@configuration = configuration
|
22
|
-
@
|
29
|
+
@sdk_logger = configuration.sdk_logger
|
23
30
|
|
24
31
|
if transport_class = configuration.transport.transport_class
|
25
32
|
@transport = transport_class.new(configuration)
|
26
33
|
else
|
27
34
|
@transport =
|
28
35
|
case configuration.dsn&.scheme
|
29
|
-
when
|
36
|
+
when "http", "https"
|
30
37
|
HTTPTransport.new(configuration)
|
31
38
|
else
|
32
39
|
DummyTransport.new(configuration)
|
33
40
|
end
|
34
41
|
end
|
42
|
+
|
43
|
+
@spotlight_transport = SpotlightTransport.new(configuration) if configuration.spotlight
|
44
|
+
|
45
|
+
if configuration.enable_logs
|
46
|
+
@log_event_buffer = LogEventBuffer.new(configuration, self).start
|
47
|
+
end
|
35
48
|
end
|
36
49
|
|
37
50
|
# Applies the given scope's data to the event and sends it to Sentry.
|
@@ -42,25 +55,36 @@ module Sentry
|
|
42
55
|
def capture_event(event, scope, hint = {})
|
43
56
|
return unless configuration.sending_allowed?
|
44
57
|
|
45
|
-
|
46
|
-
transport.record_lost_event(:sample_rate,
|
58
|
+
if event.is_a?(ErrorEvent) && !configuration.sample_allowed?
|
59
|
+
transport.record_lost_event(:sample_rate, "error")
|
47
60
|
return
|
48
61
|
end
|
49
62
|
|
50
63
|
event_type = event.is_a?(Event) ? event.type : event["type"]
|
64
|
+
data_category = Envelope::Item.data_category(event_type)
|
65
|
+
|
66
|
+
is_transaction = event.is_a?(TransactionEvent)
|
67
|
+
spans_before = is_transaction ? event.spans.size : 0
|
68
|
+
|
51
69
|
event = scope.apply_to_event(event, hint)
|
52
70
|
|
53
71
|
if event.nil?
|
54
|
-
|
55
|
-
transport.record_lost_event(:event_processor,
|
72
|
+
log_debug("Discarded event because one of the event processors returned nil")
|
73
|
+
transport.record_lost_event(:event_processor, data_category)
|
74
|
+
transport.record_lost_event(:event_processor, "span", num: spans_before + 1) if is_transaction
|
56
75
|
return
|
76
|
+
elsif is_transaction
|
77
|
+
spans_delta = spans_before - event.spans.size
|
78
|
+
transport.record_lost_event(:event_processor, "span", num: spans_delta) if spans_delta > 0
|
57
79
|
end
|
58
80
|
|
59
81
|
if async_block = configuration.async
|
60
82
|
dispatch_async_event(async_block, event, hint)
|
61
83
|
elsif configuration.background_worker_threads != 0 && hint.fetch(:background, true)
|
62
|
-
|
63
|
-
|
84
|
+
unless dispatch_background_event(event, hint)
|
85
|
+
transport.record_lost_event(:queue_overflow, data_category)
|
86
|
+
transport.record_lost_event(:queue_overflow, "span", num: spans_before + 1) if is_transaction
|
87
|
+
end
|
64
88
|
else
|
65
89
|
send_event(event, hint)
|
66
90
|
end
|
@@ -71,6 +95,30 @@ module Sentry
|
|
71
95
|
nil
|
72
96
|
end
|
73
97
|
|
98
|
+
# Buffer a log event to be sent later with other logs in a single envelope
|
99
|
+
# @param event [LogEvent] the log event to be buffered
|
100
|
+
# @return [LogEvent]
|
101
|
+
def buffer_log_event(event, scope)
|
102
|
+
return unless event.is_a?(LogEvent)
|
103
|
+
@log_event_buffer.add_event(scope.apply_to_event(event))
|
104
|
+
event
|
105
|
+
end
|
106
|
+
|
107
|
+
# Capture an envelope directly.
|
108
|
+
# @param envelope [Envelope] the envelope to be captured.
|
109
|
+
# @return [void]
|
110
|
+
def capture_envelope(envelope)
|
111
|
+
Sentry.background_worker.perform { send_envelope(envelope) }
|
112
|
+
end
|
113
|
+
|
114
|
+
# Flush pending events to Sentry.
|
115
|
+
# @return [void]
|
116
|
+
def flush
|
117
|
+
transport.flush if configuration.sending_to_dsn_allowed?
|
118
|
+
spotlight_transport.flush if spotlight_transport
|
119
|
+
@log_event_buffer&.flush
|
120
|
+
end
|
121
|
+
|
74
122
|
# Initializes an Event object with the given exception. Returns `nil` if the exception's class is excluded from reporting.
|
75
123
|
# @param exception [Exception] the exception to be reported.
|
76
124
|
# @param hint [Hash] the hint data that'll be passed to `before_send` callback and the scope's event processors.
|
@@ -82,9 +130,10 @@ module Sentry
|
|
82
130
|
return if !ignore_exclusions && !@configuration.exception_class_allowed?(exception)
|
83
131
|
|
84
132
|
integration_meta = Sentry.integrations[hint[:integration]]
|
133
|
+
mechanism = hint.delete(:mechanism) { Mechanism.new }
|
85
134
|
|
86
135
|
ErrorEvent.new(configuration: configuration, integration_meta: integration_meta).tap do |event|
|
87
|
-
event.add_exception_interface(exception)
|
136
|
+
event.add_exception_interface(exception, mechanism: mechanism)
|
88
137
|
event.add_threads_interface(crashed: true)
|
89
138
|
event.level = :error
|
90
139
|
end
|
@@ -104,6 +153,53 @@ module Sentry
|
|
104
153
|
event
|
105
154
|
end
|
106
155
|
|
156
|
+
# Initializes a CheckInEvent object with the given options.
|
157
|
+
#
|
158
|
+
# @param slug [String] identifier of this monitor
|
159
|
+
# @param status [Symbol] status of this check-in, one of {CheckInEvent::VALID_STATUSES}
|
160
|
+
# @param hint [Hash] the hint data that'll be passed to `before_send` callback and the scope's event processors.
|
161
|
+
# @param duration [Integer, nil] seconds elapsed since this monitor started
|
162
|
+
# @param monitor_config [Cron::MonitorConfig, nil] configuration for this monitor
|
163
|
+
# @param check_in_id [String, nil] for updating the status of an existing monitor
|
164
|
+
#
|
165
|
+
# @return [Event]
|
166
|
+
def event_from_check_in(
|
167
|
+
slug,
|
168
|
+
status,
|
169
|
+
hint = {},
|
170
|
+
duration: nil,
|
171
|
+
monitor_config: nil,
|
172
|
+
check_in_id: nil
|
173
|
+
)
|
174
|
+
return unless configuration.sending_allowed?
|
175
|
+
|
176
|
+
CheckInEvent.new(
|
177
|
+
configuration: configuration,
|
178
|
+
integration_meta: Sentry.integrations[hint[:integration]],
|
179
|
+
slug: slug,
|
180
|
+
status: status,
|
181
|
+
duration: duration,
|
182
|
+
monitor_config: monitor_config,
|
183
|
+
check_in_id: check_in_id
|
184
|
+
)
|
185
|
+
end
|
186
|
+
|
187
|
+
# Initializes a LogEvent object with the given message and options
|
188
|
+
#
|
189
|
+
# @param message [String] the log message
|
190
|
+
# @param level [Symbol] the log level (:trace, :debug, :info, :warn, :error, :fatal)
|
191
|
+
# @param options [Hash] additional options
|
192
|
+
# @option options [Array] :parameters Array of values to replace template tokens in the message
|
193
|
+
#
|
194
|
+
# @return [LogEvent] the created log event
|
195
|
+
def event_from_log(message, level:, **options)
|
196
|
+
return unless configuration.sending_allowed?
|
197
|
+
|
198
|
+
attributes = options.reject { |k, _| k == :level || k == :severity }
|
199
|
+
|
200
|
+
LogEvent.new(level: level, body: message, attributes: attributes)
|
201
|
+
end
|
202
|
+
|
107
203
|
# Initializes an Event object with the given Transaction object.
|
108
204
|
# @param transaction [Transaction] the transaction to be recorded.
|
109
205
|
# @return [TransactionEvent]
|
@@ -114,13 +210,26 @@ module Sentry
|
|
114
210
|
# @!macro send_event
|
115
211
|
def send_event(event, hint = nil)
|
116
212
|
event_type = event.is_a?(Event) ? event.type : event["type"]
|
213
|
+
data_category = Envelope::Item.data_category(event_type)
|
214
|
+
spans_before = event.is_a?(TransactionEvent) ? event.spans.size : 0
|
117
215
|
|
118
216
|
if event_type != TransactionEvent::TYPE && configuration.before_send
|
119
217
|
event = configuration.before_send.call(event, hint)
|
120
218
|
|
121
|
-
|
122
|
-
|
123
|
-
|
219
|
+
case event
|
220
|
+
when ErrorEvent, CheckInEvent
|
221
|
+
# do nothing
|
222
|
+
when Hash
|
223
|
+
log_debug(<<~MSG)
|
224
|
+
Returning a Hash from before_send is deprecated and will be removed in the next major version.
|
225
|
+
Please return a Sentry::ErrorEvent object instead.
|
226
|
+
MSG
|
227
|
+
else
|
228
|
+
# Avoid serializing the event object in this case because we aren't sure what it is and what it contains
|
229
|
+
log_debug(<<~MSG)
|
230
|
+
Discarded event because before_send didn't return a Sentry::ErrorEvent object but an instance of #{event.class}
|
231
|
+
MSG
|
232
|
+
transport.record_lost_event(:before_send, data_category)
|
124
233
|
return
|
125
234
|
end
|
126
235
|
end
|
@@ -128,26 +237,104 @@ module Sentry
|
|
128
237
|
if event_type == TransactionEvent::TYPE && configuration.before_send_transaction
|
129
238
|
event = configuration.before_send_transaction.call(event, hint)
|
130
239
|
|
131
|
-
if event.
|
132
|
-
|
133
|
-
|
240
|
+
if event.is_a?(TransactionEvent) || event.is_a?(Hash)
|
241
|
+
spans_after = event.is_a?(TransactionEvent) ? event.spans.size : 0
|
242
|
+
spans_delta = spans_before - spans_after
|
243
|
+
transport.record_lost_event(:before_send, "span", num: spans_delta) if spans_delta > 0
|
244
|
+
|
245
|
+
if event.is_a?(Hash)
|
246
|
+
log_debug(<<~MSG)
|
247
|
+
Returning a Hash from before_send_transaction is deprecated and will be removed in the next major version.
|
248
|
+
Please return a Sentry::TransactionEvent object instead.
|
249
|
+
MSG
|
250
|
+
end
|
251
|
+
else
|
252
|
+
# Avoid serializing the event object in this case because we aren't sure what it is and what it contains
|
253
|
+
log_debug(<<~MSG)
|
254
|
+
Discarded event because before_send_transaction didn't return a Sentry::TransactionEvent object but an instance of #{event.class}
|
255
|
+
MSG
|
256
|
+
transport.record_lost_event(:before_send, "transaction")
|
257
|
+
transport.record_lost_event(:before_send, "span", num: spans_before + 1)
|
134
258
|
return
|
135
259
|
end
|
136
260
|
end
|
137
261
|
|
138
|
-
transport.send_event(event)
|
262
|
+
transport.send_event(event) if configuration.sending_to_dsn_allowed?
|
263
|
+
spotlight_transport.send_event(event) if spotlight_transport
|
139
264
|
|
140
265
|
event
|
141
266
|
rescue => e
|
142
|
-
|
143
|
-
|
267
|
+
log_error("Event sending failed", e, debug: configuration.debug)
|
268
|
+
transport.record_lost_event(:network_error, data_category)
|
269
|
+
transport.record_lost_event(:network_error, "span", num: spans_before + 1) if event.is_a?(TransactionEvent)
|
270
|
+
raise
|
271
|
+
end
|
272
|
+
|
273
|
+
# Send an envelope with batched logs
|
274
|
+
# @param log_events [Array<LogEvent>] the log events to be sent
|
275
|
+
# @api private
|
276
|
+
# @return [void]
|
277
|
+
def send_logs(log_events)
|
278
|
+
envelope = Envelope.new(
|
279
|
+
event_id: Sentry::Utils.uuid,
|
280
|
+
sent_at: Sentry.utc_now.iso8601,
|
281
|
+
dsn: configuration.dsn,
|
282
|
+
sdk: Sentry.sdk_meta
|
283
|
+
)
|
284
|
+
|
285
|
+
discarded_count = 0
|
286
|
+
envelope_items = []
|
287
|
+
|
288
|
+
if configuration.before_send_log
|
289
|
+
log_events.each do |log_event|
|
290
|
+
processed_log_event = configuration.before_send_log.call(log_event)
|
291
|
+
|
292
|
+
if processed_log_event
|
293
|
+
envelope_items << processed_log_event.to_hash
|
294
|
+
else
|
295
|
+
discarded_count += 1
|
296
|
+
end
|
297
|
+
end
|
298
|
+
|
299
|
+
envelope_items
|
300
|
+
else
|
301
|
+
envelope_items = log_events.map(&:to_hash)
|
302
|
+
end
|
303
|
+
|
304
|
+
envelope.add_item(
|
305
|
+
{
|
306
|
+
type: "log",
|
307
|
+
item_count: envelope_items.size,
|
308
|
+
content_type: "application/vnd.sentry.items.log+json"
|
309
|
+
},
|
310
|
+
{ items: envelope_items }
|
311
|
+
)
|
312
|
+
|
313
|
+
send_envelope(envelope)
|
314
|
+
|
315
|
+
unless discarded_count.zero?
|
316
|
+
transport.record_lost_event(:before_send, "log_item", num: discarded_count)
|
317
|
+
end
|
318
|
+
end
|
319
|
+
|
320
|
+
# Send an envelope directly to Sentry.
|
321
|
+
# @param envelope [Envelope] the envelope to be sent.
|
322
|
+
# @return [void]
|
323
|
+
def send_envelope(envelope)
|
324
|
+
transport.send_envelope(envelope) if configuration.sending_to_dsn_allowed?
|
325
|
+
spotlight_transport.send_envelope(envelope) if spotlight_transport
|
326
|
+
rescue => e
|
327
|
+
log_error("Envelope sending failed", e, debug: configuration.debug)
|
328
|
+
|
329
|
+
envelope.items.map(&:data_category).each do |data_category|
|
330
|
+
transport.record_lost_event(:network_error, data_category)
|
331
|
+
end
|
144
332
|
|
145
|
-
event_info = Event.get_log_message(event.to_hash)
|
146
|
-
log_info("Unreported #{loggable_event_type}: #{event_info}")
|
147
|
-
transport.record_lost_event(:network_error, event_type)
|
148
333
|
raise
|
149
334
|
end
|
150
335
|
|
336
|
+
# @deprecated use Sentry.get_traceparent instead.
|
337
|
+
#
|
151
338
|
# Generates a Sentry trace for distribted tracing from the given Span.
|
152
339
|
# Returns `nil` if `config.propagate_traces` is `false`.
|
153
340
|
# @param span [Span] the span to generate trace from.
|
@@ -160,7 +347,9 @@ module Sentry
|
|
160
347
|
trace
|
161
348
|
end
|
162
349
|
|
163
|
-
#
|
350
|
+
# @deprecated Use Sentry.get_baggage instead.
|
351
|
+
#
|
352
|
+
# Generates a W3C Baggage header for distributed tracing from the given Span.
|
164
353
|
# Returns `nil` if `config.propagate_traces` is `false`.
|
165
354
|
# @param span [Span] the span to generate trace from.
|
166
355
|
# @return [String, nil]
|