sentry-ruby 5.3.0 → 5.8.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 (67) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +11 -0
  3. data/.rspec +2 -0
  4. data/.yardopts +2 -0
  5. data/CHANGELOG.md +313 -0
  6. data/CODE_OF_CONDUCT.md +74 -0
  7. data/Gemfile +31 -0
  8. data/Makefile +4 -0
  9. data/README.md +10 -6
  10. data/Rakefile +13 -0
  11. data/bin/console +18 -0
  12. data/bin/setup +8 -0
  13. data/lib/sentry/background_worker.rb +72 -0
  14. data/lib/sentry/backtrace.rb +124 -0
  15. data/lib/sentry/baggage.rb +81 -0
  16. data/lib/sentry/breadcrumb/sentry_logger.rb +90 -0
  17. data/lib/sentry/breadcrumb.rb +70 -0
  18. data/lib/sentry/breadcrumb_buffer.rb +64 -0
  19. data/lib/sentry/client.rb +207 -0
  20. data/lib/sentry/configuration.rb +543 -0
  21. data/lib/sentry/core_ext/object/deep_dup.rb +61 -0
  22. data/lib/sentry/core_ext/object/duplicable.rb +155 -0
  23. data/lib/sentry/dsn.rb +53 -0
  24. data/lib/sentry/envelope.rb +96 -0
  25. data/lib/sentry/error_event.rb +38 -0
  26. data/lib/sentry/event.rb +178 -0
  27. data/lib/sentry/exceptions.rb +9 -0
  28. data/lib/sentry/hub.rb +241 -0
  29. data/lib/sentry/integrable.rb +26 -0
  30. data/lib/sentry/interface.rb +16 -0
  31. data/lib/sentry/interfaces/exception.rb +43 -0
  32. data/lib/sentry/interfaces/request.rb +134 -0
  33. data/lib/sentry/interfaces/single_exception.rb +65 -0
  34. data/lib/sentry/interfaces/stacktrace.rb +87 -0
  35. data/lib/sentry/interfaces/stacktrace_builder.rb +79 -0
  36. data/lib/sentry/interfaces/threads.rb +42 -0
  37. data/lib/sentry/linecache.rb +47 -0
  38. data/lib/sentry/logger.rb +20 -0
  39. data/lib/sentry/net/http.rb +103 -0
  40. data/lib/sentry/rack/capture_exceptions.rb +82 -0
  41. data/lib/sentry/rack.rb +5 -0
  42. data/lib/sentry/rake.rb +41 -0
  43. data/lib/sentry/redis.rb +107 -0
  44. data/lib/sentry/release_detector.rb +39 -0
  45. data/lib/sentry/scope.rb +339 -0
  46. data/lib/sentry/session.rb +33 -0
  47. data/lib/sentry/session_flusher.rb +90 -0
  48. data/lib/sentry/span.rb +236 -0
  49. data/lib/sentry/test_helper.rb +78 -0
  50. data/lib/sentry/transaction.rb +345 -0
  51. data/lib/sentry/transaction_event.rb +53 -0
  52. data/lib/sentry/transport/configuration.rb +25 -0
  53. data/lib/sentry/transport/dummy_transport.rb +21 -0
  54. data/lib/sentry/transport/http_transport.rb +175 -0
  55. data/lib/sentry/transport.rb +214 -0
  56. data/lib/sentry/utils/argument_checking_helper.rb +13 -0
  57. data/lib/sentry/utils/custom_inspection.rb +14 -0
  58. data/lib/sentry/utils/encoding_helper.rb +22 -0
  59. data/lib/sentry/utils/exception_cause_chain.rb +20 -0
  60. data/lib/sentry/utils/logging_helper.rb +26 -0
  61. data/lib/sentry/utils/real_ip.rb +84 -0
  62. data/lib/sentry/utils/request_id.rb +18 -0
  63. data/lib/sentry/version.rb +5 -0
  64. data/lib/sentry-ruby.rb +511 -0
  65. data/sentry-ruby-core.gemspec +23 -0
  66. data/sentry-ruby.gemspec +24 -0
  67. metadata +66 -16
