sentry-ruby 5.10.0 → 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 (63) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile +2 -13
  3. data/README.md +10 -10
  4. data/Rakefile +1 -1
  5. data/lib/sentry/background_worker.rb +9 -2
  6. data/lib/sentry/backpressure_monitor.rb +75 -0
  7. data/lib/sentry/backtrace.rb +7 -3
  8. data/lib/sentry/breadcrumb.rb +8 -2
  9. data/lib/sentry/check_in_event.rb +60 -0
  10. data/lib/sentry/client.rb +88 -17
  11. data/lib/sentry/configuration.rb +66 -12
  12. data/lib/sentry/cron/configuration.rb +23 -0
  13. data/lib/sentry/cron/monitor_check_ins.rb +75 -0
  14. data/lib/sentry/cron/monitor_config.rb +53 -0
  15. data/lib/sentry/cron/monitor_schedule.rb +42 -0
  16. data/lib/sentry/dsn.rb +1 -1
  17. data/lib/sentry/envelope.rb +19 -2
  18. data/lib/sentry/error_event.rb +2 -2
  19. data/lib/sentry/event.rb +14 -36
  20. data/lib/sentry/hub.rb +70 -2
  21. data/lib/sentry/integrable.rb +10 -0
  22. data/lib/sentry/interface.rb +1 -0
  23. data/lib/sentry/interfaces/exception.rb +5 -3
  24. data/lib/sentry/interfaces/mechanism.rb +20 -0
  25. data/lib/sentry/interfaces/request.rb +2 -2
  26. data/lib/sentry/interfaces/single_exception.rb +10 -6
  27. data/lib/sentry/interfaces/stacktrace_builder.rb +8 -0
  28. data/lib/sentry/metrics/aggregator.rb +276 -0
  29. data/lib/sentry/metrics/configuration.rb +47 -0
  30. data/lib/sentry/metrics/counter_metric.rb +25 -0
  31. data/lib/sentry/metrics/distribution_metric.rb +25 -0
  32. data/lib/sentry/metrics/gauge_metric.rb +35 -0
  33. data/lib/sentry/metrics/local_aggregator.rb +53 -0
  34. data/lib/sentry/metrics/metric.rb +19 -0
  35. data/lib/sentry/metrics/set_metric.rb +28 -0
  36. data/lib/sentry/metrics/timing.rb +43 -0
  37. data/lib/sentry/metrics.rb +55 -0
  38. data/lib/sentry/net/http.rb +25 -22
  39. data/lib/sentry/profiler.rb +18 -7
  40. data/lib/sentry/propagation_context.rb +135 -0
  41. data/lib/sentry/puma.rb +12 -5
  42. data/lib/sentry/rack/capture_exceptions.rb +7 -5
  43. data/lib/sentry/rake.rb +3 -14
  44. data/lib/sentry/redis.rb +8 -3
  45. data/lib/sentry/release_detector.rb +1 -1
  46. data/lib/sentry/scope.rb +36 -15
  47. data/lib/sentry/session.rb +2 -2
  48. data/lib/sentry/session_flusher.rb +1 -6
  49. data/lib/sentry/span.rb +54 -3
  50. data/lib/sentry/test_helper.rb +18 -12
  51. data/lib/sentry/transaction.rb +33 -33
  52. data/lib/sentry/transaction_event.rb +5 -3
  53. data/lib/sentry/transport/configuration.rb +73 -1
  54. data/lib/sentry/transport/http_transport.rb +68 -37
  55. data/lib/sentry/transport/spotlight_transport.rb +50 -0
  56. data/lib/sentry/transport.rb +27 -37
  57. data/lib/sentry/utils/argument_checking_helper.rb +12 -0
  58. data/lib/sentry/utils/real_ip.rb +1 -1
  59. data/lib/sentry/utils/request_id.rb +1 -1
  60. data/lib/sentry/version.rb +1 -1
  61. data/lib/sentry-ruby.rb +96 -26
  62. data/sentry-ruby.gemspec +1 -0
  63. metadata +35 -2
