sentry-ruby 5.1.0 → 5.4.2
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 +4 -4
- data/.gitignore +11 -0
- data/.rspec +3 -0
- data/.yardopts +2 -0
- data/CHANGELOG.md +313 -0
- data/CODE_OF_CONDUCT.md +74 -0
- data/Gemfile +27 -0
- data/Makefile +4 -0
- data/README.md +8 -7
- data/Rakefile +13 -0
- data/bin/console +18 -0
- data/bin/setup +8 -0
- data/lib/sentry/background_worker.rb +72 -0
- data/lib/sentry/backtrace.rb +124 -0
- data/lib/sentry/breadcrumb/sentry_logger.rb +90 -0
- data/lib/sentry/breadcrumb.rb +70 -0
- data/lib/sentry/breadcrumb_buffer.rb +64 -0
- data/lib/sentry/client.rb +190 -0
- data/lib/sentry/configuration.rb +502 -0
- data/lib/sentry/core_ext/object/deep_dup.rb +61 -0
- data/lib/sentry/core_ext/object/duplicable.rb +155 -0
- data/lib/sentry/dsn.rb +53 -0
- data/lib/sentry/envelope.rb +96 -0
- data/lib/sentry/error_event.rb +38 -0
- data/lib/sentry/event.rb +178 -0
- data/lib/sentry/exceptions.rb +9 -0
- data/lib/sentry/hub.rb +220 -0
- data/lib/sentry/integrable.rb +26 -0
- data/lib/sentry/interface.rb +16 -0
- data/lib/sentry/interfaces/exception.rb +43 -0
- data/lib/sentry/interfaces/request.rb +144 -0
- data/lib/sentry/interfaces/single_exception.rb +57 -0
- data/lib/sentry/interfaces/stacktrace.rb +87 -0
- data/lib/sentry/interfaces/stacktrace_builder.rb +79 -0
- data/lib/sentry/interfaces/threads.rb +42 -0
- data/lib/sentry/linecache.rb +47 -0
- data/lib/sentry/logger.rb +20 -0
- data/lib/sentry/net/http.rb +115 -0
- data/lib/sentry/rack/capture_exceptions.rb +80 -0
- data/lib/sentry/rack.rb +5 -0
- data/lib/sentry/rake.rb +41 -0
- data/lib/sentry/redis.rb +90 -0
- data/lib/sentry/release_detector.rb +39 -0
- data/lib/sentry/scope.rb +295 -0
- data/lib/sentry/session.rb +35 -0
- data/lib/sentry/session_flusher.rb +90 -0
- data/lib/sentry/span.rb +226 -0
- data/lib/sentry/test_helper.rb +76 -0
- data/lib/sentry/transaction.rb +206 -0
- data/lib/sentry/transaction_event.rb +29 -0
- data/lib/sentry/transport/configuration.rb +25 -0
- data/lib/sentry/transport/dummy_transport.rb +21 -0
- data/lib/sentry/transport/http_transport.rb +175 -0
- data/lib/sentry/transport.rb +210 -0
- data/lib/sentry/utils/argument_checking_helper.rb +13 -0
- data/lib/sentry/utils/custom_inspection.rb +14 -0
- data/lib/sentry/utils/exception_cause_chain.rb +20 -0
- data/lib/sentry/utils/logging_helper.rb +26 -0
- data/lib/sentry/utils/real_ip.rb +84 -0
- data/lib/sentry/utils/request_id.rb +18 -0
- data/lib/sentry/version.rb +5 -0
- data/lib/sentry-ruby.rb +505 -0
- data/sentry-ruby-core.gemspec +23 -0
- data/sentry-ruby.gemspec +24 -0
- metadata +64 -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,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,190 @@
|
|
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).tap do |event|
|
109
|
+
event.transaction = transaction.name
|
110
|
+
event.contexts.merge!(trace: transaction.get_trace_context)
|
111
|
+
event.timestamp = transaction.timestamp
|
112
|
+
event.start_timestamp = transaction.start_timestamp
|
113
|
+
event.tags = transaction.tags
|
114
|
+
|
115
|
+
finished_spans = transaction.span_recorder.spans.select { |span| span.timestamp && span != transaction }
|
116
|
+
event.spans = finished_spans.map(&:to_hash)
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
# @!macro send_event
|
121
|
+
def send_event(event, hint = nil)
|
122
|
+
event_type = event.is_a?(Event) ? event.type : event["type"]
|
123
|
+
|
124
|
+
if event_type != TransactionEvent::TYPE && configuration.before_send
|
125
|
+
event = configuration.before_send.call(event, hint)
|
126
|
+
|
127
|
+
if event.nil?
|
128
|
+
log_info("Discarded event because before_send returned nil")
|
129
|
+
transport.record_lost_event(:before_send, 'event')
|
130
|
+
return
|
131
|
+
end
|
132
|
+
end
|
133
|
+
|
134
|
+
transport.send_event(event)
|
135
|
+
|
136
|
+
event
|
137
|
+
rescue => e
|
138
|
+
loggable_event_type = event_type.capitalize
|
139
|
+
log_error("#{loggable_event_type} sending failed", e, debug: configuration.debug)
|
140
|
+
|
141
|
+
event_info = Event.get_log_message(event.to_hash)
|
142
|
+
log_info("Unreported #{loggable_event_type}: #{event_info}")
|
143
|
+
transport.record_lost_event(:network_error, event_type)
|
144
|
+
raise
|
145
|
+
end
|
146
|
+
|
147
|
+
# Generates a Sentry trace for distribted tracing from the given Span.
|
148
|
+
# Returns `nil` if `config.propagate_traces` is `false`.
|
149
|
+
# @param span [Span] the span to generate trace from.
|
150
|
+
# @return [String, nil]
|
151
|
+
def generate_sentry_trace(span)
|
152
|
+
return unless configuration.propagate_traces
|
153
|
+
|
154
|
+
trace = span.to_sentry_trace
|
155
|
+
log_debug("[Tracing] Adding #{SENTRY_TRACE_HEADER_NAME} header to outgoing request: #{trace}")
|
156
|
+
trace
|
157
|
+
end
|
158
|
+
|
159
|
+
private
|
160
|
+
|
161
|
+
def dispatch_background_event(event, hint)
|
162
|
+
Sentry.background_worker.perform do
|
163
|
+
send_event(event, hint)
|
164
|
+
end
|
165
|
+
end
|
166
|
+
|
167
|
+
def dispatch_async_event(async_block, event, hint)
|
168
|
+
# We have to convert to a JSON-like hash, because background job
|
169
|
+
# processors (esp ActiveJob) may not like weird types in the event hash
|
170
|
+
|
171
|
+
event_hash =
|
172
|
+
begin
|
173
|
+
event.to_json_compatible
|
174
|
+
rescue => e
|
175
|
+
log_error("Converting #{event.type} (#{event.event_id}) to JSON compatible hash failed", e, debug: configuration.debug)
|
176
|
+
return
|
177
|
+
end
|
178
|
+
|
179
|
+
if async_block.arity == 2
|
180
|
+
hint = JSON.parse(JSON.generate(hint))
|
181
|
+
async_block.call(event_hash, hint)
|
182
|
+
else
|
183
|
+
async_block.call(event_hash)
|
184
|
+
end
|
185
|
+
rescue => e
|
186
|
+
log_error("Async #{event_hash["type"]} sending failed", e, debug: configuration.debug)
|
187
|
+
send_event(event, hint)
|
188
|
+
end
|
189
|
+
end
|
190
|
+
end
|