sentry-ruby 6.7.0 → 7.1.0

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/gemfiles/ruby-2.7_rack-2_redis-4.gemfile.lock +9 -9
  3. data/gemfiles/ruby-2.7_rack-3.1_redis-4.gemfile.lock +9 -9
  4. data/gemfiles/ruby-2.7_rack-3_redis-4.gemfile.lock +9 -9
  5. data/gemfiles/ruby-3.0_rack-2_redis-4.gemfile.lock +12 -12
  6. data/gemfiles/ruby-3.0_rack-3.1_redis-4.gemfile.lock +12 -12
  7. data/gemfiles/ruby-3.0_rack-3_redis-4.gemfile.lock +12 -12
  8. data/gemfiles/ruby-3.1_rack-2_redis-4.gemfile.lock +24 -24
  9. data/gemfiles/ruby-3.1_rack-3.1_redis-4.gemfile.lock +24 -24
  10. data/gemfiles/ruby-3.1_rack-3_redis-4.gemfile.lock +24 -24
  11. data/gemfiles/ruby-3.2_rack-0_redis-5.gemfile.lock +24 -24
  12. data/gemfiles/ruby-3.2_rack-2_redis-4.gemfile.lock +26 -26
  13. data/gemfiles/ruby-3.2_rack-2_redis-5.gemfile.lock +26 -26
  14. data/gemfiles/ruby-3.2_rack-3.1_redis-4.gemfile.lock +26 -26
  15. data/gemfiles/ruby-3.2_rack-3_redis-4.gemfile.lock +26 -26
  16. data/gemfiles/ruby-3.2_rack-3_redis-5.gemfile.lock +26 -26
  17. data/gemfiles/ruby-3.3_rack-2_redis-4.gemfile.lock +30 -30
  18. data/gemfiles/ruby-3.3_rack-3.1_redis-4.gemfile.lock +30 -30
  19. data/gemfiles/ruby-3.3_rack-3.1_redis-5.3.gemfile.lock +30 -30
  20. data/gemfiles/ruby-3.3_rack-3_redis-4.gemfile.lock +30 -30
  21. data/gemfiles/ruby-3.4_rack-2_redis-4.gemfile.lock +30 -30
  22. data/gemfiles/ruby-3.4_rack-3.1_redis-4.gemfile.lock +30 -30
  23. data/gemfiles/ruby-3.4_rack-3.1_redis-5.3.gemfile.lock +30 -30
  24. data/gemfiles/ruby-3.4_rack-3_redis-4.gemfile.lock +30 -30
  25. data/gemfiles/ruby-4.0_rack-2_redis-4.gemfile.lock +30 -30
  26. data/gemfiles/ruby-4.0_rack-3.1_redis-4.gemfile.lock +30 -30
  27. data/gemfiles/ruby-4.0_rack-3_redis-4.gemfile.lock +30 -30
  28. data/gemfiles/ruby-jruby-9.4.14.0_rack-2_redis-4.gemfile.lock +24 -24
  29. data/gemfiles/ruby-jruby-9.4.14.0_rack-3.1_redis-4.gemfile.lock +24 -24
  30. data/gemfiles/ruby-jruby-9.4.14.0_rack-3_redis-4.gemfile.lock +24 -24
  31. data/lib/sentry/backtrace.rb +10 -1
  32. data/lib/sentry/client.rb +21 -12
  33. data/lib/sentry/configuration.rb +35 -13
  34. data/lib/sentry/data_collection/key_value_collection.rb +136 -0
  35. data/lib/sentry/data_collection.rb +203 -0
  36. data/lib/sentry/debug_structured_logger.rb +2 -15
  37. data/lib/sentry/error_event.rb +6 -1
  38. data/lib/sentry/event.rb +8 -4
  39. data/lib/sentry/excon/middleware.rb +3 -8
  40. data/lib/sentry/faraday.rb +7 -5
  41. data/lib/sentry/hub.rb +11 -4
  42. data/lib/sentry/integrable.rb +12 -0
  43. data/lib/sentry/interfaces/exception.rb +8 -2
  44. data/lib/sentry/interfaces/request.rb +42 -46
  45. data/lib/sentry/interfaces/single_exception.rb +2 -2
  46. data/lib/sentry/log_event_buffer.rb +1 -1
  47. data/lib/sentry/metric_event.rb +4 -2
  48. data/lib/sentry/metric_event_buffer.rb +1 -1
  49. data/lib/sentry/metrics.rb +12 -6
  50. data/lib/sentry/net/http.rb +3 -4
  51. data/lib/sentry/redis.rb +1 -1
  52. data/lib/sentry/scope.rb +7 -3
  53. data/lib/sentry/session_flusher.rb +7 -0
  54. data/lib/sentry/std_lib_logger.rb +10 -8
  55. data/lib/sentry/telemetry_event_buffer.rb +57 -31
  56. data/lib/sentry/test_helper.rb +6 -4
  57. data/lib/sentry/threaded_periodic_worker.rb +90 -13
  58. data/lib/sentry/transaction.rb +7 -1
  59. data/lib/sentry/utils/callback_helper.rb +27 -0
  60. data/lib/sentry/utils/http_tracing.rb +42 -26
  61. data/lib/sentry/version.rb +1 -1
  62. data/lib/sentry-ruby.rb +7 -18
  63. metadata +9 -6
