sentry-ruby-core 4.2.0 → 4.3.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/CHANGELOG.md +63 -0
- data/Gemfile +1 -0
- data/README.md +39 -55
- data/lib/sentry-ruby.rb +7 -9
- data/lib/sentry/background_worker.rb +1 -0
- data/lib/sentry/breadcrumb/sentry_logger.rb +2 -1
- data/lib/sentry/breadcrumb_buffer.rb +3 -2
- data/lib/sentry/client.rb +47 -27
- data/lib/sentry/configuration.rb +16 -0
- data/lib/sentry/event.rb +28 -47
- data/lib/sentry/exceptions.rb +7 -0
- data/lib/sentry/hub.rb +12 -2
- data/lib/sentry/interfaces/exception.rb +19 -1
- data/lib/sentry/interfaces/request.rb +10 -10
- data/lib/sentry/interfaces/single_exception.rb +16 -4
- 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/rack/capture_exceptions.rb +18 -14
- data/lib/sentry/scope.rb +11 -6
- data/lib/sentry/span.rb +10 -2
- data/lib/sentry/transaction.rb +34 -36
- data/lib/sentry/transaction_event.rb +3 -1
- data/lib/sentry/transport.rb +10 -29
- data/lib/sentry/transport/configuration.rb +3 -1
- data/lib/sentry/transport/http_transport.rb +18 -2
- data/lib/sentry/utils/real_ip.rb +1 -1
- data/lib/sentry/version.rb +1 -1
- metadata +5 -3
data/lib/sentry/configuration.rb
CHANGED
@@ -4,6 +4,7 @@ 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
|
@@ -61,6 +62,9 @@ module Sentry
|
|
61
62
|
# - :active_support_logger
|
62
63
|
attr_reader :breadcrumbs_logger
|
63
64
|
|
65
|
+
# Max number of breadcrumbs a breadcrumb buffer can hold
|
66
|
+
attr_accessor :max_breadcrumbs
|
67
|
+
|
64
68
|
# Number of lines of code context to capture, or nil for none
|
65
69
|
attr_accessor :context_lines
|
66
70
|
|
@@ -171,6 +175,7 @@ module Sentry
|
|
171
175
|
|
172
176
|
def initialize
|
173
177
|
self.background_worker_threads = Concurrent.processor_count
|
178
|
+
self.max_breadcrumbs = BreadcrumbBuffer::DEFAULT_SIZE
|
174
179
|
self.breadcrumbs_logger = []
|
175
180
|
self.context_lines = 3
|
176
181
|
self.environment = environment_from_env
|
@@ -195,6 +200,7 @@ module Sentry
|
|
195
200
|
|
196
201
|
@transport = Transport::Configuration.new
|
197
202
|
@gem_specs = Hash[Gem::Specification.map { |spec| [spec.name, spec.version.to_s] }] if Gem::Specification.respond_to?(:map)
|
203
|
+
|
198
204
|
run_post_initialization_callbacks
|
199
205
|
end
|
200
206
|
|
@@ -290,6 +296,16 @@ module Sentry
|
|
290
296
|
!!((@traces_sample_rate && @traces_sample_rate > 0.0) || @traces_sampler)
|
291
297
|
end
|
292
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
|
+
)
|
307
|
+
end
|
308
|
+
|
293
309
|
private
|
294
310
|
|
295
311
|
def detect_release
|
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,47 +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
|
-
|
137
|
-
int.stacktrace =
|
138
|
-
if e.backtrace && !backtraces.include?(e.backtrace.object_id)
|
139
|
-
backtraces << e.backtrace.object_id
|
140
|
-
initialize_stacktrace_interface(e.backtrace)
|
141
|
-
end
|
142
|
-
end
|
143
|
-
end
|
144
|
-
end
|
145
|
-
end
|
146
|
-
|
147
|
-
def initialize_stacktrace_interface(backtrace)
|
148
|
-
StacktraceInterface.new(
|
149
|
-
backtrace: backtrace,
|
150
|
-
project_root: configuration.project_root.to_s,
|
151
|
-
app_dirs_pattern: configuration.app_dirs_pattern,
|
152
|
-
linecache: configuration.linecache,
|
153
|
-
context_lines: configuration.context_lines,
|
154
|
-
backtrace_cleanup_callback: configuration.backtrace_cleanup_callback
|
155
|
-
)
|
136
|
+
@exception = Sentry::ExceptionInterface.build(exception: exception, stacktrace_builder: configuration.stacktrace_builder)
|
156
137
|
end
|
157
138
|
|
158
139
|
private
|
data/lib/sentry/hub.rb
CHANGED
@@ -69,9 +69,19 @@ module Sentry
|
|
69
69
|
@stack.pop
|
70
70
|
end
|
71
71
|
|
72
|
-
def start_transaction(transaction: nil, **options)
|
72
|
+
def start_transaction(transaction: nil, configuration: Sentry.configuration, custom_sampling_context: {}, **options)
|
73
|
+
return unless configuration.tracing_enabled?
|
74
|
+
|
73
75
|
transaction ||= Transaction.new(**options)
|
74
|
-
|
76
|
+
|
77
|
+
sampling_context = {
|
78
|
+
transaction_context: transaction.to_hash,
|
79
|
+
parent_sampled: transaction.parent_sampled
|
80
|
+
}
|
81
|
+
|
82
|
+
sampling_context.merge!(custom_sampling_context)
|
83
|
+
|
84
|
+
transaction.set_initial_sample_decision(configuration: current_client.configuration, sampling_context: sampling_context)
|
75
85
|
transaction
|
76
86
|
end
|
77
87
|
|
@@ -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,14 +1,26 @@
|
|
1
1
|
module Sentry
|
2
2
|
class SingleExceptionInterface < Interface
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
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
|
7
12
|
|
8
13
|
def to_hash
|
9
14
|
data = super
|
10
15
|
data[:stacktrace] = data[:stacktrace].to_hash if data[:stacktrace]
|
11
16
|
data
|
12
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
|
13
25
|
end
|
14
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?
|
@@ -0,0 +1,50 @@
|
|
1
|
+
module Sentry
|
2
|
+
class StacktraceBuilder
|
3
|
+
attr_reader :project_root, :app_dirs_pattern, :linecache, :context_lines, :backtrace_cleanup_callback
|
4
|
+
|
5
|
+
def initialize(project_root:, app_dirs_pattern:, linecache:, context_lines:, backtrace_cleanup_callback: nil)
|
6
|
+
@project_root = project_root
|
7
|
+
@app_dirs_pattern = app_dirs_pattern
|
8
|
+
@linecache = linecache
|
9
|
+
@context_lines = context_lines
|
10
|
+
@backtrace_cleanup_callback = backtrace_cleanup_callback
|
11
|
+
end
|
12
|
+
|
13
|
+
# you can pass a block to customize/exclude frames:
|
14
|
+
#
|
15
|
+
# ```ruby
|
16
|
+
# builder.build(backtrace) do |frame|
|
17
|
+
# if frame.module.match?(/a_gem/)
|
18
|
+
# nil
|
19
|
+
# else
|
20
|
+
# frame
|
21
|
+
# end
|
22
|
+
# end
|
23
|
+
# ```
|
24
|
+
def build(backtrace:, &frame_callback)
|
25
|
+
parsed_lines = parse_backtrace_lines(backtrace).select(&:file)
|
26
|
+
|
27
|
+
frames = parsed_lines.reverse.map do |line|
|
28
|
+
frame = convert_parsed_line_into_frame(line)
|
29
|
+
frame = frame_callback.call(frame) if frame_callback
|
30
|
+
frame
|
31
|
+
end.compact
|
32
|
+
|
33
|
+
StacktraceInterface.new(frames: frames)
|
34
|
+
end
|
35
|
+
|
36
|
+
private
|
37
|
+
|
38
|
+
def convert_parsed_line_into_frame(line)
|
39
|
+
frame = StacktraceInterface::Frame.new(project_root, line)
|
40
|
+
frame.set_context(linecache, context_lines) if context_lines
|
41
|
+
frame
|
42
|
+
end
|
43
|
+
|
44
|
+
def parse_backtrace_lines(backtrace)
|
45
|
+
Backtrace.parse(
|
46
|
+
backtrace, project_root, app_dirs_pattern, &backtrace_cleanup_callback
|
47
|
+
).lines
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
@@ -1,12 +1,11 @@
|
|
1
1
|
module Sentry
|
2
2
|
class ThreadsInterface
|
3
|
-
|
4
|
-
|
5
|
-
def initialize(crashed: false)
|
3
|
+
def initialize(crashed: false, stacktrace: nil)
|
6
4
|
@id = Thread.current.object_id
|
7
5
|
@name = Thread.current.name
|
8
6
|
@current = true
|
9
7
|
@crashed = crashed
|
8
|
+
@stacktrace = stacktrace
|
10
9
|
end
|
11
10
|
|
12
11
|
def to_hash
|
@@ -22,5 +21,12 @@ module Sentry
|
|
22
21
|
]
|
23
22
|
}
|
24
23
|
end
|
24
|
+
|
25
|
+
# patch this method if you want to change a threads interface's stacktrace frames
|
26
|
+
# also see `StacktraceBuilder.build`.
|
27
|
+
def self.build(backtrace:, stacktrace_builder:, **options)
|
28
|
+
stacktrace = stacktrace_builder.build(backtrace: backtrace) if backtrace
|
29
|
+
new(**options, stacktrace: stacktrace)
|
30
|
+
end
|
25
31
|
end
|
26
32
|
end
|