@@ -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
@@ -18,7 +17,9 @@ module Sentry
18
17
  :network_error,
19
18
  :sample_rate,
20
19
  :before_send,
21
- :event_processor
20
+ :event_processor,
21
+ :insufficient_data,
22
+ :backpressure
22
23
  ]
23
24
 
24
25
  include LoggingHelper
@@ -73,7 +74,7 @@ module Sentry
73
74
  result, oversized = item.serialize
74
75
 
75
76
  if oversized
76
- log_info("Envelope item [#{item.type}] is still oversized after size reduction: {#{item.size_breakdown}}")
77
+ log_debug("Envelope item [#{item.type}] is still oversized after size reduction: {#{item.size_breakdown}}")
77
78
 
78
79
  next
79
80
  end
@@ -87,18 +88,9 @@ module Sentry
87
88
  [data, serialized_items]
88
89
  end
89
90
 
90
- def is_rate_limited?(item_type)
91
+ def is_rate_limited?(data_category)
91
92
  # check category-specific limit
92
- category_delay =
93
- case item_type
94
- when "transaction"
95
- @rate_limits["transaction"]
96
- when "sessions"
97
- @rate_limits["session"]
98
- else
99
- @rate_limits["error"]
100
- end
101
-
93
+ category_delay = @rate_limits[data_category]
102
94
  # check universal limit if not category limit
103
95
  universal_delay = @rate_limits[nil]
104
96
 
@@ -118,16 +110,8 @@ module Sentry
118
110
  !!delay && delay > Time.now
119
111
  end
120
112
 
121
- def generate_auth_header
122
- now = Sentry.utc_now.to_i
123
- fields = {
124
- 'sentry_version' => PROTOCOL_VERSION,
125
- 'sentry_client' => USER_AGENT,
126
- 'sentry_timestamp' => now,
127
- 'sentry_key' => @dsn.public_key
128
- }
129
- fields['sentry_secret'] = @dsn.secret_key if @dsn.secret_key
130
- 'Sentry ' + fields.map { |key, value| "#{key}=#{value}" }.join(', ')
113
+ def any_rate_limited?
114
+ @rate_limits.values.any? { |t| t && t > Time.now }
131
115
  end
132
116
 
133
117
  def envelope_from_event(event)
@@ -143,7 +127,7 @@ module Sentry
143
127
  sent_at: Sentry.utc_now.iso8601
144
128
  }
145
129
 
146
- if event.is_a?(TransactionEvent) && event.dynamic_sampling_context
130
+ if event.is_a?(Event) && event.dynamic_sampling_context
147
131
  envelope_headers[:trace] = event.dynamic_sampling_context
148
132
  end
149
133
 
@@ -167,26 +151,31 @@ module Sentry
167
151
  envelope
168
152
  end
169
153
 
170
- def record_lost_event(reason, item_type)
154
+ def record_lost_event(reason, data_category)
171
155
  return unless @send_client_reports
172
156
  return unless CLIENT_REPORT_REASONS.include?(reason)
173
157
 
174
- @discarded_events[[reason, item_type]] += 1
158
+ @discarded_events[[reason, data_category]] += 1
159
+ end
160
+
161
+ def flush
162
+ client_report_headers, client_report_payload = fetch_pending_client_report(force: true)
163
+ return unless client_report_headers
164
+
165
+ envelope = Envelope.new
166
+ envelope.add_item(client_report_headers, client_report_payload)
167
+ send_envelope(envelope)
175
168
  end
176
169
 
177
170
  private
178
171
 
179
- def fetch_pending_client_report
172
+ def fetch_pending_client_report(force: false)
180
173
  return nil unless @send_client_reports
181
- return nil if @last_client_report_sent > Time.now - CLIENT_REPORT_INTERVAL
174
+ return nil if !force && @last_client_report_sent > Time.now - CLIENT_REPORT_INTERVAL
182
175
  return nil if @discarded_events.empty?
183
176
 
184
177
  discarded_events_hash = @discarded_events.map do |key, val|