@@ -4,41 +4,118 @@ module Sentry
4
4
  class ThreadedPeriodicWorker
5
5
  include LoggingHelper
6
6
 
7
+ # @!visibility private
8
+ attr_reader :thread, :thread_mutex, :idle_condition
9
+
7
10
  def initialize(sdk_logger, interval)
11
+ @process_id = Process.pid
8
12
  @thread = nil
9
13
  @exited = false
10
14
  @interval = interval
11
15
  @sdk_logger = sdk_logger
16
+
17
+ @thread_mutex = Mutex.new
18
+ @wake_condition = ConditionVariable.new
19
+ @idle_condition = ConditionVariable.new
20
+
21
+ @woken = false
22
+ @running = false
12
23
  end
13
24
 
14
25
  def ensure_thread
15
- return false if @exited
16
- return true if @thread&.alive?
26
+ reset_if_forked
17
27
 
18
- @thread = Thread.new do
19
- loop do
20
- sleep(@interval)
21
- run
22
- end
23
- end
28
+ @thread_mutex.synchronize do
29
+ return false if @exited
30
+ return true if @thread&.alive?
24
31
 
25
- true
32
+ @thread = Thread.new { worker_loop }
33
+
34
+ true
35
+ end
26
36
  rescue ThreadError
37
+ @thread_mutex.synchronize { @exited = true }
27
38
  log_debug("[#{self.class.name}] thread creation failed")
28
- @exited = true
29
39
  false
30
40
  end
31
41
 
42
+ def wake
43
+ @thread_mutex.synchronize do
44
+ next false if @exited
45
+
46
+ @woken = true
47
+ @wake_condition.signal
48
+ true
49
+ end
50
+ end
51
+
52
+ def wait_until_idle(timeout = nil)
53
+ deadline = Process.clock_gettime(Process::CLOCK_MONOTONIC) + timeout if timeout
54
+
55
+ @thread_mutex.synchronize do
56
+ while !@exited && (@running || @woken)
57
+ remaining = deadline - Process.clock_gettime(Process::CLOCK_MONOTONIC) if deadline
58
+ return false if remaining && remaining <= 0
59
+
60
+ @idle_condition.wait(@thread_mutex, remaining)
61
+ end
62
+
63
+ true
64
+ end
65
+ end
66
+
32
67
  def kill
33
- @exited = true
68
+ thread = @thread_mutex.synchronize do
69
+ @exited = true
70
+ @woken = false
71
+ @idle_condition.broadcast
72
+ @thread
73
+ end
34
74
 
35
75
  # Only a started worker has a thread to kill (and to log about).
36
76
  # Guarding here keeps a never-started worker's teardown silent, so
37
77
  # killing one during test reset can't emit a stray debug line.
38
- return unless @thread
78
+ return unless thread
39
79
 
40
80
  log_debug("[#{self.class.name}] thread killed")
