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/transaction.rb
CHANGED
@@ -2,21 +2,18 @@
|
|
2
2
|
|
3
3
|
require "sentry/baggage"
|
4
4
|
require "sentry/profiler"
|
5
|
+
require "sentry/propagation_context"
|
5
6
|
|
6
7
|
module Sentry
|
7
8
|
class Transaction < Span
|
8
|
-
SENTRY_TRACE_REGEXP
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
"-?([01])?" + # sampled
|
13
|
-
"[ \t]*$" # whitespace
|
14
|
-
)
|
15
|
-
UNLABELD_NAME = "<unlabeled transaction>".freeze
|
9
|
+
# @deprecated Use Sentry::PropagationContext::SENTRY_TRACE_REGEXP instead.
|
10
|
+
SENTRY_TRACE_REGEXP = PropagationContext::SENTRY_TRACE_REGEXP
|
11
|
+
|
12
|
+
UNLABELD_NAME = "<unlabeled transaction>"
|
16
13
|
MESSAGE_PREFIX = "[Tracing]"
|
17
14
|
|
18
15
|
# https://develop.sentry.dev/sdk/event-payloads/transaction/#transaction-annotations
|
19
|
-
SOURCES = %i
|
16
|
+
SOURCES = %i[custom url route view component task]
|
20
17
|
|
21
18
|
include LoggingHelper
|
22
19
|
|
@@ -48,9 +45,6 @@ module Sentry
|
|
48
45
|
# @deprecated Use Sentry.configuration instead.
|
49
46
|
attr_reader :configuration
|
50
47
|
|
51
|
-
# @deprecated Use Sentry.logger instead.
|
52
|
-
attr_reader :logger
|
53
|
-
|
54
48
|
# The effective sample rate at which this transaction was sampled.
|
55
49
|
# @return [Float, nil]
|
56
50
|
attr_reader :effective_sample_rate
|
@@ -81,17 +75,23 @@ module Sentry
|
|
81
75
|
@tracing_enabled = hub.configuration.tracing_enabled?
|
82
76
|
@traces_sampler = hub.configuration.traces_sampler
|
83
77
|
@traces_sample_rate = hub.configuration.traces_sample_rate
|
84
|
-
@
|
78
|
+
@sdk_logger = hub.configuration.sdk_logger
|
85
79
|
@release = hub.configuration.release
|
86
80
|
@environment = hub.configuration.environment
|
87
81
|
@dsn = hub.configuration.dsn
|
88
82
|
@effective_sample_rate = nil
|
89
83
|
@contexts = {}
|
90
84
|
@measurements = {}
|
91
|
-
|
85
|
+
|
86
|
+
unless @hub.profiler_running?
|
87
|
+
@profiler = @configuration.profiler_class.new(@configuration)
|
88
|
+
end
|
89
|
+
|
92
90
|
init_span_recorder
|
93
91
|
end
|
94
92
|
|
93
|
+
# @deprecated use Sentry.continue_trace instead.
|
94
|
+
#
|
95
95
|
# Initalizes a Transaction instance with a Sentry trace string from another transaction (usually from an external request).
|
96
96
|
#
|
97
97
|
# The original transaction will become the parent of the new Transaction instance. And they will share the same `trace_id`.
|
@@ -111,14 +111,15 @@ module Sentry
|
|
111
111
|
|
112
112
|
trace_id, parent_span_id, parent_sampled = sentry_trace_data
|
113
113
|
|
114
|
-
baggage =
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
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
|
122
123
|
|
123
124
|
baggage.freeze!
|
124
125
|
|
@@ -132,18 +133,10 @@ module Sentry
|
|
132
133
|
)
|
133
134
|
end
|
134
135
|
|
135
|
-
#
|
136
|
-
#
|
137
|
-
# @param sentry_trace [String] the sentry-trace header value from the previous transaction.
|
136
|
+
# @deprecated Use Sentry::PropagationContext.extract_sentry_trace instead.
|
138
137
|
# @return [Array, nil]
|
139
138
|
def self.extract_sentry_trace(sentry_trace)
|
140
|
-
|
141
|
-
return nil if match.nil?
|
142
|
-
|
143
|
-
trace_id, parent_span_id, sampled_flag = match[1..3]
|
144
|
-
parent_sampled = sampled_flag.nil? ? nil : sampled_flag != "0"
|
145
|
-
|
146
|
-
[trace_id, parent_span_id, parent_sampled]
|
139
|
+
PropagationContext.extract_sentry_trace(sentry_trace)
|
147
140
|
end
|
148
141
|
|
149
142
|
# @return [Hash]
|
@@ -227,7 +220,12 @@ module Sentry
|
|
227
220
|
if sample_rate == true
|
228
221
|
@sampled = true
|
229
222
|
else
|
230
|
-
|
223
|
+
if Sentry.backpressure_monitor
|
224
|
+
factor = Sentry.backpressure_monitor.downsample_factor
|
225
|
+
@effective_sample_rate /= 2**factor
|
226
|
+
end
|
227
|
+
|
228
|
+
@sampled = Random.rand < @effective_sample_rate
|
231
229
|
end
|
232
230
|
|
233
231
|
if @sampled
|
@@ -260,13 +258,16 @@ module Sentry
|
|
260
258
|
@name = UNLABELD_NAME
|
261
259
|
end
|
262
260
|
|
263
|
-
@
|
261
|
+
@hub.stop_profiler!(self)
|
264
262
|
|
265
263
|
if @sampled
|
266
264
|
event = hub.current_client.event_from_transaction(self)
|
267
265
|
hub.capture_event(event)
|
268
266
|
else
|
269
|
-
|
267
|
+
is_backpressure = Sentry.backpressure_monitor&.downsample_factor&.positive?
|
268
|
+
reason = is_backpressure ? :backpressure : :sample_rate
|
269
|
+
hub.current_client.transport.record_lost_event(reason, "transaction")
|
270
|
+
hub.current_client.transport.record_lost_event(reason, "span")
|
270
271
|
end
|
271
272
|
end
|
272
273
|
|
@@ -299,10 +300,17 @@ module Sentry
|
|
299
300
|
# Start the profiler.
|
300
301
|
# @return [void]
|
301
302
|
def start_profiler!
|
303
|
+
return unless profiler
|
304
|
+
|
302
305
|
profiler.set_initial_sample_decision(sampled)
|
303
306
|
profiler.start
|
304
307
|
end
|
305
308
|
|
309
|
+
# These are high cardinality and thus bad
|
310
|
+
def source_low_quality?
|
311
|
+
source == :url
|
312
|
+
end
|
313
|
+
|
306
314
|
protected
|
307
315
|
|
308
316
|
def init_span_recorder(limit = 1000)
|
@@ -323,6 +331,7 @@ module Sentry
|
|
323
331
|
items = {
|
324
332
|
"trace_id" => trace_id,
|
325
333
|
"sample_rate" => effective_sample_rate&.to_s,
|
334
|
+
"sampled" => sampled&.to_s,
|
326
335
|
"environment" => @environment,
|
327
336
|
"release" => @release,
|
328
337
|
"public_key" => @dsn&.public_key
|
@@ -330,18 +339,10 @@ module Sentry
|
|
330
339
|
|
331
340
|
items["transaction"] = name unless source_low_quality?
|
332
341
|
|
333
|
-
user = @hub.current_scope&.user
|
334
|
-
items["user_segment"] = user["segment"] if user && user["segment"]
|
335
|
-
|
336
342
|
items.compact!
|
337
343
|
@baggage = Baggage.new(items, mutable: false)
|
338
344
|
end
|
339
345
|
|
340
|
-
# These are high cardinality and thus bad
|
341
|
-
def source_low_quality?
|
342
|
-
source == :url
|
343
|
-
end
|
344
|
-
|
345
346
|
class SpanRecorder
|
346
347
|
attr_reader :max_length, :spans
|
347
348
|
|
@@ -8,9 +8,6 @@ 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
11
|
# @return [Hash]
|
15
12
|
attr_accessor :measurements
|
16
13
|
|
@@ -20,6 +17,9 @@ module Sentry
|
|
20
17
|
# @return [Hash, nil]
|
21
18
|
attr_accessor :profile
|
22
19
|
|
20
|
+
# @return [Hash, nil]
|
21
|
+
attr_accessor :metrics_summary
|
22
|
+
|
23
23
|
def initialize(transaction:, **options)
|
24
24
|
super(**options)
|
25
25
|
|
@@ -32,6 +32,7 @@ module Sentry
|
|
32
32
|
self.tags = transaction.tags
|
33
33
|
self.dynamic_sampling_context = transaction.get_baggage.dynamic_sampling_context
|
34
34
|
self.measurements = transaction.measurements
|
35
|
+
self.metrics_summary = transaction.metrics_summary
|
35
36
|
|
36
37
|
finished_spans = transaction.span_recorder.spans.select { |span| span.timestamp && span != transaction }
|
37
38
|
self.spans = finished_spans.map(&:to_hash)
|
@@ -52,13 +53,17 @@ module Sentry
|
|
52
53
|
data[:spans] = @spans.map(&:to_hash) if @spans
|
53
54
|
data[:start_timestamp] = @start_timestamp
|
54
55
|
data[:measurements] = @measurements
|
56
|
+
data[:_metrics_summary] = @metrics_summary if @metrics_summary
|
55
57
|
data
|
56
58
|
end
|
57
59
|
|
58
60
|
private
|
59
61
|
|
62
|
+
EMPTY_PROFILE = {}.freeze
|
63
|
+
|
60
64
|
def populate_profile(transaction)
|
61
|
-
profile_hash = transaction.profiler
|
65
|
+
profile_hash = transaction.profiler&.to_hash || EMPTY_PROFILE
|
66
|
+
|
62
67
|
return if profile_hash.empty?
|
63
68
|
|
64
69
|
profile_hash.merge!(
|
@@ -72,8 +77,7 @@ module Sentry
|
|
72
77
|
id: event_id,
|
73
78
|
name: transaction.name,
|
74
79
|
trace_id: transaction.trace_id,
|
75
|
-
|
76
|
-
active_thead_id: '0'
|
80
|
+
active_thread_id: transaction.profiler.active_thread_id.to_s
|
77
81
|
}
|
78
82
|
)
|
79
83
|
|
@@ -3,7 +3,79 @@
|
|
3
3
|
module Sentry
|
4
4
|
class Transport
|
5
5
|
class Configuration
|
6
|
-
|
6
|
+
# The timeout in seconds to open a connection to Sentry, in seconds.
|
7
|
+
# Default value is 2.
|
8
|
+
#
|
9
|
+
# @return [Integer]
|
10
|
+
attr_accessor :timeout
|
11
|
+
|
12
|
+
# The timeout in seconds to read data from Sentry, in seconds.
|
13
|
+
# Default value is 1.
|
14
|
+
#
|
15
|
+
# @return [Integer]
|
16
|
+
attr_accessor :open_timeout
|
17
|
+
|
18
|
+
# The proxy configuration to use to connect to Sentry.
|
19
|
+
# Accepts either a URI formatted string, URI, or a hash with the `uri`,
|
20
|
+
# `user`, and `password` keys.
|
21
|
+
#
|
22
|
+
# @example
|
23
|
+
# # setup proxy using a string:
|
24
|
+
# config.transport.proxy = "https://user:password@proxyhost:8080"
|
25
|
+
#
|
26
|
+
# # setup proxy using a URI:
|
27
|
+
# config.transport.proxy = URI("https://user:password@proxyhost:8080")
|
28
|
+
#
|
29
|
+
# # setup proxy using a hash:
|
30
|
+
# config.transport.proxy = {
|
31
|
+
# uri: URI("https://proxyhost:8080"),
|
32
|
+
# user: "user",
|
33
|
+
# password: "password"
|
34
|
+
# }
|
35
|
+
#
|
36
|
+
# If you're using the default transport (`Sentry::HTTPTransport`),
|
37
|
+
# proxy settings will also automatically be read from tne environment
|
38
|
+
# variables (`HTTP_PROXY`, `HTTPS_PROXY`, `NO_PROXY`).
|
39
|
+
#
|
40
|
+
# @return [String, URI, Hash, nil]
|
41
|
+
attr_accessor :proxy
|
42
|
+
|
43
|
+
# The SSL configuration to use to connect to Sentry.
|
44
|
+
# You can either pass a `Hash` containing `ca_file` and `verification` keys,
|
45
|
+
# or you can set those options directly on the `Sentry::HTTPTransport::Configuration` object:
|
46
|
+
#
|
47
|
+
# @example
|
48
|
+
# config.transport.ssl = {
|
49
|
+
# ca_file: "/path/to/ca_file",
|
50
|
+
# verification: true
|
51
|
+
# end
|
52
|
+
#
|
53
|
+
# @return [Hash, nil]
|
54
|
+
attr_accessor :ssl
|
55
|
+
|
56
|
+
# The path to the CA file to use to verify the SSL connection.
|
57
|
+
# Default value is `nil`.
|
58
|
+
#
|
59
|
+
# @return [String, nil]
|
60
|
+
attr_accessor :ssl_ca_file
|
61
|
+
|
62
|
+
# Whether to verify that the peer certificate is valid in SSL connections.
|
63
|
+
# Default value is `true`.
|
64
|
+
#
|
65
|
+
# @return [Boolean]
|
66
|
+
attr_accessor :ssl_verification
|
67
|
+
|
68
|
+
# The encoding to use to compress the request body.
|
69
|
+
# Default value is `Sentry::HTTPTransport::GZIP_ENCODING`.
|
70
|
+
#
|
71
|
+
# @return [String]
|
72
|
+
attr_accessor :encoding
|
73
|
+
|
74
|
+
# The class to use as a transport to connect to Sentry.
|
75
|
+
# If this option not set, it will return `nil`, and Sentry will use
|
76
|
+
# `Sentry::HTTPTransport` by default.
|
77
|
+
#
|
78
|
+
# @return [Class, nil]
|
7
79
|
attr_reader :transport_class
|
8
80
|
|
9
81
|
def initialize
|
@@ -7,18 +7,25 @@ module Sentry
|
|
7
7
|
class HTTPTransport < Transport
|
8
8
|
GZIP_ENCODING = "gzip"
|
9
9
|
GZIP_THRESHOLD = 1024 * 30
|
10
|
-
CONTENT_TYPE =
|
10
|
+
CONTENT_TYPE = "application/x-sentry-envelope"
|
11
11
|
|
12
12
|
DEFAULT_DELAY = 60
|
13
13
|
RETRY_AFTER_HEADER = "retry-after"
|
14
14
|
RATE_LIMIT_HEADER = "x-sentry-rate-limits"
|
15
15
|
USER_AGENT = "sentry-ruby/#{Sentry::VERSION}"
|
16
16
|
|
17
|
+
# The list of errors ::Net::HTTP is known to raise
|
18
|
+
# See https://github.com/ruby/ruby/blob/b0c639f249165d759596f9579fa985cb30533de6/lib/bundler/fetcher.rb#L281-L286
|
19
|
+
HTTP_ERRORS = [
|
20
|
+
Timeout::Error, EOFError, SocketError, Errno::ENETDOWN, Errno::ENETUNREACH,
|
21
|
+
Errno::EINVAL, Errno::ECONNRESET, Errno::ETIMEDOUT, Errno::EAGAIN,
|
22
|
+
Net::HTTPBadResponse, Net::HTTPHeaderSyntaxError, Net::ProtocolError,
|
23
|
+
Zlib::BufError, Errno::EHOSTUNREACH, Errno::ECONNREFUSED
|
24
|
+
].freeze
|
25
|
+
|
17
26
|
def initialize(*args)
|
18
27
|
super
|
19
|
-
@
|
20
|
-
|
21
|
-
log_debug("Sentry HTTP Transport will connect to #{@dsn.server}")
|
28
|
+
log_debug("Sentry HTTP Transport will connect to #{@dsn.server}") if @dsn
|
22
29
|
end
|
23
30
|
|
24
31
|
def send_data(data)
|
@@ -30,36 +37,78 @@ module Sentry
|
|
30
37
|
end
|
31
38
|
|
32
39
|
headers = {
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
'User-Agent' => USER_AGENT
|
40
|
+
"Content-Type" => CONTENT_TYPE,
|
41
|
+
"Content-Encoding" => encoding,
|
42
|
+
"User-Agent" => USER_AGENT
|
37
43
|
}
|
38
44
|
|
45
|
+
auth_header = generate_auth_header
|
46
|
+
headers["X-Sentry-Auth"] = auth_header if auth_header
|
47
|
+
|
39
48
|
response = conn.start do |http|
|
40
|
-
request = ::Net::HTTP::Post.new(
|
49
|
+
request = ::Net::HTTP::Post.new(endpoint, headers)
|
41
50
|
request.body = data
|
42
51
|
http.request(request)
|
43
52
|
end
|
44
53
|
|
45
54
|
if response.code.match?(/\A2\d{2}/)
|
46
|
-
if has_rate_limited_header?(response)
|
47
|
-
|
48
|
-
|
55
|
+
handle_rate_limited_response(response) if has_rate_limited_header?(response)
|
56
|
+
elsif response.code == "429"
|
57
|
+
log_debug("the server responded with status 429")
|
58
|
+
handle_rate_limited_response(response)
|
49
59
|
else
|
50
60
|
error_info = "the server responded with status #{response.code}"
|
61
|
+
error_info += "\nbody: #{response.body}"
|
62
|
+
error_info += " Error in headers is: #{response['x-sentry-error']}" if response["x-sentry-error"]
|
63
|
+
|
64
|
+
raise Sentry::ExternalError, error_info
|
65
|
+
end
|
66
|
+
rescue SocketError, *HTTP_ERRORS => e
|
67
|
+
on_error if respond_to?(:on_error)
|
68
|
+
raise Sentry::ExternalError.new(e&.message)
|
69
|
+
end
|
70
|
+
|
71
|
+
def endpoint
|
72
|
+
@dsn.envelope_endpoint
|
73
|
+
end
|
74
|
+
|
75
|
+
def generate_auth_header
|
76
|
+
return nil unless @dsn
|
77
|
+
|
78
|
+
now = Sentry.utc_now.to_i
|
79
|
+
fields = {
|
80
|
+
"sentry_version" => PROTOCOL_VERSION,
|
81
|
+
"sentry_client" => USER_AGENT,
|
82
|
+
"sentry_timestamp" => now,
|
83
|
+
"sentry_key" => @dsn.public_key
|
84
|
+
}
|
85
|
+
fields["sentry_secret"] = @dsn.secret_key if @dsn.secret_key
|
86
|
+
"Sentry " + fields.map { |key, value| "#{key}=#{value}" }.join(", ")
|
87
|
+
end
|
88
|
+
|
89
|
+
def conn
|
90
|
+
server = URI(@dsn.server)
|
51
91
|
|
52
|
-
|
53
|
-
|
92
|
+
# connection respects proxy setting from @transport_configuration, or environment variables (HTTP_PROXY, HTTPS_PROXY, NO_PROXY)
|
93
|
+
# Net::HTTP will automatically read the env vars.
|
94
|
+
# See https://ruby-doc.org/3.2.2/stdlibs/net/Net/HTTP.html#class-Net::HTTP-label-Proxies
|
95
|
+
connection =
|
96
|
+
if proxy = normalize_proxy(@transport_configuration.proxy)
|
97
|
+
::Net::HTTP.new(server.hostname, server.port, proxy[:uri].hostname, proxy[:uri].port, proxy[:user], proxy[:password])
|
54
98
|
else
|
55
|
-
|
56
|
-
error_info += " Error in headers is: #{response['x-sentry-error']}" if response['x-sentry-error']
|
99
|
+
::Net::HTTP.new(server.hostname, server.port)
|
57
100
|
end
|
58
101
|
|
59
|
-
|
102
|
+
connection.use_ssl = server.scheme == "https"
|
103
|
+
connection.read_timeout = @transport_configuration.timeout
|
104
|
+
connection.write_timeout = @transport_configuration.timeout if connection.respond_to?(:write_timeout)
|
105
|
+
connection.open_timeout = @transport_configuration.open_timeout
|
106
|
+
|
107
|
+
ssl_configuration.each do |key, value|
|
108
|
+
connection.send("#{key}=", value)
|
60
109
|
end
|
61
|
-
|
62
|
-
|
110
|
+
|
111
|
+
connection
|
63
112
|
end
|
64
113
|
|
65
114
|
private
|
@@ -126,28 +175,9 @@ module Sentry
|
|
126
175
|
@transport_configuration.encoding == GZIP_ENCODING && data.bytesize >= GZIP_THRESHOLD
|
127
176
|
end
|
128
177
|
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
connection =
|
133
|
-
if proxy = normalize_proxy(@transport_configuration.proxy)
|
134
|
-
::Net::HTTP.new(server.hostname, server.port, proxy[:uri].hostname, proxy[:uri].port, proxy[:user], proxy[:password])
|
135
|
-
else
|
136
|
-
::Net::HTTP.new(server.hostname, server.port, nil)
|
137
|
-
end
|
138
|
-
|
139
|
-
connection.use_ssl = server.scheme == "https"
|
140
|
-
connection.read_timeout = @transport_configuration.timeout
|
141
|
-
connection.write_timeout = @transport_configuration.timeout if connection.respond_to?(:write_timeout)
|
142
|
-
connection.open_timeout = @transport_configuration.open_timeout
|
143
|
-
|
144
|
-
ssl_configuration.each do |key, value|
|
145
|
-
connection.send("#{key}=", value)
|
146
|
-
end
|
147
|
-
|
148
|
-
connection
|
149
|
-
end
|
150
|
-
|
178
|
+
# @param proxy [String, URI, Hash] Proxy config value passed into `config.transport`.
|
179
|
+
# Accepts either a URI formatted string, URI, or a hash with the `uri`, `user`, and `password` keys.
|
180
|
+
# @return [Hash] Normalized proxy config that will be passed into `Net::HTTP`
|
151
181
|
def normalize_proxy(proxy)
|
152
182
|
return proxy unless proxy
|
153
183
|
|
@@ -0,0 +1,50 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "net/http"
|
4
|
+
require "zlib"
|
5
|
+
|
6
|
+
module Sentry
|
7
|
+
# Designed to just report events to Spotlight in development.
|
8
|
+
class SpotlightTransport < HTTPTransport
|
9
|
+
DEFAULT_SIDECAR_URL = "http://localhost:8969/stream"
|
10
|
+
MAX_FAILED_REQUESTS = 3
|
11
|
+
|
12
|
+
def initialize(configuration)
|
13
|
+
super
|
14
|
+
@sidecar_url = configuration.spotlight.is_a?(String) ? configuration.spotlight : DEFAULT_SIDECAR_URL
|
15
|
+
@failed = 0
|
16
|
+
@logged = false
|
17
|
+
|
18
|
+
log_debug("[Spotlight] initialized for url #{@sidecar_url}")
|
19
|
+
end
|
20
|
+
|
21
|
+
def endpoint
|
22
|
+
"/stream"
|
23
|
+
end
|
24
|
+
|
25
|
+
def send_data(data)
|
26
|
+
if @failed >= MAX_FAILED_REQUESTS
|
27
|
+
unless @logged
|
28
|
+
log_debug("[Spotlight] disabling because of too many request failures")
|
29
|
+
@logged = true
|
30
|
+
end
|
31
|
+
|
32
|
+
return
|
33
|
+
end
|
34
|
+
|
35
|
+
super
|
36
|
+
end
|
37
|
+
|
38
|
+
def on_error
|
39
|
+
@failed += 1
|
40
|
+
end
|
41
|
+
|
42
|
+
# Similar to HTTPTransport connection, but does not support Proxy and SSL
|
43
|
+
def conn
|
44
|
+
sidecar = URI(@sidecar_url)
|
45
|
+
connection = ::Net::HTTP.new(sidecar.hostname, sidecar.port, nil)
|
46
|
+
connection.use_ssl = false
|
47
|
+
connection
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|