@@ -0,0 +1,124 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Sentry
4
+ # @api private
5
+ class Backtrace
6
+ # Handles backtrace parsing line by line
7
+ class Line
8
+ RB_EXTENSION = ".rb"
9
+ # regexp (optional leading X: on windows, or JRuby9000 class-prefix)
10
+ RUBY_INPUT_FORMAT = /
11
+ ^ \s* (?: [a-zA-Z]: | uri:classloader: )? ([^:]+ | <.*>):
12
+ (\d+)
13
+ (?: :in \s `([^']+)')?$
14
+ /x.freeze
15
+
16
+ # org.jruby.runtime.callsite.CachingCallSite.call(CachingCallSite.java:170)
17
+ JAVA_INPUT_FORMAT = /^(.+)\.([^\.]+)\(([^\:]+)\:(\d+)\)$/.freeze
18
+
19
+ # The file portion of the line (such as app/models/user.rb)
20
+ attr_reader :file
21
+
22
+ # The line number portion of the line
23
+ attr_reader :number
24
+
25
+ # The method of the line (such as index)
26
+ attr_reader :method
27
+
28
+ # The module name (JRuby)
29
+ attr_reader :module_name
30
+
31
+ attr_reader :in_app_pattern
32
+
33
+ # Parses a single line of a given backtrace
34
+ # @param [String] unparsed_line The raw line from +caller+ or some backtrace
35
+ # @return [Line] The parsed backtrace line
36
+ def self.parse(unparsed_line, in_app_pattern)
37
+ ruby_match = unparsed_line.match(RUBY_INPUT_FORMAT)
38
+ if ruby_match
39
+ _, file, number, method = ruby_match.to_a
40
+ file.sub!(/\.class$/, RB_EXTENSION)
41
+ module_name = nil
42
+ else
43
+ java_match = unparsed_line.match(JAVA_INPUT_FORMAT)
44
+ _, module_name, method, file, number = java_match.to_a
45
+ end
46
+ new(file, number, method, module_name, in_app_pattern)
47
+ end
48
+
49
+ def initialize(file, number, method, module_name, in_app_pattern)
50
+ @file = file
51
+ @module_name = module_name
52
+ @number = number.to_i
53
+ @method = method
54
+ @in_app_pattern = in_app_pattern
55
+ end
56
+
57
+ def in_app
58
+ if file =~ in_app_pattern
59
+ true
60
+ else
61
+ false
62
+ end
63
+ end
64
+
65
+ # Reconstructs the line in a readable fashion
66
+ def to_s
67
+ "#{file}:#{number}:in `#{method}'"
68
+ end
69
+
70
+ def ==(other)
71
+ to_s == other.to_s
72
+ end
73
+
74
+ def inspect
75
+ "<Line:#{self}>"
76
+ end
77
+ end
78
+
79
+ APP_DIRS_PATTERN = /(bin|exe|app|config|lib|test)/.freeze
80
+
81
+ # holder for an Array of Backtrace::Line instances
82
+ attr_reader :lines
83
+
84
+ def self.parse(backtrace, project_root, app_dirs_pattern, &backtrace_cleanup_callback)
85
+ ruby_lines = backtrace.is_a?(Array) ? backtrace : backtrace.split(/\n\s*/)
86
+
87
+ ruby_lines = backtrace_cleanup_callback.call(ruby_lines) if backtrace_cleanup_callback
88
+
89
+ in_app_pattern ||= begin
90
+ Regexp.new("^(#{project_root}/)?#{app_dirs_pattern || APP_DIRS_PATTERN}")
91
+ end
92
+
93
+ lines = ruby_lines.to_a.map do |unparsed_line|
94
+ Line.parse(unparsed_line, in_app_pattern)
95
+ end
96
+
97
+ new(lines)
98
+ end
99
+
100
+ def initialize(lines)
101
+ @lines = lines
102
+ end
103
+
104
+ def inspect
105
+ "<Backtrace: " + lines.map(&:inspect).join(", ") + ">"
106
+ end
107
+
108
+ def to_s
109
+ content = []
110
+ lines.each do |line|
111
+ content << line
112
+ end
113
+ content.join("\n")
114
+ end
115
+
116
+ def ==(other)
117
+ if other.respond_to?(:lines)
118
+ lines == other.lines
119
+ else
120
+ false
121
+ end
122
+ end
123
+ end
124
+ end
@@ -0,0 +1,81 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'cgi'
4
+
5
+ module Sentry
6
+ # A {https://www.w3.org/TR/baggage W3C Baggage Header} implementation.
7
+ class Baggage
8
+ SENTRY_PREFIX = 'sentry-'
9
+ SENTRY_PREFIX_REGEX = /^sentry-/.freeze
10
+
11
+ DSC_KEYS = %w(
12
+ trace_id
13
+ public_key
14
+ sample_rate
15
+ release
16
+ environment
17
+ transaction
18
+ user_id
19
+ user_segment
20
+ ).freeze
21
+
22
+ # @return [Hash]
23
+ attr_reader :items
24
+
25
+ # @return [Boolean]
26
+ attr_reader :mutable
27
+
28
+ def initialize(items, mutable: true)
29
+ @items = items
30
+ @mutable = mutable
31
+ end
32
+
33
+ # Creates a Baggage object from an incoming W3C Baggage header string.
34
+ #
35
+ # Sentry items are identified with the 'sentry-' prefix and stored in a hash.
36
+ # The presence of a Sentry item makes the baggage object immutable.
37
+ #
38
+ # @param header [String] The incoming Baggage header string.
39
+ # @return [Baggage, nil]
40
+ def self.from_incoming_header(header)
41
+ items = {}
42
+ mutable = true
43
+
44
+ header.split(',').each do |item|
45
+ item = item.strip
46
+ key, val = item.split('=')
47
+
48
+ next unless key && val
49
+ next unless key =~ SENTRY_PREFIX_REGEX
50
+
51
+ baggage_key = key.split('-')[1]
52
+ next unless baggage_key
53
+
54
+ items[CGI.unescape(baggage_key)] = CGI.unescape(val)
55
+ mutable = false
56
+ end
57
+
58
+ new(items, mutable: mutable)
59
+ end
60
+
61
+ # Make the Baggage immutable.
62
+ # @return [void]
63
+ def freeze!
64
+ @mutable = false
65
+ end
66
+
67
+ # A {https://develop.sentry.dev/sdk/performance/dynamic-sampling-context/#envelope-header Dynamic Sampling Context}
68
+ # hash to be used in the trace envelope header.
69
+ # @return [Hash]
70
+ def dynamic_sampling_context
71
+ @items.select { |k, _v| DSC_KEYS.include?(k) }
72
+ end
73
+
74
+ # Serialize the Baggage object back to a string.
75
+ # @return [String]
76
+ def serialize
77
+ items = @items.map { |k, v| "#{SENTRY_PREFIX}#{CGI.escape(k)}=#{CGI.escape(v)}" }
78
+ items.join(',')
79
+ end
80
+ end
81
+ end
@@ -0,0 +1,90 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'logger'
4
+
5
+ module Sentry
6
+ class Breadcrumb
7
+ module SentryLogger
8
+ LEVELS = {
9
+ ::Logger::DEBUG => 'debug',
10
+ ::Logger::INFO => 'info',
11
+ ::Logger::WARN => 'warn',
12
+ ::Logger::ERROR => 'error',
13
+ ::Logger::FATAL => 'fatal'
14
+ }.freeze
15
+
16
+ def add(*args, &block)
17
+ super
18
+ add_breadcrumb(*args, &block)
19
+ nil
20
+ end
21
+
22
+ def add_breadcrumb(severity, message = nil, progname = nil)
23
+ # because the breadcrumbs now belongs to different Hub's Scope in different threads
24
+ # we need to make sure the current thread's Hub has been set before adding breadcrumbs
25
+ return unless Sentry.initialized? && Sentry.get_current_hub
26
+
27
+ category = "logger"
28
+
29
+ # this is because the nature of Ruby Logger class:
30
+ #
31
+ # when given 1 argument, the argument will become both message and progname
32
+ #
33
+ # ```
34
+ # logger.info("foo")
35
+ # # message == progname == "foo"
36
+ # ```
37
+ #
38
+ # and to specify progname with a different message,
39
+ # we need to pass the progname as the argument and pass the message as a proc
40
+ #
41
+ # ```
42
+ # logger.info("progname") { "the message" }
43
+ # ```
44
+ #
45
+ # so the condition below is to replicate the similar behavior
46
+ if message.nil?
47
+ if block_given?
48
+ message = yield
49
+ category = progname
50
+ else
51
+ message = progname
52
+ end
53
+ end
54
+
55
+ return if ignored_logger?(progname) || message == ""
56
+
57
+ # some loggers will add leading/trailing space as they (incorrectly, mind you)
58
+ # think of logging as a shortcut to std{out,err}
59
+ message = message.to_s.strip
60
+
61
+ last_crumb = current_breadcrumbs.peek
62
+ # try to avoid dupes from logger broadcasts
63
+ if last_crumb.nil? || last_crumb.message != message
64
+ level = Sentry::Breadcrumb::SentryLogger::LEVELS.fetch(severity, nil)
65
+ crumb = Sentry::Breadcrumb.new(
66
+ level: level,
67
+ category: category,
68
+ message: message,
69
+ type: severity >= 3 ? "error" : level
70
+ )
71
+
72
+ Sentry.add_breadcrumb(crumb, hint: { severity: severity })
73
+ end
74
+ end
75
+
76
+ private
77
+
78
+ def ignored_logger?(progname)
79
+ progname == LOGGER_PROGNAME ||
80
+ Sentry.configuration.exclude_loggers.include?(progname)
81
+ end
82
+
83
+ def current_breadcrumbs
84
+ Sentry.get_current_scope.breadcrumbs
85
+ end
86
+ end
87
+ end
88
+ end
89
+
90
+ ::Logger.send(:prepend, Sentry::Breadcrumb::SentryLogger)
@@ -0,0 +1,70 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Sentry
4
+ class Breadcrumb
5
+ DATA_SERIALIZATION_ERROR_MESSAGE = "[data were removed due to serialization issues]"
6
+
7
+ # @return [String, nil]
8
+ attr_accessor :category
9
+ # @return [Hash, nil]
10
+ attr_accessor :data
11
+ # @return [String, nil]
12
+ attr_accessor :level
13
+ # @return [Time, Integer, nil]
14
+ attr_accessor :timestamp
15
+ # @return [String, nil]
16
+ attr_accessor :type
17
+ # @return [String, nil]
18
+ attr_reader :message
19
+
20
+ # @param category [String, nil]
21
+ # @param data [Hash, nil]
22
+ # @param message [String, nil]
23
+ # @param timestamp [Time, Integer, nil]
24
+ # @param level [String, nil]
25
+ # @param type [String, nil]
26
+ def initialize(category: nil, data: nil, message: nil, timestamp: nil, level: nil, type: nil)
27
+ @category = category
28
+ @data = data || {}
29
+ @level = level
30
+ @timestamp = timestamp || Sentry.utc_now.to_i
31
+ @type = type
32
+ self.message = message
33
+ end
34
+
35
+ # @return [Hash]
36
+ def to_hash
37
+ {
38
+ category: @category,
39
+ data: serialized_data,
40
+ level: @level,
41
+ message: @message,
42
+ timestamp: @timestamp,
43
+ type: @type
44
+ }
45
+ end
46
+
47
+ # @param message [String]
48
+ # @return [void]
49
+ def message=(message)
50
+ @message = (message || "").byteslice(0..Event::MAX_MESSAGE_SIZE_IN_BYTES)
51
+ end
52
+
53
+ private
54
+
55
+ def serialized_data
56
+ begin
57
+ ::JSON.parse(::JSON.generate(@data))
58
+ rescue Exception => e
59
+ Sentry.logger.debug(LOGGER_PROGNAME) do
60
+ <<~MSG
61
+ can't serialize breadcrumb data because of error: #{e}
62
+ data: #{@data}
63
+ MSG
64
+ end
65
+
66
+ DATA_SERIALIZATION_ERROR_MESSAGE
67
+ end
68
+ end
69
+ end
70
+ end
@@ -0,0 +1,64 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "sentry/breadcrumb"
4
+
5
+ module Sentry
6
+ class BreadcrumbBuffer
7
+ DEFAULT_SIZE = 100
8
+ include Enumerable
9
+
10
+ # @return [Array]
11
+ attr_accessor :buffer
12
+
13
+ # @param size [Integer, nil] If it's not provided, it'll fallback to DEFAULT_SIZE
14
+ def initialize(size = nil)
15
+ @buffer = Array.new(size || DEFAULT_SIZE)
16
+ end
17
+
18
+ # @param crumb [Breadcrumb]
19
+ # @return [void]
20
+ def record(crumb)
21
+ yield(crumb) if block_given?
22
+ @buffer.slice!(0)
23
+ @buffer << crumb
24
+ end
25
+
26
+ # @return [Array]
27
+ def members
28
+ @buffer.compact
29
+ end
30
+
31
+ # Returns the last breadcrumb stored in the buffer. If the buffer it's empty, it returns nil.
32
+ # @return [Breadcrumb, nil]
33
+ def peek
34
+ members.last
35
+ end
36
+
37
+ # Iterates through all breadcrumbs.
38
+ # @param block [Proc]
39
+ # @yieldparam crumb [Breadcrumb]
40
+ # @return [Array]
41
+ def each(&block)
42
+ members.each(&block)
43
+ end
44
+
45
+ # @return [Boolean]
46
+ def empty?
47
+ members.none?
48
+ end
49
+
50
+ # @return [Hash]
51
+ def to_hash
52
+ {
53
+ values: members.map(&:to_hash)
54
+ }
55
+ end
56
+
57
+ # @return [BreadcrumbBuffer]
58
+ def dup
59
+ copy = super
60
+ copy.buffer = buffer.deep_dup
61
+ copy
62
+ end
63
+ end
64
+ end
@@ -0,0 +1,207 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "sentry/transport"
4
+
5
+ module Sentry
6
+ class Client
7
+ include LoggingHelper
8
+
9
+ # The Transport object that'll send events for the client.
10
+ # @return [Transport]
11
+ attr_reader :transport
12
+
13
+ # @!macro configuration
14
+ attr_reader :configuration
15
+
16
+ # @deprecated Use Sentry.logger to retrieve the current logger instead.
17
+ attr_reader :logger
18
+
19
+ # @param configuration [Configuration]
20
+ def initialize(configuration)
21
+ @configuration = configuration
22
+ @logger = configuration.logger
23
+
24
+ if transport_class = configuration.transport.transport_class
25
+ @transport = transport_class.new(configuration)
26
+ else
27
+ @transport =
28
+ case configuration.dsn&.scheme
29
+ when 'http', 'https'
30
+ HTTPTransport.new(configuration)
31
+ else
32
+ DummyTransport.new(configuration)
33
+ end
34
+ end
35
+ end
36
+
37
+ # Applies the given scope's data to the event and sends it to Sentry.
38
+ # @param event [Event] the event to be sent.
39
+ # @param scope [Scope] the scope with contextual data that'll be applied to the event before it's sent.
40
+ # @param hint [Hash] the hint data that'll be passed to `before_send` callback and the scope's event processors.
41
+ # @return [Event, nil]
42
+ def capture_event(event, scope, hint = {})
43
+ return unless configuration.sending_allowed?
44
+
45
+ unless event.is_a?(TransactionEvent) || configuration.sample_allowed?
46
+ transport.record_lost_event(:sample_rate, 'event')
47
+ return
48
+ end
49
+
50
+ event_type = event.is_a?(Event) ? event.type : event["type"]
51
+ event = scope.apply_to_event(event, hint)
52
+
53
+ if event.nil?
54
+ log_info("Discarded event because one of the event processors returned nil")
55
+ transport.record_lost_event(:event_processor, event_type)
56
+ return
57
+ end
58
+
59
+ if async_block = configuration.async
60
+ dispatch_async_event(async_block, event, hint)
61
+ elsif configuration.background_worker_threads != 0 && hint.fetch(:background, true)
62
+ queued = dispatch_background_event(event, hint)
63
+ transport.record_lost_event(:queue_overflow, event_type) unless queued
64
+ else
65
+ send_event(event, hint)
66
+ end
67
+
68
+ event
69
+ rescue => e
70
+ log_error("Event capturing failed", e, debug: configuration.debug)
71
+ nil
72
+ end
73
+
74
+ # Initializes an Event object with the given exception. Returns `nil` if the exception's class is excluded from reporting.
75
+ # @param exception [Exception] the exception to be reported.
76
+ # @param hint [Hash] the hint data that'll be passed to `before_send` callback and the scope's event processors.
77
+ # @return [Event, nil]
78
+ def event_from_exception(exception, hint = {})
79
+ return unless @configuration.sending_allowed? && @configuration.exception_class_allowed?(exception)
80
+
81
+ integration_meta = Sentry.integrations[hint[:integration]]
82
+
83
+ ErrorEvent.new(configuration: configuration, integration_meta: integration_meta).tap do |event|
84
+ event.add_exception_interface(exception)
85
+ event.add_threads_interface(crashed: true)
86
+ event.level = :error
87
+ end
88
+ end
89
+
90
+ # Initializes an Event object with the given message.
91
+ # @param message [String] the message to be reported.
92
+ # @param hint [Hash] the hint data that'll be passed to `before_send` callback and the scope's event processors.
93
+ # @return [Event]
94
+ def event_from_message(message, hint = {}, backtrace: nil)
95
+ return unless @configuration.sending_allowed?
96
+
97
+ integration_meta = Sentry.integrations[hint[:integration]]
98
+ event = ErrorEvent.new(configuration: configuration, integration_meta: integration_meta, message: message)
99
+ event.add_threads_interface(backtrace: backtrace || caller)
100
+ event.level = :error
101
+ event
102
+ end
103
+
104
+ # Initializes an Event object with the given Transaction object.
105
+ # @param transaction [Transaction] the transaction to be recorded.
106
+ # @return [TransactionEvent]
107
+ def event_from_transaction(transaction)
108
+ TransactionEvent.new(configuration: configuration, transaction: transaction)
109
+ end
110
+
111
+ # @!macro send_event
112
+ def send_event(event, hint = nil)
113
+ event_type = event.is_a?(Event) ? event.type : event["type"]
114
+
115
+ if event_type != TransactionEvent::TYPE && configuration.before_send
116
+ event = configuration.before_send.call(event, hint)
117
+
118
+ if event.nil?
119
+ log_info("Discarded event because before_send returned nil")
120
+ transport.record_lost_event(:before_send, 'event')
121
+ return
122
+ end
123
+ end
124
+
125
+ if event_type == TransactionEvent::TYPE && configuration.before_send_transaction
126
+ event = configuration.before_send_transaction.call(event, hint)
127
+
128
+ if event.nil?
129
+ log_info("Discarded event because before_send_transaction returned nil")
130
+ transport.record_lost_event(:before_send, 'transaction')
131
+ return
132
+ end
133
+ end
134
+
135
+ transport.send_event(event)
136
+
137
+ event
138
+ rescue => e
139
+ loggable_event_type = event_type.capitalize
140
+ log_error("#{loggable_event_type} sending failed", e, debug: configuration.debug)
141
+
142
+ event_info = Event.get_log_message(event.to_hash)
143
+ log_info("Unreported #{loggable_event_type}: #{event_info}")
144
+ transport.record_lost_event(:network_error, event_type)
145
+ raise
146
+ end
147
+
148
+ # Generates a Sentry trace for distribted tracing from the given Span.
149
+ # Returns `nil` if `config.propagate_traces` is `false`.
150
+ # @param span [Span] the span to generate trace from.
151
+ # @return [String, nil]
152
+ def generate_sentry_trace(span)
153
+ return unless configuration.propagate_traces
154
+
155
+ trace = span.to_sentry_trace
156
+ log_debug("[Tracing] Adding #{SENTRY_TRACE_HEADER_NAME} header to outgoing request: #{trace}")
157
+ trace
158
+ end
159
+
160
+ # Generates a W3C Baggage header for distribted tracing from the given Span.
161
+ # Returns `nil` if `config.propagate_traces` is `false`.
162
+ # @param span [Span] the span to generate trace from.
163
+ # @return [String, nil]
164
+ def generate_baggage(span)
165
+ return unless configuration.propagate_traces
166
+
167
+ baggage = span.to_baggage
168
+
169
+ if baggage && !baggage.empty?
170
+ log_debug("[Tracing] Adding #{BAGGAGE_HEADER_NAME} header to outgoing request: #{baggage}")
171
+ end
172
+
173
+ baggage
174
+ end
175
+
176
+ private
177
+
178
+ def dispatch_background_event(event, hint)
179
+ Sentry.background_worker.perform do
180
+ send_event(event, hint)
181
+ end
182
+ end
183
+
184
+ def dispatch_async_event(async_block, event, hint)
185
+ # We have to convert to a JSON-like hash, because background job
186
+ # processors (esp ActiveJob) may not like weird types in the event hash
187
+
188
+ event_hash =
189
+ begin
190
+ event.to_json_compatible
191
+ rescue => e
192
+ log_error("Converting #{event.type} (#{event.event_id}) to JSON compatible hash failed", e, debug: configuration.debug)
193
+ return
194
+ end
195
+
196
+ if async_block.arity == 2
197
+ hint = JSON.parse(JSON.generate(hint))
198
+ async_block.call(event_hash, hint)
199
+ else
200
+ async_block.call(event_hash)
201
+ end
202
+ rescue => e
203
+ log_error("Async #{event_hash["type"]} sending failed", e, debug: configuration.debug)
204
+ send_event(event, hint)
205
+ end
206
+ end
207
+ end