sentry-ruby-core 4.2.2 → 4.4.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 +4 -4
- data/.craft.yml +4 -3
- data/CHANGELOG.md +111 -0
- data/Gemfile +4 -0
- data/README.md +20 -22
- data/lib/sentry-ruby.rb +9 -9
- data/lib/sentry/background_worker.rb +8 -4
- data/lib/sentry/breadcrumb/sentry_logger.rb +2 -1
- data/lib/sentry/breadcrumb_buffer.rb +3 -2
- data/lib/sentry/client.rb +50 -27
- data/lib/sentry/configuration.rb +27 -11
- data/lib/sentry/event.rb +28 -48
- data/lib/sentry/exceptions.rb +7 -0
- data/lib/sentry/hub.rb +17 -3
- data/lib/sentry/interfaces/exception.rb +19 -1
- data/lib/sentry/interfaces/request.rb +10 -10
- data/lib/sentry/interfaces/single_exception.rb +16 -5
- data/lib/sentry/interfaces/stacktrace.rb +9 -26
- data/lib/sentry/interfaces/stacktrace_builder.rb +50 -0
- data/lib/sentry/interfaces/threads.rb +9 -3
- data/lib/sentry/net/http.rb +87 -0
- data/lib/sentry/rack/capture_exceptions.rb +18 -11
- data/lib/sentry/scope.rb +12 -6
- data/lib/sentry/span.rb +21 -4
- data/lib/sentry/transaction.rb +53 -42
- data/lib/sentry/transaction_event.rb +3 -1
- data/lib/sentry/transport.rb +57 -26
- data/lib/sentry/transport/configuration.rb +3 -1
- data/lib/sentry/transport/http_transport.rb +92 -6
- data/lib/sentry/utils/logging_helper.rb +24 -0
- data/lib/sentry/version.rb +1 -1
- metadata +7 -3
data/lib/sentry/configuration.rb
CHANGED
@@ -4,9 +4,11 @@ require "sentry/utils/exception_cause_chain"
|
|
4
4
|
require "sentry/dsn"
|
5
5
|
require "sentry/transport/configuration"
|
6
6
|
require "sentry/linecache"
|
7
|
+
require "sentry/interfaces/stacktrace_builder"
|
7
8
|
|
8
9
|
module Sentry
|
9
10
|
class Configuration
|
11
|
+
include LoggingHelper
|
10
12
|
# Directories to be recognized as part of your app. e.g. if you
|
11
13
|
# have an `engines` dir at the root of your project, you may want
|
12
14
|
# to set this to something like /(app|config|engines|lib)/
|
@@ -61,12 +63,19 @@ module Sentry
|
|
61
63
|
# - :active_support_logger
|
62
64
|
attr_reader :breadcrumbs_logger
|
63
65
|
|
66
|
+
# Max number of breadcrumbs a breadcrumb buffer can hold
|
67
|
+
attr_accessor :max_breadcrumbs
|
68
|
+
|
64
69
|
# Number of lines of code context to capture, or nil for none
|
65
70
|
attr_accessor :context_lines
|
66
71
|
|
67
72
|
# RACK_ENV by default.
|
68
73
|
attr_reader :environment
|
69
74
|
|
75
|
+
# Whether the SDK should run in the debugging mode. Default is false.
|
76
|
+
# If set to true, SDK errors will be logged with backtrace
|
77
|
+
attr_accessor :debug
|
78
|
+
|
70
79
|
# the dsn value, whether it's set via `config.dsn=` or `ENV["SENTRY_DSN"]`
|
71
80
|
attr_reader :dsn
|
72
81
|
|
@@ -163,14 +172,14 @@ module Sentry
|
|
163
172
|
LOG_PREFIX = "** [Sentry] ".freeze
|
164
173
|
MODULE_SEPARATOR = "::".freeze
|
165
174
|
|
166
|
-
AVAILABLE_BREADCRUMBS_LOGGERS = [:sentry_logger, :active_support_logger].freeze
|
167
|
-
|
168
175
|
# Post initialization callbacks are called at the end of initialization process
|
169
176
|
# allowing extending the configuration of sentry-ruby by multiple extensions
|
170
177
|
@@post_initialization_callbacks = []
|
171
178
|
|
172
179
|
def initialize
|
180
|
+
self.debug = false
|
173
181
|
self.background_worker_threads = Concurrent.processor_count
|
182
|
+
self.max_breadcrumbs = BreadcrumbBuffer::DEFAULT_SIZE
|
174
183
|
self.breadcrumbs_logger = []
|
175
184
|
self.context_lines = 3
|
176
185
|
self.environment = environment_from_env
|
@@ -195,6 +204,7 @@ module Sentry
|
|
195
204
|
|
196
205
|
@transport = Transport::Configuration.new
|
197
206
|
@gem_specs = Hash[Gem::Specification.map { |spec| [spec.name, spec.version.to_s] }] if Gem::Specification.respond_to?(:map)
|
207
|
+
|
198
208
|
run_post_initialization_callbacks
|
199
209
|
end
|
200
210
|
|
@@ -220,10 +230,6 @@ module Sentry
|
|
220
230
|
if logger.is_a?(Array)
|
221
231
|
logger
|
222
232
|
else
|
223
|
-
unless AVAILABLE_BREADCRUMBS_LOGGERS.include?(logger)
|
224
|
-
raise Sentry::Error, "Unsupported breadcrumbs logger. Supported loggers: #{AVAILABLE_BREADCRUMBS_LOGGERS}"
|
225
|
-
end
|
226
|
-
|
227
233
|
Array(logger)
|
228
234
|
end
|
229
235
|
|
@@ -272,10 +278,10 @@ module Sentry
|
|
272
278
|
def exception_class_allowed?(exc)
|
273
279
|
if exc.is_a?(Sentry::Error)
|
274
280
|
# Try to prevent error reporting loops
|
275
|
-
|
281
|
+
log_debug("Refusing to capture Sentry error: #{exc.inspect}")
|
276
282
|
false
|
277
283
|
elsif excluded_exception?(exc)
|
278
|
-
|
284
|
+
log_debug("User excluded error: #{exc.inspect}")
|
279
285
|
false
|
280
286
|
else
|
281
287
|
true
|
@@ -287,7 +293,17 @@ module Sentry
|
|
287
293
|
end
|
288
294
|
|
289
295
|
def tracing_enabled?
|
290
|
-
!!((@traces_sample_rate && @traces_sample_rate
|
296
|
+
!!((@traces_sample_rate && @traces_sample_rate >= 0.0 && @traces_sample_rate <= 1.0) || @traces_sampler) && sending_allowed?
|
297
|
+
end
|
298
|
+
|
299
|
+
def stacktrace_builder
|
300
|
+
@stacktrace_builder ||= StacktraceBuilder.new(
|
301
|
+
project_root: @project_root.to_s,
|
302
|
+
app_dirs_pattern: @app_dirs_pattern,
|
303
|
+
linecache: @linecache,
|
304
|
+
context_lines: @context_lines,
|
305
|
+
backtrace_cleanup_callback: @backtrace_cleanup_callback
|
306
|
+
)
|
291
307
|
end
|
292
308
|
|
293
309
|
private
|
@@ -298,7 +314,7 @@ module Sentry
|
|
298
314
|
detect_release_from_capistrano ||
|
299
315
|
detect_release_from_heroku
|
300
316
|
rescue => e
|
301
|
-
|
317
|
+
log_error("Error detecting release", e, debug: debug)
|
302
318
|
end
|
303
319
|
|
304
320
|
def excluded_exception?(incoming_exception)
|
@@ -333,7 +349,7 @@ module Sentry
|
|
333
349
|
def detect_release_from_heroku
|
334
350
|
return unless running_on_heroku?
|
335
351
|
return if ENV['CI']
|
336
|
-
|
352
|
+
log_warn(HEROKU_DYNO_METADATA_MESSAGE) && return unless ENV['HEROKU_SLUG_COMMIT']
|
337
353
|
|
338
354
|
ENV['HEROKU_SLUG_COMMIT']
|
339
355
|
end
|
data/lib/sentry/event.rb
CHANGED
@@ -20,7 +20,7 @@ module Sentry
|
|
20
20
|
MAX_MESSAGE_SIZE_IN_BYTES = 1024 * 8
|
21
21
|
|
22
22
|
attr_accessor(*ATTRIBUTES)
|
23
|
-
attr_reader :configuration, :request, :exception, :
|
23
|
+
attr_reader :configuration, :request, :exception, :threads
|
24
24
|
|
25
25
|
def initialize(configuration:, integration_meta: nil, message: nil)
|
26
26
|
# this needs to go first because some setters rely on configuration
|
@@ -52,19 +52,26 @@ module Sentry
|
|
52
52
|
class << self
|
53
53
|
def get_log_message(event_hash)
|
54
54
|
message = event_hash[:message] || event_hash['message']
|
55
|
-
|
56
|
-
|
57
|
-
|
55
|
+
|
56
|
+
return message unless message.nil? || message.empty?
|
57
|
+
|
58
|
+
message = get_message_from_exception(event_hash)
|
59
|
+
|
60
|
+
return message unless message.nil? || message.empty?
|
61
|
+
|
62
|
+
message = event_hash[:transaction] || event_hash["transaction"]
|
63
|
+
|
64
|
+
return message unless message.nil? || message.empty?
|
65
|
+
|
66
|
+
'<no message value>'
|
58
67
|
end
|
59
68
|
|
60
69
|
def get_message_from_exception(event_hash)
|
61
|
-
(
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
"#{event_hash[:exception][:values][0][:type]}: #{event_hash[:exception][:values][0][:value]}"
|
67
|
-
)
|
70
|
+
if exception = event_hash.dig(:exception, :values, 0)
|
71
|
+
"#{exception[:type]}: #{exception[:value]}"
|
72
|
+
elsif exception = event_hash.dig("exception", "values", 0)
|
73
|
+
"#{exception["type"]}: #{exception["value"]}"
|
74
|
+
end
|
68
75
|
end
|
69
76
|
end
|
70
77
|
|
@@ -93,13 +100,11 @@ module Sentry
|
|
93
100
|
end
|
94
101
|
|
95
102
|
def type
|
96
|
-
"event"
|
97
103
|
end
|
98
104
|
|
99
105
|
def to_hash
|
100
106
|
data = serialize_attributes
|
101
107
|
data[:breadcrumbs] = breadcrumbs.to_hash if breadcrumbs
|
102
|
-
data[:stacktrace] = stacktrace.to_hash if stacktrace
|
103
108
|
data[:request] = request.to_hash if request
|
104
109
|
data[:exception] = exception.to_hash if exception
|
105
110
|
data[:threads] = threads.to_hash if threads
|
@@ -112,48 +117,23 @@ module Sentry
|
|
112
117
|
end
|
113
118
|
|
114
119
|
def add_request_interface(env)
|
115
|
-
@request = Sentry::RequestInterface.
|
120
|
+
@request = Sentry::RequestInterface.build(env: env)
|
116
121
|
end
|
117
122
|
|
118
123
|
def add_threads_interface(backtrace: nil, **options)
|
119
|
-
@threads = ThreadsInterface.
|
120
|
-
|
124
|
+
@threads = ThreadsInterface.build(
|
125
|
+
backtrace: backtrace,
|
126
|
+
stacktrace_builder: configuration.stacktrace_builder,
|
127
|
+
**options
|
128
|
+
)
|
121
129
|
end
|
122
130
|
|
123
|
-
def add_exception_interface(
|
124
|
-
if
|
125
|
-
@extra.merge!(
|
131
|
+
def add_exception_interface(exception)
|
132
|
+
if exception.respond_to?(:sentry_context)
|
133
|
+
@extra.merge!(exception.sentry_context)
|
126
134
|
end
|
127
135
|
|
128
|
-
@exception = Sentry::ExceptionInterface.
|
129
|
-
exceptions = Sentry::Utils::ExceptionCauseChain.exception_to_array(exc).reverse
|
130
|
-
backtraces = Set.new
|
131
|
-
exc_int.values = exceptions.map do |e|
|
132
|
-
SingleExceptionInterface.new.tap do |int|
|
133
|
-
int.type = e.class.to_s
|
134
|
-
int.value = e.message.byteslice(0..MAX_MESSAGE_SIZE_IN_BYTES)
|
135
|
-
int.module = e.class.to_s.split('::')[0...-1].join('::')
|
136
|
-
int.thread_id = Thread.current.object_id
|
137
|
-
|
138
|
-
int.stacktrace =
|
139
|
-
if e.backtrace && !backtraces.include?(e.backtrace.object_id)
|
140
|
-
backtraces << e.backtrace.object_id
|
141
|
-
initialize_stacktrace_interface(e.backtrace)
|
142
|
-
end
|
143
|
-
end
|
144
|
-
end
|
145
|
-
end
|
146
|
-
end
|
147
|
-
|
148
|
-
def initialize_stacktrace_interface(backtrace)
|
149
|
-
StacktraceInterface.new(
|
150
|
-
backtrace: backtrace,
|
151
|
-
project_root: configuration.project_root.to_s,
|
152
|
-
app_dirs_pattern: configuration.app_dirs_pattern,
|
153
|
-
linecache: configuration.linecache,
|
154
|
-
context_lines: configuration.context_lines,
|
155
|
-
backtrace_cleanup_callback: configuration.backtrace_cleanup_callback
|
156
|
-
)
|
136
|
+
@exception = Sentry::ExceptionInterface.build(exception: exception, stacktrace_builder: configuration.stacktrace_builder)
|
157
137
|
end
|
158
138
|
|
159
139
|
private
|
data/lib/sentry/hub.rb
CHANGED
@@ -21,6 +21,10 @@ module Sentry
|
|
21
21
|
current_layer&.client
|
22
22
|
end
|
23
23
|
|
24
|
+
def configuration
|
25
|
+
current_client.configuration
|
26
|
+
end
|
27
|
+
|
24
28
|
def current_scope
|
25
29
|
current_layer&.scope
|
26
30
|
end
|
@@ -69,9 +73,19 @@ module Sentry
|
|
69
73
|
@stack.pop
|
70
74
|
end
|
71
75
|
|
72
|
-
def start_transaction(transaction: nil, **options)
|
73
|
-
|
74
|
-
|
76
|
+
def start_transaction(transaction: nil, custom_sampling_context: {}, **options)
|
77
|
+
return unless configuration.tracing_enabled?
|
78
|
+
|
79
|
+
transaction ||= Transaction.new(**options.merge(hub: self))
|
80
|
+
|
81
|
+
sampling_context = {
|
82
|
+
transaction_context: transaction.to_hash,
|
83
|
+
parent_sampled: transaction.parent_sampled
|
84
|
+
}
|
85
|
+
|
86
|
+
sampling_context.merge!(custom_sampling_context)
|
87
|
+
|
88
|
+
transaction.set_initial_sample_decision(sampling_context: sampling_context)
|
75
89
|
transaction
|
76
90
|
end
|
77
91
|
|
@@ -1,11 +1,29 @@
|
|
1
1
|
module Sentry
|
2
2
|
class ExceptionInterface < Interface
|
3
|
-
|
3
|
+
def initialize(values:)
|
4
|
+
@values = values
|
5
|
+
end
|
4
6
|
|
5
7
|
def to_hash
|
6
8
|
data = super
|
7
9
|
data[:values] = data[:values].map(&:to_hash) if data[:values]
|
8
10
|
data
|
9
11
|
end
|
12
|
+
|
13
|
+
def self.build(exception:, stacktrace_builder:)
|
14
|
+
exceptions = Sentry::Utils::ExceptionCauseChain.exception_to_array(exception).reverse
|
15
|
+
processed_backtrace_ids = Set.new
|
16
|
+
|
17
|
+
exceptions = exceptions.map do |e|
|
18
|
+
if e.backtrace && !processed_backtrace_ids.include?(e.backtrace.object_id)
|
19
|
+
processed_backtrace_ids << e.backtrace.object_id
|
20
|
+
SingleExceptionInterface.build_with_stacktrace(exception: e, stacktrace_builder: stacktrace_builder)
|
21
|
+
else
|
22
|
+
SingleExceptionInterface.new(exception: exception)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
new(values: exceptions)
|
27
|
+
end
|
10
28
|
end
|
11
29
|
end
|
@@ -17,10 +17,10 @@ module Sentry
|
|
17
17
|
|
18
18
|
attr_accessor :url, :method, :data, :query_string, :cookies, :headers, :env
|
19
19
|
|
20
|
-
def self.
|
20
|
+
def self.build(env:)
|
21
21
|
env = clean_env(env)
|
22
|
-
|
23
|
-
self.new(
|
22
|
+
request = ::Rack::Request.new(env)
|
23
|
+
self.new(request: request)
|
24
24
|
end
|
25
25
|
|
26
26
|
def self.clean_env(env)
|
@@ -34,17 +34,17 @@ module Sentry
|
|
34
34
|
env
|
35
35
|
end
|
36
36
|
|
37
|
-
def initialize(
|
38
|
-
env =
|
37
|
+
def initialize(request:)
|
38
|
+
env = request.env
|
39
39
|
|
40
40
|
if Sentry.configuration.send_default_pii
|
41
|
-
self.data = read_data_from(
|
42
|
-
self.cookies =
|
41
|
+
self.data = read_data_from(request)
|
42
|
+
self.cookies = request.cookies
|
43
|
+
self.query_string = request.query_string
|
43
44
|
end
|
44
45
|
|
45
|
-
self.url =
|
46
|
-
self.method =
|
47
|
-
self.query_string = req.query_string
|
46
|
+
self.url = request.scheme && request.url.split('?').first
|
47
|
+
self.method = request.request_method
|
48
48
|
|
49
49
|
self.headers = filter_and_format_headers(env)
|
50
50
|
self.env = filter_and_format_env(env)
|
@@ -1,15 +1,26 @@
|
|
1
1
|
module Sentry
|
2
2
|
class SingleExceptionInterface < Interface
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
3
|
+
attr_reader :type, :value, :module, :thread_id, :stacktrace
|
4
|
+
|
5
|
+
def initialize(exception:, stacktrace: nil)
|
6
|
+
@type = exception.class.to_s
|
7
|
+
@value = (exception.message || "").byteslice(0..Event::MAX_MESSAGE_SIZE_IN_BYTES)
|
8
|
+
@module = exception.class.to_s.split('::')[0...-1].join('::')
|
9
|
+
@thread_id = Thread.current.object_id
|
10
|
+
@stacktrace = stacktrace
|
11
|
+
end
|
8
12
|
|
9
13
|
def to_hash
|
10
14
|
data = super
|
11
15
|
data[:stacktrace] = data[:stacktrace].to_hash if data[:stacktrace]
|
12
16
|
data
|
13
17
|
end
|
18
|
+
|
19
|
+
# patch this method if you want to change an exception's stacktrace frames
|
20
|
+
# also see `StacktraceBuilder.build`.
|
21
|
+
def self.build_with_stacktrace(exception:, stacktrace_builder:)
|
22
|
+
stacktrace = stacktrace_builder.build(backtrace: exception.backtrace)
|
23
|
+
new(exception: exception, stacktrace: stacktrace)
|
24
|
+
end
|
14
25
|
end
|
15
26
|
end
|
@@ -2,18 +2,8 @@ module Sentry
|
|
2
2
|
class StacktraceInterface
|
3
3
|
attr_reader :frames
|
4
4
|
|
5
|
-
def initialize(
|
6
|
-
@
|
7
|
-
@frames = []
|
8
|
-
|
9
|
-
parsed_backtrace_lines = Backtrace.parse(
|
10
|
-
backtrace, project_root, app_dirs_pattern, &backtrace_cleanup_callback
|
11
|
-
).lines
|
12
|
-
|
13
|
-
parsed_backtrace_lines.reverse.each_with_object(@frames) do |line, frames|
|
14
|
-
frame = convert_parsed_line_into_frame(line, project_root, linecache, context_lines)
|
15
|
-
frames << frame if frame.filename
|
16
|
-
end
|
5
|
+
def initialize(frames:)
|
6
|
+
@frames = frames
|
17
7
|
end
|
18
8
|
|
19
9
|
def to_hash
|
@@ -22,30 +12,24 @@ module Sentry
|
|
22
12
|
|
23
13
|
private
|
24
14
|
|
25
|
-
def convert_parsed_line_into_frame(line, project_root, linecache, context_lines)
|
26
|
-
frame = StacktraceInterface::Frame.new(@project_root, line)
|
27
|
-
frame.set_context(linecache, context_lines) if context_lines
|
28
|
-
frame
|
29
|
-
end
|
30
|
-
|
31
15
|
# Not actually an interface, but I want to use the same style
|
32
16
|
class Frame < Interface
|
33
|
-
attr_accessor :abs_path, :context_line, :function, :in_app,
|
34
|
-
|
17
|
+
attr_accessor :abs_path, :context_line, :function, :in_app, :filename,
|
18
|
+
:lineno, :module, :pre_context, :post_context, :vars
|
35
19
|
|
36
20
|
def initialize(project_root, line)
|
37
21
|
@project_root = project_root
|
38
22
|
|
39
|
-
@abs_path = line.file
|
23
|
+
@abs_path = line.file
|
40
24
|
@function = line.method if line.method
|
41
25
|
@lineno = line.number
|
42
26
|
@in_app = line.in_app
|
43
27
|
@module = line.module_name if line.module_name
|
28
|
+
@filename = compute_filename
|
44
29
|
end
|
45
30
|
|
46
|
-
def
|
31
|
+
def compute_filename
|
47
32
|
return if abs_path.nil?
|
48
|
-
return @filename if instance_variable_defined?(:@filename)
|
49
33
|
|
50
34
|
prefix =
|
51
35
|
if under_project_root? && in_app
|
@@ -56,19 +40,18 @@ module Sentry
|
|
56
40
|
longest_load_path
|
57
41
|
end
|
58
42
|
|
59
|
-
|
43
|
+
prefix ? abs_path[prefix.to_s.chomp(File::SEPARATOR).length + 1..-1] : abs_path
|
60
44
|
end
|
61
45
|
|
62
46
|
def set_context(linecache, context_lines)
|
63
47
|
return unless abs_path
|
64
48
|
|
65
|
-
|
49
|
+
@pre_context, @context_line, @post_context = \
|
66
50
|
linecache.get_file_context(abs_path, lineno, context_lines)
|
67
51
|
end
|
68
52
|
|
69
53
|
def to_hash(*args)
|
70
54
|
data = super(*args)
|
71
|
-
data[:filename] = filename
|
72
55
|
data.delete(:vars) unless vars && !vars.empty?
|
73
56
|
data.delete(:pre_context) unless pre_context && !pre_context.empty?
|
74
57
|
data.delete(:post_context) unless post_context && !post_context.empty?
|