sentry-ruby 5.16.1 → 5.17.3

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.
Files changed (50) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +1 -1
  3. data/Rakefile +1 -1
  4. data/lib/sentry/background_worker.rb +1 -1
  5. data/lib/sentry/backtrace.rb +7 -3
  6. data/lib/sentry/check_in_event.rb +1 -1
  7. data/lib/sentry/client.rb +42 -9
  8. data/lib/sentry/configuration.rb +19 -11
  9. data/lib/sentry/cron/monitor_schedule.rb +1 -1
  10. data/lib/sentry/dsn.rb +1 -1
  11. data/lib/sentry/envelope.rb +18 -1
  12. data/lib/sentry/error_event.rb +2 -2
  13. data/lib/sentry/event.rb +8 -8
  14. data/lib/sentry/hub.rb +1 -1
  15. data/lib/sentry/integrable.rb +4 -0
  16. data/lib/sentry/interface.rb +1 -0
  17. data/lib/sentry/interfaces/exception.rb +5 -3
  18. data/lib/sentry/interfaces/mechanism.rb +20 -0
  19. data/lib/sentry/interfaces/request.rb +2 -2
  20. data/lib/sentry/interfaces/single_exception.rb +6 -4
  21. data/lib/sentry/interfaces/stacktrace_builder.rb +8 -0
  22. data/lib/sentry/metrics/aggregator.rb +276 -0
  23. data/lib/sentry/metrics/configuration.rb +47 -0
  24. data/lib/sentry/metrics/counter_metric.rb +25 -0
  25. data/lib/sentry/metrics/distribution_metric.rb +25 -0
  26. data/lib/sentry/metrics/gauge_metric.rb +35 -0
  27. data/lib/sentry/metrics/local_aggregator.rb +53 -0
  28. data/lib/sentry/metrics/metric.rb +19 -0
  29. data/lib/sentry/metrics/set_metric.rb +28 -0
  30. data/lib/sentry/metrics/timing.rb +43 -0
  31. data/lib/sentry/metrics.rb +55 -0
  32. data/lib/sentry/propagation_context.rb +9 -8
  33. data/lib/sentry/puma.rb +1 -1
  34. data/lib/sentry/rack/capture_exceptions.rb +6 -1
  35. data/lib/sentry/rake.rb +3 -1
  36. data/lib/sentry/scope.rb +7 -2
  37. data/lib/sentry/session.rb +2 -2
  38. data/lib/sentry/session_flusher.rb +1 -6
  39. data/lib/sentry/span.rb +16 -2
  40. data/lib/sentry/transaction.rb +15 -14
  41. data/lib/sentry/transaction_event.rb +5 -0
  42. data/lib/sentry/transport/configuration.rb +0 -1
  43. data/lib/sentry/transport.rb +7 -21
  44. data/lib/sentry/utils/argument_checking_helper.rb +6 -0
  45. data/lib/sentry/utils/real_ip.rb +1 -1
  46. data/lib/sentry/utils/request_id.rb +1 -1
  47. data/lib/sentry/version.rb +1 -1
  48. data/lib/sentry-ruby.rb +15 -3
  49. data/sentry-ruby.gemspec +1 -0
  50. metadata +27 -2
data/lib/sentry/scope.rb CHANGED
@@ -252,6 +252,12 @@ module Sentry
252
252
  @transaction_sources.last
253
253
  end
254
254
 
255
+ # These are high cardinality and thus bad.
256
+ # @return [Boolean]
257
+ def transaction_source_low_quality?
258
+ transaction_source == :url
259
+ end
260
+
255
261
  # Returns the associated Transaction object.
256
262
  # @return [Transaction, nil]
257
263
  def get_transaction
@@ -295,7 +301,7 @@ module Sentry
295
301
  private
296
302
 
297
303
  def set_default_value
298
- @contexts = { :os => self.class.os_context, :runtime => self.class.runtime_context }
304
+ @contexts = { os: self.class.os_context, runtime: self.class.runtime_context }
299
305
  @extra = {}
300
306
  @tags = {}
301
307
  @user = {}
@@ -355,6 +361,5 @@ module Sentry
355
361
  global_event_processors << block
356
362
  end
357
363
  end
358
-
359
364
  end
360
365
  end
@@ -5,8 +5,8 @@ module Sentry
5
5
  attr_reader :started, :status, :aggregation_key
6
6
 
7
7
  # TODO-neel add :crashed after adding handled mechanism
8
- STATUSES = %i(ok errored exited)
9
- AGGREGATE_STATUSES = %i(errored exited)
8
+ STATUSES = %i[ok errored exited]
9
+ AGGREGATE_STATUSES = %i[errored exited]
10
10
 
11
11
  def initialize
12
12
  @started = Sentry.utc_now
@@ -20,12 +20,8 @@ module Sentry
20
20
 
21
21
  def flush
