sentry-ruby 5.21.0 → 5.22.1

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: cf62f77a0a968d4080fac612bf67f996bee72b51e050e17bf8d40c04c9c895ea
4
- data.tar.gz: 348af55b8f56829cbf5fc5569ddc8308d4cd6ae508480de206443349e1221235
3
+ metadata.gz: 7cf4ebc58389dd621be706b53b86c30e689f9f52c5d5cbda72ef01528c540e5a
4
+ data.tar.gz: f347cb6245d5d37f2d883870b26d846244d33ef6cbb67b838fe231d1c17e95f8
5
5
  SHA512:
6
- metadata.gz: 5baaf6d507772d4a3882a99a43b3f4c88ae4ee56ddfd505433dc88b1e1f21430a2b8fa374932ec4b94478e054a9d1baf12789594bcfe9574db94f449b6526fc2
7
- data.tar.gz: 8eacb9b0e326b529c743af650920b17e382b379032895d112a83689965a53fe42ae48eb9c070cd86085b1d7d5d9cc7300e0d9199b975edc25ee4aca1f093ecd6
6
+ metadata.gz: 2dae846cd0a9d3c10d41d44a775bd39e6d175f559924bf101cd2a88816145ea2cf048962504615d5f05acca0b59451483a2b602a9fb87bba3b2be4364493fd27
7
+ data.tar.gz: b17875017e20c0126c8ddc5d360873430c92d69f0633134743198140c2258f828b3b444748c7a4cd8e0bb9137a9990f2dc394182ffcfe6a19d1e0f3795beebcd
data/Gemfile CHANGED
@@ -28,5 +28,6 @@ gem "benchmark-memory"
28
28
  gem "yard", github: "lsegal/yard"
29
29
  gem "webrick"
30
30
  gem "faraday"
31
+ gem "excon"
31
32
 
32
33
  eval_gemfile File.expand_path("../Gemfile", __dir__)
@@ -0,0 +1,77 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Sentry
4
+ module Excon
5
+ OP_NAME = "http.client"
6
+
7
+ class Middleware < ::Excon::Middleware::Base
8
+ def initialize(stack)
9
+ super
10
+ @instrumenter = Instrumenter.new
11
+ end
12
+
13
+ def request_call(datum)
14
+ @instrumenter.start_transaction(datum)
15
+ @stack.request_call(datum)
16
+ end
17
+
18
+ def response_call(datum)
19
+ @instrumenter.finish_transaction(datum)
20
+ @stack.response_call(datum)
21
+ end
22
+ end
23
+
24
+ class Instrumenter
25
+ SPAN_ORIGIN = "auto.http.excon"
26
+ BREADCRUMB_CATEGORY = "http"
27
+
28
+ include Utils::HttpTracing
29
+
30
+ def start_transaction(env)
31
+ return unless Sentry.initialized?
32
+
33
+ current_span = Sentry.get_current_scope&.span
34
+ @span = current_span&.start_child(op: OP_NAME, start_timestamp: Sentry.utc_now.to_f, origin: SPAN_ORIGIN)
35
+
36
+ request_info = extract_request_info(env)
37
+
38
+ if propagate_trace?(request_info[:url])
39
+ set_propagation_headers(env[:headers])
40
+ end
41
+ end
42
+
43
+ def finish_transaction(response)
44
+ return unless @span
45
+
46
+ response_status = response[:response][:status]
47
+ request_info = extract_request_info(response)
48
+
49
+ if record_sentry_breadcrumb?
50
+ record_sentry_breadcrumb(request_info, response_status)
51
+ end
52
+
53
+ set_span_info(@span, request_info, response_status)
54
+ ensure
55
+ @span&.finish
56
+ end
57
+
58
+ private
59
+
60
+ def extract_request_info(env)
61
+ url = env[:scheme] + "://" + env[:hostname] + env[:path]
62
+ result = { method: env[:method].to_s.upcase, url: url }
63
+
64
+ if Sentry.configuration.send_default_pii
65
+ result[:query] = env[:query]
66
+
67
+ # Handle excon 1.0.0+
68
+ result[:query] = build_nested_query(result[:query]) unless result[:query].is_a?(String)
69
+
70
+ result[:body] = env[:body]
71
+ end
72
+
73
+ result
74
+ end
75
+ end
76
+ end
77
+ end
@@ -0,0 +1,10 @@
1
+ # frozen_string_literal: true
2
+
3
+ Sentry.register_patch(:excon) do
4
+ if defined?(::Excon)
5
+ require "sentry/excon/middleware"
6
+ if Excon.defaults[:middlewares]
7
+ Excon.defaults[:middlewares] << Sentry::Excon::Middleware unless Excon.defaults[:middlewares].include?(Sentry::Excon::Middleware)
8
+ end
9
+ end
10
+ end
data/lib/sentry/hub.rb CHANGED
@@ -255,7 +255,11 @@ module Sentry
255
255
 
