sentry-ruby-core 5.21.0 → 5.22.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 890c37ed857f90c7d0b5afbdb4fb07d223b8b3d1a820c1888d680bd653f28688
4
- data.tar.gz: 348af55b8f56829cbf5fc5569ddc8308d4cd6ae508480de206443349e1221235
3
+ metadata.gz: d6ea6ea7aa1656256faf78dd295dddffdea94bac58ff263f49d3a67433a35fd6
4
+ data.tar.gz: 9f8b2d74f31fc7a313d4170bdbbc0707b61c52ae658102c6d353a09f84df81e9
5
5
  SHA512:
6
- metadata.gz: a5b8ad23c029b84648a38095ddd52b289983f9ad18096eeb3fb718a5b8106c6d2513476bc9e3c8ddcad35e66e3f1b20567edbce5f12b661448a811764f9ba4de
7
- data.tar.gz: 8eacb9b0e326b529c743af650920b17e382b379032895d112a83689965a53fe42ae48eb9c070cd86085b1d7d5d9cc7300e0d9199b975edc25ee4aca1f093ecd6
6
+ metadata.gz: 312560c682b07cee10690c180e7e2780daf616574dfcb792bebf1315feeb9a47ad428dbb7c7627439e781b8db85c9bc9166feb77dfa14974373df12982ae6324
7
+ data.tar.gz: 7f2d782aa3b58c473c0c06efbcb1e8ff8ff039a5d396d8066187d2ff863008145fb08a7ad97eedc69841b685d09da01a56c67938bb19032d65fab7ddbce05e7f
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
@@ -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
@@ -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.0"
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-core
3
3
  version: !ruby/object:Gem::Version
4
- version: 5.21.0
4
+ version: 5.22.0
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-04 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: sentry-ruby
@@ -16,14 +16,14 @@ dependencies:
16
16
  requirements:
17
17
  - - '='
18
18
  - !ruby/object:Gem::Version
19
- version: 5.21.0
19
+ version: 5.22.0
20
20
  type: :runtime
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
24
  - - '='
25
25
  - !ruby/object:Gem::Version
26
- version: 5.21.0
26
+ version: 5.22.0
27
27
  - !ruby/object:Gem::Dependency
28
28
  name: concurrent-ruby
29
29
  requirement: !ruby/object:Gem::Requirement
@@ -81,6 +81,8 @@ files:
81
81
  - lib/sentry/error_event.rb
82
82
  - lib/sentry/event.rb
83
83
  - lib/sentry/exceptions.rb
84
+ - lib/sentry/excon.rb
85
+ - lib/sentry/excon/middleware.rb
84
86
  - lib/sentry/faraday.rb
85
87
  - lib/sentry/graphql.rb
86
88
  - lib/sentry/hub.rb
@@ -115,6 +117,7 @@ files:
115
117
  - lib/sentry/rake.rb
116
118
  - lib/sentry/redis.rb
117
119
  - lib/sentry/release_detector.rb
120
+ - lib/sentry/rspec.rb
118
121
  - lib/sentry/scope.rb
119
122
  - lib/sentry/session.rb
120
123
  - lib/sentry/session_flusher.rb
@@ -164,7 +167,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
164
167
  - !ruby/object:Gem::Version
165
168
  version: '0'
166
169
  requirements: []
167
- rubygems_version: 3.5.16
170
+ rubygems_version: 3.5.22
168
171
  signing_key:
169
172
  specification_version: 4
170
173
  summary: A gem that provides a client interface for the Sentry error logger