185
- reason, type = key
186
-
187
- # 'event' has to be mapped to 'error'
188
- category = type == 'transaction' ? 'transaction' : 'error'
189
-
178
+ reason, category = key
190
179
  { reason: reason, category: category, quantity: val }
191
180
  end
192
181
 
@@ -204,9 +193,9 @@ module Sentry
204
193
 
205
194
  def reject_rate_limited_items(envelope)
206
195
  envelope.items.reject! do |item|
207
- if is_rate_limited?(item.type)
208
- log_info("[Transport] Envelope item [#{item.type}] not sent: rate limiting")
209
- record_lost_event(:ratelimit_backoff, item.type)
196
+ if is_rate_limited?(item.data_category)
197
+ log_debug("[Transport] Envelope item [#{item.type}] not sent: rate limiting")
198
+ record_lost_event(:ratelimit_backoff, item.data_category)
210
199
 
211
200
  true
212
201
  else
@@ -219,3 +208,4 @@ end
219
208
 
220
209
  require "sentry/transport/dummy_transport"
221
210
  require "sentry/transport/http_transport"
211
+ require "sentry/transport/spotlight_transport"
@@ -9,5 +9,17 @@ module Sentry
9
9
  raise ArgumentError, "expect the argument to be a #{expected_types.join(' or ')}, got #{argument.class} (#{argument.inspect})"
10
10
  end
11
11
  end
12
+
13
+ def check_argument_includes!(argument, values)
14
+ unless values.include?(argument)
15
+ raise ArgumentError, "expect the argument to be one of #{values.map(&:inspect).join(' or ')}, got #{argument.inspect}"
16
+ end
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
12
24
  end
13
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.10.0"
4
+ VERSION = "5.17.3"
5
5
  end
data/lib/sentry-ruby.rb CHANGED
@@ -15,15 +15,19 @@ require "sentry/logger"
15
15
  require "sentry/event"
16
16
  require "sentry/error_event"
17
17
  require "sentry/transaction_event"
18
+ require "sentry/check_in_event"
18
19
  require "sentry/span"
19
20
  require "sentry/transaction"
20
21
  require "sentry/hub"
21
22
  require "sentry/background_worker"
22
23
  require "sentry/session_flusher"
24
+ require "sentry/backpressure_monitor"
25
+ require "sentry/cron/monitor_check_ins"
26
+ require "sentry/metrics"
23
27
 
24
28
  [
25
29
  "sentry/rake",
26
- "sentry/rack",
30
+ "sentry/rack"
27
31
  ].each do |lib|
28
32
  begin
29
33
  require lib
@@ -63,25 +67,33 @@ module Sentry
63
67
  end
64
68
 
65
69
  # @!attribute [rw] background_worker
66
- # @return [BackgroundWorker, nil]
70
+ # @return [BackgroundWorker]
67
71
  attr_accessor :background_worker
68
72
 
69
73
  # @!attribute [r] session_flusher
70
74
  # @return [SessionFlusher, nil]
71
75
  attr_reader :session_flusher
72
76
 
77
+ # @!attribute [r] backpressure_monitor
78
+ # @return [BackpressureMonitor, nil]
79
+ attr_reader :backpressure_monitor
80
+
81
+ # @!attribute [r] metrics_aggregator
82
+ # @return [Metrics::Aggregator, nil]
83
+ attr_reader :metrics_aggregator
84
+
73
85
  ##### Patch Registration #####
74
86
 
75
87
  # @!visibility private
76
- def register_patch(patch = nil, target = nil, &block)
88
+ def register_patch(key, patch = nil, target = nil, &block)
77
89
  if patch && block
78
90
  raise ArgumentError.new("Please provide either a patch and its target OR a block, but not both")
79
91
  end
80
92
 
81
93
  if block
82
- registered_patches << block
94
+ registered_patches[key] = block
83
95
  else
84
- registered_patches << proc do
96
+ registered_patches[key] = proc do
85
97
  target.send(:prepend, patch) unless target.ancestors.include?(patch)