41
- @thread.kill
81
+ thread.kill
82
+ end
83
+
84
+ private
85
+
86
+ def reset_if_forked
87
+ return false if @process_id == Process.pid
88
+
89
+ @process_id = Process.pid
90
+ @thread = nil
91
+ @exited = false
92
+ @woken = false
93
+ @running = false
94
+ @thread_mutex = Mutex.new
95
+ @wake_condition = ConditionVariable.new
96
+ @idle_condition = ConditionVariable.new
97
+ true
98
+ end
99
+
100
+ def worker_loop
101
+ loop do
102
+ @thread_mutex.synchronize do
103
+ @wake_condition.wait(@thread_mutex, @interval) unless @woken
104
+ @woken = false
105
+ @running = true
106
+ end
107
+
108
+ begin
109
+ run
110
+ rescue Exception => e
111
+ log_error("[#{self.class.name}] run failed", e)
112
+ ensure
113
+ @thread_mutex.synchronize do
114
+ @running = false
115
+ @idle_condition.broadcast
116
+ end
117
+ end
118
+ end
42
119
  end
43
120
  end
44
121
  end
@@ -14,6 +14,7 @@ module Sentry
14
14
  SOURCES = %i[custom url route view component task]
15
15
 
16
16
  include LoggingHelper
17
+ include CallbackHelper
17
18
 
18
19
  # The name of the transaction.
19
20
  # @return [String]
@@ -142,7 +143,12 @@ module Sentry
142
143
 
143
144
  sample_rate =
144
145
  if configuration.traces_sampler.is_a?(Proc)
145
- configuration.traces_sampler.call(sampling_context)
146
+ safe_dispatch_callback(
147
+ "traces_sampler",
148
+ configuration.traces_sampler,
149
+ [sampling_context],
150
+ fallback: configuration.traces_sample_rate
151
+ )
146
152
  elsif !sampling_context[:parent_sampled].nil?
147
153
  sampling_context[:parent_sampled]
148
154
  else
@@ -0,0 +1,27 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Sentry
4
+ # @private
5
+ module CallbackHelper
6
+ # @!visibility private
7
+ def safe_dispatch_callback(callback_name, callback, args, fallback: nil)
8
+ callback.call(*args)
9
+ rescue => e
10
+ log_callback_error(callback_name, e)
11
+ fallback
12
+ end
13
+
14
+ private
15
+
16
+ def log_callback_error(callback_name, exception)
17
+ return unless Sentry.initialized?
18
+
19
+ message = "Error in #{callback_name} callback: #{exception.message}"
20
+ message += "\n#{exception.backtrace.join("\n")}" if Sentry.configuration.debug && exception.backtrace
21
+
22
+ Sentry.sdk_logger.error(LOGGER_PROGNAME) { message }
23
+ rescue StandardError
24
+ # Callback error reporting must not propagate to the application.
25
+ end
26
+ end
27
+ end
@@ -1,11 +1,39 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require "uri"
4
+
3
5
  module Sentry
4
6
  module Utils
5
7
  module HttpTracing
8
+ def filter_query_params(query)
9
+ return nil unless query
10
+ return nil unless query.is_a?(String) || query.is_a?(Hash)
11
+
12
+ query_hash = if query.is_a?(String)
13
+ URI.decode_www_form(query).each_with_object({}) do |(key, value), params|
14
+ params[key] = params.key?(key) ? Array(params[key]) + [value] : value
15
+ end
16
+ else
17
+ query
18
+ end
19
+
20
+ filtered_query_hash = Sentry.configuration.data_collection.url_query_params.filter(query_hash)
21
+ return nil if filtered_query_hash.empty?
22
+
23
+ HttpTracing.format_query(filtered_query_hash)
24
+ rescue
25
+ nil
26
+ end
27
+
28
+ def self.format_query(query)
29
+ query.flat_map do |key, value|
30
+ Array(value).map { |item| "#{key}=#{item}" }
31
+ end.join("&")
32
+ end
33
+
6
34
  def set_span_info(sentry_span, request_info, response_status)
7
35
  sentry_span.set_description("#{request_info[:method]} #{request_info[:url]}")
8
- sentry_span.set_data(Span::DataConventions::URL, request_info[:url])
36
+ sentry_span.set_data(Span::DataConventions::URL, url_with_query(request_info))
9
37
  sentry_span.set_data(Span::DataConventions::HTTP_METHOD, request_info[:method])
