sentry-ruby 5.18.2 → 5.28.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/.rspec +3 -1
- data/Gemfile +9 -3
- data/README.md +12 -7
- data/Rakefile +9 -11
- data/bin/console +1 -0
- data/lib/sentry/attachment.rb +40 -0
- data/lib/sentry/background_worker.rb +2 -3
- data/lib/sentry/backpressure_monitor.rb +1 -1
- data/lib/sentry/backtrace.rb +7 -8
- data/lib/sentry/baggage.rb +7 -7
- data/lib/sentry/breadcrumb/sentry_logger.rb +6 -6
- data/lib/sentry/breadcrumb.rb +5 -4
- data/lib/sentry/check_in_event.rb +5 -4
- data/lib/sentry/client.rb +121 -19
- data/lib/sentry/configuration.rb +207 -38
- data/lib/sentry/core_ext/object/deep_dup.rb +1 -1
- data/lib/sentry/cron/monitor_check_ins.rb +6 -4
- data/lib/sentry/cron/monitor_config.rb +1 -1
- data/lib/sentry/debug_structured_logger.rb +94 -0
- data/lib/sentry/dsn.rb +35 -3
- data/lib/sentry/envelope/item.rb +88 -0
- data/lib/sentry/envelope.rb +2 -85
- data/lib/sentry/event.rb +13 -8
- data/lib/sentry/excon/middleware.rb +77 -0
- data/lib/sentry/excon.rb +10 -0
- data/lib/sentry/faraday.rb +77 -0
- data/lib/sentry/graphql.rb +1 -1
- data/lib/sentry/hub.rb +57 -4
- data/lib/sentry/interfaces/mechanism.rb +1 -1
- data/lib/sentry/interfaces/request.rb +6 -6
- data/lib/sentry/interfaces/single_exception.rb +3 -3
- data/lib/sentry/interfaces/stacktrace.rb +3 -1
- data/lib/sentry/interfaces/stacktrace_builder.rb +15 -2
- data/lib/sentry/linecache.rb +3 -3
- data/lib/sentry/log_event.rb +219 -0
- data/lib/sentry/log_event_buffer.rb +75 -0
- data/lib/sentry/logger.rb +1 -1
- data/lib/sentry/metrics/aggregator.rb +13 -13
- data/lib/sentry/metrics/configuration.rb +12 -2
- data/lib/sentry/metrics/set_metric.rb +2 -2
- data/lib/sentry/metrics/timing.rb +8 -0
- data/lib/sentry/metrics.rb +27 -15
- data/lib/sentry/net/http.rb +17 -38
- data/lib/sentry/profiler/helpers.rb +46 -0
- data/lib/sentry/profiler.rb +27 -57
- data/lib/sentry/propagation_context.rb +60 -23
- data/lib/sentry/rack/capture_exceptions.rb +2 -2
- data/lib/sentry/rack.rb +2 -2
- data/lib/sentry/rake.rb +2 -2
- data/lib/sentry/release_detector.rb +4 -4
- data/lib/sentry/rspec.rb +91 -0
- data/lib/sentry/scope.rb +27 -2
- data/lib/sentry/session_flusher.rb +10 -6
- data/lib/sentry/span.rb +17 -4
- data/lib/sentry/std_lib_logger.rb +55 -0
- data/lib/sentry/structured_logger.rb +138 -0
- data/lib/sentry/test_helper.rb +45 -1
- data/lib/sentry/threaded_periodic_worker.rb +3 -3
- data/lib/sentry/transaction.rb +41 -13
- data/lib/sentry/transaction_event.rb +5 -3
- data/lib/sentry/transport/debug_transport.rb +70 -0
- data/lib/sentry/transport/dummy_transport.rb +1 -0
- data/lib/sentry/transport/http_transport.rb +21 -18
- data/lib/sentry/transport.rb +26 -11
- data/lib/sentry/utils/env_helper.rb +21 -0
- data/lib/sentry/utils/http_tracing.rb +74 -0
- data/lib/sentry/utils/logging_helper.rb +10 -3
- data/lib/sentry/utils/real_ip.rb +1 -1
- data/lib/sentry/utils/sample_rand.rb +97 -0
- data/lib/sentry/utils/uuid.rb +13 -0
- data/lib/sentry/vernier/output.rb +89 -0
- data/lib/sentry/vernier/profiler.rb +132 -0
- data/lib/sentry/version.rb +1 -1
- data/lib/sentry-ruby.rb +106 -12
- data/sentry-ruby-core.gemspec +3 -1
- data/sentry-ruby.gemspec +4 -2
- metadata +28 -12
data/lib/sentry/configuration.rb
CHANGED
|
@@ -3,7 +3,8 @@
|
|
|
3
3
|
require "concurrent/utility/processor_counter"
|
|
4
4
|
|
|
5
5
|
require "sentry/utils/exception_cause_chain"
|
|
6
|
-
require
|
|
6
|
+
require "sentry/utils/custom_inspection"
|
|
7
|
+
require "sentry/utils/env_helper"
|
|
7
8
|
require "sentry/dsn"
|
|
8
9
|
require "sentry/release_detector"
|
|
9
10
|
require "sentry/transport/configuration"
|
|
@@ -11,6 +12,9 @@ require "sentry/cron/configuration"
|
|
|
11
12
|
require "sentry/metrics/configuration"
|
|
12
13
|
require "sentry/linecache"
|
|
13
14
|
require "sentry/interfaces/stacktrace_builder"
|
|
15
|
+
require "sentry/logger"
|
|
16
|
+
require "sentry/structured_logger"
|
|
17
|
+
require "sentry/log_event_buffer"
|
|
14
18
|
|
|
15
19
|
module Sentry
|
|
16
20
|
class Configuration
|
|
@@ -22,6 +26,8 @@ module Sentry
|
|
|
22
26
|
# have an `engines` dir at the root of your project, you may want
|
|
23
27
|
# to set this to something like /(app|config|engines|lib)/
|
|
24
28
|
#
|
|
29
|
+
# The default is value is /(bin|exe|app|config|lib|test|spec)/
|
|
30
|
+
#
|
|
25
31
|
# @return [Regexp, nil]
|
|
26
32
|
attr_accessor :app_dirs_pattern
|
|
27
33
|
|
|
@@ -96,6 +102,15 @@ module Sentry
|
|
|
96
102
|
# @return [Proc]
|
|
97
103
|
attr_reader :before_send_transaction
|
|
98
104
|
|
|
105
|
+
# Optional Proc, called before sending an event to the server
|
|
106
|
+
# @example
|
|
107
|
+
# config.before_send_log = lambda do |log|
|
|
108
|
+
# log.attributes["sentry"] = true
|
|
109
|
+
# log
|
|
110
|
+
# end
|
|
111
|
+
# @return [Proc]
|
|
112
|
+
attr_accessor :before_send_log
|
|
113
|
+
|
|
99
114
|
# An array of breadcrumbs loggers to be used. Available options are:
|
|
100
115
|
# - :sentry_logger
|
|
101
116
|
# - :http_logger
|
|
@@ -180,13 +195,35 @@ module Sentry
|
|
|
180
195
|
# Logger used by Sentry. In Rails, this is the Rails logger, otherwise
|
|
181
196
|
# Sentry provides its own Sentry::Logger.
|
|
182
197
|
# @return [Logger]
|
|
183
|
-
attr_accessor :
|
|
198
|
+
attr_accessor :sdk_logger
|
|
199
|
+
|
|
200
|
+
# File path for DebugTransport to log events to. If not set, defaults to a temporary file.
|
|
201
|
+
# This is useful for debugging and testing purposes.
|
|
202
|
+
# @return [String, nil]
|
|
203
|
+
attr_accessor :sdk_debug_transport_log_file
|
|
204
|
+
|
|
205
|
+
# @deprecated Use {#sdk_logger=} instead.
|
|
206
|
+
def logger=(logger)
|
|
207
|
+
warn "[sentry] `config.logger=` is deprecated. Please use `config.sdk_logger=` instead."
|
|
208
|
+
self.sdk_logger = logger
|
|
209
|
+
end
|
|
210
|
+
|
|
211
|
+
# @deprecated Use {#sdk_logger} instead.
|
|
212
|
+
def logger
|
|
213
|
+
warn "[sentry] `config.logger` is deprecated. Please use `config.sdk_logger` instead."
|
|
214
|
+
self.sdk_logger
|
|
215
|
+
end
|
|
184
216
|
|
|
185
217
|
# Project directory root for in_app detection. Could be Rails root, etc.
|
|
186
218
|
# Set automatically for Rails.
|
|
187
219
|
# @return [String]
|
|
188
220
|
attr_accessor :project_root
|
|
189
221
|
|
|
222
|
+
# Whether to strip the load path while constructing the backtrace frame filename.
|
|
223
|
+
# Defaults to true.
|
|
224
|
+
# @return [Boolean]
|
|
225
|
+
attr_accessor :strip_backtrace_load_path
|
|
226
|
+
|
|
190
227
|
# Insert sentry-trace to outgoing requests' headers
|
|
191
228
|
# @return [Boolean]
|
|
192
229
|
attr_accessor :propagate_traces
|
|
@@ -254,8 +291,17 @@ module Sentry
|
|
|
254
291
|
# @return [Proc]
|
|
255
292
|
attr_accessor :traces_sampler
|
|
256
293
|
|
|
294
|
+
# Enable Structured Logging
|
|
295
|
+
# @return [Boolean]
|
|
296
|
+
attr_accessor :enable_logs
|
|
297
|
+
|
|
298
|
+
# Structured logging configuration.
|
|
299
|
+
# @return [StructuredLoggingConfiguration]
|
|
300
|
+
attr_reader :structured_logging
|
|
301
|
+
|
|
257
302
|
# Easier way to use performance tracing
|
|
258
303
|
# If set to true, will set traces_sample_rate to 1.0
|
|
304
|
+
# @deprecated It will be removed in the next major release.
|
|
259
305
|
# @return [Boolean, nil]
|
|
260
306
|
attr_reader :enable_tracing
|
|
261
307
|
|
|
@@ -283,6 +329,10 @@ module Sentry
|
|
|
283
329
|
# @return [Symbol]
|
|
284
330
|
attr_reader :instrumenter
|
|
285
331
|
|
|
332
|
+
# The profiler class
|
|
333
|
+
# @return [Class]
|
|
334
|
+
attr_reader :profiler_class
|
|
335
|
+
|
|
286
336
|
# Take a float between 0.0 and 1.0 as the sample rate for capturing profiles.
|
|
287
337
|
# Note that this rate is relative to traces_sample_rate / traces_sampler,
|
|
288
338
|
# i.e. the profile is sampled by this rate after the transaction is sampled.
|
|
@@ -294,6 +344,10 @@ module Sentry
|
|
|
294
344
|
# @return [Array<Symbol>]
|
|
295
345
|
attr_accessor :enabled_patches
|
|
296
346
|
|
|
347
|
+
# Maximum number of log events to buffer before sending
|
|
348
|
+
# @return [Integer]
|
|
349
|
+
attr_accessor :max_log_events
|
|
350
|
+
|
|
297
351
|
# these are not config options
|
|
298
352
|
# @!visibility private
|
|
299
353
|
attr_reader :errors, :gem_specs
|
|
@@ -302,18 +356,18 @@ module Sentry
|
|
|
302
356
|
# But they are mostly considered as noise and should be ignored by default
|
|
303
357
|
# Please see https://github.com/getsentry/sentry-ruby/pull/2026 for more information
|
|
304
358
|
PUMA_IGNORE_DEFAULT = [
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
359
|
+
"Puma::MiniSSL::SSLError",
|
|
360
|
+
"Puma::HttpParserError",
|
|
361
|
+
"Puma::HttpParserError501"
|
|
308
362
|
].freeze
|
|
309
363
|
|
|
310
364
|
# Most of these errors generate 4XX responses. In general, Sentry clients
|
|
311
365
|
# only automatically report 5xx responses.
|
|
312
366
|
IGNORE_DEFAULT = [
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
367
|
+
"Mongoid::Errors::DocumentNotFound",
|
|
368
|
+
"Rack::QueryParser::InvalidParameterError",
|
|
369
|
+
"Rack::QueryParser::ParameterTypeError",
|
|
370
|
+
"Sinatra::NotFound"
|
|
317
371
|
].freeze
|
|
318
372
|
|
|
319
373
|
RACK_ENV_WHITELIST_DEFAULT = %w[
|
|
@@ -323,18 +377,20 @@ module Sentry
|
|
|
323
377
|
].freeze
|
|
324
378
|
|
|
325
379
|
HEROKU_DYNO_METADATA_MESSAGE = "You are running on Heroku but haven't enabled Dyno Metadata. For Sentry's "\
|
|
326
|
-
"release detection to work correctly, please run `heroku labs:enable runtime-dyno-metadata`"
|
|
380
|
+
"release detection to work correctly, please run `heroku labs:enable runtime-dyno-metadata`"
|
|
327
381
|
|
|
328
|
-
LOG_PREFIX = "** [Sentry] "
|
|
329
|
-
MODULE_SEPARATOR = "::"
|
|
382
|
+
LOG_PREFIX = "** [Sentry] "
|
|
383
|
+
MODULE_SEPARATOR = "::"
|
|
330
384
|
SKIP_INSPECTION_ATTRIBUTES = [:@linecache, :@stacktrace_builder]
|
|
331
385
|
|
|
332
386
|
INSTRUMENTERS = [:sentry, :otel]
|
|
333
387
|
|
|
334
|
-
PROPAGATION_TARGETS_MATCH_ALL =
|
|
388
|
+
PROPAGATION_TARGETS_MATCH_ALL = /.*/
|
|
335
389
|
|
|
336
390
|
DEFAULT_PATCHES = %i[redis puma http].freeze
|
|
337
391
|
|
|
392
|
+
APP_DIRS_PATTERN = /(bin|exe|app|config|lib|test|spec)/
|
|
393
|
+
|
|
338
394
|
class << self
|
|
339
395
|
# Post initialization callbacks are called at the end of initialization process
|
|
340
396
|
# allowing extending the configuration of sentry-ruby by multiple extensions
|
|
@@ -344,16 +400,74 @@ module Sentry
|
|
|
344
400
|
|
|
345
401
|
# allow extensions to add their hooks to the Configuration class
|
|
346
402
|
def add_post_initialization_callback(&block)
|
|
347
|
-
|
|
403
|
+
callbacks[:initialize][:after] << block
|
|
404
|
+
end
|
|
405
|
+
|
|
406
|
+
def before(event, &block)
|
|
407
|
+
callbacks[event.to_sym][:before] << block
|
|
408
|
+
end
|
|
409
|
+
|
|
410
|
+
def after(event, &block)
|
|
411
|
+
callbacks[event.to_sym][:after] << block
|
|
412
|
+
end
|
|
413
|
+
|
|
414
|
+
# @!visibility private
|
|
415
|
+
def callbacks
|
|
416
|
+
@callbacks ||= {
|
|
417
|
+
initialize: { before: [], after: [] },
|
|
418
|
+
configured: { before: [], after: [] }
|
|
419
|
+
}
|
|
420
|
+
end
|
|
421
|
+
|
|
422
|
+
def validations
|
|
423
|
+
@validations ||= {}
|
|
424
|
+
end
|
|
425
|
+
|
|
426
|
+
def validate(attribute, optional: false, type: nil)
|
|
427
|
+
validations[attribute] = {
|
|
428
|
+
optional: optional,
|
|
429
|
+
type: type,
|
|
430
|
+
proc: build_validation_proc(optional, type)
|
|
431
|
+
}
|
|
432
|
+
end
|
|
433
|
+
|
|
434
|
+
private
|
|
435
|
+
|
|
436
|
+
def build_validation_proc(optional, type)
|
|
437
|
+
case type
|
|
438
|
+
when :numeric
|
|
439
|
+
->(value) do
|
|
440
|
+
if optional && value.nil?
|
|
441
|
+
true
|
|
442
|
+
else
|
|
443
|
+
unless value.is_a?(Numeric)
|
|
444
|
+
message = "must be a Numeric"
|
|
445
|
+
message += " or nil" if optional
|
|
446
|
+
|
|
447
|
+
{ error: message, value: value }
|
|
448
|
+
else
|
|
449
|
+
true
|
|
450
|
+
end
|
|
451
|
+
end
|
|
452
|
+
end
|
|
453
|
+
else
|
|
454
|
+
->(value) { true }
|
|
455
|
+
end
|
|
348
456
|
end
|
|
349
457
|
end
|
|
350
458
|
|
|
459
|
+
validate :traces_sample_rate, optional: true, type: :numeric
|
|
460
|
+
validate :profiles_sample_rate, optional: true, type: :numeric
|
|
461
|
+
|
|
351
462
|
def initialize
|
|
352
|
-
|
|
353
|
-
|
|
463
|
+
run_callbacks(:before, :initialize)
|
|
464
|
+
|
|
465
|
+
self.app_dirs_pattern = APP_DIRS_PATTERN
|
|
466
|
+
self.debug = Sentry::Utils::EnvHelper.env_to_bool(ENV["SENTRY_DEBUG"])
|
|
354
467
|
self.background_worker_threads = (processor_count / 2.0).ceil
|
|
355
468
|
self.background_worker_max_queue = BackgroundWorker::DEFAULT_MAX_QUEUE
|
|
356
469
|
self.backtrace_cleanup_callback = nil
|
|
470
|
+
self.strip_backtrace_load_path = true
|
|
357
471
|
self.max_breadcrumbs = BreadcrumbBuffer::DEFAULT_SIZE
|
|
358
472
|
self.breadcrumbs_logger = []
|
|
359
473
|
self.context_lines = 3
|
|
@@ -364,7 +478,7 @@ module Sentry
|
|
|
364
478
|
self.excluded_exceptions = IGNORE_DEFAULT + PUMA_IGNORE_DEFAULT
|
|
365
479
|
self.inspect_exception_causes_for_exclusion = true
|
|
366
480
|
self.linecache = ::Sentry::LineCache.new
|
|
367
|
-
self.
|
|
481
|
+
self.sdk_logger = ::Sentry::Logger.new(STDOUT)
|
|
368
482
|
self.project_root = Dir.pwd
|
|
369
483
|
self.propagate_traces = true
|
|
370
484
|
|
|
@@ -376,8 +490,11 @@ module Sentry
|
|
|
376
490
|
self.auto_session_tracking = true
|
|
377
491
|
self.enable_backpressure_handling = false
|
|
378
492
|
self.trusted_proxies = []
|
|
379
|
-
self.dsn = ENV[
|
|
380
|
-
|
|
493
|
+
self.dsn = ENV["SENTRY_DSN"]
|
|
494
|
+
|
|
495
|
+
spotlight_env = ENV["SENTRY_SPOTLIGHT"]
|
|
496
|
+
spotlight_bool = Sentry::Utils::EnvHelper.env_to_bool(spotlight_env, strict: true)
|
|
497
|
+
self.spotlight = spotlight_bool.nil? ? (spotlight_env || false) : spotlight_bool
|
|
381
498
|
self.server_name = server_name_from_env
|
|
382
499
|
self.instrumenter = :sentry
|
|
383
500
|
self.trace_propagation_targets = [PROPAGATION_TARGETS_MATCH_ALL]
|
|
@@ -385,16 +502,45 @@ module Sentry
|
|
|
385
502
|
|
|
386
503
|
self.before_send = nil
|
|
387
504
|
self.before_send_transaction = nil
|
|
505
|
+
self.before_send_log = nil
|
|
388
506
|
self.rack_env_whitelist = RACK_ENV_WHITELIST_DEFAULT
|
|
389
507
|
self.traces_sampler = nil
|
|
390
508
|
self.enable_tracing = nil
|
|
509
|
+
self.enable_logs = false
|
|
510
|
+
|
|
511
|
+
self.profiler_class = Sentry::Profiler
|
|
391
512
|
|
|
392
513
|
@transport = Transport::Configuration.new
|
|
393
514
|
@cron = Cron::Configuration.new
|
|
394
|
-
@metrics = Metrics::Configuration.new
|
|
515
|
+
@metrics = Metrics::Configuration.new(self.sdk_logger)
|
|
516
|
+
@structured_logging = StructuredLoggingConfiguration.new
|
|
395
517
|
@gem_specs = Hash[Gem::Specification.map { |spec| [spec.name, spec.version.to_s] }] if Gem::Specification.respond_to?(:map)
|
|
396
518
|
|
|
397
|
-
|
|
519
|
+
self.max_log_events = LogEventBuffer::DEFAULT_MAX_EVENTS
|
|
520
|
+
|
|
521
|
+
run_callbacks(:after, :initialize)
|
|
522
|
+
|
|
523
|
+
yield(self) if block_given?
|
|
524
|
+
|
|
525
|
+
run_callbacks(:after, :configured)
|
|
526
|
+
end
|
|
527
|
+
|
|
528
|
+
def validate
|
|
529
|
+
if profiler_class == Sentry::Profiler && profiles_sample_rate && !Sentry.dependency_installed?(:StackProf)
|
|
530
|
+
log_warn("Please add the 'stackprof' gem to your Gemfile to use the StackProf profiler with Sentry.")
|
|
531
|
+
end
|
|
532
|
+
|
|
533
|
+
if profiler_class == Sentry::Vernier::Profiler && profiles_sample_rate && !Sentry.dependency_installed?(:Vernier)
|
|
534
|
+
log_warn("Please add the 'vernier' gem to your Gemfile to use the Vernier profiler with Sentry.")
|
|
535
|
+
end
|
|
536
|
+
|
|
537
|
+
self.class.validations.each do |attribute, validation|
|
|
538
|
+
value = public_send(attribute)
|
|
539
|
+
|
|
540
|
+
next if (result = validation[:proc].call(value)) === true
|
|
541
|
+
|
|
542
|
+
raise ArgumentError, result[:error]
|
|
543
|
+
end
|
|
398
544
|
end
|
|
399
545
|
|
|
400
546
|
def dsn=(value)
|
|
@@ -465,25 +611,35 @@ module Sentry
|
|
|
465
611
|
end
|
|
466
612
|
|
|
467
613
|
def enable_tracing=(enable_tracing)
|
|
614
|
+
unless enable_tracing.nil?
|
|
615
|
+
log_warn <<~MSG
|
|
616
|
+
`enable_tracing` is now deprecated in favor of `traces_sample_rate = 1.0`.
|
|
617
|
+
MSG
|
|
618
|
+
end
|
|
619
|
+
|
|
468
620
|
@enable_tracing = enable_tracing
|
|
469
621
|
@traces_sample_rate ||= 1.0 if enable_tracing
|
|
470
622
|
end
|
|
471
623
|
|
|
472
|
-
def is_numeric_or_nil?(value)
|
|
473
|
-
value.is_a?(Numeric) || value.nil?
|
|
474
|
-
end
|
|
475
|
-
|
|
476
624
|
def traces_sample_rate=(traces_sample_rate)
|
|
477
|
-
raise ArgumentError, "traces_sample_rate must be a Numeric or nil" unless is_numeric_or_nil?(traces_sample_rate)
|
|
478
625
|
@traces_sample_rate = traces_sample_rate
|
|
479
626
|
end
|
|
480
627
|
|
|
481
628
|
def profiles_sample_rate=(profiles_sample_rate)
|
|
482
|
-
raise ArgumentError, "profiles_sample_rate must be a Numeric or nil" unless is_numeric_or_nil?(profiles_sample_rate)
|
|
483
|
-
log_warn("Please make sure to include the 'stackprof' gem in your Gemfile to use Profiling with Sentry.") unless defined?(StackProf)
|
|
484
629
|
@profiles_sample_rate = profiles_sample_rate
|
|
485
630
|
end
|
|
486
631
|
|
|
632
|
+
def profiler_class=(profiler_class)
|
|
633
|
+
if profiler_class == Sentry::Vernier::Profiler
|
|
634
|
+
begin
|
|
635
|
+
require "vernier"
|
|
636
|
+
rescue LoadError
|
|
637
|
+
end
|
|
638
|
+
end
|
|
639
|
+
|
|
640
|
+
@profiler_class = profiler_class
|
|
641
|
+
end
|
|
642
|
+
|
|
487
643
|
def sending_allowed?
|
|
488
644
|
spotlight || sending_to_dsn_allowed?
|
|
489
645
|
end
|
|
@@ -555,7 +711,8 @@ module Sentry
|
|
|
555
711
|
app_dirs_pattern: @app_dirs_pattern,
|
|
556
712
|
linecache: @linecache,
|
|
557
713
|
context_lines: @context_lines,
|
|
558
|
-
backtrace_cleanup_callback: @backtrace_cleanup_callback
|
|
714
|
+
backtrace_cleanup_callback: @backtrace_cleanup_callback,
|
|
715
|
+
strip_backtrace_load_path: @strip_backtrace_load_path
|
|
559
716
|
)
|
|
560
717
|
end
|
|
561
718
|
|
|
@@ -632,12 +789,12 @@ module Sentry
|
|
|
632
789
|
end
|
|
633
790
|
|
|
634
791
|
def environment_from_env
|
|
635
|
-
ENV[
|
|
792
|
+
ENV["SENTRY_CURRENT_ENV"] || ENV["SENTRY_ENVIRONMENT"] || ENV["RAILS_ENV"] || ENV["RACK_ENV"] || "development"
|
|
636
793
|
end
|
|
637
794
|
|
|
638
795
|
def server_name_from_env
|
|
639
796
|
if running_on_heroku?
|
|
640
|
-
ENV[
|
|
797
|
+
ENV["DYNO"]
|
|
641
798
|
else
|
|
642
799
|
# Try to resolve the hostname to an FQDN, but fall back to whatever
|
|
643
800
|
# the load name is.
|
|
@@ -649,18 +806,30 @@ module Sentry
|
|
|
649
806
|
File.directory?("/etc/heroku") && !ENV["CI"]
|
|
650
807
|
end
|
|
651
808
|
|
|
652
|
-
def
|
|
653
|
-
self.class.
|
|
809
|
+
def run_callbacks(hook, event)
|
|
810
|
+
self.class.callbacks[event][hook].each do |hook|
|
|
654
811
|
instance_eval(&hook)
|
|
655
812
|
end
|
|
656
813
|
end
|
|
657
814
|
|
|
658
815
|
def processor_count
|
|
659
|
-
if Concurrent.respond_to?(:
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
816
|
+
available_processor_count = Concurrent.available_processor_count if Concurrent.respond_to?(:available_processor_count)
|
|
817
|
+
available_processor_count || Concurrent.processor_count
|
|
818
|
+
end
|
|
819
|
+
end
|
|
820
|
+
|
|
821
|
+
class StructuredLoggingConfiguration
|
|
822
|
+
# File path for DebugStructuredLogger to log events to
|
|
823
|
+
# @return [String, Pathname, nil]
|
|
824
|
+
attr_accessor :file_path
|
|
825
|
+
|
|
826
|
+
# The class to use as a structured logger.
|
|
827
|
+
# @return [Class]
|
|
828
|
+
attr_accessor :logger_class
|
|
829
|
+
|
|
830
|
+
def initialize
|
|
831
|
+
@file_path = nil
|
|
832
|
+
@logger_class = Sentry::StructuredLogger
|
|
664
833
|
end
|
|
665
834
|
end
|
|
666
835
|
end
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
module Sentry
|
|
2
4
|
module Cron
|
|
3
5
|
module MonitorCheckIns
|
|
@@ -12,12 +14,12 @@ module Sentry
|
|
|
12
14
|
:in_progress,
|
|
13
15
|
monitor_config: monitor_config)
|
|
14
16
|
|
|
15
|
-
start =
|
|
17
|
+
start = Metrics::Timing.duration_start
|
|
16
18
|
|
|
17
19
|
begin
|
|
18
20
|
# need to do this on ruby <= 2.6 sadly
|
|
19
21
|
ret = method(:perform).super_method.arity == 0 ? super() : super
|
|
20
|
-
duration =
|
|
22
|
+
duration = Metrics::Timing.duration_end(start)
|
|
21
23
|
|
|
22
24
|
Sentry.capture_check_in(slug,
|
|
23
25
|
:ok,
|
|
@@ -27,7 +29,7 @@ module Sentry
|
|
|
27
29
|
|
|
28
30
|
ret
|
|
29
31
|
rescue Exception
|
|
30
|
-
duration =
|
|
32
|
+
duration = Metrics::Timing.duration_end(start)
|
|
31
33
|
|
|
32
34
|
Sentry.capture_check_in(slug,
|
|
33
35
|
:error,
|
|
@@ -57,7 +59,7 @@ module Sentry
|
|
|
57
59
|
|
|
58
60
|
def sentry_monitor_slug(name: self.name)
|
|
59
61
|
@sentry_monitor_slug ||= begin
|
|
60
|
-
slug = name.gsub(
|
|
62
|
+
slug = name.gsub("::", "-").downcase
|
|
61
63
|
slug[-MAX_SLUG_LENGTH..-1] || slug
|
|
62
64
|
end
|
|
63
65
|
end
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "json"
|
|
4
|
+
require "fileutils"
|
|
5
|
+
require "pathname"
|
|
6
|
+
require "delegate"
|
|
7
|
+
|
|
8
|
+
module Sentry
|
|
9
|
+
# DebugStructuredLogger is a logger that captures structured log events to a file for debugging purposes.
|
|
10
|
+
#
|
|
11
|
+
# It can optionally also send log events to Sentry via the normal structured logger if logging
|
|
12
|
+
# is enabled.
|
|
13
|
+
class DebugStructuredLogger < SimpleDelegator
|
|
14
|
+
DEFAULT_LOG_FILE_PATH = File.join("log", "sentry_debug_logs.log")
|
|
15
|
+
|
|
16
|
+
attr_reader :log_file, :backend
|
|
17
|
+
|
|
18
|
+
def initialize(configuration)
|
|
19
|
+
@log_file = initialize_log_file(
|
|
20
|
+
configuration.structured_logging.file_path || DEFAULT_LOG_FILE_PATH
|
|
21
|
+
)
|
|
22
|
+
@backend = initialize_backend(configuration)
|
|
23
|
+
|
|
24
|
+
super(@backend)
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
# Override all log level methods to capture events
|
|
28
|
+
%i[trace debug info warn error fatal].each do |level|
|
|
29
|
+
define_method(level) do |message, parameters = [], **attributes|
|
|
30
|
+
log_event = capture_log_event(level, message, parameters, **attributes)
|
|
31
|
+
backend.public_send(level, message, parameters, **attributes)
|
|
32
|
+
log_event
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def log(level, message, parameters:, **attributes)
|
|
37
|
+
log_event = capture_log_event(level, message, parameters, **attributes)
|
|
38
|
+
backend.log(level, message, parameters: parameters, **attributes)
|
|
39
|
+
log_event
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def capture_log_event(level, message, parameters, **attributes)
|
|
43
|
+
log_event_json = {
|
|
44
|
+
timestamp: Time.now.utc.iso8601,
|
|
45
|
+
level: level.to_s,
|
|
46
|
+
message: message,
|
|
47
|
+
parameters: parameters,
|
|
48
|
+
attributes: attributes
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
File.open(log_file, "a") { |file| file << JSON.dump(log_event_json) << "\n" }
|
|
52
|
+
log_event_json
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
def logged_events
|
|
56
|
+
File.readlines(log_file).map do |line|
|
|
57
|
+
JSON.parse(line)
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
def clear
|
|
62
|
+
File.write(log_file, "")
|
|
63
|
+
if backend.respond_to?(:config)
|
|
64
|
+
backend.config.sdk_logger.debug("DebugStructuredLogger: Cleared events from #{log_file}")
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
private
|
|
69
|
+
|
|
70
|
+
def initialize_backend(configuration)
|
|
71
|
+
if configuration.enable_logs
|
|
72
|
+
StructuredLogger.new(configuration)
|
|
73
|
+
else
|
|
74
|
+
# Create a no-op logger if logging is disabled
|
|
75
|
+
NoOpLogger.new
|
|
76
|
+
end
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
def initialize_log_file(log_file_path)
|
|
80
|
+
log_file = Pathname(log_file_path)
|
|
81
|
+
|
|
82
|
+
FileUtils.mkdir_p(log_file.dirname) unless log_file.dirname.exist?
|
|
83
|
+
|
|
84
|
+
log_file
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
# No-op logger for when structured logging is disabled
|
|
88
|
+
class NoOpLogger
|
|
89
|
+
%i[trace debug info warn error fatal log].each do |method|
|
|
90
|
+
define_method(method) { |*args, **kwargs| nil }
|
|
91
|
+
end
|
|
92
|
+
end
|
|
93
|
+
end
|
|
94
|
+
end
|
data/lib/sentry/dsn.rb
CHANGED
|
@@ -1,11 +1,15 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
require "uri"
|
|
4
|
+
require "ipaddr"
|
|
5
|
+
require "resolv"
|
|
4
6
|
|
|
5
7
|
module Sentry
|
|
6
8
|
class DSN
|
|
7
|
-
PORT_MAP = {
|
|
9
|
+
PORT_MAP = { "http" => 80, "https" => 443 }.freeze
|
|
8
10
|
REQUIRED_ATTRIBUTES = %w[host path public_key project_id].freeze
|
|
11
|
+
LOCALHOST_NAMES = %w[localhost 127.0.0.1 ::1 [::1]].freeze
|
|
12
|
+
LOCALHOST_PATTERN = /\.local(host|domain)?$/i
|
|
9
13
|
|
|
10
14
|
attr_reader :scheme, :secret_key, :port, *REQUIRED_ATTRIBUTES
|
|
11
15
|
|
|
@@ -13,7 +17,7 @@ module Sentry
|
|
|
13
17
|
@raw_value = dsn_string
|
|
14
18
|
|
|
15
19
|
uri = URI.parse(dsn_string)
|
|
16
|
-
uri_path = uri.path.split(
|
|
20
|
+
uri_path = uri.path.split("/")
|
|
17
21
|
|
|
18
22
|
if uri.user
|
|
19
23
|
# DSN-style string
|
|
@@ -25,7 +29,7 @@ module Sentry
|
|
|
25
29
|
@scheme = uri.scheme
|
|
26
30
|
@host = uri.host
|
|
27
31
|
@port = uri.port if uri.port
|
|
28
|
-
@path = uri_path.join(
|
|
32
|
+
@path = uri_path.join("/")
|
|
29
33
|
end
|
|
30
34
|
|
|
31
35
|
def valid?
|
|
@@ -49,5 +53,33 @@ module Sentry
|
|
|
49
53
|
def envelope_endpoint
|
|
50
54
|
"#{path}/api/#{project_id}/envelope/"
|
|
51
55
|
end
|
|
56
|
+
|
|
57
|
+
def local?
|
|
58
|
+
@local ||= (localhost? || private_ip? || resolved_ips_private?)
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
def localhost?
|
|
62
|
+
LOCALHOST_NAMES.include?(host.downcase) || LOCALHOST_PATTERN.match?(host)
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
def private_ip?
|
|
66
|
+
@private_ip ||= begin
|
|
67
|
+
begin
|
|
68
|
+
IPAddr.new(host).private?
|
|
69
|
+
rescue IPAddr::InvalidAddressError
|
|
70
|
+
false
|
|
71
|
+
end
|
|
72
|
+
end
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
def resolved_ips_private?
|
|
76
|
+
@resolved_ips_private ||= begin
|
|
77
|
+
begin
|
|
78
|
+
Resolv.getaddresses(host).any? { |ip| IPAddr.new(ip).private? }
|
|
79
|
+
rescue Resolv::ResolvError, IPAddr::InvalidAddressError
|
|
80
|
+
false
|
|
81
|
+
end
|
|
82
|
+
end
|
|
83
|
+
end
|
|
52
84
|
end
|
|
53
85
|
end
|