sentry-ruby-core 4.1.6 → 4.5.1
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 +3 -3
- data/CHANGELOG.md +171 -1
- data/Gemfile +6 -3
- data/README.md +39 -49
- data/lib/sentry-ruby.rb +30 -9
- data/lib/sentry/background_worker.rb +8 -4
- data/lib/sentry/breadcrumb.rb +7 -2
- data/lib/sentry/breadcrumb/sentry_logger.rb +2 -1
- data/lib/sentry/breadcrumb_buffer.rb +3 -2
- data/lib/sentry/client.rb +62 -28
- data/lib/sentry/configuration.rb +80 -17
- data/lib/sentry/event.rb +33 -45
- data/lib/sentry/exceptions.rb +7 -0
- data/lib/sentry/hub.rb +24 -4
- data/lib/sentry/interface.rb +1 -0
- 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 +32 -0
- data/lib/sentry/net/http.rb +126 -0
- data/lib/sentry/rack/capture_exceptions.rb +18 -14
- data/lib/sentry/rake.rb +1 -1
- data/lib/sentry/scope.rb +12 -6
- data/lib/sentry/span.rb +21 -4
- data/lib/sentry/transaction.rb +55 -43
- data/lib/sentry/transaction_event.rb +3 -1
- data/lib/sentry/transport.rb +58 -27
- 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/utils/real_ip.rb +13 -7
- data/lib/sentry/version.rb +1 -1
- data/sentry-ruby-core.gemspec +1 -1
- data/sentry-ruby.gemspec +1 -1
- metadata +9 -4
@@ -1,23 +1,27 @@
|
|
1
1
|
require "concurrent/executor/thread_pool_executor"
|
2
2
|
require "concurrent/executor/immediate_executor"
|
3
|
+
require "concurrent/configuration"
|
3
4
|
|
4
5
|
module Sentry
|
5
6
|
class BackgroundWorker
|
6
|
-
|
7
|
+
include LoggingHelper
|
8
|
+
|
9
|
+
attr_reader :max_queue, :number_of_threads, :logger
|
7
10
|
|
8
11
|
def initialize(configuration)
|
9
12
|
@max_queue = 30
|
10
13
|
@number_of_threads = configuration.background_worker_threads
|
14
|
+
@logger = configuration.logger
|
11
15
|
|
12
16
|
@executor =
|
13
17
|
if configuration.async
|
14
|
-
|
18
|
+
log_debug("config.async is set, BackgroundWorker is disabled")
|
15
19
|
Concurrent::ImmediateExecutor.new
|
16
20
|
elsif @number_of_threads == 0
|
17
|
-
|
21
|
+
log_debug("config.background_worker_threads is set to 0, all events will be sent synchronously")
|
18
22
|
Concurrent::ImmediateExecutor.new
|
19
23
|
else
|
20
|
-
|
24
|
+
log_debug("initialized a background worker with #{@number_of_threads} threads")
|
21
25
|
|
22
26
|
Concurrent::ThreadPoolExecutor.new(
|
23
27
|
min_threads: 0,
|
data/lib/sentry/breadcrumb.rb
CHANGED
@@ -2,15 +2,16 @@ module Sentry
|
|
2
2
|
class Breadcrumb
|
3
3
|
DATA_SERIALIZATION_ERROR_MESSAGE = "[data were removed due to serialization issues]"
|
4
4
|
|
5
|
-
attr_accessor :category, :data, :
|
5
|
+
attr_accessor :category, :data, :level, :timestamp, :type
|
6
|
+
attr_reader :message
|
6
7
|
|
7
8
|
def initialize(category: nil, data: nil, message: nil, timestamp: nil, level: nil, type: nil)
|
8
9
|
@category = category
|
9
10
|
@data = data || {}
|
10
11
|
@level = level
|
11
|
-
@message = message
|
12
12
|
@timestamp = timestamp || Sentry.utc_now.to_i
|
13
13
|
@type = type
|
14
|
+
self.message = message
|
14
15
|
end
|
15
16
|
|
16
17
|
def to_hash
|
@@ -24,6 +25,10 @@ module Sentry
|
|
24
25
|
}
|
25
26
|
end
|
26
27
|
|
28
|
+
def message=(msg)
|
29
|
+
@message = (msg || "").byteslice(0..Event::MAX_MESSAGE_SIZE_IN_BYTES)
|
30
|
+
end
|
31
|
+
|
27
32
|
private
|
28
33
|
|
29
34
|
def serialized_data
|
@@ -14,6 +14,7 @@ module Sentry
|
|
14
14
|
def add(*args, &block)
|
15
15
|
super
|
16
16
|
add_breadcrumb(*args, &block)
|
17
|
+
nil
|
17
18
|
end
|
18
19
|
|
19
20
|
def add_breadcrumb(severity, message = nil, progname = nil)
|
@@ -49,7 +50,7 @@ module Sentry
|
|
49
50
|
end
|
50
51
|
end
|
51
52
|
|
52
|
-
return if ignored_logger?(progname) || message
|
53
|
+
return if ignored_logger?(progname) || message == ""
|
53
54
|
|
54
55
|
# some loggers will add leading/trailing space as they (incorrectly, mind you)
|
55
56
|
# think of logging as a shortcut to std{out,err}
|
@@ -2,12 +2,13 @@ require "sentry/breadcrumb"
|
|
2
2
|
|
3
3
|
module Sentry
|
4
4
|
class BreadcrumbBuffer
|
5
|
+
DEFAULT_SIZE = 100
|
5
6
|
include Enumerable
|
6
7
|
|
7
8
|
attr_accessor :buffer
|
8
9
|
|
9
|
-
def initialize(size =
|
10
|
-
@buffer = Array.new(size)
|
10
|
+
def initialize(size = nil)
|
11
|
+
@buffer = Array.new(size || DEFAULT_SIZE)
|
11
12
|
end
|
12
13
|
|
13
14
|
def record(crumb)
|
data/lib/sentry/client.rb
CHANGED
@@ -2,10 +2,13 @@ require "sentry/transport"
|
|
2
2
|
|
3
3
|
module Sentry
|
4
4
|
class Client
|
5
|
-
|
5
|
+
include LoggingHelper
|
6
|
+
|
7
|
+
attr_reader :transport, :configuration, :logger
|
6
8
|
|
7
9
|
def initialize(configuration)
|
8
10
|
@configuration = configuration
|
11
|
+
@logger = configuration.logger
|
9
12
|
|
10
13
|
if transport_class = configuration.transport.transport_class
|
11
14
|
@transport = transport_class.new(configuration)
|
@@ -26,32 +29,17 @@ module Sentry
|
|
26
29
|
scope.apply_to_event(event, hint)
|
27
30
|
|
28
31
|
if async_block = configuration.async
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
event_hash = event.to_json_compatible
|
33
|
-
|
34
|
-
if async_block.arity == 2
|
35
|
-
hint = JSON.parse(JSON.generate(hint))
|
36
|
-
async_block.call(event_hash, hint)
|
37
|
-
else
|
38
|
-
async_block.call(event_hash)
|
39
|
-
end
|
40
|
-
rescue => e
|
41
|
-
configuration.logger.error(LOGGER_PROGNAME) { "async event sending failed: #{e.message}" }
|
42
|
-
send_event(event, hint)
|
43
|
-
end
|
32
|
+
dispatch_async_event(async_block, event, hint)
|
33
|
+
elsif configuration.background_worker_threads != 0 && hint.fetch(:background, true)
|
34
|
+
dispatch_background_event(event, hint)
|
44
35
|
else
|
45
|
-
|
46
|
-
Sentry.background_worker.perform do
|
47
|
-
send_event(event, hint)
|
48
|
-
end
|
49
|
-
else
|
50
|
-
send_event(event, hint)
|
51
|
-
end
|
36
|
+
send_event(event, hint)
|
52
37
|
end
|
53
38
|
|
54
39
|
event
|
40
|
+
rescue => e
|
41
|
+
log_error("Event capturing failed", e, debug: configuration.debug)
|
42
|
+
nil
|
55
43
|
end
|
56
44
|
|
57
45
|
def event_from_exception(exception, hint = {})
|
@@ -60,12 +48,15 @@ module Sentry
|
|
60
48
|
|
61
49
|
Event.new(configuration: configuration, integration_meta: integration_meta).tap do |event|
|
62
50
|
event.add_exception_interface(exception)
|
51
|
+
event.add_threads_interface(crashed: true)
|
63
52
|
end
|
64
53
|
end
|
65
54
|
|
66
55
|
def event_from_message(message, hint = {})
|
67
56
|
integration_meta = Sentry.integrations[hint[:integration]]
|
68
|
-
Event.new(configuration: configuration, integration_meta: integration_meta, message: message)
|
57
|
+
event = Event.new(configuration: configuration, integration_meta: integration_meta, message: message)
|
58
|
+
event.add_threads_interface(backtrace: caller)
|
59
|
+
event
|
69
60
|
end
|
70
61
|
|
71
62
|
def event_from_transaction(transaction)
|
@@ -82,16 +73,59 @@ module Sentry
|
|
82
73
|
|
83
74
|
def send_event(event, hint = nil)
|
84
75
|
event_type = event.is_a?(Event) ? event.type : event["type"]
|
85
|
-
event = configuration.before_send.call(event, hint) if configuration.before_send && event_type == "event"
|
86
76
|
|
87
|
-
if
|
88
|
-
configuration.
|
89
|
-
|
77
|
+
if event_type != TransactionEvent::TYPE && configuration.before_send
|
78
|
+
event = configuration.before_send.call(event, hint)
|
79
|
+
|
80
|
+
if event.nil?
|
81
|
+
log_info("Discarded event because before_send returned nil")
|
82
|
+
return
|
83
|
+
end
|
90
84
|
end
|
91
85
|
|
92
86
|
transport.send_event(event)
|
93
87
|
|
94
88
|
event
|
89
|
+
rescue => e
|
90
|
+
loggable_event_type = (event_type || "event").capitalize
|
91
|
+
log_error("#{loggable_event_type} sending failed", e, debug: configuration.debug)
|
92
|
+
|
93
|
+
event_info = Event.get_log_message(event.to_hash)
|
94
|
+
log_info("Unreported #{loggable_event_type}: #{event_info}")
|
95
|
+
raise
|
96
|
+
end
|
97
|
+
|
98
|
+
def generate_sentry_trace(span)
|
99
|
+
return unless configuration.propagate_traces
|
100
|
+
|
101
|
+
trace = span.to_sentry_trace
|
102
|
+
log_debug("[Tracing] Adding #{SENTRY_TRACE_HEADER_NAME} header to outgoing request: #{trace}")
|
103
|
+
trace
|
104
|
+
end
|
105
|
+
|
106
|
+
private
|
107
|
+
|
108
|
+
def dispatch_background_event(event, hint)
|
109
|
+
Sentry.background_worker.perform do
|
110
|
+
send_event(event, hint)
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
114
|
+
def dispatch_async_event(async_block, event, hint)
|
115
|
+
# We have to convert to a JSON-like hash, because background job
|
116
|
+
# processors (esp ActiveJob) may not like weird types in the event hash
|
117
|
+
event_hash = event.to_json_compatible
|
118
|
+
|
119
|
+
if async_block.arity == 2
|
120
|
+
hint = JSON.parse(JSON.generate(hint))
|
121
|
+
async_block.call(event_hash, hint)
|
122
|
+
else
|
123
|
+
async_block.call(event_hash)
|
124
|
+
end
|
125
|
+
rescue => e
|
126
|
+
loggable_event_type = event_hash["type"] || "event"
|
127
|
+
log_error("Async #{loggable_event_type} sending failed", e, debug: configuration.debug)
|
128
|
+
send_event(event, hint)
|
95
129
|
end
|
96
130
|
end
|
97
131
|
end
|
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)/
|
@@ -38,10 +40,19 @@ module Sentry
|
|
38
40
|
#
|
39
41
|
attr_accessor :backtrace_cleanup_callback
|
40
42
|
|
43
|
+
# Optional Proc, called before adding the breadcrumb to the current scope
|
44
|
+
# E.g.: lambda { |breadcrumb, hint| breadcrumb }
|
45
|
+
# E.g.: lambda { |breadcrumb, hint| nil }
|
46
|
+
# E.g.: lambda { |breadcrumb, hint|
|
47
|
+
# breadcrumb.message = 'a'
|
48
|
+
# breadcrumb
|
49
|
+
# }
|
50
|
+
attr_reader :before_breadcrumb
|
51
|
+
|
41
52
|
# Optional Proc, called before sending an event to the server/
|
42
|
-
# E.g.: lambda { |event| event }
|
43
|
-
# E.g.: lambda { |event| nil }
|
44
|
-
# E.g.: lambda { |event|
|
53
|
+
# E.g.: lambda { |event, hint| event }
|
54
|
+
# E.g.: lambda { |event, hint| nil }
|
55
|
+
# E.g.: lambda { |event, hint|
|
45
56
|
# event[:message] = 'a'
|
46
57
|
# event
|
47
58
|
# }
|
@@ -52,12 +63,19 @@ module Sentry
|
|
52
63
|
# - :active_support_logger
|
53
64
|
attr_reader :breadcrumbs_logger
|
54
65
|
|
66
|
+
# Max number of breadcrumbs a breadcrumb buffer can hold
|
67
|
+
attr_accessor :max_breadcrumbs
|
68
|
+
|
55
69
|
# Number of lines of code context to capture, or nil for none
|
56
70
|
attr_accessor :context_lines
|
57
71
|
|
58
72
|
# RACK_ENV by default.
|
59
73
|
attr_reader :environment
|
60
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
|
+
|
61
79
|
# the dsn value, whether it's set via `config.dsn=` or `ENV["SENTRY_DSN"]`
|
62
80
|
attr_reader :dsn
|
63
81
|
|
@@ -88,6 +106,9 @@ module Sentry
|
|
88
106
|
# Set automatically for Rails.
|
89
107
|
attr_reader :project_root
|
90
108
|
|
109
|
+
# Insert sentry-trace to outgoing requests' headers
|
110
|
+
attr_accessor :propagate_traces
|
111
|
+
|
91
112
|
# Array of rack env parameters to be included in the event sent to sentry.
|
92
113
|
attr_accessor :rack_env_whitelist
|
93
114
|
|
@@ -109,6 +130,12 @@ module Sentry
|
|
109
130
|
# will not be sent to Sentry.
|
110
131
|
attr_accessor :send_default_pii
|
111
132
|
|
133
|
+
# Allow to skip Sentry emails within rake tasks
|
134
|
+
attr_accessor :skip_rake_integration
|
135
|
+
|
136
|
+
# IP ranges for trusted proxies that will be skipped when calculating IP address.
|
137
|
+
attr_accessor :trusted_proxies
|
138
|
+
|
112
139
|
attr_accessor :server_name
|
113
140
|
|
114
141
|
# Return a Transport::Configuration object for transport-related configurations.
|
@@ -151,25 +178,32 @@ module Sentry
|
|
151
178
|
LOG_PREFIX = "** [Sentry] ".freeze
|
152
179
|
MODULE_SEPARATOR = "::".freeze
|
153
180
|
|
154
|
-
|
181
|
+
# Post initialization callbacks are called at the end of initialization process
|
182
|
+
# allowing extending the configuration of sentry-ruby by multiple extensions
|
183
|
+
@@post_initialization_callbacks = []
|
155
184
|
|
156
185
|
def initialize
|
186
|
+
self.debug = false
|
157
187
|
self.background_worker_threads = Concurrent.processor_count
|
188
|
+
self.max_breadcrumbs = BreadcrumbBuffer::DEFAULT_SIZE
|
158
189
|
self.breadcrumbs_logger = []
|
159
190
|
self.context_lines = 3
|
160
191
|
self.environment = environment_from_env
|
161
192
|
self.enabled_environments = []
|
162
193
|
self.exclude_loggers = []
|
163
194
|
self.excluded_exceptions = IGNORE_DEFAULT.dup
|
164
|
-
self.inspect_exception_causes_for_exclusion =
|
195
|
+
self.inspect_exception_causes_for_exclusion = true
|
165
196
|
self.linecache = ::Sentry::LineCache.new
|
166
197
|
self.logger = ::Sentry::Logger.new(STDOUT)
|
167
198
|
self.project_root = Dir.pwd
|
199
|
+
self.propagate_traces = true
|
168
200
|
|
169
201
|
self.release = detect_release
|
170
202
|
self.sample_rate = 1.0
|
171
203
|
self.send_modules = true
|
172
204
|
self.send_default_pii = false
|
205
|
+
self.skip_rake_integration = false
|
206
|
+
self.trusted_proxies = []
|
173
207
|
self.dsn = ENV['SENTRY_DSN']
|
174
208
|
self.server_name = server_name_from_env
|
175
209
|
|
@@ -178,7 +212,8 @@ module Sentry
|
|
178
212
|
|
179
213
|
@transport = Transport::Configuration.new
|
180
214
|
@gem_specs = Hash[Gem::Specification.map { |spec| [spec.name, spec.version.to_s] }] if Gem::Specification.respond_to?(:map)
|
181
|
-
|
215
|
+
|
216
|
+
run_post_initialization_callbacks
|
182
217
|
end
|
183
218
|
|
184
219
|
def dsn=(value)
|
@@ -203,10 +238,6 @@ module Sentry
|
|
203
238
|
if logger.is_a?(Array)
|
204
239
|
logger
|
205
240
|
else
|
206
|
-
unless AVAILABLE_BREADCRUMBS_LOGGERS.include?(logger)
|
207
|
-
raise Sentry::Error, "Unsupported breadcrumbs logger. Supported loggers: #{AVAILABLE_BREADCRUMBS_LOGGERS}"
|
208
|
-
end
|
209
|
-
|
210
241
|
Array(logger)
|
211
242
|
end
|
212
243
|
|
@@ -223,6 +254,14 @@ module Sentry
|
|
223
254
|
@before_send = value
|
224
255
|
end
|
225
256
|
|
257
|
+
def before_breadcrumb=(value)
|
258
|
+
unless value.nil? || value.respond_to?(:call)
|
259
|
+
raise ArgumentError, "before_breadcrumb must be callable (or nil to disable)"
|
260
|
+
end
|
261
|
+
|
262
|
+
@before_breadcrumb = value
|
263
|
+
end
|
264
|
+
|
226
265
|
def environment=(environment)
|
227
266
|
@environment = environment.to_s
|
228
267
|
end
|
@@ -247,10 +286,10 @@ module Sentry
|
|
247
286
|
def exception_class_allowed?(exc)
|
248
287
|
if exc.is_a?(Sentry::Error)
|
249
288
|
# Try to prevent error reporting loops
|
250
|
-
|
289
|
+
log_debug("Refusing to capture Sentry error: #{exc.inspect}")
|
251
290
|
false
|
252
291
|
elsif excluded_exception?(exc)
|
253
|
-
|
292
|
+
log_debug("User excluded error: #{exc.inspect}")
|
254
293
|
false
|
255
294
|
else
|
256
295
|
true
|
@@ -262,7 +301,17 @@ module Sentry
|
|
262
301
|
end
|
263
302
|
|
264
303
|
def tracing_enabled?
|
265
|
-
!!((@traces_sample_rate && @traces_sample_rate
|
304
|
+
!!((@traces_sample_rate && @traces_sample_rate >= 0.0 && @traces_sample_rate <= 1.0) || @traces_sampler) && sending_allowed?
|
305
|
+
end
|
306
|
+
|
307
|
+
def stacktrace_builder
|
308
|
+
@stacktrace_builder ||= StacktraceBuilder.new(
|
309
|
+
project_root: @project_root.to_s,
|
310
|
+
app_dirs_pattern: @app_dirs_pattern,
|
311
|
+
linecache: @linecache,
|
312
|
+
context_lines: @context_lines,
|
313
|
+
backtrace_cleanup_callback: @backtrace_cleanup_callback
|
314
|
+
)
|
266
315
|
end
|
267
316
|
|
268
317
|
private
|
@@ -273,7 +322,7 @@ module Sentry
|
|
273
322
|
detect_release_from_capistrano ||
|
274
323
|
detect_release_from_heroku
|
275
324
|
rescue => e
|
276
|
-
|
325
|
+
log_error("Error detecting release", e, debug: debug)
|
277
326
|
end
|
278
327
|
|
279
328
|
def excluded_exception?(incoming_exception)
|
@@ -308,7 +357,7 @@ module Sentry
|
|
308
357
|
def detect_release_from_heroku
|
309
358
|
return unless running_on_heroku?
|
310
359
|
return if ENV['CI']
|
311
|
-
|
360
|
+
log_warn(HEROKU_DYNO_METADATA_MESSAGE) && return unless ENV['HEROKU_SLUG_COMMIT']
|
312
361
|
|
313
362
|
ENV['HEROKU_SLUG_COMMIT']
|
314
363
|
end
|
@@ -382,7 +431,21 @@ module Sentry
|
|
382
431
|
end
|
383
432
|
end
|
384
433
|
|
385
|
-
|
386
|
-
|
434
|
+
def run_post_initialization_callbacks
|
435
|
+
self.class.post_initialization_callbacks.each do |hook|
|
436
|
+
instance_eval(&hook)
|
437
|
+
end
|
438
|
+
end
|
439
|
+
|
440
|
+
# allow extensions to add their hooks to the Configuration class
|
441
|
+
def self.add_post_initialization_callback(&block)
|
442
|
+
self.post_initialization_callbacks << block
|
443
|
+
end
|
444
|
+
|
445
|
+
protected
|
446
|
+
|
447
|
+
def self.post_initialization_callbacks
|
448
|
+
@@post_initialization_callbacks
|
449
|
+
end
|
387
450
|
end
|
388
451
|
end
|