10
38
  sentry_span.set_data(Span::DataConventions::HTTP_QUERY, request_info[:query]) if request_info[:query]
11
39
  sentry_span.set_data(Span::DataConventions::HTTP_STATUS_CODE, response_status)
@@ -43,37 +71,25 @@ module Sentry
43
71
  Sentry.configuration.trace_propagation_targets.any? { |target| url.match?(target) }
44
72
  end
45
73
 
46
- # Kindly borrowed from Rack::Utils
47
- def build_nested_query(value, prefix = nil)
48
- case value
49
- when Array
50
- value.map { |v|
51
- build_nested_query(v, "#{prefix}[]")
52
- }.join("&")
53
- when Hash
54
- value.map { |k, v|
55
- build_nested_query(v, prefix ? "#{prefix}[#{k}]" : k)
56
- }.delete_if(&:empty?).join("&")
57
- when nil
58
- URI.encode_www_form_component(prefix)
59
- else
60
- raise ArgumentError, "value must be a Hash" if prefix.nil?
61
- "#{URI.encode_www_form_component(prefix)}=#{URI.encode_www_form_component(value)}"
62
- end
63
- end
64
74
 
65
75
  private
66
76
 
77
+ def url_with_query(request_info)
78
+ return request_info[:url] unless request_info[:query]
79
+
80
+ "#{request_info[:url]}?#{request_info[:query]}"
81
+ end
82
+
67
83
  def get_level(status)
68
84
  return :info unless status && status.is_a?(Integer)
69
85
 
70
- if status >= 500
71
- :error
72
- elsif status >= 400
73
- :warning
74
- else
75
- :info
76
- end
86
+ if status >= 500
87
+ :error
88
+ elsif status >= 400
89
+ :warning
90
+ else
91
+ :info
92
+ end
77
93
  end
78
94
  end
79
95
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Sentry
4
- VERSION = "6.7.0"
4
+ VERSION = "7.1.0"
5
5
  end
data/lib/sentry-ruby.rb CHANGED
@@ -10,6 +10,7 @@ require "sentry/core_ext/object/deep_dup"
10
10
  require "sentry/utils/argument_checking_helper"
11
11
  require "sentry/utils/encoding_helper"
12
12
  require "sentry/utils/logging_helper"
13
+ require "sentry/utils/callback_helper"
13
14
  require "sentry/utils/sample_rand"
14
15
  require "sentry/configuration"
15
16
  require "sentry/structured_logger"
@@ -277,7 +278,7 @@ module Sentry
277
278
  @background_worker = Sentry::BackgroundWorker.new(config)
278
279
  @session_flusher = config.session_tracking? ? Sentry::SessionFlusher.new(config, client) : nil
279
280
  @backpressure_monitor = config.enable_backpressure_handling ? Sentry::BackpressureMonitor.new(config, client) : nil
280
- exception_locals_tp.enable if config.include_local_variables
281
+ exception_locals_tp.enable if config.data_collection.collect_stack_frame_variables?
281
282
  at_exit { close }
282
283
  end
283
284
 
@@ -300,8 +301,10 @@ module Sentry
300
301
  if client = get_current_client
301
302
  client.configuration.run_after_close_callbacks
302
303
  client.flush
304
+ client.log_event_buffer.kill
305
+ client.metric_event_buffer.kill
303
306
 
304
- if client.configuration.include_local_variables
307
+ if client.configuration.data_collection.collect_stack_frame_variables?
305
308
  exception_locals_tp.disable
306
309
  end
307
310
  end
@@ -527,7 +530,7 @@ module Sentry
527
530
  # Sentry.capture_log("User logged in", level: :info, user_id: 123)
528
531
  #
529
532
  # @see https://develop.sentry.dev/sdk/telemetry/logs/ Sentry SDK Telemetry Logs Protocol
530
- # @return [LogEvent, nil] The created log event or nil if logging is disabled
533
+ # @return [LogEvent, nil] The created log event or nil if Sentry is not initialized
531
534
  def capture_log(message, **options)
532
535
  return unless initialized?
533
536
  get_current_hub.capture_log_event(message, **options)
@@ -639,14 +642,6 @@ module Sentry
639
642
 
640
643
  # Returns the structured logger instance that implements Sentry's SDK telemetry logs protocol.