86
98
  end
87
99
  end
@@ -89,14 +101,14 @@ module Sentry
89
101
 
90
102
  # @!visibility private
91
103
  def apply_patches(config)
92
- registered_patches.each do |patch|
93
- patch.call(config)
104
+ registered_patches.each do |key, patch|
105
+ patch.call(config) if config.enabled_patches.include?(key)
94
106
  end
95
107
  end
96
108
 
97
109
  # @!visibility private
98
110
  def registered_patches
99
- @registered_patches ||= []
111
+ @registered_patches ||= {}
100
112
  end
101
113
 
102
114
  ##### Integrations #####
@@ -215,17 +227,10 @@ module Sentry
215
227
  Thread.current.thread_variable_set(THREAD_LOCAL, hub)
216
228
  @main_hub = hub
217
229
  @background_worker = Sentry::BackgroundWorker.new(config)
218
-
219
- @session_flusher = if config.auto_session_tracking
220
- Sentry::SessionFlusher.new(config, client)
221
- else
222
- nil
223
- end
224
-
225
- if config.include_local_variables
226
- exception_locals_tp.enable
227
- end
228
-
230
+ @session_flusher = config.session_tracking? ? Sentry::SessionFlusher.new(config, client) : nil
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
233
+ exception_locals_tp.enable if config.include_local_variables
229
234
  at_exit { close }
230
235
  end
231
236
 
@@ -234,20 +239,33 @@ module Sentry
234
239
  #
235
240
  # @return [void]
236
241
  def close
237
- if @background_worker
238
- @background_worker.shutdown
239
- @background_worker = nil
240
- end
241
-
242
242
  if @session_flusher
243
+ @session_flusher.flush
243
244
  @session_flusher.kill
244
245
  @session_flusher = nil
245
246
  end
246
247
 
247
- if configuration&.include_local_variables
248
- exception_locals_tp.disable
248
+ if @backpressure_monitor
249
+ @backpressure_monitor.kill
250
+ @backpressure_monitor = nil
251
+ end
252
+
253
+ if @metrics_aggregator
254
+ @metrics_aggregator.flush(force: true)
255
+ @metrics_aggregator.kill
256
+ @metrics_aggregator = nil
257
+ end
258
+
259
+ if client = get_current_client
260
+ client.flush
261
+
262
+ if client.configuration.include_local_variables
263
+ exception_locals_tp.disable
264
+ end
249
265
  end
250
266
 
267
+ @background_worker.shutdown
268
+
251
269
  @main_hub = nil
252
270
  Thread.current.thread_variable_set(THREAD_LOCAL, nil)
253
271
  end
@@ -430,6 +448,22 @@ module Sentry
430
448
  get_current_hub.capture_event(event)
431
449
  end
432
450
 
451
+ # Captures a check-in and sends it to Sentry via the currently active hub.
452
+ #
453
+ # @param slug [String] identifier of this monitor
454
+ # @param status [Symbol] status of this check-in, one of {CheckInEvent::VALID_STATUSES}
455
+ #
456
+ # @param [Hash] options extra check-in options
457
+ # @option options [String] check_in_id for updating the status of an existing monitor
458
+ # @option options [Integer] duration seconds elapsed since this monitor started
459
+ # @option options [Cron::MonitorConfig] monitor_config configuration for this monitor
460
+ #
461
+ # @return [String, nil] The {CheckInEvent#check_in_id} to use for later updates on the same slug
462
+ def capture_check_in(slug, status, **options)
463
+ return unless initialized?
464
+ get_current_hub.capture_check_in(slug, status, **options)
465
+ end
466
+
433
467
  # Takes or initializes a new Sentry::Transaction and makes a sampling decision for it.
434
468
  #
435
469
  # @return [Transaction, nil]
@@ -489,6 +523,42 @@ module Sentry
489
523
  Scope.add_global_event_processor(&block)
490
524
  end
491
525
 
