sentry-ruby 5.16.1 → 5.20.1
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 +3 -0
- data/README.md +20 -10
- data/Rakefile +1 -1
- data/bin/console +1 -0
- data/lib/sentry/attachment.rb +40 -0
- data/lib/sentry/background_worker.rb +1 -1
- data/lib/sentry/backpressure_monitor.rb +2 -32
- data/lib/sentry/backtrace.rb +8 -6
- data/lib/sentry/baggage.rb +6 -6
- data/lib/sentry/breadcrumb/sentry_logger.rb +6 -6
- data/lib/sentry/check_in_event.rb +5 -5
- data/lib/sentry/client.rb +61 -11
- data/lib/sentry/configuration.rb +53 -25
- data/lib/sentry/core_ext/object/deep_dup.rb +1 -1
- data/lib/sentry/cron/monitor_check_ins.rb +1 -1
- data/lib/sentry/cron/monitor_config.rb +1 -1
- data/lib/sentry/cron/monitor_schedule.rb +1 -1
- data/lib/sentry/dsn.rb +4 -4
- data/lib/sentry/envelope.rb +19 -2
- data/lib/sentry/error_event.rb +2 -2
- data/lib/sentry/event.rb +20 -18
- data/lib/sentry/faraday.rb +77 -0
- data/lib/sentry/graphql.rb +9 -0
- data/lib/sentry/hub.rb +23 -3
- 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 +7 -7
- data/lib/sentry/interfaces/single_exception.rb +7 -5
- data/lib/sentry/interfaces/stacktrace.rb +3 -1
- data/lib/sentry/interfaces/stacktrace_builder.rb +23 -2
- 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 +43 -0
- data/lib/sentry/metrics.rb +56 -0
- data/lib/sentry/net/http.rb +18 -39
- data/lib/sentry/profiler.rb +19 -20
- data/lib/sentry/propagation_context.rb +10 -9
- data/lib/sentry/puma.rb +1 -1
- data/lib/sentry/rack/capture_exceptions.rb +15 -3
- data/lib/sentry/rack.rb +2 -2
- data/lib/sentry/rake.rb +4 -2
- data/lib/sentry/redis.rb +2 -1
- data/lib/sentry/release_detector.rb +4 -4
- data/lib/sentry/scope.rb +36 -26
- data/lib/sentry/session.rb +2 -2
- data/lib/sentry/session_flusher.rb +7 -39
- data/lib/sentry/span.rb +46 -5
- data/lib/sentry/test_helper.rb +3 -2
- data/lib/sentry/threaded_periodic_worker.rb +39 -0
- data/lib/sentry/transaction.rb +17 -15
- data/lib/sentry/transaction_event.rb +6 -1
- data/lib/sentry/transport/configuration.rb +0 -1
- data/lib/sentry/transport/http_transport.rb +12 -12
- data/lib/sentry/transport.rb +18 -26
- data/lib/sentry/utils/argument_checking_helper.rb +6 -0
- data/lib/sentry/utils/env_helper.rb +21 -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 +2 -2
- data/lib/sentry/utils/request_id.rb +1 -1
- data/lib/sentry/version.rb +1 -1
- data/lib/sentry-ruby.rb +34 -3
- data/sentry-ruby-core.gemspec +1 -1
- data/sentry-ruby.gemspec +13 -6
- metadata +40 -7
@@ -1,58 +1,38 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
module Sentry
|
4
|
-
class SessionFlusher
|
5
|
-
include LoggingHelper
|
6
|
-
|
4
|
+
class SessionFlusher < ThreadedPeriodicWorker
|
7
5
|
FLUSH_INTERVAL = 60
|
8
6
|
|
9
7
|
def initialize(configuration, client)
|
10
|
-
|
11
|
-
@exited = false
|
8
|
+
super(configuration.logger, FLUSH_INTERVAL)
|
12
9
|
@client = client
|
13
10
|
@pending_aggregates = {}
|
14
11
|
@release = configuration.release
|
15
12
|
@environment = configuration.environment
|
16
|
-
@logger = configuration.logger
|
17
13
|
|
18
14
|
log_debug("[Sessions] Sessions won't be captured without a valid release") unless @release
|
19
15
|
end
|
20
16
|
|
21
17
|
def flush
|
22
18
|
return if @pending_aggregates.empty?
|
23
|
-
envelope = pending_envelope
|
24
|
-
|
25
|
-
Sentry.background_worker.perform do
|
26
|
-
@client.transport.send_envelope(envelope)
|
27
|
-
end
|
28
19
|
|
20
|
+
@client.capture_envelope(pending_envelope)
|
29
21
|
@pending_aggregates = {}
|
30
22
|
end
|
31
23
|
|
24
|
+
alias_method :run, :flush
|
25
|
+
|
32
26
|
def add_session(session)
|
33
|
-
return if @exited
|
34
27
|
return unless @release
|
35
28
|
|
36
|
-
|
37
|
-
ensure_thread
|
38
|
-
rescue ThreadError
|
39
|
-
log_debug("Session flusher thread creation failed")
|
40
|
-
@exited = true
|
41
|
-
return
|
42
|
-
end
|
29
|
+
return unless ensure_thread
|
43
30
|
|
44
31
|
return unless Session::AGGREGATE_STATUSES.include?(session.status)
|
45
32
|
@pending_aggregates[session.aggregation_key] ||= init_aggregates(session.aggregation_key)
|
46
33
|
@pending_aggregates[session.aggregation_key][session.status] += 1
|
47
34
|
end
|
48
35
|
|
49
|
-
def kill
|
50
|
-
log_debug("Killing session flusher")
|
51
|
-
|
52
|
-
@exited = true
|
53
|
-
@thread&.kill
|
54
|
-
end
|
55
|
-
|
56
36
|
private
|
57
37
|
|
58
38
|
def init_aggregates(aggregation_key)
|
@@ -64,7 +44,7 @@ module Sentry
|
|
64
44
|
def pending_envelope
|
65
45
|
envelope = Envelope.new
|
66
46
|
|
67
|
-
header = { type:
|
47
|
+
header = { type: "sessions" }
|
68
48
|
payload = { attrs: attrs, aggregates: @pending_aggregates.values }
|
69
49
|
|
70
50
|
envelope.add_item(header, payload)
|
@@ -74,17 +54,5 @@ module Sentry
|
|
74
54
|
def attrs
|
75
55
|
{ release: @release, environment: @environment }
|
76
56
|
end
|
77
|
-
|
78
|
-
def ensure_thread
|
79
|
-
return if @thread&.alive?
|
80
|
-
|
81
|
-
@thread = Thread.new do
|
82
|
-
loop do
|
83
|
-
sleep(FLUSH_INTERVAL)
|
84
|
-
flush
|
85
|
-
end
|
86
|
-
end
|
87
|
-
end
|
88
|
-
|
89
57
|
end
|
90
58
|
end
|
data/lib/sentry/span.rb
CHANGED
@@ -1,10 +1,10 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require "securerandom"
|
4
|
+
require "sentry/metrics/local_aggregator"
|
4
5
|
|
5
6
|
module Sentry
|
6
7
|
class Span
|
7
|
-
|
8
8
|
# We will try to be consistent with OpenTelemetry on this front going forward.
|
9
9
|
# https://develop.sentry.dev/sdk/performance/span-data-conventions/
|
10
10
|
module DataConventions
|
@@ -39,6 +39,11 @@ module Sentry
|
|
39
39
|
# Recommended: If different than server.port.
|
40
40
|
# Example: 16456
|
41
41
|
SERVER_SOCKET_PORT = "server.socket.port"
|
42
|
+
|
43
|
+
FILEPATH = "code.filepath"
|
44
|
+
LINENO = "code.lineno"
|
45
|
+
FUNCTION = "code.function"
|
46
|
+
NAMESPACE = "code.namespace"
|
42
47
|
end
|
43
48
|
|
44
49
|
STATUS_MAP = {
|
@@ -55,6 +60,8 @@ module Sentry
|
|
55
60
|
504 => "deadline_exceeded"
|
56
61
|
}
|
57
62
|
|
63
|
+
DEFAULT_SPAN_ORIGIN = "manual"
|
64
|
+
|
58
65
|
# An uuid that can be used to identify a trace.
|
59
66
|
# @return [String]
|
60
67
|
attr_reader :trace_id
|
@@ -88,6 +95,9 @@ module Sentry
|
|
88
95
|
# Span data
|
89
96
|
# @return [Hash]
|
90
97
|
attr_reader :data
|
98
|
+
# Span origin that tracks what kind of instrumentation created a span
|
99
|
+
# @return [String]
|
100
|
+
attr_reader :origin
|
91
101
|
|
92
102
|
# The SpanRecorder the current span belongs to.
|
93
103
|
# SpanRecorder holds all spans under the same Transaction object (including the Transaction itself).
|
@@ -109,7 +119,8 @@ module Sentry
|
|
109
119
|
parent_span_id: nil,
|
110
120
|
sampled: nil,
|
111
121
|
start_timestamp: nil,
|
112
|
-
timestamp: nil
|
122
|
+
timestamp: nil,
|
123
|
+
origin: nil
|
113
124
|
)
|
114
125
|
@trace_id = trace_id || SecureRandom.uuid.delete("-")
|
115
126
|
@span_id = span_id || SecureRandom.uuid.delete("-").slice(0, 16)
|
@@ -123,6 +134,7 @@ module Sentry
|
|
123
134
|
@status = status
|
124
135
|
@data = {}
|
125
136
|
@tags = {}
|
137
|
+
@origin = origin || DEFAULT_SPAN_ORIGIN
|
126
138
|
end
|
127
139
|
|
128
140
|
# Finishes the span by adding a timestamp.
|
@@ -148,9 +160,15 @@ module Sentry
|
|
148
160
|
transaction.get_baggage&.serialize
|
149
161
|
end
|
150
162
|
|
163
|
+
# Returns the Dynamic Sampling Context from the transaction baggage.
|
164
|
+
# @return [Hash, nil]
|
165
|
+
def get_dynamic_sampling_context
|
166
|
+
transaction.get_baggage&.dynamic_sampling_context
|
167
|
+
end
|
168
|
+
|
151
169
|
# @return [Hash]
|
152
170
|
def to_hash
|
153
|
-
{
|
171
|
+
hash = {
|
154
172
|
trace_id: @trace_id,
|
155
173
|
span_id: @span_id,
|
156
174
|
parent_span_id: @parent_span_id,
|
@@ -160,8 +178,14 @@ module Sentry
|
|
160
178
|
op: @op,
|
161
179
|
status: @status,
|
162
180
|
tags: @tags,
|
163
|
-
data: @data
|
181
|
+
data: @data,
|
182
|
+
origin: @origin
|
164
183
|
}
|
184
|
+
|
185
|
+
summary = metrics_summary
|
186
|
+
hash[:_metrics_summary] = summary if summary
|
187
|
+
|
188
|
+
hash
|
165
189
|
end
|
166
190
|
|
167
191
|
# Returns the span's context that can be used to embed in an Event.
|
@@ -173,7 +197,9 @@ module Sentry
|
|
173
197
|
parent_span_id: @parent_span_id,
|
174
198
|
description: @description,
|
175
199
|
op: @op,
|
176
|
-
status: @status
|
200
|
+
status: @status,
|
201
|
+
origin: @origin,
|
202
|
+
data: @data
|
177
203
|
}
|
178
204
|
end
|
179
205
|
|
@@ -269,5 +295,20 @@ module Sentry
|
|
269
295
|
def set_tag(key, value)
|
270
296
|
@tags[key] = value
|
271
297
|
end
|
298
|
+
|
299
|
+
# Sets the origin of the span.
|
300
|
+
# @param origin [String]
|
301
|
+
def set_origin(origin)
|
302
|
+
@origin = origin
|
303
|
+
end
|
304
|
+
|
305
|
+
# Collects gauge metrics on the span for metric summaries.
|
306
|
+
def metrics_local_aggregator
|
307
|
+
@metrics_local_aggregator ||= Sentry::Metrics::LocalAggregator.new
|
308
|
+
end
|
309
|
+
|
310
|
+
def metrics_summary
|
311
|
+
@metrics_local_aggregator&.to_hash
|
312
|
+
end
|
272
313
|
end
|
273
314
|
end
|
data/lib/sentry/test_helper.rb
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
module Sentry
|
2
2
|
module TestHelper
|
3
|
-
DUMMY_DSN =
|
3
|
+
DUMMY_DSN = "http://12345:67890@sentry.localdomain/sentry/42"
|
4
4
|
|
5
5
|
# Alters the existing SDK configuration with test-suitable options. Mainly:
|
6
6
|
# - Sets a dummy DSN instead of `nil` or an actual DSN.
|
@@ -20,7 +20,7 @@ module Sentry
|
|
20
20
|
# set transport to DummyTransport, so we can easily intercept the captured events
|
21
21
|
dummy_config.transport.transport_class = Sentry::DummyTransport
|
22
22
|
# make sure SDK allows sending under the current environment
|
23
|
-
dummy_config.enabled_environments
|
23
|
+
dummy_config.enabled_environments += [dummy_config.environment] unless dummy_config.enabled_environments.include?(dummy_config.environment)
|
24
24
|
# disble async event sending
|
25
25
|
dummy_config.background_worker_threads = 0
|
26
26
|
|
@@ -50,6 +50,7 @@ module Sentry
|
|
50
50
|
if Sentry.get_current_hub.instance_variable_get(:@stack).size > 1
|
51
51
|
Sentry.get_current_hub.pop_scope
|
52
52
|
end
|
53
|
+
Sentry::Scope.global_event_processors.clear
|
53
54
|
end
|
54
55
|
|
55
56
|
# @return [Transport]
|
@@ -0,0 +1,39 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Sentry
|
4
|
+
class ThreadedPeriodicWorker
|
5
|
+
include LoggingHelper
|
6
|
+
|
7
|
+
def initialize(logger, internal)
|
8
|
+
@thread = nil
|
9
|
+
@exited = false
|
10
|
+
@interval = internal
|
11
|
+
@logger = logger
|
12
|
+
end
|
13
|
+
|
14
|
+
def ensure_thread
|
15
|
+
return false if @exited
|
16
|
+
return true if @thread&.alive?
|
17
|
+
|
18
|
+
@thread = Thread.new do
|
19
|
+
loop do
|
20
|
+
sleep(@interval)
|
21
|
+
run
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
true
|
26
|
+
rescue ThreadError
|
27
|
+
log_debug("[#{self.class.name}] thread creation failed")
|
28
|
+
@exited = true
|
29
|
+
false
|
30
|
+
end
|
31
|
+
|
32
|
+
def kill
|
33
|
+
log_debug("[#{self.class.name}] thread killed")
|
34
|
+
|
35
|
+
@exited = true
|
36
|
+
@thread&.kill
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
data/lib/sentry/transaction.rb
CHANGED
@@ -13,7 +13,7 @@ module Sentry
|
|
13
13
|
MESSAGE_PREFIX = "[Tracing]"
|
14
14
|
|
15
15
|
# https://develop.sentry.dev/sdk/event-payloads/transaction/#transaction-annotations
|
16
|
-
SOURCES = %i
|
16
|
+
SOURCES = %i[custom url route view component task]
|
17
17
|
|
18
18
|
include LoggingHelper
|
19
19
|
|
@@ -110,14 +110,15 @@ module Sentry
|
|
110
110
|
|
111
111
|
trace_id, parent_span_id, parent_sampled = sentry_trace_data
|
112
112
|
|
113
|
-
baggage =
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
113
|
+
baggage =
|
114
|
+
if baggage && !baggage.empty?
|
115
|
+
Baggage.from_incoming_header(baggage)
|
116
|
+
else
|
117
|
+
# If there's an incoming sentry-trace but no incoming baggage header,
|
118
|
+
# for instance in traces coming from older SDKs,
|
119
|
+
# baggage will be empty and frozen and won't be populated as head SDK.
|
120
|
+
Baggage.new({})
|
121
|
+
end
|
121
122
|
|
122
123
|
baggage.freeze!
|
123
124
|
|
@@ -264,7 +265,8 @@ module Sentry
|
|
264
265
|
else
|
265
266
|
is_backpressure = Sentry.backpressure_monitor&.downsample_factor&.positive?
|
266
267
|
reason = is_backpressure ? :backpressure : :sample_rate
|
267
|
-
hub.current_client.transport.record_lost_event(reason,
|
268
|
+
hub.current_client.transport.record_lost_event(reason, "transaction")
|
269
|
+
hub.current_client.transport.record_lost_event(reason, "span")
|
268
270
|
end
|
269
271
|
end
|
270
272
|
|
@@ -301,6 +303,11 @@ module Sentry
|
|
301
303
|
profiler.start
|
302
304
|
end
|
303
305
|
|
306
|
+
# These are high cardinality and thus bad
|
307
|
+
def source_low_quality?
|
308
|
+
source == :url
|
309
|
+
end
|
310
|
+
|
304
311
|
protected
|
305
312
|
|
306
313
|
def init_span_recorder(limit = 1000)
|
@@ -336,11 +343,6 @@ module Sentry
|
|
336
343
|
@baggage = Baggage.new(items, mutable: false)
|
337
344
|
end
|
338
345
|
|
339
|
-
# These are high cardinality and thus bad
|
340
|
-
def source_low_quality?
|
341
|
-
source == :url
|
342
|
-
end
|
343
|
-
|
344
346
|
class SpanRecorder
|
345
347
|
attr_reader :max_length, :spans
|
346
348
|
|
@@ -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
|
|
@@ -70,7 +75,7 @@ module Sentry
|
|
70
75
|
name: transaction.name,
|
71
76
|
trace_id: transaction.trace_id,
|
72
77
|
# TODO-neel-profiler stubbed for now, see thread_id note in profiler.rb
|
73
|
-
active_thead_id:
|
78
|
+
active_thead_id: "0"
|
74
79
|
}
|
75
80
|
)
|
76
81
|
|
@@ -7,7 +7,7 @@ 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"
|
@@ -38,13 +38,13 @@ module Sentry
|
|
38
38
|
end
|
39
39
|
|
40
40
|
headers = {
|
41
|
-
|
42
|
-
|
43
|
-
|
41
|
+
"Content-Type" => CONTENT_TYPE,
|
42
|
+
"Content-Encoding" => encoding,
|
43
|
+
"User-Agent" => USER_AGENT
|
44
44
|
}
|
45
45
|
|
46
46
|
auth_header = generate_auth_header
|
47
|
-
headers[
|
47
|
+
headers["X-Sentry-Auth"] = auth_header if auth_header
|
48
48
|
|
49
49
|
response = conn.start do |http|
|
50
50
|
request = ::Net::HTTP::Post.new(endpoint, headers)
|
@@ -60,7 +60,7 @@ module Sentry
|
|
60
60
|
else
|
61
61
|
error_info = "the server responded with status #{response.code}"
|
62
62
|
error_info += "\nbody: #{response.body}"
|
63
|
-
error_info += " Error in headers is: #{response['x-sentry-error']}" if response[
|
63
|
+
error_info += " Error in headers is: #{response['x-sentry-error']}" if response["x-sentry-error"]
|
64
64
|
|
65
65
|
raise Sentry::ExternalError, error_info
|
66
66
|
end
|
@@ -78,13 +78,13 @@ module Sentry
|
|
78
78
|
|
79
79
|
now = Sentry.utc_now.to_i
|
80
80
|
fields = {
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
81
|
+
"sentry_version" => PROTOCOL_VERSION,
|
82
|
+
"sentry_client" => USER_AGENT,
|
83
|
+
"sentry_timestamp" => now,
|
84
|
+
"sentry_key" => @dsn.public_key
|
85
85
|
}
|
86
|
-
fields[
|
87
|
-
|
86
|
+
fields["sentry_secret"] = @dsn.secret_key if @dsn.secret_key
|
87
|
+
"Sentry " + fields.map { |key, value| "#{key}=#{value}" }.join(", ")
|
88
88
|
end
|
89
89
|
|
90
90
|
def conn
|
data/lib/sentry/transport.rb
CHANGED
@@ -1,12 +1,11 @@
|
|
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
|
8
7
|
class Transport
|
9
|
-
PROTOCOL_VERSION =
|
8
|
+
PROTOCOL_VERSION = "7"
|
10
9
|
USER_AGENT = "sentry-ruby/#{Sentry::VERSION}"
|
11
10
|
CLIENT_REPORT_INTERVAL = 30
|
12
11
|
|
@@ -62,7 +61,7 @@ module Sentry
|
|
62
61
|
data, serialized_items = serialize_envelope(envelope)
|
63
62
|
|
64
63
|
if data
|
65
|
-
|
64
|
+
log_debug("[Transport] Sending envelope with items [#{serialized_items.map(&:type).join(', ')}] #{envelope.event_id} to Sentry")
|
66
65
|
send_data(data)
|
67
66
|
end
|
68
67
|
end
|
@@ -89,18 +88,9 @@ module Sentry
|
|
89
88
|
[data, serialized_items]
|
90
89
|
end
|
91
90
|
|
92
|
-
def is_rate_limited?(
|
91
|
+
def is_rate_limited?(data_category)
|
93
92
|
# check category-specific limit
|
94
|
-
category_delay =
|
95
|
-
case item_type
|
96
|
-
when "transaction"
|
97
|
-
@rate_limits["transaction"]
|
98
|
-
when "sessions"
|
99
|
-
@rate_limits["session"]
|
100
|
-
else
|
101
|
-
@rate_limits["error"]
|
102
|
-
end
|
103
|
-
|
93
|
+
category_delay = @rate_limits[data_category]
|
104
94
|
# check universal limit if not category limit
|
105
95
|
universal_delay = @rate_limits[nil]
|
106
96
|
|
@@ -144,28 +134,34 @@ module Sentry
|
|
144
134
|
envelope = Envelope.new(envelope_headers)
|
145
135
|
|
146
136
|
envelope.add_item(
|
147
|
-
{ type: item_type, content_type:
|
137
|
+
{ type: item_type, content_type: "application/json" },
|
148
138
|
event_payload
|
149
139
|
)
|
150
140
|
|
151
141
|
if event.is_a?(TransactionEvent) && event.profile
|
152
142
|
envelope.add_item(
|
153
|
-
{ type:
|
143
|
+
{ type: "profile", content_type: "application/json" },
|
154
144
|
event.profile
|
155
145
|
)
|
156
146
|
end
|
157
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
|
+
|
158
154
|
client_report_headers, client_report_payload = fetch_pending_client_report
|
159
155
|
envelope.add_item(client_report_headers, client_report_payload) if client_report_headers
|
160
156
|
|
161
157
|
envelope
|
162
158
|
end
|
163
159
|
|
164
|
-
def record_lost_event(reason,
|
160
|
+
def record_lost_event(reason, data_category, num: 1)
|
165
161
|
return unless @send_client_reports
|
166
162
|
return unless CLIENT_REPORT_REASONS.include?(reason)
|
167
163
|
|
168
|
-
@discarded_events[[reason,
|
164
|
+
@discarded_events[[reason, data_category]] += num
|
169
165
|
end
|
170
166
|
|
171
167
|
def flush
|
@@ -185,15 +181,11 @@ module Sentry
|
|
185
181
|
return nil if @discarded_events.empty?
|
186
182
|
|
187
183
|
discarded_events_hash = @discarded_events.map do |key, val|
|
188
|
-
reason,
|
189
|
-
|
190
|
-
# 'event' has to be mapped to 'error'
|
191
|
-
category = type == 'event' ? 'error' : type
|
192
|
-
|
184
|
+
reason, category = key
|
193
185
|
{ reason: reason, category: category, quantity: val }
|
194
186
|
end
|
195
187
|
|
196
|
-
item_header = { type:
|
188
|
+
item_header = { type: "client_report" }
|
197
189
|
item_payload = {
|
198
190
|
timestamp: Sentry.utc_now.iso8601,
|
199
191
|
discarded_events: discarded_events_hash
|
@@ -207,9 +199,9 @@ module Sentry
|
|
207
199
|
|
208
200
|
def reject_rate_limited_items(envelope)
|
209
201
|
envelope.items.reject! do |item|
|
210
|
-
if is_rate_limited?(item.
|
202
|
+
if is_rate_limited?(item.data_category)
|
211
203
|
log_debug("[Transport] Envelope item [#{item.type}] not sent: rate limiting")
|
212
|
-
record_lost_event(:ratelimit_backoff, item.
|
204
|
+
record_lost_event(:ratelimit_backoff, item.data_category)
|
213
205
|
|
214
206
|
true
|
215
207
|
else
|
@@ -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,21 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Sentry
|
4
|
+
module Utils
|
5
|
+
module EnvHelper
|
6
|
+
TRUTHY_ENV_VALUES = %w[t true yes y 1 on].freeze
|
7
|
+
FALSY_ENV_VALUES = %w[f false no n 0 off].freeze
|
8
|
+
|
9
|
+
def self.env_to_bool(value, strict: false)
|
10
|
+
value = value.to_s
|
11
|
+
normalized = value.downcase
|
12
|
+
|
13
|
+
return false if FALSY_ENV_VALUES.include?(normalized)
|
14
|
+
|
15
|
+
return true if TRUTHY_ENV_VALUES.include?(normalized)
|
16
|
+
|
17
|
+
strict ? nil : !(value.nil? || value.empty?)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
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
@@ -1,6 +1,6 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require
|
3
|
+
require "ipaddr"
|
4
4
|
|
5
5
|
# Based on ActionDispatch::RemoteIp. All security-related precautions from that
|
6
6
|
# middleware have been removed, because the Event IP just needs to be accurate,
|
@@ -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