sentry-ruby 5.10.0 → 5.17.3

Sign up to get free protection for your applications and to get access to all the features.
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