sentry-ruby 5.13.0 → 5.15.0
Sign up to get free protection for your applications and to get access to all the features.
- 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 +3 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 8ad04d5359f4cc44d2a2e398d1083b72f11c96efabc4a3b2148327261e327a20
|
4
|
+
data.tar.gz: 2800581dce649c81f5a17464f3b5619158900d8c5eb23e35347507dddb594b04
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: d8bce6cfd5f48acb743b6018e5d34e4832de9eb05e68a0c114fd90bb13e8c80fcf6da97cb8e80a64d78e930b3ee6b8ee22711c2efa2e018f43b33d0ff09cf68b
|
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
|
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: concurrent-ruby
|
@@ -100,6 +100,7 @@ files:
|
|
100
100
|
- lib/sentry/transport/configuration.rb
|
101
101
|
- lib/sentry/transport/dummy_transport.rb
|
102
102
|
- lib/sentry/transport/http_transport.rb
|
103
|
+
- lib/sentry/transport/spotlight_transport.rb
|
103
104
|
- lib/sentry/utils/argument_checking_helper.rb
|
104
105
|
- lib/sentry/utils/custom_inspection.rb
|
105
106
|
- lib/sentry/utils/encoding_helper.rb
|