sentry-ruby 5.13.0 → 5.19.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/Gemfile +4 -18
- data/README.md +20 -10
- data/Rakefile +1 -1
- data/bin/console +1 -0
- data/lib/sentry/attachment.rb +42 -0
- data/lib/sentry/background_worker.rb +9 -2
- data/lib/sentry/backpressure_monitor.rb +45 -0
- data/lib/sentry/backtrace.rb +7 -3
- data/lib/sentry/check_in_event.rb +1 -1
- data/lib/sentry/client.rb +69 -16
- data/lib/sentry/configuration.rb +57 -14
- data/lib/sentry/cron/configuration.rb +23 -0
- data/lib/sentry/cron/monitor_check_ins.rb +40 -26
- data/lib/sentry/cron/monitor_schedule.rb +1 -1
- data/lib/sentry/dsn.rb +1 -1
- data/lib/sentry/envelope.rb +18 -1
- data/lib/sentry/error_event.rb +2 -2
- data/lib/sentry/event.rb +13 -39
- data/lib/sentry/faraday.rb +77 -0
- data/lib/sentry/graphql.rb +9 -0
- data/lib/sentry/hub.rb +17 -4
- data/lib/sentry/integrable.rb +4 -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 +2 -2
- data/lib/sentry/interfaces/single_exception.rb +7 -4
- data/lib/sentry/interfaces/stacktrace_builder.rb +8 -0
- 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 +43 -0
- data/lib/sentry/metrics.rb +56 -0
- data/lib/sentry/net/http.rb +22 -39
- data/lib/sentry/propagation_context.rb +9 -8
- data/lib/sentry/puma.rb +1 -1
- data/lib/sentry/rack/capture_exceptions.rb +14 -2
- data/lib/sentry/rake.rb +3 -14
- data/lib/sentry/redis.rb +2 -1
- data/lib/sentry/release_detector.rb +1 -1
- data/lib/sentry/scope.rb +47 -37
- data/lib/sentry/session.rb +2 -2
- data/lib/sentry/session_flusher.rb +6 -38
- data/lib/sentry/span.rb +40 -5
- data/lib/sentry/test_helper.rb +2 -1
- data/lib/sentry/threaded_periodic_worker.rb +39 -0
- data/lib/sentry/transaction.rb +25 -16
- data/lib/sentry/transaction_event.rb +5 -0
- data/lib/sentry/transport/configuration.rb +73 -1
- data/lib/sentry/transport/http_transport.rb +68 -37
- data/lib/sentry/transport/spotlight_transport.rb +50 -0
- data/lib/sentry/transport.rb +32 -37
- data/lib/sentry/utils/argument_checking_helper.rb +6 -0
- data/lib/sentry/utils/http_tracing.rb +41 -0
- data/lib/sentry/utils/logging_helper.rb +0 -4
- data/lib/sentry/utils/real_ip.rb +1 -1
- data/lib/sentry/utils/request_id.rb +1 -1
- data/lib/sentry/version.rb +1 -1
- data/lib/sentry-ruby.rb +57 -24
- data/sentry-ruby.gemspec +12 -5
- metadata +42 -7
@@ -17,6 +17,9 @@ module Sentry
|
|
17
17
|
# @return [Hash, nil]
|
18
18
|
attr_accessor :profile
|
19
19
|
|
20
|
+
# @return [Hash, nil]
|
21
|
+
attr_accessor :metrics_summary
|
22
|
+
|
20
23
|
def initialize(transaction:, **options)
|
21
24
|
super(**options)
|
22
25
|
|
@@ -29,6 +32,7 @@ module Sentry
|
|
29
32
|
self.tags = transaction.tags
|
30
33
|
self.dynamic_sampling_context = transaction.get_baggage.dynamic_sampling_context
|
31
34
|
self.measurements = transaction.measurements
|
35
|
+
self.metrics_summary = transaction.metrics_summary
|
32
36
|
|
33
37
|
finished_spans = transaction.span_recorder.spans.select { |span| span.timestamp && span != transaction }
|
34
38
|
self.spans = finished_spans.map(&:to_hash)
|
@@ -49,6 +53,7 @@ module Sentry
|
|
49
53
|
data[:spans] = @spans.map(&:to_hash) if @spans
|
50
54
|
data[:start_timestamp] = @start_timestamp
|
51
55
|
data[:measurements] = @measurements
|
56
|
+
data[:_metrics_summary] = @metrics_summary if @metrics_summary
|
52
57
|
data
|
53
58
|
end
|
54
59
|
|
@@ -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
|
@@ -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,34 +40,76 @@ 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
|
44
54
|
|
45
55
|
if response.code.match?(/\A2\d{2}/)
|
46
|
-
if has_rate_limited_header?(response)
|
47
|
-
|
48
|
-
|
56
|
+
handle_rate_limited_response(response) if has_rate_limited_header?(response)
|
57
|
+
elsif response.code == "429"
|
58
|
+
log_debug("the server responded with status 429")
|
59
|
+
handle_rate_limited_response(response)
|
49
60
|
else
|
50
61
|
error_info = "the server responded with status #{response.code}"
|
62
|
+
error_info += "\nbody: #{response.body}"
|
63
|
+
error_info += " Error in headers is: #{response['x-sentry-error']}" if response['x-sentry-error']
|
64
|
+
|
65
|
+
raise Sentry::ExternalError, error_info
|
66
|
+
end
|
67
|
+
rescue SocketError, *HTTP_ERRORS => e
|
68
|
+
on_error if respond_to?(:on_error)
|
69
|
+
raise Sentry::ExternalError.new(e&.message)
|
70
|
+
end
|
71
|
+
|
72
|
+
def endpoint
|
73
|
+
@dsn.envelope_endpoint
|
74
|
+
end
|
75
|
+
|
76
|
+
def generate_auth_header
|
77
|
+
return nil unless @dsn
|
78
|
+
|
79
|
+
now = Sentry.utc_now.to_i
|
80
|
+
fields = {
|
81
|
+
'sentry_version' => PROTOCOL_VERSION,
|
82
|
+
'sentry_client' => USER_AGENT,
|
83
|
+
'sentry_timestamp' => now,
|
84
|
+
'sentry_key' => @dsn.public_key
|
85
|
+
}
|
86
|
+
fields['sentry_secret'] = @dsn.secret_key if @dsn.secret_key
|
87
|
+
'Sentry ' + fields.map { |key, value| "#{key}=#{value}" }.join(', ')
|
88
|
+
end
|
51
89
|
|
52
|
-
|
53
|
-
|
90
|
+
def conn
|
91
|
+
server = URI(@dsn.server)
|
92
|
+
|
93
|
+
# connection respects proxy setting from @transport_configuration, or environment variables (HTTP_PROXY, HTTPS_PROXY, NO_PROXY)
|
94
|
+
# Net::HTTP will automatically read the env vars.
|
95
|
+
# See https://ruby-doc.org/3.2.2/stdlibs/net/Net/HTTP.html#class-Net::HTTP-label-Proxies
|
96
|
+
connection =
|
97
|
+
if proxy = normalize_proxy(@transport_configuration.proxy)
|
98
|
+
::Net::HTTP.new(server.hostname, server.port, proxy[:uri].hostname, proxy[:uri].port, proxy[:user], proxy[:password])
|
54
99
|
else
|
55
|
-
|
56
|
-
error_info += " Error in headers is: #{response['x-sentry-error']}" if response['x-sentry-error']
|
100
|
+
::Net::HTTP.new(server.hostname, server.port)
|
57
101
|
end
|
58
102
|
|
59
|
-
|
103
|
+
connection.use_ssl = server.scheme == "https"
|
104
|
+
connection.read_timeout = @transport_configuration.timeout
|
105
|
+
connection.write_timeout = @transport_configuration.timeout if connection.respond_to?(:write_timeout)
|
106
|
+
connection.open_timeout = @transport_configuration.open_timeout
|
107
|
+
|
108
|
+
ssl_configuration.each do |key, value|
|
109
|
+
connection.send("#{key}=", value)
|
60
110
|
end
|
61
|
-
|
62
|
-
|
111
|
+
|
112
|
+
connection
|
63
113
|
end
|
64
114
|
|
65
115
|
private
|
@@ -126,28 +176,9 @@ module Sentry
|
|
126
176
|
@transport_configuration.encoding == GZIP_ENCODING && data.bytesize >= GZIP_THRESHOLD
|
127
177
|
end
|
128
178
|
|
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
|
-
|
179
|
+
# @param proxy [String, URI, Hash] Proxy config value passed into `config.transport`.
|
180
|
+
# Accepts either a URI formatted string, URI, or a hash with the `uri`, `user`, and `password` keys.
|
181
|
+
# @return [Hash] Normalized proxy config that will be passed into `Net::HTTP`
|
151
182
|
def normalize_proxy(proxy)
|
152
183
|
return proxy unless proxy
|
153
184
|
|
@@ -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
@@ -1,7 +1,6 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require "json"
|
4
|
-
require "base64"
|
5
4
|
require "sentry/envelope"
|
6
5
|
|
7
6
|
module Sentry
|
@@ -19,7 +18,8 @@ module Sentry
|
|
19
18
|
:sample_rate,
|
20
19
|
:before_send,
|
21
20
|
:event_processor,
|
22
|
-
:insufficient_data
|
21
|
+
:insufficient_data,
|
22
|
+
:backpressure
|
23
23
|
]
|
24
24
|
|
25
25
|
include LoggingHelper
|
@@ -61,7 +61,7 @@ module Sentry
|
|
61
61
|
data, serialized_items = serialize_envelope(envelope)
|
62
62
|
|
63
63
|
if data
|
64
|
-
|
64
|
+
log_debug("[Transport] Sending envelope with items [#{serialized_items.map(&:type).join(', ')}] #{envelope.event_id} to Sentry")
|
65
65
|
send_data(data)
|
66
66
|
end
|
67
67
|
end
|
@@ -74,7 +74,7 @@ module Sentry
|
|
74
74
|
result, oversized = item.serialize
|
75
75
|
|
76
76
|
if oversized
|
77
|
-
|
77
|
+
log_debug("Envelope item [#{item.type}] is still oversized after size reduction: {#{item.size_breakdown}}")
|
78
78
|
|
79
79
|
next
|
80
80
|
end
|
@@ -88,18 +88,9 @@ module Sentry
|
|
88
88
|
[data, serialized_items]
|
89
89
|
end
|
90
90
|
|
91
|
-
def is_rate_limited?(
|
91
|
+
def is_rate_limited?(data_category)
|
92
92
|
# check category-specific limit
|
93
|
-
category_delay =
|
94
|
-
case item_type
|
95
|
-
when "transaction"
|
96
|
-
@rate_limits["transaction"]
|
97
|
-
when "sessions"
|
98
|
-
@rate_limits["session"]
|
99
|
-
else
|
100
|
-
@rate_limits["error"]
|
101
|
-
end
|
102
|
-
|
93
|
+
category_delay = @rate_limits[data_category]
|
103
94
|
# check universal limit if not category limit
|
104
95
|
universal_delay = @rate_limits[nil]
|
105
96
|
|
@@ -119,16 +110,8 @@ module Sentry
|
|
119
110
|
!!delay && delay > Time.now
|
120
111
|
end
|
121
112
|
|
122
|
-
def
|
123
|
-
|
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(', ')
|
113
|
+
def any_rate_limited?
|
114
|
+
@rate_limits.values.any? { |t| t && t > Time.now }
|
132
115
|
end
|
133
116
|
|
134
117
|
def envelope_from_event(event)
|
@@ -162,32 +145,43 @@ module Sentry
|
|
162
145
|
)
|
163
146
|
end
|
164
147
|
|
148
|
+
if event.is_a?(Event) && event.attachments.any?
|
149
|
+
event.attachments.each do |attachment|
|
150
|
+
envelope.add_item(attachment.to_envelope_headers, attachment.payload)
|
151
|
+
end
|
152
|
+
end
|
153
|
+
|
165
154
|
client_report_headers, client_report_payload = fetch_pending_client_report
|
166
155
|
envelope.add_item(client_report_headers, client_report_payload) if client_report_headers
|
167
156
|
|
168
157
|
envelope
|
169
158
|
end
|
170
159
|
|
171
|
-
def record_lost_event(reason,
|
160
|
+
def record_lost_event(reason, data_category, num: 1)
|
172
161
|
return unless @send_client_reports
|
173
162
|
return unless CLIENT_REPORT_REASONS.include?(reason)
|
174
163
|
|
175
|
-
@discarded_events[[reason,
|
164
|
+
@discarded_events[[reason, data_category]] += num
|
165
|
+
end
|
166
|
+
|
167
|
+
def flush
|
168
|
+
client_report_headers, client_report_payload = fetch_pending_client_report(force: true)
|
169
|
+
return unless client_report_headers
|
170
|
+
|
171
|
+
envelope = Envelope.new
|
172
|
+
envelope.add_item(client_report_headers, client_report_payload)
|
173
|
+
send_envelope(envelope)
|
176
174
|
end
|
177
175
|
|
178
176
|
private
|
179
177
|
|
180
|
-
def fetch_pending_client_report
|
178
|
+
def fetch_pending_client_report(force: false)
|
181
179
|
return nil unless @send_client_reports
|
182
|
-
return nil if @last_client_report_sent > Time.now - CLIENT_REPORT_INTERVAL
|
180
|
+
return nil if !force && @last_client_report_sent > Time.now - CLIENT_REPORT_INTERVAL
|
183
181
|
return nil if @discarded_events.empty?
|
184
182
|
|
185
183
|
discarded_events_hash = @discarded_events.map do |key, val|
|
186
|
-
reason,
|
187
|
-
|
188
|
-
# 'event' has to be mapped to 'error'
|
189
|
-
category = type == 'event' ? 'error' : type
|
190
|
-
|
184
|
+
reason, category = key
|
191
185
|
{ reason: reason, category: category, quantity: val }
|
192
186
|
end
|
193
187
|
|
@@ -205,9 +199,9 @@ module Sentry
|
|
205
199
|
|
206
200
|
def reject_rate_limited_items(envelope)
|
207
201
|
envelope.items.reject! do |item|
|
208
|
-
if is_rate_limited?(item.
|
209
|
-
|
210
|
-
record_lost_event(:ratelimit_backoff, item.
|
202
|
+
if is_rate_limited?(item.data_category)
|
203
|
+
log_debug("[Transport] Envelope item [#{item.type}] not sent: rate limiting")
|
204
|
+
record_lost_event(:ratelimit_backoff, item.data_category)
|
211
205
|
|
212
206
|
true
|
213
207
|
else
|
@@ -220,3 +214,4 @@ end
|
|
220
214
|
|
221
215
|
require "sentry/transport/dummy_transport"
|
222
216
|
require "sentry/transport/http_transport"
|
217
|
+
require "sentry/transport/spotlight_transport"
|
@@ -15,5 +15,11 @@ module Sentry
|
|
15
15
|
raise ArgumentError, "expect the argument to be one of #{values.map(&:inspect).join(' or ')}, got #{argument.inspect}"
|
16
16
|
end
|
17
17
|
end
|
18
|
+
|
19
|
+
def check_callable!(name, value)
|
20
|
+
unless value == nil || value.respond_to?(:call)
|
21
|
+
raise ArgumentError, "#{name} must be callable (or nil to disable)"
|
22
|
+
end
|
23
|
+
end
|
18
24
|
end
|
19
25
|
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Sentry
|
4
|
+
module Utils
|
5
|
+
module HttpTracing
|
6
|
+
def set_span_info(sentry_span, request_info, response_status)
|
7
|
+
sentry_span.set_description("#{request_info[:method]} #{request_info[:url]}")
|
8
|
+
sentry_span.set_data(Span::DataConventions::URL, request_info[:url])
|
9
|
+
sentry_span.set_data(Span::DataConventions::HTTP_METHOD, request_info[:method])
|
10
|
+
sentry_span.set_data(Span::DataConventions::HTTP_QUERY, request_info[:query]) if request_info[:query]
|
11
|
+
sentry_span.set_data(Span::DataConventions::HTTP_STATUS_CODE, response_status)
|
12
|
+
end
|
13
|
+
|
14
|
+
def set_propagation_headers(req)
|
15
|
+
Sentry.get_trace_propagation_headers&.each { |k, v| req[k] = v }
|
16
|
+
end
|
17
|
+
|
18
|
+
def record_sentry_breadcrumb(request_info, response_status)
|
19
|
+
crumb = Sentry::Breadcrumb.new(
|
20
|
+
level: :info,
|
21
|
+
category: self.class::BREADCRUMB_CATEGORY,
|
22
|
+
type: :info,
|
23
|
+
data: { status: response_status, **request_info }
|
24
|
+
)
|
25
|
+
|
26
|
+
Sentry.add_breadcrumb(crumb)
|
27
|
+
end
|
28
|
+
|
29
|
+
def record_sentry_breadcrumb?
|
30
|
+
Sentry.initialized? && Sentry.configuration.breadcrumbs_logger.include?(:http_logger)
|
31
|
+
end
|
32
|
+
|
33
|
+
def propagate_trace?(url)
|
34
|
+
url &&
|
35
|
+
Sentry.initialized? &&
|
36
|
+
Sentry.configuration.propagate_traces &&
|
37
|
+
Sentry.configuration.trace_propagation_targets.any? { |target| url.match?(target) }
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
data/lib/sentry/utils/real_ip.rb
CHANGED
@@ -15,7 +15,7 @@ module Sentry
|
|
15
15
|
"fc00::/7", # private IPv6 range fc00::/7
|
16
16
|
"10.0.0.0/8", # private IPv4 range 10.x.x.x
|
17
17
|
"172.16.0.0/12", # private IPv4 range 172.16.0.0 .. 172.31.255.255
|
18
|
-
"192.168.0.0/16"
|
18
|
+
"192.168.0.0/16" # private IPv4 range 192.168.x.x
|
19
19
|
]
|
20
20
|
|
21
21
|
attr_reader :ip
|
@@ -3,7 +3,7 @@
|
|
3
3
|
module Sentry
|
4
4
|
module Utils
|
5
5
|
module RequestId
|
6
|
-
REQUEST_ID_HEADERS = %w
|
6
|
+
REQUEST_ID_HEADERS = %w[action_dispatch.request_id HTTP_X_REQUEST_ID].freeze
|
7
7
|
|
8
8
|
# Request ID based on ActionDispatch::RequestId
|
9
9
|
def self.read_from(env)
|
data/lib/sentry/version.rb
CHANGED
data/lib/sentry-ruby.rb
CHANGED
@@ -20,12 +20,15 @@ require "sentry/span"
|
|
20
20
|
require "sentry/transaction"
|
21
21
|
require "sentry/hub"
|
22
22
|
require "sentry/background_worker"
|
23
|
+
require "sentry/threaded_periodic_worker"
|
23
24
|
require "sentry/session_flusher"
|
25
|
+
require "sentry/backpressure_monitor"
|
24
26
|
require "sentry/cron/monitor_check_ins"
|
27
|
+
require "sentry/metrics"
|
25
28
|
|
26
29
|
[
|
27
30
|
"sentry/rake",
|
28
|
-
"sentry/rack"
|
31
|
+
"sentry/rack"
|
29
32
|
].each do |lib|
|
30
33
|
begin
|
31
34
|
require lib
|
@@ -65,13 +68,21 @@ module Sentry
|
|
65
68
|
end
|
66
69
|
|
67
70
|
# @!attribute [rw] background_worker
|
68
|
-
# @return [BackgroundWorker
|
71
|
+
# @return [BackgroundWorker]
|
69
72
|
attr_accessor :background_worker
|
70
73
|
|
71
74
|
# @!attribute [r] session_flusher
|
72
75
|
# @return [SessionFlusher, nil]
|
73
76
|
attr_reader :session_flusher
|
74
77
|
|
78
|
+
# @!attribute [r] backpressure_monitor
|
79
|
+
# @return [BackpressureMonitor, nil]
|
80
|
+
attr_reader :backpressure_monitor
|
81
|
+
|
82
|
+
# @!attribute [r] metrics_aggregator
|
83
|
+
# @return [Metrics::Aggregator, nil]
|
84
|
+
attr_reader :metrics_aggregator
|
85
|
+
|
75
86
|
##### Patch Registration #####
|
76
87
|
|
77
88
|
# @!visibility private
|
@@ -200,6 +211,13 @@ module Sentry
|
|
200
211
|
get_current_scope.set_context(*args)
|
201
212
|
end
|
202
213
|
|
214
|
+
# @!method add_attachment
|
215
|
+
# @!macro add_attachment
|
216
|
+
def add_attachment(**opts)
|
217
|
+
return unless initialized?
|
218
|
+
get_current_scope.add_attachment(**opts)
|
219
|
+
end
|
220
|
+
|
203
221
|
##### Main APIs #####
|
204
222
|
|
205
223
|
# Initializes the SDK with given configuration.
|
@@ -217,17 +235,10 @@ module Sentry
|
|
217
235
|
Thread.current.thread_variable_set(THREAD_LOCAL, hub)
|
218
236
|
@main_hub = hub
|
219
237
|
@background_worker = Sentry::BackgroundWorker.new(config)
|
220
|
-
|
221
|
-
@
|
222
|
-
|
223
|
-
|
224
|
-
nil
|
225
|
-
end
|
226
|
-
|
227
|
-
if config.include_local_variables
|
228
|
-
exception_locals_tp.enable
|
229
|
-
end
|
230
|
-
|
238
|
+
@session_flusher = config.session_tracking? ? Sentry::SessionFlusher.new(config, client) : nil
|
239
|
+
@backpressure_monitor = config.enable_backpressure_handling ? Sentry::BackpressureMonitor.new(config, client) : nil
|
240
|
+
@metrics_aggregator = config.metrics.enabled ? Sentry::Metrics::Aggregator.new(config, client) : nil
|
241
|
+
exception_locals_tp.enable if config.include_local_variables
|
231
242
|
at_exit { close }
|
232
243
|
end
|
233
244
|
|
@@ -236,20 +247,33 @@ module Sentry
|
|
236
247
|
#
|
237
248
|
# @return [void]
|
238
249
|
def close
|
239
|
-
if @background_worker
|
240
|
-
@background_worker.shutdown
|
241
|
-
@background_worker = nil
|
242
|
-
end
|
243
|
-
|
244
250
|
if @session_flusher
|
251
|
+
@session_flusher.flush
|
245
252
|
@session_flusher.kill
|
246
253
|
@session_flusher = nil
|
247
254
|
end
|
248
255
|
|
249
|
-
if
|
250
|
-
|
256
|
+
if @backpressure_monitor
|
257
|
+
@backpressure_monitor.kill
|
258
|
+
@backpressure_monitor = nil
|
259
|
+
end
|
260
|
+
|
261
|
+
if @metrics_aggregator
|
262
|
+
@metrics_aggregator.flush(force: true)
|
263
|
+
@metrics_aggregator.kill
|
264
|
+
@metrics_aggregator = nil
|
251
265
|
end
|
252
266
|
|
267
|
+
if client = get_current_client
|
268
|
+
client.flush
|
269
|
+
|
270
|
+
if client.configuration.include_local_variables
|
271
|
+
exception_locals_tp.disable
|
272
|
+
end
|
273
|
+
end
|
274
|
+
|
275
|
+
@background_worker.shutdown
|
276
|
+
|
253
277
|
@main_hub = nil
|
254
278
|
Thread.current.thread_variable_set(THREAD_LOCAL, nil)
|
255
279
|
end
|
@@ -442,12 +466,10 @@ module Sentry
|
|
442
466
|
# @option options [Integer] duration seconds elapsed since this monitor started
|
443
467
|
# @option options [Cron::MonitorConfig] monitor_config configuration for this monitor
|
444
468
|
#
|
445
|
-
# @yieldparam scope [Scope]
|
446
|
-
#
|
447
469
|
# @return [String, nil] The {CheckInEvent#check_in_id} to use for later updates on the same slug
|
448
|
-
def capture_check_in(slug, status, **options
|
470
|
+
def capture_check_in(slug, status, **options)
|
449
471
|
return unless initialized?
|
450
|
-
get_current_hub.capture_check_in(slug, status, **options
|
472
|
+
get_current_hub.capture_check_in(slug, status, **options)
|
451
473
|
end
|
452
474
|
|
453
475
|
# Takes or initializes a new Sentry::Transaction and makes a sampling decision for it.
|
@@ -536,6 +558,15 @@ module Sentry
|
|
536
558
|
get_current_hub.get_trace_propagation_headers
|
537
559
|
end
|
538
560
|
|
561
|
+
# Returns the a Hash containing sentry-trace and baggage.
|
562
|
+
# Can be either from the currently active span or the propagation context.
|
563
|
+
#
|
564
|
+
# @return [String]
|
565
|
+
def get_trace_propagation_meta
|
566
|
+
return '' unless initialized?
|
567
|
+
get_current_hub.get_trace_propagation_meta
|
568
|
+
end
|
569
|
+
|
539
570
|
# Continue an incoming trace from a rack env like hash.
|
540
571
|
#
|
541
572
|
# @param env [Hash]
|
@@ -576,3 +607,5 @@ end
|
|
576
607
|
require "sentry/net/http"
|
577
608
|
require "sentry/redis"
|
578
609
|
require "sentry/puma"
|
610
|
+
require "sentry/graphql"
|
611
|
+
require "sentry/faraday"
|