sentry-ruby 5.4.2 → 5.16.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/.rspec +0 -1
- data/Gemfile +13 -14
- data/README.md +11 -8
- data/Rakefile +8 -1
- data/lib/sentry/background_worker.rb +8 -1
- data/lib/sentry/backpressure_monitor.rb +75 -0
- data/lib/sentry/backtrace.rb +1 -1
- data/lib/sentry/baggage.rb +70 -0
- data/lib/sentry/breadcrumb.rb +8 -2
- data/lib/sentry/check_in_event.rb +60 -0
- data/lib/sentry/client.rb +77 -19
- data/lib/sentry/configuration.rb +177 -29
- data/lib/sentry/cron/configuration.rb +23 -0
- data/lib/sentry/cron/monitor_check_ins.rb +75 -0
- data/lib/sentry/cron/monitor_config.rb +53 -0
- data/lib/sentry/cron/monitor_schedule.rb +42 -0
- data/lib/sentry/envelope.rb +2 -5
- data/lib/sentry/event.rb +7 -29
- data/lib/sentry/hub.rb +100 -4
- data/lib/sentry/integrable.rb +6 -0
- data/lib/sentry/interfaces/request.rb +6 -16
- data/lib/sentry/interfaces/single_exception.rb +13 -3
- data/lib/sentry/net/http.rb +37 -46
- data/lib/sentry/profiler.rb +233 -0
- data/lib/sentry/propagation_context.rb +134 -0
- data/lib/sentry/puma.rb +32 -0
- data/lib/sentry/rack/capture_exceptions.rb +4 -5
- data/lib/sentry/rake.rb +1 -14
- data/lib/sentry/redis.rb +41 -23
- data/lib/sentry/release_detector.rb +1 -1
- data/lib/sentry/scope.rb +81 -16
- data/lib/sentry/session.rb +5 -7
- data/lib/sentry/span.rb +57 -10
- data/lib/sentry/test_helper.rb +19 -11
- data/lib/sentry/transaction.rb +183 -30
- data/lib/sentry/transaction_event.rb +51 -0
- data/lib/sentry/transport/configuration.rb +74 -1
- data/lib/sentry/transport/http_transport.rb +68 -37
- data/lib/sentry/transport/spotlight_transport.rb +50 -0
- data/lib/sentry/transport.rb +39 -24
- data/lib/sentry/utils/argument_checking_helper.rb +9 -3
- data/lib/sentry/utils/encoding_helper.rb +22 -0
- data/lib/sentry/version.rb +1 -1
- data/lib/sentry-ruby.rb +116 -41
- metadata +14 -3
- data/CODE_OF_CONDUCT.md +0 -74
data/lib/sentry/span.rb
CHANGED
@@ -4,6 +4,43 @@ require "securerandom"
|
|
4
4
|
|
5
5
|
module Sentry
|
6
6
|
class Span
|
7
|
+
|
8
|
+
# We will try to be consistent with OpenTelemetry on this front going forward.
|
9
|
+
# https://develop.sentry.dev/sdk/performance/span-data-conventions/
|
10
|
+
module DataConventions
|
11
|
+
URL = "url"
|
12
|
+
HTTP_STATUS_CODE = "http.response.status_code"
|
13
|
+
HTTP_QUERY = "http.query"
|
14
|
+
HTTP_METHOD = "http.request.method"
|
15
|
+
|
16
|
+
# An identifier for the database management system (DBMS) product being used.
|
17
|
+
# Example: postgresql
|
18
|
+
DB_SYSTEM = "db.system"
|
19
|
+
|
20
|
+
# The name of the database being accessed.
|
21
|
+
# For commands that switch the database, this should be set to the target database
|
22
|
+
# (even if the command fails).
|
23
|
+
# Example: myDatabase
|
24
|
+
DB_NAME = "db.name"
|
25
|
+
|
26
|
+
# Name of the database host.
|
27
|
+
# Example: example.com
|
28
|
+
SERVER_ADDRESS = "server.address"
|
29
|
+
|
30
|
+
# Logical server port number
|
31
|
+
# Example: 80; 8080; 443
|
32
|
+
SERVER_PORT = "server.port"
|
33
|
+
|
34
|
+
# Physical server IP address or Unix socket address.
|
35
|
+
# Example: 10.5.3.2
|
36
|
+
SERVER_SOCKET_ADDRESS = "server.socket.address"
|
37
|
+
|
38
|
+
# Physical server port.
|
39
|
+
# Recommended: If different than server.port.
|
40
|
+
# Example: 16456
|
41
|
+
SERVER_SOCKET_PORT = "server.socket.port"
|
42
|
+
end
|
43
|
+
|
7
44
|
STATUS_MAP = {
|
8
45
|
400 => "invalid_argument",
|
9
46
|
401 => "unauthenticated",
|
@@ -60,25 +97,28 @@ module Sentry
|
|
60
97
|
# The Transaction object the Span belongs to.
|
61
98
|
# Every span needs to be attached to a Transaction and their child spans will also inherit the same transaction.
|
62
99
|
# @return [Transaction]
|
63
|
-
|
100
|
+
attr_reader :transaction
|
64
101
|
|
65
102
|
def initialize(
|
103
|
+
transaction:,
|
66
104
|
description: nil,
|
67
105
|
op: nil,
|
68
106
|
status: nil,
|
69
107
|
trace_id: nil,
|
108
|
+
span_id: nil,
|
70
109
|
parent_span_id: nil,
|
71
110
|
sampled: nil,
|
72
111
|
start_timestamp: nil,
|
73
112
|
timestamp: nil
|
74
113
|
)
|
75
114
|
@trace_id = trace_id || SecureRandom.uuid.delete("-")
|
76
|
-
@span_id = SecureRandom.
|
115
|
+
@span_id = span_id || SecureRandom.uuid.delete("-").slice(0, 16)
|
77
116
|
@parent_span_id = parent_span_id
|
78
117
|
@sampled = sampled
|
79
118
|
@start_timestamp = start_timestamp || Sentry.utc_now.to_f
|
80
119
|
@timestamp = timestamp
|
81
120
|
@description = description
|
121
|
+
@transaction = transaction
|
82
122
|
@op = op
|
83
123
|
@status = status
|
84
124
|
@data = {}
|
@@ -87,11 +127,8 @@ module Sentry
|
|
87
127
|
|
88
128
|
# Finishes the span by adding a timestamp.
|
89
129
|
# @return [self]
|
90
|
-
def finish
|
91
|
-
|
92
|
-
return if @timestamp
|
93
|
-
|
94
|
-
@timestamp = Sentry.utc_now.to_f
|
130
|
+
def finish(end_timestamp: nil)
|
131
|
+
@timestamp = end_timestamp || @timestamp || Sentry.utc_now.to_f
|
95
132
|
self
|
96
133
|
end
|
97
134
|
|
@@ -104,6 +141,13 @@ module Sentry
|
|
104
141
|
"#{@trace_id}-#{@span_id}-#{sampled_flag}"
|
105
142
|
end
|
106
143
|
|
144
|
+
# Generates a W3C Baggage header string for distributed tracing
|
145
|
+
# from the incoming baggage stored on the transaction.
|
146
|
+
# @return [String, nil]
|
147
|
+
def to_baggage
|
148
|
+
transaction.get_baggage&.serialize
|
149
|
+
end
|
150
|
+
|
107
151
|
# @return [Hash]
|
108
152
|
def to_hash
|
109
153
|
{
|
@@ -136,9 +180,8 @@ module Sentry
|
|
136
180
|
# Starts a child span with given attributes.
|
137
181
|
# @param attributes [Hash] the attributes for the child span.
|
138
182
|
def start_child(**attributes)
|
139
|
-
attributes = attributes.dup.merge(trace_id: @trace_id, parent_span_id: @span_id, sampled: @sampled)
|
183
|
+
attributes = attributes.dup.merge(transaction: @transaction, trace_id: @trace_id, parent_span_id: @span_id, sampled: @sampled)
|
140
184
|
new_span = Span.new(**attributes)
|
141
|
-
new_span.transaction = transaction
|
142
185
|
new_span.span_recorder = span_recorder
|
143
186
|
|
144
187
|
if span_recorder
|
@@ -163,6 +206,10 @@ module Sentry
|
|
163
206
|
yield(child_span)
|
164
207
|
|
165
208
|
child_span.finish
|
209
|
+
rescue
|
210
|
+
child_span.set_http_status(500)
|
211
|
+
child_span.finish
|
212
|
+
raise
|
166
213
|
end
|
167
214
|
|
168
215
|
def deep_dup
|
@@ -198,7 +245,7 @@ module Sentry
|
|
198
245
|
# @param status_code [String] example: "500".
|
199
246
|
def set_http_status(status_code)
|
200
247
|
status_code = status_code.to_i
|
201
|
-
set_data(
|
248
|
+
set_data(DataConventions::HTTP_STATUS_CODE, status_code)
|
202
249
|
|
203
250
|
status =
|
204
251
|
if status_code >= 200 && status_code < 299
|
data/lib/sentry/test_helper.rb
CHANGED
@@ -14,22 +14,27 @@ module Sentry
|
|
14
14
|
# @return [void]
|
15
15
|
def setup_sentry_test(&block)
|
16
16
|
raise "please make sure the SDK is initialized for testing" unless Sentry.initialized?
|
17
|
-
|
17
|
+
dummy_config = Sentry.configuration.dup
|
18
18
|
# configure dummy DSN, so the events will not be sent to the actual service
|
19
|
-
|
19
|
+
dummy_config.dsn = DUMMY_DSN
|
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
|
-
|
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
|
|
27
27
|
# user can overwrite some of the configs, with a few exceptions like:
|
28
|
-
# -
|
28
|
+
# - include_local_variables
|
29
29
|
# - auto_session_tracking
|
30
|
-
block&.call(
|
30
|
+
block&.call(dummy_config)
|
31
31
|
|
32
|
-
|
32
|
+
# the base layer's client should already use the dummy config so nothing will be sent by accident
|
33
|
+
base_client = Sentry::Client.new(dummy_config)
|
34
|
+
Sentry.get_current_hub.bind_client(base_client)
|
35
|
+
# create a new layer so mutations made to the testing scope or configuration could be simply popped later
|
36
|
+
Sentry.get_current_hub.push_scope
|
37
|
+
test_client = Sentry::Client.new(dummy_config.dup)
|
33
38
|
Sentry.get_current_hub.bind_client(test_client)
|
34
39
|
end
|
35
40
|
|
@@ -39,8 +44,12 @@ module Sentry
|
|
39
44
|
def teardown_sentry_test
|
40
45
|
return unless Sentry.initialized?
|
41
46
|
|
42
|
-
|
43
|
-
|
47
|
+
# pop testing layer created by `setup_sentry_test`
|
48
|
+
# but keep the base layer to avoid nil-pointer errors
|
49
|
+
# TODO: find a way to notify users if they somehow popped the test layer before calling this method
|
50
|
+
if Sentry.get_current_hub.instance_variable_get(:@stack).size > 1
|
51
|
+
Sentry.get_current_hub.pop_scope
|
52
|
+
end
|
44
53
|
end
|
45
54
|
|
46
55
|
# @return [Transport]
|
@@ -73,4 +82,3 @@ module Sentry
|
|
73
82
|
end
|
74
83
|
end
|
75
84
|
end
|
76
|
-
|
data/lib/sentry/transaction.rb
CHANGED
@@ -1,27 +1,44 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require "sentry/baggage"
|
4
|
+
require "sentry/profiler"
|
5
|
+
require "sentry/propagation_context"
|
6
|
+
|
3
7
|
module Sentry
|
4
8
|
class Transaction < Span
|
5
|
-
SENTRY_TRACE_REGEXP
|
6
|
-
|
7
|
-
|
8
|
-
"-?([0-9a-f]{16})?" + # span_id
|
9
|
-
"-?([01])?" + # sampled
|
10
|
-
"[ \t]*$" # whitespace
|
11
|
-
)
|
9
|
+
# @deprecated Use Sentry::PropagationContext::SENTRY_TRACE_REGEXP instead.
|
10
|
+
SENTRY_TRACE_REGEXP = PropagationContext::SENTRY_TRACE_REGEXP
|
11
|
+
|
12
12
|
UNLABELD_NAME = "<unlabeled transaction>".freeze
|
13
13
|
MESSAGE_PREFIX = "[Tracing]"
|
14
14
|
|
15
|
+
# https://develop.sentry.dev/sdk/event-payloads/transaction/#transaction-annotations
|
16
|
+
SOURCES = %i(custom url route view component task)
|
17
|
+
|
15
18
|
include LoggingHelper
|
16
19
|
|
17
20
|
# The name of the transaction.
|
18
21
|
# @return [String]
|
19
22
|
attr_reader :name
|
20
23
|
|
24
|
+
# The source of the transaction name.
|
25
|
+
# @return [Symbol]
|
26
|
+
attr_reader :source
|
27
|
+
|
21
28
|
# The sampling decision of the parent transaction, which will be considered when making the current transaction's sampling decision.
|
22
29
|
# @return [String]
|
23
30
|
attr_reader :parent_sampled
|
24
31
|
|
32
|
+
# The parsed incoming W3C baggage header.
|
33
|
+
# This is only for accessing the current baggage variable.
|
34
|
+
# Please use the #get_baggage method for interfacing outside this class.
|
35
|
+
# @return [Baggage, nil]
|
36
|
+
attr_reader :baggage
|
37
|
+
|
38
|
+
# The measurements added to the transaction.
|
39
|
+
# @return [Hash]
|
40
|
+
attr_reader :measurements
|
41
|
+
|
25
42
|
# @deprecated Use Sentry.get_current_hub instead.
|
26
43
|
attr_reader :hub
|
27
44
|
|
@@ -31,52 +48,106 @@ module Sentry
|
|
31
48
|
# @deprecated Use Sentry.logger instead.
|
32
49
|
attr_reader :logger
|
33
50
|
|
34
|
-
|
35
|
-
|
51
|
+
# The effective sample rate at which this transaction was sampled.
|
52
|
+
# @return [Float, nil]
|
53
|
+
attr_reader :effective_sample_rate
|
36
54
|
|
37
|
-
|
55
|
+
# Additional contexts stored directly on the transaction object.
|
56
|
+
# @return [Hash]
|
57
|
+
attr_reader :contexts
|
58
|
+
|
59
|
+
# The Profiler instance for this transaction.
|
60
|
+
# @return [Profiler]
|
61
|
+
attr_reader :profiler
|
62
|
+
|
63
|
+
def initialize(
|
64
|
+
hub:,
|
65
|
+
name: nil,
|
66
|
+
source: :custom,
|
67
|
+
parent_sampled: nil,
|
68
|
+
baggage: nil,
|
69
|
+
**options
|
70
|
+
)
|
71
|
+
super(transaction: self, **options)
|
72
|
+
|
73
|
+
set_name(name, source: source)
|
38
74
|
@parent_sampled = parent_sampled
|
39
|
-
@transaction = self
|
40
75
|
@hub = hub
|
76
|
+
@baggage = baggage
|
41
77
|
@configuration = hub.configuration # to be removed
|
42
78
|
@tracing_enabled = hub.configuration.tracing_enabled?
|
43
79
|
@traces_sampler = hub.configuration.traces_sampler
|
44
80
|
@traces_sample_rate = hub.configuration.traces_sample_rate
|
45
81
|
@logger = hub.configuration.logger
|
82
|
+
@release = hub.configuration.release
|
83
|
+
@environment = hub.configuration.environment
|
84
|
+
@dsn = hub.configuration.dsn
|
85
|
+
@effective_sample_rate = nil
|
86
|
+
@contexts = {}
|
87
|
+
@measurements = {}
|
88
|
+
@profiler = Profiler.new(@configuration)
|
46
89
|
init_span_recorder
|
47
90
|
end
|
48
91
|
|
92
|
+
# @deprecated use Sentry.continue_trace instead.
|
93
|
+
#
|
49
94
|
# Initalizes a Transaction instance with a Sentry trace string from another transaction (usually from an external request).
|
50
95
|
#
|
51
96
|
# The original transaction will become the parent of the new Transaction instance. And they will share the same `trace_id`.
|
52
97
|
#
|
53
98
|
# The child transaction will also store the parent's sampling decision in its `parent_sampled` attribute.
|
54
99
|
# @param sentry_trace [String] the trace string from the previous transaction.
|
100
|
+
# @param baggage [String, nil] the incoming baggage header string.
|
55
101
|
# @param hub [Hub] the hub that'll be responsible for sending this transaction when it's finished.
|
56
102
|
# @param options [Hash] the options you want to use to initialize a Transaction instance.
|
57
103
|
# @return [Transaction, nil]
|
58
|
-
def self.from_sentry_trace(sentry_trace, hub: Sentry.get_current_hub, **options)
|
104
|
+
def self.from_sentry_trace(sentry_trace, baggage: nil, hub: Sentry.get_current_hub, **options)
|
59
105
|
return unless hub.configuration.tracing_enabled?
|
60
106
|
return unless sentry_trace
|
61
107
|
|
62
|
-
|
63
|
-
return
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
108
|
+
sentry_trace_data = extract_sentry_trace(sentry_trace)
|
109
|
+
return unless sentry_trace_data
|
110
|
+
|
111
|
+
trace_id, parent_span_id, parent_sampled = sentry_trace_data
|
112
|
+
|
113
|
+
baggage = if baggage && !baggage.empty?
|
114
|
+
Baggage.from_incoming_header(baggage)
|
115
|
+
else
|
116
|
+
# If there's an incoming sentry-trace but no incoming baggage header,
|
117
|
+
# for instance in traces coming from older SDKs,
|
118
|
+
# baggage will be empty and frozen and won't be populated as head SDK.
|
119
|
+
Baggage.new({})
|
120
|
+
end
|
121
|
+
|
122
|
+
baggage.freeze!
|
123
|
+
|
124
|
+
new(
|
125
|
+
trace_id: trace_id,
|
126
|
+
parent_span_id: parent_span_id,
|
127
|
+
parent_sampled: parent_sampled,
|
128
|
+
hub: hub,
|
129
|
+
baggage: baggage,
|
130
|
+
**options
|
131
|
+
)
|
132
|
+
end
|
72
133
|
|
73
|
-
|
134
|
+
# @deprecated Use Sentry::PropagationContext.extract_sentry_trace instead.
|
135
|
+
# @return [Array, nil]
|
136
|
+
def self.extract_sentry_trace(sentry_trace)
|
137
|
+
PropagationContext.extract_sentry_trace(sentry_trace)
|
74
138
|
end
|
75
139
|
|
76
140
|
# @return [Hash]
|
77
141
|
def to_hash
|
78
142
|
hash = super
|
79
|
-
|
143
|
+
|
144
|
+
hash.merge!(
|
145
|
+
name: @name,
|
146
|
+
source: @source,
|
147
|
+
sampled: @sampled,
|
148
|
+
parent_sampled: @parent_sampled
|
149
|
+
)
|
150
|
+
|
80
151
|
hash
|
81
152
|
end
|
82
153
|
|
@@ -94,6 +165,15 @@ module Sentry
|
|
94
165
|
copy
|
95
166
|
end
|
96
167
|
|
168
|
+
# Sets a custom measurement on the transaction.
|
169
|
+
# @param name [String] name of the measurement
|
170
|
+
# @param value [Float] value of the measurement
|
171
|
+
# @param unit [String] unit of the measurement
|
172
|
+
# @return [void]
|
173
|
+
def set_measurement(name, value, unit = "")
|
174
|
+
@measurements[name] = { value: value, unit: unit }
|
175
|
+
end
|
176
|
+
|
97
177
|
# Sets initial sampling decision of the transaction.
|
98
178
|
# @param sampling_context [Hash] a context Hash that'll be passed to `traces_sampler` (if provided).
|
99
179
|
# @return [void]
|
@@ -103,7 +183,10 @@ module Sentry
|
|
103
183
|
return
|
104
184
|
end
|
105
185
|
|
106
|
-
|
186
|
+
unless @sampled.nil?
|
187
|
+
@effective_sample_rate = @sampled ? 1.0 : 0.0
|
188
|
+
return
|
189
|
+
end
|
107
190
|
|
108
191
|
sample_rate =
|
109
192
|
if @traces_sampler.is_a?(Proc)
|
@@ -116,7 +199,11 @@ module Sentry
|
|
116
199
|
|
117
200
|
transaction_description = generate_transaction_description
|
118
201
|
|
119
|
-
|
202
|
+
if [true, false].include?(sample_rate)
|
203
|
+
@effective_sample_rate = sample_rate ? 1.0 : 0.0
|
204
|
+
elsif sample_rate.is_a?(Numeric) && sample_rate >= 0.0 && sample_rate <= 1.0
|
205
|
+
@effective_sample_rate = sample_rate.to_f
|
206
|
+
else
|
120
207
|
@sampled = false
|
121
208
|
log_warn("#{MESSAGE_PREFIX} Discarding #{transaction_description} because of invalid sample_rate: #{sample_rate}")
|
122
209
|
return
|
@@ -131,7 +218,12 @@ module Sentry
|
|
131
218
|
if sample_rate == true
|
132
219
|
@sampled = true
|
133
220
|
else
|
134
|
-
|
221
|
+
if Sentry.backpressure_monitor
|
222
|
+
factor = Sentry.backpressure_monitor.downsample_factor
|
223
|
+
@effective_sample_rate /= 2**factor
|
224
|
+
end
|
225
|
+
|
226
|
+
@sampled = Random.rand < @effective_sample_rate
|
135
227
|
end
|
136
228
|
|
137
229
|
if @sampled
|
@@ -146,7 +238,7 @@ module Sentry
|
|
146
238
|
# Finishes the transaction's recording and send it to Sentry.
|
147
239
|
# @param hub [Hub] the hub that'll send this transaction. (Deprecated)
|
148
240
|
# @return [TransactionEvent]
|
149
|
-
def finish(hub: nil)
|
241
|
+
def finish(hub: nil, end_timestamp: nil)
|
150
242
|
if hub
|
151
243
|
log_warn(
|
152
244
|
<<~MSG
|
@@ -158,20 +250,57 @@ module Sentry
|
|
158
250
|
|
159
251
|
hub ||= @hub
|
160
252
|
|
161
|
-
super()
|
253
|
+
super(end_timestamp: end_timestamp)
|
162
254
|
|
163
255
|
if @name.nil?
|
164
256
|
@name = UNLABELD_NAME
|
165
257
|
end
|
166
258
|
|
259
|
+
@profiler.stop
|
260
|
+
|
167
261
|
if @sampled
|
168
262
|
event = hub.current_client.event_from_transaction(self)
|
169
263
|
hub.capture_event(event)
|
170
264
|
else
|
171
|
-
|
265
|
+
is_backpressure = Sentry.backpressure_monitor&.downsample_factor&.positive?
|
266
|
+
reason = is_backpressure ? :backpressure : :sample_rate
|
267
|
+
hub.current_client.transport.record_lost_event(reason, 'transaction')
|
172
268
|
end
|
173
269
|
end
|
174
270
|
|
271
|
+
# Get the existing frozen incoming baggage
|
272
|
+
# or populate one with sentry- items as the head SDK.
|
273
|
+
# @return [Baggage]
|
274
|
+
def get_baggage
|
275
|
+
populate_head_baggage if @baggage.nil? || @baggage.mutable
|
276
|
+
@baggage
|
277
|
+
end
|
278
|
+
|
279
|
+
# Set the transaction name directly.
|
280
|
+
# Considered internal api since it bypasses the usual scope logic.
|
281
|
+
# @param name [String]
|
282
|
+
# @param source [Symbol]
|
283
|
+
# @return [void]
|
284
|
+
def set_name(name, source: :custom)
|
285
|
+
@name = name
|
286
|
+
@source = SOURCES.include?(source) ? source.to_sym : :custom
|
287
|
+
end
|
288
|
+
|
289
|
+
# Set contexts directly on the transaction.
|
290
|
+
# @param key [String, Symbol]
|
291
|
+
# @param value [Object]
|
292
|
+
# @return [void]
|
293
|
+
def set_context(key, value)
|
294
|
+
@contexts[key] = value
|
295
|
+
end
|
296
|
+
|
297
|
+
# Start the profiler.
|
298
|
+
# @return [void]
|
299
|
+
def start_profiler!
|
300
|
+
profiler.set_initial_sample_decision(sampled)
|
301
|
+
profiler.start
|
302
|
+
end
|
303
|
+
|
175
304
|
protected
|
176
305
|
|
177
306
|
def init_span_recorder(limit = 1000)
|
@@ -188,6 +317,30 @@ module Sentry
|
|
188
317
|
result
|
189
318
|
end
|
190
319
|
|
320
|
+
def populate_head_baggage
|
321
|
+
items = {
|
322
|
+
"trace_id" => trace_id,
|
323
|
+
"sample_rate" => effective_sample_rate&.to_s,
|
324
|
+
"sampled" => sampled&.to_s,
|
325
|
+
"environment" => @environment,
|
326
|
+
"release" => @release,
|
327
|
+
"public_key" => @dsn&.public_key
|
328
|
+
}
|
329
|
+
|
330
|
+
items["transaction"] = name unless source_low_quality?
|
331
|
+
|
332
|
+
user = @hub.current_scope&.user
|
333
|
+
items["user_segment"] = user["segment"] if user && user["segment"]
|
334
|
+
|
335
|
+
items.compact!
|
336
|
+
@baggage = Baggage.new(items, mutable: false)
|
337
|
+
end
|
338
|
+
|
339
|
+
# These are high cardinality and thus bad
|
340
|
+
def source_low_quality?
|
341
|
+
source == :url
|
342
|
+
end
|
343
|
+
|
191
344
|
class SpanRecorder
|
192
345
|
attr_reader :max_length, :spans
|
193
346
|
|
@@ -8,9 +8,34 @@ module Sentry
|
|
8
8
|
# @return [<Array[Span]>]
|
9
9
|
attr_accessor :spans
|
10
10
|
|
11
|
+
# @return [Hash]
|
12
|
+
attr_accessor :measurements
|
13
|
+
|
11
14
|
# @return [Float, nil]
|
12
15
|
attr_reader :start_timestamp
|
13
16
|
|
17
|
+
# @return [Hash, nil]
|
18
|
+
attr_accessor :profile
|
19
|
+
|
20
|
+
def initialize(transaction:, **options)
|
21
|
+
super(**options)
|
22
|
+
|
23
|
+
self.transaction = transaction.name
|
24
|
+
self.transaction_info = { source: transaction.source }
|
25
|
+
self.contexts.merge!(transaction.contexts)
|
26
|
+
self.contexts.merge!(trace: transaction.get_trace_context)
|
27
|
+
self.timestamp = transaction.timestamp
|
28
|
+
self.start_timestamp = transaction.start_timestamp
|
29
|
+
self.tags = transaction.tags
|
30
|
+
self.dynamic_sampling_context = transaction.get_baggage.dynamic_sampling_context
|
31
|
+
self.measurements = transaction.measurements
|
32
|
+
|
33
|
+
finished_spans = transaction.span_recorder.spans.select { |span| span.timestamp && span != transaction }
|
34
|
+
self.spans = finished_spans.map(&:to_hash)
|
35
|
+
|
36
|
+
populate_profile(transaction)
|
37
|
+
end
|
38
|
+
|
14
39
|
# Sets the event's start_timestamp.
|
15
40
|
# @param time [Time, Float]
|
16
41
|
# @return [void]
|
@@ -23,7 +48,33 @@ module Sentry
|
|
23
48
|
data = super
|
24
49
|
data[:spans] = @spans.map(&:to_hash) if @spans
|
25
50
|
data[:start_timestamp] = @start_timestamp
|
51
|
+
data[:measurements] = @measurements
|
26
52
|
data
|
27
53
|
end
|
54
|
+
|
55
|
+
private
|
56
|
+
|
57
|
+
def populate_profile(transaction)
|
58
|
+
profile_hash = transaction.profiler.to_hash
|
59
|
+
return if profile_hash.empty?
|
60
|
+
|
61
|
+
profile_hash.merge!(
|
62
|
+
environment: environment,
|
63
|
+
release: release,
|
64
|
+
timestamp: Time.at(start_timestamp).iso8601,
|
65
|
+
device: { architecture: Scope.os_context[:machine] },
|
66
|
+
os: { name: Scope.os_context[:name], version: Scope.os_context[:version] },
|
67
|
+
runtime: Scope.runtime_context,
|
68
|
+
transaction: {
|
69
|
+
id: event_id,
|
70
|
+
name: transaction.name,
|
71
|
+
trace_id: transaction.trace_id,
|
72
|
+
# TODO-neel-profiler stubbed for now, see thread_id note in profiler.rb
|
73
|
+
active_thead_id: '0'
|
74
|
+
}
|
75
|
+
)
|
76
|
+
|
77
|
+
self.profile = profile_hash
|
78
|
+
end
|
28
79
|
end
|
29
80
|
end
|
@@ -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
|