256
256
  return unless session
257
257
  session.close
258
- Sentry.session_flusher.add_session(session)
258
+
259
+ # NOTE: Under some circumstances, session_flusher nilified out of sync
260
+ # See: https://github.com/getsentry/sentry-ruby/issues/2378
261
+ # See: https://github.com/getsentry/sentry-ruby/pull/2396
262
+ Sentry.session_flusher&.add_session(session)
259
263
  end
260
264
 
261
265
  def with_session_tracking(&block)
@@ -0,0 +1,91 @@
1
+ # frozen_string_literal: true
2
+
3
+ RSpec::Matchers.define :include_sentry_event do |event_message = "", **opts|
4
+ match do |sentry_events|
5
+ @expected_exception = expected_exception(**opts)
6
+ @context = context(**opts)
7
+ @tags = tags(**opts)
8
+
9
+ @expected_event = expected_event(event_message)
10
+ @matched_event = find_matched_event(event_message, sentry_events)
11
+
12
+ return false unless @matched_event
13
+
14
+ [verify_context(), verify_tags()].all?
15
+ end
16
+
17
+ chain :with_context do |context|
18
+ @context = context
19
+ end
20
+
21
+ chain :with_tags do |tags|
22
+ @tags = tags
23
+ end
24
+
25
+ failure_message do |sentry_events|
26
+ info = ["Failed to find event matching:\n"]
27
+ info << " message: #{@expected_event.message.inspect}"
28
+ info << " exception: #{@expected_exception.inspect}"
29
+ info << " context: #{@context.inspect}"
30
+ info << " tags: #{@tags.inspect}"
31
+ info << "\n"
32
+ info << "Captured events:\n"
33
+ info << dump_events(sentry_events)
34
+ info.join("\n")
35
+ end
36
+
37
+ def expected_event(event_message)
38
+ if @expected_exception
39
+ Sentry.get_current_client.event_from_exception(@expected_exception)
40
+ else
41
+ Sentry.get_current_client.event_from_message(event_message)
42
+ end
43
+ end
44
+
45
+ def expected_exception(**opts)
46
+ opts[:exception].new(opts[:message]) if opts[:exception]
47
+ end
48
+
49
+ def context(**opts)
50
+ opts.fetch(:context, @context || {})
51
+ end
52
+
53
+ def tags(**opts)
54
+ opts.fetch(:tags, @tags || {})
55
+ end
56
+
57
+ def find_matched_event(event_message, sentry_events)
58
+ @matched_event ||= sentry_events
59
+ .find { |event|
60
+ if @expected_exception
61
+ # Is it OK that we only compare the first exception?
62
+ event_exception = event.exception.values.first
63
+ expected_event_exception = @expected_event.exception.values.first
64
+
65
+ event_exception.type == expected_event_exception.type && event_exception.value == expected_event_exception.value
66
+ else
67
+ event.message == @expected_event.message
68
+ end
69
+ }
70
+ end
71
+
72
+ def dump_events(sentry_events)
73
+ sentry_events.map(&Kernel.method(:Hash)).map do |hash|
74
+ hash.select { |k, _| [:message, :contexts, :tags, :exception].include?(k) }
75
+ end.map do |hash|
76
+ JSON.pretty_generate(hash)
77
+ end.join("\n\n")
78
+ end
79
+
80
+ def verify_context
81
+ return true if @context.empty?
82
+
83
+ @matched_event.contexts.any? { |key, value| value == @context[key] }
84
+ end
85
+
86
+ def verify_tags
87
+ return true if @tags.empty?
88
+
89
+ @tags.all? { |key, value| @matched_event.tags.include?(key) && @matched_event.tags[key] == value }
90
+ end
91
+ end
@@ -10,6 +10,7 @@ module Sentry
10
10
  @pending_aggregates = {}
