sentry-ruby-core 5.13.0 → 5.15.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/lib/sentry/client.rb +7 -0
- data/lib/sentry/configuration.rb +10 -1
- data/lib/sentry/cron/monitor_check_ins.rb +8 -3
- data/lib/sentry/interfaces/single_exception.rb +1 -0
- data/lib/sentry/net/http.rb +5 -1
- data/lib/sentry/release_detector.rb +1 -1
- data/lib/sentry/transport/configuration.rb +74 -1
- data/lib/sentry/transport/http_transport.rb +64 -29
- data/lib/sentry/transport/spotlight_transport.rb +50 -0
- data/lib/sentry/transport.rb +1 -12
- data/lib/sentry/version.rb +1 -1
- metadata +5 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 48fa1a3c7cda110c509559a16870fa80634d7f03e18433883753acaaf1b8d582
|
4
|
+
data.tar.gz: 2800581dce649c81f5a17464f3b5619158900d8c5eb23e35347507dddb594b04
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 70204057a726d6c05a21229789706e011a05e52a569ffd4d68e51255111aff50c3afb2ec39beaa66e0e43f1af6e502bdc8be9ddcb65efed446afa869fc98cfe3
|
7
|
+
data.tar.gz: 1f9749bf1ccee5a541c4949dbf2b0f79da9508f6a86aa3d603811b252a49f4ca3054e91dfa7a0e93dac723965e8e316f56c8433ddf796c7d8f38069075b80268
|
data/lib/sentry/client.rb
CHANGED
@@ -10,6 +10,10 @@ module Sentry
|
|
10
10
|
# @return [Transport]
|
11
11
|
attr_reader :transport
|
12
12
|
|
13
|
+
# The Transport object that'll send events for the client.
|
14
|
+
# @return [SpotlightTransport, nil]
|
15
|
+
attr_reader :spotlight_transport
|
16
|
+
|
13
17
|
# @!macro configuration
|
14
18
|
attr_reader :configuration
|
15
19
|
|
@@ -32,6 +36,8 @@ module Sentry
|
|
32
36
|
DummyTransport.new(configuration)
|
33
37
|
end
|
34
38
|
end
|
39
|
+
|
40
|
+
@spotlight_transport = SpotlightTransport.new(configuration) if configuration.spotlight
|
35
41
|
end
|
36
42
|
|
37
43
|
# Applies the given scope's data to the event and sends it to Sentry.
|
@@ -167,6 +173,7 @@ module Sentry
|
|
167
173
|
end
|
168
174
|
|
169
175
|
transport.send_event(event)
|
176
|
+
spotlight_transport&.send_event(event)
|
170
177
|
|
171
178
|
event
|
172
179
|
rescue => e
|
data/lib/sentry/configuration.rb
CHANGED
@@ -142,6 +142,14 @@ module Sentry
|
|
142
142
|
# @return [Boolean]
|
143
143
|
attr_accessor :include_local_variables
|
144
144
|
|
145
|
+
# Whether to capture events and traces into Spotlight. Default is false.
|
146
|
+
# If you set this to true, Sentry will send events and traces to the local
|
147
|
+
# Sidecar proxy at http://localhost:8969/stream.
|
148
|
+
# If you want to use a different Sidecar proxy address, set this to String
|
149
|
+
# with the proxy URL.
|
150
|
+
# @return [Boolean, String]
|
151
|
+
attr_accessor :spotlight
|
152
|
+
|
145
153
|
# @deprecated Use {#include_local_variables} instead.
|
146
154
|
alias_method :capture_exception_frame_locals, :include_local_variables
|
147
155
|
|
@@ -344,6 +352,7 @@ module Sentry
|
|
344
352
|
self.auto_session_tracking = true
|
345
353
|
self.trusted_proxies = []
|
346
354
|
self.dsn = ENV['SENTRY_DSN']
|
355
|
+
self.spotlight = false
|
347
356
|
self.server_name = server_name_from_env
|
348
357
|
self.instrumenter = :sentry
|
349
358
|
self.trace_propagation_targets = [PROPAGATION_TARGETS_MATCH_ALL]
|
@@ -451,7 +460,7 @@ module Sentry
|
|
451
460
|
def sending_allowed?
|
452
461
|
@errors = []
|
453
462
|
|
454
|
-
valid? && capture_in_environment?
|
463
|
+
spotlight || (valid? && capture_in_environment?)
|
455
464
|
end
|
456
465
|
|
457
466
|
def sample_allowed?
|
@@ -1,9 +1,11 @@
|
|
1
1
|
module Sentry
|
2
2
|
module Cron
|
3
3
|
module MonitorCheckIns
|
4
|
+
MAX_SLUG_LENGTH = 50
|
5
|
+
|
4
6
|
module Patch
|
5
7
|
def perform(*args)
|
6
|
-
slug = self.class.sentry_monitor_slug
|
8
|
+
slug = self.class.sentry_monitor_slug
|
7
9
|
monitor_config = self.class.sentry_monitor_config
|
8
10
|
|
9
11
|
check_in_id = Sentry.capture_check_in(slug,
|
@@ -42,8 +44,11 @@ module Sentry
|
|
42
44
|
prepend Patch
|
43
45
|
end
|
44
46
|
|
45
|
-
def sentry_monitor_slug
|
46
|
-
@sentry_monitor_slug
|
47
|
+
def sentry_monitor_slug(name: self.name)
|
48
|
+
@sentry_monitor_slug ||= begin
|
49
|
+
slug = name.gsub('::', '-').downcase
|
50
|
+
slug[-MAX_SLUG_LENGTH..-1] || slug
|
51
|
+
end
|
47
52
|
end
|
48
53
|
|
49
54
|
def sentry_monitor_config
|
@@ -22,6 +22,7 @@ module Sentry
|
|
22
22
|
else
|
23
23
|
exception.message || ""
|
24
24
|
end
|
25
|
+
exception_message = exception_message.inspect unless exception_message.is_a?(String)
|
25
26
|
|
26
27
|
@value = Utils::EncodingHelper.encode_to_utf_8(exception_message.byteslice(0..Event::MAX_MESSAGE_SIZE_IN_BYTES))
|
27
28
|
|
data/lib/sentry/net/http.rb
CHANGED
@@ -1,6 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require "net/http"
|
4
|
+
require "resolv"
|
4
5
|
|
5
6
|
module Sentry
|
6
7
|
# @api private
|
@@ -77,7 +78,10 @@ module Sentry
|
|
77
78
|
end
|
78
79
|
|
79
80
|
def extract_request_info(req)
|
80
|
-
|
81
|
+
# IPv6 url could look like '::1/path', and that won't parse without
|
82
|
+
# wrapping it in square brackets.
|
83
|
+
hostname = address =~ Resolv::IPv6::Regex ? "[#{address}]" : address
|
84
|
+
uri = req.uri || URI.parse("#{use_ssl? ? 'https' : 'http'}://#{hostname}#{req.path}")
|
81
85
|
url = "#{uri.scheme}://#{uri.host}#{uri.path}" rescue uri.to_s
|
82
86
|
|
83
87
|
result = { method: req.method, url: url }
|
@@ -3,7 +3,80 @@
|
|
3
3
|
module Sentry
|
4
4
|
class Transport
|
5
5
|
class Configuration
|
6
|
-
|
6
|
+
|
7
|
+
# The timeout in seconds to open a connection to Sentry, in seconds.
|
8
|
+
# Default value is 2.
|
9
|
+
#
|
10
|
+
# @return [Integer]
|
11
|
+
attr_accessor :timeout
|
12
|
+
|
13
|
+
# The timeout in seconds to read data from Sentry, in seconds.
|
14
|
+
# Default value is 1.
|
15
|
+
#
|
16
|
+
# @return [Integer]
|
17
|
+
attr_accessor :open_timeout
|
18
|
+
|
19
|
+
# The proxy configuration to use to connect to Sentry.
|
20
|
+
# Accepts either a URI formatted string, URI, or a hash with the `uri`,
|
21
|
+
# `user`, and `password` keys.
|
22
|
+
#
|
23
|
+
# @example
|
24
|
+
# # setup proxy using a string:
|
25
|
+
# config.transport.proxy = "https://user:password@proxyhost:8080"
|
26
|
+
#
|
27
|
+
# # setup proxy using a URI:
|
28
|
+
# config.transport.proxy = URI("https://user:password@proxyhost:8080")
|
29
|
+
#
|
30
|
+
# # setup proxy using a hash:
|
31
|
+
# config.transport.proxy = {
|
32
|
+
# uri: URI("https://proxyhost:8080"),
|
33
|
+
# user: "user",
|
34
|
+
# password: "password"
|
35
|
+
# }
|
36
|
+
#
|
37
|
+
# If you're using the default transport (`Sentry::HTTPTransport`),
|
38
|
+
# proxy settings will also automatically be read from tne environment
|
39
|
+
# variables (`HTTP_PROXY`, `HTTPS_PROXY`, `NO_PROXY`).
|
40
|
+
#
|
41
|
+
# @return [String, URI, Hash, nil]
|
42
|
+
attr_accessor :proxy
|
43
|
+
|
44
|
+
# The SSL configuration to use to connect to Sentry.
|
45
|
+
# You can either pass a `Hash` containing `ca_file` and `verification` keys,
|
46
|
+
# or you can set those options directly on the `Sentry::HTTPTransport::Configuration` object:
|
47
|
+
#
|
48
|
+
# @example
|
49
|
+
# config.transport.ssl = {
|
50
|
+
# ca_file: "/path/to/ca_file",
|
51
|
+
# verification: true
|
52
|
+
# end
|
53
|
+
#
|
54
|
+
# @return [Hash, nil]
|
55
|
+
attr_accessor :ssl
|
56
|
+
|
57
|
+
# The path to the CA file to use to verify the SSL connection.
|
58
|
+
# Default value is `nil`.
|
59
|
+
#
|
60
|
+
# @return [String, nil]
|
61
|
+
attr_accessor :ssl_ca_file
|
62
|
+
|
63
|
+
# Whether to verify that the peer certificate is valid in SSL connections.
|
64
|
+
# Default value is `true`.
|
65
|
+
#
|
66
|
+
# @return [Boolean]
|
67
|
+
attr_accessor :ssl_verification
|
68
|
+
|
69
|
+
# The encoding to use to compress the request body.
|
70
|
+
# Default value is `Sentry::HTTPTransport::GZIP_ENCODING`.
|
71
|
+
#
|
72
|
+
# @return [String]
|
73
|
+
attr_accessor :encoding
|
74
|
+
|
75
|
+
# The class to use as a transport to connect to Sentry.
|
76
|
+
# If this option not set, it will return `nil`, and Sentry will use
|
77
|
+
# `Sentry::HTTPTransport` by default.
|
78
|
+
#
|
79
|
+
# @return [Class, nil]
|
7
80
|
attr_reader :transport_class
|
8
81
|
|
9
82
|
def initialize
|
@@ -14,11 +14,19 @@ module Sentry
|
|
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
|
+
|
26
|
+
|
17
27
|
def initialize(*args)
|
18
28
|
super
|
19
|
-
@
|
20
|
-
|
21
|
-
log_debug("Sentry HTTP Transport will connect to #{@dsn.server}")
|
29
|
+
log_debug("Sentry HTTP Transport will connect to #{@dsn.server}") if @dsn
|
22
30
|
end
|
23
31
|
|
24
32
|
def send_data(data)
|
@@ -32,12 +40,14 @@ module Sentry
|
|
32
40
|
headers = {
|
33
41
|
'Content-Type' => CONTENT_TYPE,
|
34
42
|
'Content-Encoding' => encoding,
|
35
|
-
'X-Sentry-Auth' => generate_auth_header,
|
36
43
|
'User-Agent' => USER_AGENT
|
37
44
|
}
|
38
45
|
|
46
|
+
auth_header = generate_auth_header
|
47
|
+
headers['X-Sentry-Auth'] = auth_header if auth_header
|
48
|
+
|
39
49
|
response = conn.start do |http|
|
40
|
-
request = ::Net::HTTP::Post.new(
|
50
|
+
request = ::Net::HTTP::Post.new(endpoint, headers)
|
41
51
|
request.body = data
|
42
52
|
http.request(request)
|
43
53
|
end
|
@@ -58,8 +68,52 @@ module Sentry
|
|
58
68
|
|
59
69
|
raise Sentry::ExternalError, error_info
|
60
70
|
end
|
61
|
-
rescue SocketError => e
|
62
|
-
|
71
|
+
rescue SocketError, *HTTP_ERRORS => e
|
72
|
+
on_error if respond_to?(:on_error)
|
73
|
+
raise Sentry::ExternalError.new(e&.message)
|
74
|
+
end
|
75
|
+
|
76
|
+
def endpoint
|
77
|
+
@dsn.envelope_endpoint
|
78
|
+
end
|
79
|
+
|
80
|
+
def generate_auth_header
|
81
|
+
return nil unless @dsn
|
82
|
+
|
83
|
+
now = Sentry.utc_now.to_i
|
84
|
+
fields = {
|
85
|
+
'sentry_version' => PROTOCOL_VERSION,
|
86
|
+
'sentry_client' => USER_AGENT,
|
87
|
+
'sentry_timestamp' => now,
|
88
|
+
'sentry_key' => @dsn.public_key
|
89
|
+
}
|
90
|
+
fields['sentry_secret'] = @dsn.secret_key if @dsn.secret_key
|
91
|
+
'Sentry ' + fields.map { |key, value| "#{key}=#{value}" }.join(', ')
|
92
|
+
end
|
93
|
+
|
94
|
+
def conn
|
95
|
+
server = URI(@dsn.server)
|
96
|
+
|
97
|
+
# connection respects proxy setting from @transport_configuration, or environment variables (HTTP_PROXY, HTTPS_PROXY, NO_PROXY)
|
98
|
+
# Net::HTTP will automatically read the env vars.
|
99
|
+
# See https://ruby-doc.org/3.2.2/stdlibs/net/Net/HTTP.html#class-Net::HTTP-label-Proxies
|
100
|
+
connection =
|
101
|
+
if proxy = normalize_proxy(@transport_configuration.proxy)
|
102
|
+
::Net::HTTP.new(server.hostname, server.port, proxy[:uri].hostname, proxy[:uri].port, proxy[:user], proxy[:password])
|
103
|
+
else
|
104
|
+
::Net::HTTP.new(server.hostname, server.port)
|
105
|
+
end
|
106
|
+
|
107
|
+
connection.use_ssl = server.scheme == "https"
|
108
|
+
connection.read_timeout = @transport_configuration.timeout
|
109
|
+
connection.write_timeout = @transport_configuration.timeout if connection.respond_to?(:write_timeout)
|
110
|
+
connection.open_timeout = @transport_configuration.open_timeout
|
111
|
+
|
112
|
+
ssl_configuration.each do |key, value|
|
113
|
+
connection.send("#{key}=", value)
|
114
|
+
end
|
115
|
+
|
116
|
+
connection
|
63
117
|
end
|
64
118
|
|
65
119
|
private
|
@@ -126,28 +180,9 @@ module Sentry
|
|
126
180
|
@transport_configuration.encoding == GZIP_ENCODING && data.bytesize >= GZIP_THRESHOLD
|
127
181
|
end
|
128
182
|
|
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
|
-
|
183
|
+
# @param proxy [String, URI, Hash] Proxy config value passed into `config.transport`.
|
184
|
+
# Accepts either a URI formatted string, URI, or a hash with the `uri`, `user`, and `password` keys.
|
185
|
+
# @return [Hash] Normalized proxy config that will be passed into `Net::HTTP`
|
151
186
|
def normalize_proxy(proxy)
|
152
187
|
return proxy unless proxy
|
153
188
|
|
@@ -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
|
data/lib/sentry/transport.rb
CHANGED
@@ -119,18 +119,6 @@ module Sentry
|
|
119
119
|
!!delay && delay > Time.now
|
120
120
|
end
|
121
121
|
|
122
|
-
def generate_auth_header
|
123
|
-
now = Sentry.utc_now.to_i
|
124
|
-
fields = {
|
125
|
-
'sentry_version' => PROTOCOL_VERSION,
|
126
|
-
'sentry_client' => USER_AGENT,
|
127
|
-
'sentry_timestamp' => now,
|
128
|
-
'sentry_key' => @dsn.public_key
|
129
|
-
}
|
130
|
-
fields['sentry_secret'] = @dsn.secret_key if @dsn.secret_key
|
131
|
-
'Sentry ' + fields.map { |key, value| "#{key}=#{value}" }.join(', ')
|
132
|
-
end
|
133
|
-
|
134
122
|
def envelope_from_event(event)
|
135
123
|
# Convert to hash
|
136
124
|
event_payload = event.to_hash
|
@@ -220,3 +208,4 @@ end
|
|
220
208
|
|
221
209
|
require "sentry/transport/dummy_transport"
|
222
210
|
require "sentry/transport/http_transport"
|
211
|
+
require "sentry/transport/spotlight_transport"
|
data/lib/sentry/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: sentry-ruby-core
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 5.
|
4
|
+
version: 5.15.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Sentry Team
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2023-
|
11
|
+
date: 2023-12-05 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: sentry-ruby
|
@@ -16,14 +16,14 @@ dependencies:
|
|
16
16
|
requirements:
|
17
17
|
- - '='
|
18
18
|
- !ruby/object:Gem::Version
|
19
|
-
version: 5.
|
19
|
+
version: 5.15.0
|
20
20
|
type: :runtime
|
21
21
|
prerelease: false
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
23
23
|
requirements:
|
24
24
|
- - '='
|
25
25
|
- !ruby/object:Gem::Version
|
26
|
-
version: 5.
|
26
|
+
version: 5.15.0
|
27
27
|
- !ruby/object:Gem::Dependency
|
28
28
|
name: concurrent-ruby
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
@@ -108,6 +108,7 @@ files:
|
|
108
108
|
- lib/sentry/transport/configuration.rb
|
109
109
|
- lib/sentry/transport/dummy_transport.rb
|
110
110
|
- lib/sentry/transport/http_transport.rb
|
111
|
+
- lib/sentry/transport/spotlight_transport.rb
|
111
112
|
- lib/sentry/utils/argument_checking_helper.rb
|
112
113
|
- lib/sentry/utils/custom_inspection.rb
|
113
114
|
- lib/sentry/utils/encoding_helper.rb
|