526
+ # Returns the traceparent (sentry-trace) header for distributed tracing.
527
+ # Can be either from the currently active span or the propagation context.
528
+ #
529
+ # @return [String, nil]
530
+ def get_traceparent
531
+ return nil unless initialized?
532
+ get_current_hub.get_traceparent
533
+ end
534
+
535
+ # Returns the baggage header for distributed tracing.
536
+ # Can be either from the currently active span or the propagation context.
537
+ #
538
+ # @return [String, nil]
539
+ def get_baggage
540
+ return nil unless initialized?
541
+ get_current_hub.get_baggage
542
+ end
543
+
544
+ # Returns the a Hash containing sentry-trace and baggage.
545
+ # Can be either from the currently active span or the propagation context.
546
+ #
547
+ # @return [Hash, nil]
548
+ def get_trace_propagation_headers
549
+ return nil unless initialized?
550
+ get_current_hub.get_trace_propagation_headers
551
+ end
552
+
553
+ # Continue an incoming trace from a rack env like hash.
554
+ #
555
+ # @param env [Hash]
556
+ # @return [Transaction, nil]
557
+ def continue_trace(env, **options)
558
+ return nil unless initialized?
559
+ get_current_hub.continue_trace(env, **options)
560
+ end
561
+
492
562
  ##### Helpers #####
493
563
 
494
564
  # @!visibility private
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.10.0
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: 2023-07-04 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: []
@@ -51,15 +65,21 @@ files:
51
65
  - bin/setup
52
66
  - lib/sentry-ruby.rb
53
67
  - lib/sentry/background_worker.rb
68
+ - lib/sentry/backpressure_monitor.rb
54
69
  - lib/sentry/backtrace.rb
55
70
  - lib/sentry/baggage.rb
56
71
  - lib/sentry/breadcrumb.rb
57
72
  - lib/sentry/breadcrumb/sentry_logger.rb
58
73
  - lib/sentry/breadcrumb_buffer.rb
74
+ - lib/sentry/check_in_event.rb
59
75
  - lib/sentry/client.rb
60
76
  - lib/sentry/configuration.rb
61
77
  - lib/sentry/core_ext/object/deep_dup.rb
62
78
  - lib/sentry/core_ext/object/duplicable.rb
79
+ - lib/sentry/cron/configuration.rb
80
+ - lib/sentry/cron/monitor_check_ins.rb
81
+ - lib/sentry/cron/monitor_config.rb
82
+ - lib/sentry/cron/monitor_schedule.rb
63
83
  - lib/sentry/dsn.rb
64
84
  - lib/sentry/envelope.rb
65
85
  - lib/sentry/error_event.rb
@@ -69,6 +89,7 @@ files:
69
89
  - lib/sentry/integrable.rb
70
90
  - lib/sentry/interface.rb
71
91
  - lib/sentry/interfaces/exception.rb
92
+ - lib/sentry/interfaces/mechanism.rb
72
93
  - lib/sentry/interfaces/request.rb
73
94
  - lib/sentry/interfaces/single_exception.rb
74
95
  - lib/sentry/interfaces/stacktrace.rb
@@ -76,8 +97,19 @@ files:
76
97
  - lib/sentry/interfaces/threads.rb
77
98
  - lib/sentry/linecache.rb
78
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
79
110
  - lib/sentry/net/http.rb
80
111
  - lib/sentry/profiler.rb
112
+ - lib/sentry/propagation_context.rb
81
113
  - lib/sentry/puma.rb
82
114
  - lib/sentry/rack.rb
83
115
  - lib/sentry/rack/capture_exceptions.rb
@@ -95,6 +127,7 @@ files:
95
127
  - lib/sentry/transport/configuration.rb
96
128
  - lib/sentry/transport/dummy_transport.rb
97
129
  - lib/sentry/transport/http_transport.rb
130
+ - lib/sentry/transport/spotlight_transport.rb
98
131
  - lib/sentry/utils/argument_checking_helper.rb
99
132
  - lib/sentry/utils/custom_inspection.rb
100
133
  - lib/sentry/utils/encoding_helper.rb