11
11
  @release = configuration.release
12
12
  @environment = configuration.environment
13
+ @mutex = Mutex.new
13
14
 
14
15
  log_debug("[Sessions] Sessions won't be captured without a valid release") unless @release
15
16
  end
@@ -18,7 +19,6 @@ module Sentry
18
19
  return if @pending_aggregates.empty?
19
20
 
20
21
  @client.capture_envelope(pending_envelope)
21
- @pending_aggregates = {}
22
22
  end
23
23
 
24
24
  alias_method :run, :flush
@@ -42,11 +42,15 @@ module Sentry
42
42
  end
43
43
 
44
44
  def pending_envelope
45
- envelope = Envelope.new
45
+ aggregates = @mutex.synchronize do
46
+ aggregates = @pending_aggregates.values
47
+ @pending_aggregates = {}
48
+ aggregates
49
+ end
46
50
 
51
+ envelope = Envelope.new
47
52
  header = { type: "sessions" }
48
- payload = { attrs: attrs, aggregates: @pending_aggregates.values }
49
-
53
+ payload = { attrs: attrs, aggregates: aggregates }
50
54
  envelope.add_item(header, payload)
51
55
  envelope
52
56
  end
data/lib/sentry/span.rb CHANGED
@@ -44,6 +44,11 @@ module Sentry
44
44
  LINENO = "code.lineno"
45
45
  FUNCTION = "code.function"
46
46
  NAMESPACE = "code.namespace"
47
+
48
+ MESSAGING_MESSAGE_ID = "messaging.message.id"
49
+ MESSAGING_DESTINATION_NAME = "messaging.destination.name"
50
+ MESSAGING_MESSAGE_RECEIVE_LATENCY = "messaging.message.receive.latency"
51
+ MESSAGING_MESSAGE_RETRY_COUNT = "messaging.message.retry.count"
47
52
  end
48
53
 