22
22
  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
23
 
24
+ @client.capture_envelope(pending_envelope)
29
25
  @pending_aggregates = {}
30
26
  end
31
27
 
@@ -85,6 +81,5 @@ module Sentry
85
81
  end
86
82
  end
87
83
  end
88
-
89
84
  end
90
85
  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
@@ -150,7 +150,7 @@ module Sentry
150
150
 
151
151
  # @return [Hash]
152
152
  def to_hash
153
- {
153
+ hash = {
154
154
  trace_id: @trace_id,
155
155
  span_id: @span_id,
156
156
  parent_span_id: @parent_span_id,
@@ -162,6 +162,11 @@ module Sentry
162
162
  tags: @tags,
163
163
  data: @data
164
164
  }
165
+
166
+ summary = metrics_summary
167
+ hash[:_metrics_summary] = summary if summary
168
+
169
+ hash
165
170
  end
166
171
 
167
172
  # Returns the span's context that can be used to embed in an Event.
@@ -269,5 +274,14 @@ module Sentry
269
274
  def set_tag(key, value)
270
275
  @tags[key] = value
271
276
  end
277
+
278
+ # Collects gauge metrics on the span for metric summaries.
279
+ def metrics_local_aggregator
280
+ @metrics_local_aggregator ||= Sentry::Metrics::LocalAggregator.new
281
+ end
282
+
283
+ def metrics_summary
284
+ @metrics_local_aggregator&.to_hash
285
+ end
272
286
  end
273
287
  end
@@ -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(custom url route view component task)
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 = 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
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
 
@@ -301,6 +302,11 @@ module Sentry
301
302
  profiler.start
302
303
  end
303
304
 
305
+ # These are high cardinality and thus bad
306
+ def source_low_quality?
307
+ source == :url
308
+ end
309
+
304
310
  protected
305
311
 
306
312
  def init_span_recorder(limit = 1000)
@@ -336,11 +342,6 @@ module Sentry
336
342
  @baggage = Baggage.new(items, mutable: false)
337
343
  end
338
344
 
339
- # These are high cardinality and thus bad
340
- def source_low_quality?
341
- source == :url
342
- end
343
-
344
345
  class SpanRecorder
345
346
  attr_reader :max_length, :spans
346
347
 
@@ -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,6 @@
3
3
  module Sentry
4
4
  class Transport
5
5
  class Configuration
6
-
7
6
  # The timeout in seconds to open a connection to Sentry, in seconds.
8
7
  # Default value is 2.
9
8
  #
@@ -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
@@ -89,18 +88,9 @@ module Sentry
89
88
  [data, serialized_items]
90
89
  end
91
90
 
92
- def is_rate_limited?(item_type)
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
 
@@ -161,11 +151,11 @@ module Sentry
161
151
  envelope
162
152
  end
163
153
 
164
- def record_lost_event(reason, item_type)
154
+ def record_lost_event(reason, data_category)
165
155
  return unless @send_client_reports
166
156
  return unless CLIENT_REPORT_REASONS.include?(reason)
167
157
 
168
- @discarded_events[[reason, item_type]] += 1
158
+ @discarded_events[[reason, data_category]] += 1
169
159
  end
170
160
 
171
161
  def flush
@@ -185,11 +175,7 @@ module Sentry
185
175
  return nil if @discarded_events.empty?
186
176
 
187
177
  discarded_events_hash = @discarded_events.map do |key, val|
188
- reason, type = key
189
-
190
- # 'event' has to be mapped to 'error'
191
- category = type == 'event' ? 'error' : type
192
-
178
+ reason, category = key
193
179
  { reason: reason, category: category, quantity: val }
194
180
  end
195
181
 
@@ -207,9 +193,9 @@ module Sentry
207
193
 
208
194
  def reject_rate_limited_items(envelope)
209
195
  envelope.items.reject! do |item|
210
- if is_rate_limited?(item.type)
196
+ if is_rate_limited?(item.data_category)
211
197
  log_debug("[Transport] Envelope item [#{item.type}] not sent: rate limiting")
212
- record_lost_event(:ratelimit_backoff, item.type)
198
+ record_lost_event(:ratelimit_backoff, item.data_category)
213
199
 
214
200
  true
215
201
  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
@@ -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", # private IPv4 range 192.168.x.x
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(action_dispatch.request_id HTTP_X_REQUEST_ID).freeze
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)
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Sentry
4
- VERSION = "5.16.1"
4
+ VERSION = "5.17.3"
5
5
  end
data/lib/sentry-ruby.rb CHANGED
@@ -23,10 +23,11 @@ require "sentry/background_worker"
23
23
  require "sentry/session_flusher"
24
24
  require "sentry/backpressure_monitor"
25
25
  require "sentry/cron/monitor_check_ins"
26
+ require "sentry/metrics"
26
27
 