641
644
  #
642
- # This logger is only available when logs are enabled in the configuration.
643
- #
644
- # @example Enable logs in configuration
645
- # Sentry.init do |config|
646
- # config.dsn = "YOUR_DSN"
647
- # config.enable_logs = true
648
- # end
649
- #
650
645
  # @example Basic usage
651
646
  # Sentry.logger.info("User logged in successfully", user_id: 123)
652
647
  # Sentry.logger.error("Failed to process payment",
@@ -656,19 +651,13 @@ module Sentry
656
651
  #
657
652
  # @see https://develop.sentry.dev/sdk/telemetry/logs/ Sentry SDK Telemetry Logs Protocol
658
653
  #
659
- # @return [StructuredLogger] The structured logger instance or nil if logs are disabled
654
+ # @return [StructuredLogger] The structured logger instance
660
655
  def logger
661
656
  @logger ||= configuration.structured_logging.logger_class.new(configuration)
662
657
  end
663
658
 
664
659
  # Returns the metrics API for capturing custom metrics.
665
660
  #
666
- # @example Enable metrics
667
- # Sentry.init do |config|
668
- # config.dsn = "YOUR_DSN"
669
- # config.enable_metrics = true
670
- # end
671
- #
672
661
  # @example Usage
673
662
  # Sentry.metrics.count("button.click", 1, attributes: { button_id: "submit" })
674
663
  # Sentry.metrics.distribution("response.time", 120.5, unit: "millisecond")
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: sentry-ruby
3
3
  version: !ruby/object:Gem::Version
4
- version: 6.7.0
4
+ version: 7.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Sentry Team
@@ -125,6 +125,8 @@ files:
125
125
  - lib/sentry/cron/monitor_check_ins.rb
126
126
  - lib/sentry/cron/monitor_config.rb
127
127
  - lib/sentry/cron/monitor_schedule.rb
128
+ - lib/sentry/data_collection.rb
129
+ - lib/sentry/data_collection/key_value_collection.rb
128
130
  - lib/sentry/debug_structured_logger.rb
129
131
  - lib/sentry/dsn.rb
130
132
  - lib/sentry/envelope.rb
@@ -183,6 +185,7 @@ files:
183
185
  - lib/sentry/transport/http_transport.rb
184
186
  - lib/sentry/transport/spotlight_transport.rb
185
187
  - lib/sentry/utils/argument_checking_helper.rb
188
+ - lib/sentry/utils/callback_helper.rb
186
189
  - lib/sentry/utils/custom_inspection.rb
187
190
  - lib/sentry/utils/encoding_helper.rb
188
191
  - lib/sentry/utils/env_helper.rb
@@ -200,15 +203,15 @@ files:
200
203
  - lib/sentry/version.rb
201
204
  - sentry-ruby-core.gemspec
202
205
  - sentry-ruby.gemspec
203
- homepage: https://github.com/getsentry/sentry-ruby/tree/6.7.0/sentry-ruby
206
+ homepage: https://github.com/getsentry/sentry-ruby/tree/7.1.0/sentry-ruby
204
207
  licenses:
205
208
  - MIT
206
209
  metadata:
207
- homepage_uri: https://github.com/getsentry/sentry-ruby/tree/6.7.0/sentry-ruby
208
- source_code_uri: https://github.com/getsentry/sentry-ruby/tree/6.7.0/sentry-ruby
209
- changelog_uri: https://github.com/getsentry/sentry-ruby/blob/6.7.0/CHANGELOG.md
210
+ homepage_uri: https://github.com/getsentry/sentry-ruby/tree/7.1.0/sentry-ruby
211
+ source_code_uri: https://github.com/getsentry/sentry-ruby/tree/7.1.0/sentry-ruby
212
+ changelog_uri: https://github.com/getsentry/sentry-ruby/blob/7.1.0/CHANGELOG.md
210
213
  bug_tracker_uri: https://github.com/getsentry/sentry-ruby/issues
211
- documentation_uri: http://www.rubydoc.info/gems/sentry-ruby/6.7.0
214
+ documentation_uri: http://www.rubydoc.info/gems/sentry-ruby/7.1.0
212
215
  rdoc_options: []
213
216
  require_paths:
214
217
  - lib