sentry-ruby 5.4.2 → 5.16.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (47) hide show
  1. checksums.yaml +4 -4
  2. data/.rspec +0 -1
  3. data/Gemfile +13 -14
  4. data/README.md +11 -8
  5. data/Rakefile +8 -1
  6. data/lib/sentry/background_worker.rb +8 -1
  7. data/lib/sentry/backpressure_monitor.rb +75 -0
  8. data/lib/sentry/backtrace.rb +1 -1
  9. data/lib/sentry/baggage.rb +70 -0
  10. data/lib/sentry/breadcrumb.rb +8 -2
  11. data/lib/sentry/check_in_event.rb +60 -0
  12. data/lib/sentry/client.rb +77 -19
  13. data/lib/sentry/configuration.rb +177 -29
  14. data/lib/sentry/cron/configuration.rb +23 -0
  15. data/lib/sentry/cron/monitor_check_ins.rb +75 -0
  16. data/lib/sentry/cron/monitor_config.rb +53 -0
  17. data/lib/sentry/cron/monitor_schedule.rb +42 -0
  18. data/lib/sentry/envelope.rb +2 -5
  19. data/lib/sentry/event.rb +7 -29
  20. data/lib/sentry/hub.rb +100 -4
  21. data/lib/sentry/integrable.rb +6 -0
  22. data/lib/sentry/interfaces/request.rb +6 -16
  23. data/lib/sentry/interfaces/single_exception.rb +13 -3
  24. data/lib/sentry/net/http.rb +37 -46
  25. data/lib/sentry/profiler.rb +233 -0
  26. data/lib/sentry/propagation_context.rb +134 -0
  27. data/lib/sentry/puma.rb +32 -0
  28. data/lib/sentry/rack/capture_exceptions.rb +4 -5
  29. data/lib/sentry/rake.rb +1 -14
  30. data/lib/sentry/redis.rb +41 -23
  31. data/lib/sentry/release_detector.rb +1 -1
  32. data/lib/sentry/scope.rb +81 -16
  33. data/lib/sentry/session.rb +5 -7
  34. data/lib/sentry/span.rb +57 -10
  35. data/lib/sentry/test_helper.rb +19 -11
  36. data/lib/sentry/transaction.rb +183 -30
  37. data/lib/sentry/transaction_event.rb +51 -0
  38. data/lib/sentry/transport/configuration.rb +74 -1
  39. data/lib/sentry/transport/http_transport.rb +68 -37
  40. data/lib/sentry/transport/spotlight_transport.rb +50 -0
  41. data/lib/sentry/transport.rb +39 -24
  42. data/lib/sentry/utils/argument_checking_helper.rb +9 -3
  43. data/lib/sentry/utils/encoding_helper.rb +22 -0
  44. data/lib/sentry/version.rb +1 -1
  45. data/lib/sentry-ruby.rb +116 -41
  46. metadata +14 -3
  47. 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
- attr_accessor :transaction
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.hex(8)
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
- # already finished
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("status_code", status_code)
248
+ set_data(DataConventions::HTTP_STATUS_CODE, status_code)
202
249
 
203
250
  status =
204
251
  if status_code >= 200 && status_code < 299
@@ -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
- copied_config = Sentry.configuration.dup
17
+ dummy_config = Sentry.configuration.dup
18
18
  # configure dummy DSN, so the events will not be sent to the actual service
19
- copied_config.dsn = DUMMY_DSN
19
+ dummy_config.dsn = DUMMY_DSN
20
20
  # set transport to DummyTransport, so we can easily intercept the captured events
21
- copied_config.transport.transport_class = Sentry::DummyTransport
21
+ dummy_config.transport.transport_class = Sentry::DummyTransport
22
22
  # make sure SDK allows sending under the current environment
23
- copied_config.enabled_environments << copied_config.environment unless copied_config.enabled_environments.include?(copied_config.environment)
23
+ dummy_config.enabled_environments << dummy_config.environment unless dummy_config.enabled_environments.include?(dummy_config.environment)
24
24
  # disble async event sending
25
- copied_config.background_worker_threads = 0
25
+ dummy_config.background_worker_threads = 0
26
26
 
27
27
  # user can overwrite some of the configs, with a few exceptions like:
28
- # - capture_exception_frame_locals
28
+ # - include_local_variables
29
29
  # - auto_session_tracking
30
- block&.call(copied_config)
30
+ block&.call(dummy_config)
31
31
 
32
- test_client = Sentry::Client.new(copied_config)
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
- sentry_transport.events = []
43
- sentry_transport.envelopes = []
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
-
@@ -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 = Regexp.new(
6
- "^[ \t]*" + # whitespace
7
- "([0-9a-f]{32})?" + # trace_id
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
- def initialize(name: nil, parent_sampled: nil, hub:, **options)
35
- super(**options)
51
+ # The effective sample rate at which this transaction was sampled.
52
+ # @return [Float, nil]
53
+ attr_reader :effective_sample_rate
36
54
 
37
- @name = name
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
- match = SENTRY_TRACE_REGEXP.match(sentry_trace)
63
- return if match.nil?
64
- trace_id, parent_span_id, sampled_flag = match[1..3]
65
-
66
- parent_sampled =
67
- if sampled_flag.nil?
68
- nil
69
- else
70
- sampled_flag != "0"
71
- end
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
- new(trace_id: trace_id, parent_span_id: parent_span_id, parent_sampled: parent_sampled, hub: hub, **options)
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
- hash.merge!(name: @name, sampled: @sampled, parent_sampled: @parent_sampled)
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
- return unless @sampled.nil?
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
- unless [true, false].include?(sample_rate) || (sample_rate.is_a?(Numeric) && sample_rate >= 0.0 && sample_rate <= 1.0)
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
- @sampled = Random.rand < sample_rate
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() # Span#finish doesn't take arguments
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
- hub.current_client.transport.record_lost_event(:sample_rate, 'transaction')
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
- attr_accessor :timeout, :open_timeout, :proxy, :ssl, :ssl_ca_file, :ssl_verification, :encoding
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