27
28
  [
28
29
  "sentry/rake",
29
- "sentry/rack",
30
+ "sentry/rack"
30
31
  ].each do |lib|
31
32
  begin
32
33
  require lib
@@ -77,6 +78,10 @@ module Sentry
77
78
  # @return [BackpressureMonitor, nil]
78
79
  attr_reader :backpressure_monitor
79
80
 
81
+ # @!attribute [r] metrics_aggregator
82
+ # @return [Metrics::Aggregator, nil]
83
+ attr_reader :metrics_aggregator
84
+
80
85
  ##### Patch Registration #####
81
86
 
82
87
  # @!visibility private
@@ -222,8 +227,9 @@ module Sentry
222
227
  Thread.current.thread_variable_set(THREAD_LOCAL, hub)
223
228
  @main_hub = hub
224
229
  @background_worker = Sentry::BackgroundWorker.new(config)
225
- @session_flusher = config.auto_session_tracking ? Sentry::SessionFlusher.new(config, client) : nil
230
+ @session_flusher = config.session_tracking? ? Sentry::SessionFlusher.new(config, client) : nil
226
231
  @backpressure_monitor = config.enable_backpressure_handling ? Sentry::BackpressureMonitor.new(config, client) : nil
232
+ @metrics_aggregator = config.metrics.enabled ? Sentry::Metrics::Aggregator.new(config, client) : nil
227
233
  exception_locals_tp.enable if config.include_local_variables
228
234
  at_exit { close }
229
235
  end
@@ -244,8 +250,14 @@ module Sentry
244
250
  @backpressure_monitor = nil
245
251
  end
246
252
 
253
+ if @metrics_aggregator
254
+ @metrics_aggregator.flush(force: true)
255
+ @metrics_aggregator.kill
256
+ @metrics_aggregator = nil
257
+ end
258
+
247
259
  if client = get_current_client
248
- client.transport.flush
260
+ client.flush
249
261
 
250
262
  if client.configuration.include_local_variables
251
263
  exception_locals_tp.disable
data/sentry-ruby.gemspec CHANGED
@@ -21,4 +21,5 @@ Gem::Specification.new do |spec|
21
21
  spec.require_paths = ["lib"]
22
22
 
23
23
  spec.add_dependency "concurrent-ruby", '~> 1.0', '>= 1.0.2'
24
+ spec.add_dependency "bigdecimal"
24
25
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: sentry-ruby
3
3
  version: !ruby/object:Gem::Version
4
- version: 5.16.1
4
+ version: 5.17.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - Sentry Team
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2024-01-09 00:00:00.000000000 Z
11
+ date: 2024-04-11 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: concurrent-ruby
@@ -30,6 +30,20 @@ dependencies:
30
30
  - - ">="
31
31
  - !ruby/object:Gem::Version
32
32
  version: 1.0.2
33
+ - !ruby/object:Gem::Dependency
34
+ name: bigdecimal
35
+ requirement: !ruby/object:Gem::Requirement
36
+ requirements:
37
+ - - ">="
38
+ - !ruby/object:Gem::Version
39
+ version: '0'
40
+ type: :runtime
41
+ prerelease: false
42
+ version_requirements: !ruby/object:Gem::Requirement
43
+ requirements:
44
+ - - ">="
45
+ - !ruby/object:Gem::Version
46
+ version: '0'
33
47
  description: A gem that provides a client interface for the Sentry error logger
34
48
  email: accounts@sentry.io
35
49
  executables: []
@@ -75,6 +89,7 @@ files:
75
89
  - lib/sentry/integrable.rb
76
90
  - lib/sentry/interface.rb
77
91
  - lib/sentry/interfaces/exception.rb
92
+ - lib/sentry/interfaces/mechanism.rb
78
93
  - lib/sentry/interfaces/request.rb
79
94
  - lib/sentry/interfaces/single_exception.rb
80
95
  - lib/sentry/interfaces/stacktrace.rb
@@ -82,6 +97,16 @@ files:
82
97
  - lib/sentry/interfaces/threads.rb
83
98
  - lib/sentry/linecache.rb
84
99
  - lib/sentry/logger.rb
100
+ - lib/sentry/metrics.rb
101
+ - lib/sentry/metrics/aggregator.rb
102
+ - lib/sentry/metrics/configuration.rb
103
+ - lib/sentry/metrics/counter_metric.rb
104
+ - lib/sentry/metrics/distribution_metric.rb
105
+ - lib/sentry/metrics/gauge_metric.rb
106
+ - lib/sentry/metrics/local_aggregator.rb
107
+ - lib/sentry/metrics/metric.rb
108
+ - lib/sentry/metrics/set_metric.rb
109
+ - lib/sentry/metrics/timing.rb
85
110
  - lib/sentry/net/http.rb
86
111
  - lib/sentry/profiler.rb
87
112
  - lib/sentry/propagation_context.rb