49
54
  STATUS_MAP = {
@@ -19,7 +19,7 @@ module Sentry
19
19
  crumb = Sentry::Breadcrumb.new(
20
20
  level: :info,
21
21
  category: self.class::BREADCRUMB_CATEGORY,
22
- type: :info,
22
+ type: "info",
23
23
  data: { status: response_status, **request_info }
24
24
  )
25
25
 
@@ -36,6 +36,25 @@ module Sentry
36
36
  Sentry.configuration.propagate_traces &&
37
37
  Sentry.configuration.trace_propagation_targets.any? { |target| url.match?(target) }
38
38
  end
39
+
40
+ # Kindly borrowed from Rack::Utils
41
+ def build_nested_query(value, prefix = nil)
42
+ case value
43
+ when Array
44
+ value.map { |v|
45
+ build_nested_query(v, "#{prefix}[]")
46
+ }.join("&")
47
+ when Hash
48
+ value.map { |k, v|
49
+ build_nested_query(v, prefix ? "#{prefix}[#{k}]" : k)
50
+ }.delete_if(&:empty?).join("&")
51
+ when nil
52
+ URI.encode_www_form_component(prefix)
53
+ else
54
+ raise ArgumentError, "value must be a Hash" if prefix.nil?
55
+ "#{URI.encode_www_form_component(prefix)}=#{URI.encode_www_form_component(value)}"
56
+ end
57
+ end
39
58
  end
40
59
  end
41
60
  end
@@ -55,8 +55,7 @@ module Sentry
55
55
  return unless @sampled
56
56
  return if @started
57
57
 
58
- ::Vernier.start_profile
59
- @started = true
58
+ @started = ::Vernier.start_profile
60
59
 
61
60
  log("Started")
62
61
 
@@ -77,6 +76,12 @@ module Sentry
77
76
  @result = ::Vernier.stop_profile
78
77
 
79
78
  log("Stopped")
79
+ rescue RuntimeError => e
80
+ if e.message.include?("Profile not started")
81
+ log("Not stopped since not started")
82
+ else
83
+ log("Failed to stop Vernier: #{e.message}")
84
+ end
80
85
  end
81
86
 
82
87
  def active_thread_id
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Sentry
4
- VERSION = "5.21.0"
4
+ VERSION = "5.22.1"
5
5
  end
data/lib/sentry-ruby.rb CHANGED
@@ -50,6 +50,8 @@ module Sentry
50
50
 
51
51
  THREAD_LOCAL = :sentry_hub
52
52
 
53
+ MUTEX = Mutex.new
54
+
53
55
  class << self
54
56
  # @!visibility private
55
57
  def exception_locals_tp
@@ -275,8 +277,10 @@ module Sentry
275
277
 
276
278
  @background_worker.shutdown
277
279
 
278
- @main_hub = nil
279
- Thread.current.thread_variable_set(THREAD_LOCAL, nil)
280
+ MUTEX.synchronize do
281
+ @main_hub = nil
282
+ Thread.current.thread_variable_set(THREAD_LOCAL, nil)
283
+ end
280
284
  end
281
285
 
282
286
  # Returns true if the SDK is initialized.
@@ -303,7 +307,7 @@ module Sentry
303
307
  #
304
308
  # @return [Hub]
305
309
  def get_main_hub
306
- @main_hub
310
+ MUTEX.synchronize { @main_hub }
307
311
  end
308
312
 
309
313
  # Takes an instance of Sentry::Breadcrumb and stores it to the current active scope.
@@ -610,3 +614,4 @@ require "sentry/redis"
610
614
  require "sentry/puma"
611
615
  require "sentry/graphql"
612
616
  require "sentry/faraday"
617
+ require "sentry/excon"
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.21.0
4
+ version: 5.22.1
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-10-07 00:00:00.000000000 Z
11
+ date: 2024-12-16 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: concurrent-ruby
@@ -87,6 +87,8 @@ files:
87
87
  - lib/sentry/error_event.rb
88
88
  - lib/sentry/event.rb
89
89
  - lib/sentry/exceptions.rb
90
+ - lib/sentry/excon.rb
91
+ - lib/sentry/excon/middleware.rb
90
92
  - lib/sentry/faraday.rb
91
93
  - lib/sentry/graphql.rb
92
94
  - lib/sentry/hub.rb
@@ -121,6 +123,7 @@ files:
121
123
  - lib/sentry/rake.rb
122
124
  - lib/sentry/redis.rb
123
125
  - lib/sentry/release_detector.rb
126
+ - lib/sentry/rspec.rb
124
127
  - lib/sentry/scope.rb
125
128
  - lib/sentry/session.rb
126
129
  - lib/sentry/session_flusher.rb
@@ -148,15 +151,15 @@ files:
148
151
  - lib/sentry/version.rb
149
152
  - sentry-ruby-core.gemspec
150
153
  - sentry-ruby.gemspec
151
- homepage: https://github.com/getsentry/sentry-ruby/tree/5.21.0/sentry-ruby
154
+ homepage: https://github.com/getsentry/sentry-ruby/tree/5.22.1/sentry-ruby
152
155
  licenses:
153
156
  - MIT
154
157
  metadata:
155
- homepage_uri: https://github.com/getsentry/sentry-ruby/tree/5.21.0/sentry-ruby
156
- source_code_uri: https://github.com/getsentry/sentry-ruby/tree/5.21.0/sentry-ruby
157
- changelog_uri: https://github.com/getsentry/sentry-ruby/blob/5.21.0/CHANGELOG.md
158
+ homepage_uri: https://github.com/getsentry/sentry-ruby/tree/5.22.1/sentry-ruby
159
+ source_code_uri: https://github.com/getsentry/sentry-ruby/tree/5.22.1/sentry-ruby
160
+ changelog_uri: https://github.com/getsentry/sentry-ruby/blob/5.22.1/CHANGELOG.md
158
161
  bug_tracker_uri: https://github.com/getsentry/sentry-ruby/issues
159
- documentation_uri: http://www.rubydoc.info/gems/sentry-ruby/5.21.0
162
+ documentation_uri: http://www.rubydoc.info/gems/sentry-ruby/5.22.1
160
163
  post_install_message:
161
164
  rdoc_options: []
162
165
  require_paths:
@@ -172,7 +175,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
172
175
  - !ruby/object:Gem::Version
173
176
  version: '0'
174
177
  requirements: []
175
- rubygems_version: 3.5.16
178
+ rubygems_version: 3.5.22
176
179
  signing_key:
177
180
  specification_version: 4
178
181
  summary: A gem that provides a client interface for the Sentry error logger