sentry-ruby 5.26.0 → 6.5.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.
Files changed (71) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile +27 -5
  3. data/README.md +3 -3
  4. data/lib/sentry/background_worker.rb +1 -4
  5. data/lib/sentry/backtrace/line.rb +99 -0
  6. data/lib/sentry/backtrace.rb +44 -76
  7. data/lib/sentry/baggage.rb +2 -2
  8. data/lib/sentry/breadcrumb.rb +1 -1
  9. data/lib/sentry/breadcrumb_buffer.rb +2 -2
  10. data/lib/sentry/check_in_event.rb +2 -2
  11. data/lib/sentry/client.rb +59 -136
  12. data/lib/sentry/configuration.rb +206 -78
  13. data/lib/sentry/cron/monitor_check_ins.rb +3 -3
  14. data/lib/sentry/cron/monitor_config.rb +2 -2
  15. data/lib/sentry/cron/monitor_schedule.rb +2 -2
  16. data/lib/sentry/debug_structured_logger.rb +94 -0
  17. data/lib/sentry/dsn.rb +65 -1
  18. data/lib/sentry/envelope/item.rb +3 -3
  19. data/lib/sentry/error_event.rb +3 -3
  20. data/lib/sentry/event.rb +4 -10
  21. data/lib/sentry/exceptions.rb +3 -0
  22. data/lib/sentry/graphql.rb +1 -1
  23. data/lib/sentry/hub.rb +29 -5
  24. data/lib/sentry/interface.rb +1 -1
  25. data/lib/sentry/interfaces/exception.rb +2 -2
  26. data/lib/sentry/interfaces/request.rb +2 -0
  27. data/lib/sentry/interfaces/single_exception.rb +4 -4
  28. data/lib/sentry/interfaces/stacktrace.rb +3 -3
  29. data/lib/sentry/interfaces/stacktrace_builder.rb +0 -8
  30. data/lib/sentry/interfaces/threads.rb +2 -2
  31. data/lib/sentry/log_event.rb +33 -138
  32. data/lib/sentry/log_event_buffer.rb +13 -60
  33. data/lib/sentry/metric_event.rb +49 -0
  34. data/lib/sentry/metric_event_buffer.rb +28 -0
  35. data/lib/sentry/metrics.rb +47 -42
  36. data/lib/sentry/profiler.rb +4 -5
  37. data/lib/sentry/propagation_context.rb +101 -24
  38. data/lib/sentry/rack/capture_exceptions.rb +90 -2
  39. data/lib/sentry/release_detector.rb +1 -1
  40. data/lib/sentry/rspec.rb +1 -1
  41. data/lib/sentry/scope.rb +51 -18
  42. data/lib/sentry/sequel.rb +35 -0
  43. data/lib/sentry/span.rb +5 -17
  44. data/lib/sentry/std_lib_logger.rb +10 -1
  45. data/lib/sentry/telemetry_event_buffer.rb +130 -0
  46. data/lib/sentry/test_helper.rb +30 -0
  47. data/lib/sentry/transaction.rb +73 -95
  48. data/lib/sentry/transaction_event.rb +4 -9
  49. data/lib/sentry/transport/debug_transport.rb +70 -0
  50. data/lib/sentry/transport/dummy_transport.rb +1 -0
  51. data/lib/sentry/transport/http_transport.rb +16 -16
  52. data/lib/sentry/transport.rb +10 -7
  53. data/lib/sentry/utils/encoding_helper.rb +6 -0
  54. data/lib/sentry/utils/logging_helper.rb +25 -9
  55. data/lib/sentry/utils/sample_rand.rb +97 -0
  56. data/lib/sentry/utils/telemetry_attributes.rb +30 -0
  57. data/lib/sentry/vernier/profiler.rb +4 -3
  58. data/lib/sentry/version.rb +1 -1
  59. data/lib/sentry-ruby.rb +57 -30
  60. data/sentry-ruby-core.gemspec +1 -1
  61. data/sentry-ruby.gemspec +2 -1
  62. metadata +31 -17
  63. data/lib/sentry/metrics/aggregator.rb +0 -248
  64. data/lib/sentry/metrics/configuration.rb +0 -47
  65. data/lib/sentry/metrics/counter_metric.rb +0 -25
  66. data/lib/sentry/metrics/distribution_metric.rb +0 -25
  67. data/lib/sentry/metrics/gauge_metric.rb +0 -35
  68. data/lib/sentry/metrics/local_aggregator.rb +0 -53
  69. data/lib/sentry/metrics/metric.rb +0 -19
  70. data/lib/sentry/metrics/set_metric.rb +0 -28
  71. data/lib/sentry/metrics/timing.rb +0 -51
@@ -1,5 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require "cgi/escape"
3
4
  require "concurrent/utility/processor_counter"
4
5
 
5
6
  require "sentry/utils/exception_cause_chain"
@@ -9,11 +10,12 @@ require "sentry/dsn"
9
10
  require "sentry/release_detector"
10
11
  require "sentry/transport/configuration"
11
12
  require "sentry/cron/configuration"
12
- require "sentry/metrics/configuration"
13
13
  require "sentry/linecache"
14
14
  require "sentry/interfaces/stacktrace_builder"
15
15
  require "sentry/logger"
16
+ require "sentry/structured_logger"
16
17
  require "sentry/log_event_buffer"
18
+ require "sentry/metric_event_buffer"
17
19
 
18
20
  module Sentry
19
21
  class Configuration
@@ -30,13 +32,6 @@ module Sentry
30
32
  # @return [Regexp, nil]
31
33
  attr_accessor :app_dirs_pattern
32
34
 
33
- # Provide an object that responds to `call` to send events asynchronously.
34
- # E.g.: lambda { |event| Thread.new { Sentry.send_event(event) } }
35
- #
36
- # @deprecated It will be removed in the next major release. Please read https://github.com/getsentry/sentry-ruby/issues/1522 for more information
37
- # @return [Proc, nil]
38
- attr_reader :async
39
-
40
35
  # to send events in a non-blocking way, sentry-ruby has its own background worker
41
36
  # by default, the worker holds a thread pool that has [the number of processors] threads
42
37
  # but you can configure it with this configuration option
@@ -74,11 +69,10 @@ module Sentry
74
69
  # @return [Proc]
75
70
  attr_reader :before_breadcrumb
76
71
 
77
- # Optional Proc, called before sending an event to the server
72
+ # Optional Proc, called before sending an error event to the server
78
73
  # @example
79
74
  # config.before_send = lambda do |event, hint|
80
75
  # # skip ZeroDivisionError exceptions
81
- # # note: hint[:exception] would be a String if you use async callback
82
76
  # if hint[:exception].is_a?(ZeroDivisionError)
83
77
  # nil
84
78
  # else
@@ -88,7 +82,7 @@ module Sentry
88
82
  # @return [Proc]
89
83
  attr_reader :before_send
90
84
 
91
- # Optional Proc, called before sending an event to the server
85
+ # Optional Proc, called before sending a transaction event to the server
92
86
  # @example
93
87
  # config.before_send_transaction = lambda do |event, hint|
94
88
  # # skip unimportant transactions or strip sensitive data
@@ -101,6 +95,18 @@ module Sentry
101
95
  # @return [Proc]
102
96
  attr_reader :before_send_transaction
103
97
 
98
+ # Optional Proc, called before sending a check-in event to the server
99
+ # @example
100
+ # config.before_send_check_in = lambda do |event, hint|
101
+ # if event.monitor_slug == "unimportant_job"
102
+ # nil
103
+ # else
104
+ # event
105
+ # end
106
+ # end
107
+ # @return [Proc]
108
+ attr_reader :before_send_check_in
109
+
104
110
  # Optional Proc, called before sending an event to the server
105
111
  # @example
106
112
  # config.before_send_log = lambda do |log|
@@ -117,7 +123,6 @@ module Sentry
117
123
  #
118
124
  # And if you also use sentry-rails:
119
125
  # - :active_support_logger
120
- # - :monotonic_active_support_logger
121
126
  #
122
127
  # @return [Array<Symbol>]
123
128
  attr_reader :breadcrumbs_logger
@@ -144,7 +149,7 @@ module Sentry
144
149
  attr_reader :dsn
145
150
 
146
151
  # Whitelist of enabled_environments that will send notifications to Sentry. Array of Strings.
147
- # @return [Array<String>]
152
+ # @return [Array<String>, nil]
148
153
  attr_accessor :enabled_environments
149
154
 
150
155
  # Logger 'progname's to exclude from breadcrumbs
@@ -173,18 +178,6 @@ module Sentry
173
178
  # @return [Boolean, String]
174
179
  attr_accessor :spotlight
175
180
 
176
- # @deprecated Use {#include_local_variables} instead.
177
- alias_method :capture_exception_frame_locals, :include_local_variables
178
-
179
- # @deprecated Use {#include_local_variables=} instead.
180
- def capture_exception_frame_locals=(value)
181
- log_warn <<~MSG
182
- `capture_exception_frame_locals` is now deprecated in favor of `include_local_variables`.
183
- MSG
184
-
185
- self.include_local_variables = value
186
- end
187
-
188
181
  # You may provide your own LineCache for matching paths with source files.
189
182
  # This may be useful if you need to get source code from places other than the disk.
190
183
  # @see LineCache
@@ -196,17 +189,10 @@ module Sentry
196
189
  # @return [Logger]
197
190
  attr_accessor :sdk_logger
198
191
 
199
- # @deprecated Use {#sdk_logger=} instead.
200
- def logger=(logger)
201
- warn "[sentry] `config.logger=` is deprecated. Please use `config.sdk_logger=` instead."
202
- self.sdk_logger = logger
203
- end
204
-
205
- # @deprecated Use {#sdk_logger} instead.
206
- def logger
207
- warn "[sentry] `config.logger` is deprecated. Please use `config.sdk_logger` instead."
208
- self.sdk_logger
209
- end
192
+ # File path for DebugTransport to log events to. If not set, defaults to a temporary file.
193
+ # This is useful for debugging and testing purposes.
194
+ # @return [String, nil]
195
+ attr_accessor :sdk_debug_transport_log_file
210
196
 
211
197
  # Project directory root for in_app detection. Could be Rails root, etc.
212
198
  # Set automatically for Rails.
@@ -249,6 +235,12 @@ module Sentry
249
235
  # @return [Boolean]
250
236
  attr_accessor :send_default_pii
251
237
 
238
+ # Capture queue time from X-Request-Start header set by reverse proxies.
239
+ # Works with any Rack app behind Nginx, HAProxy, Heroku router, etc.
240
+ # Defaults to true.
241
+ # @return [Boolean]
242
+ attr_accessor :capture_queue_time
243
+
252
244
  # Allow to skip Sentry emails within rake tasks
253
245
  # @return [Boolean]
254
246
  attr_accessor :skip_rake_integration
@@ -267,10 +259,6 @@ module Sentry
267
259
  # @return [Cron::Configuration]
268
260
  attr_reader :cron
269
261
 
270
- # Metrics related configuration.
271
- # @return [Metrics::Configuration]
272
- attr_reader :metrics
273
-
274
262
  # Take a float between 0.0 and 1.0 as the sample rate for tracing events (transactions).
275
263
  # @return [Float, nil]
276
264
  attr_reader :traces_sample_rate
@@ -289,11 +277,9 @@ module Sentry
289
277
  # @return [Boolean]
290
278
  attr_accessor :enable_logs
291
279
 
292
- # Easier way to use performance tracing
293
- # If set to true, will set traces_sample_rate to 1.0
294
- # @deprecated It will be removed in the next major release.
295
- # @return [Boolean, nil]
296
- attr_reader :enable_tracing
280
+ # Structured logging configuration.
281
+ # @return [StructuredLoggingConfiguration]
282
+ attr_reader :structured_logging
297
283
 
298
284
  # Send diagnostic client reports about dropped events, true by default
299
285
  # tries to attach to an existing envelope max once every 30s
@@ -315,6 +301,18 @@ module Sentry
315
301
  # @return [Array<String, Regexp>]
316
302
  attr_accessor :trace_propagation_targets
317
303
 
304
+ # Collection of HTTP status codes or ranges of codes to ignore when tracing incoming requests.
305
+ # If a transaction's http.response.status_code matches one of these values,
306
+ # the transaction will be dropped and marked as not sampled.
307
+ # Defaults to TRACE_IGNORE_STATUS_CODES_DEFAULT.
308
+ #
309
+ # @example
310
+ # # ignore 404 and 502 <= status_code <= 511
311
+ # config.trace_ignore_status_codes = [404, (502..511)]
312
+ #
313
+ # @return [Array<Integer>, Array<Range>]
314
+ attr_reader :trace_ignore_status_codes
315
+
318
316
  # The instrumenter to use, :sentry or :otel
319
317
  # @return [Symbol]
320
318
  attr_reader :instrumenter
@@ -329,6 +327,15 @@ module Sentry
329
327
  # @return [Float, nil]
330
328
  attr_reader :profiles_sample_rate
331
329
 
330
+ # Interval in microseconds at which to take samples.
331
+ # The default is 1e6 / 101, or 101Hz.
332
+ # Note that the 101 is intentional to avoid lockstep sampling.
333
+ #
334
+ # @example
335
+ # config.profiles_sample_interval = 1e5 / 101
336
+ # @return [Float]
337
+ attr_accessor :profiles_sample_interval
338
+
332
339
  # Array of patches to apply.
333
340
  # Default is {DEFAULT_PATCHES}
334
341
  # @return [Array<Symbol>]
@@ -338,6 +345,50 @@ module Sentry
338
345
  # @return [Integer]
339
346
  attr_accessor :max_log_events
340
347
 
348
+ # Enable metrics collection, defaults to true
349
+ # @return [Boolean]
350
+ attr_accessor :enable_metrics
351
+
352
+ # Maximum number of metric events to buffer before sending
353
+ # @return [Integer]
354
+ attr_accessor :max_metric_events
355
+
356
+ # Optional Proc, called before sending a metric
357
+ # @example
358
+ # config.before_send_metric = lambda do |metric|
359
+ # # return nil to drop the metric
360
+ # metric
361
+ # end
362
+ # @return [Proc, nil]
363
+ attr_reader :before_send_metric
364
+
365
+ # Optional Proc, called to filter log messages before sending to Sentry
366
+ # @example
367
+ # config.std_lib_logger_filter = lambda do |logger, message, level|
368
+ # # Only send error and fatal logs to Sentry
369
+ # [:error, :fatal].include?(level)
370
+ # end
371
+ # @return [Proc, nil]
372
+ attr_reader :std_lib_logger_filter
373
+
374
+ # An optional organization ID. The SDK will try to extract it from the DSN in most cases
375
+ # but you can provide it explicitly for self-hosted and Relay setups.
376
+ # This value is used for trace propagation and for features like strict_trace_continuation.
377
+ # @return [String, nil]
378
+ attr_reader :org_id
379
+
380
+ # If set to true, the SDK will only continue a trace if the org_id of the incoming trace found in the
381
+ # baggage header matches the org_id of the current Sentry client and only if BOTH are present.
382
+ #
383
+ # If set to false, consistency of org_id will only be enforced if both are present.
384
+ # If either are missing, the trace will be continued.
385
+ #
386
+ # The client's organization ID is extracted from the DSN or can be set with the org_id option.
387
+ # If the organization IDs do not match, the SDK will start a new trace instead of continuing the incoming one.
388
+ # This is useful to prevent traces of unknown third-party services from being continued in your application.
389
+ # @return [Boolean]
390
+ attr_accessor :strict_trace_continuation
391
+
341
392
  # these are not config options
342
393
  # @!visibility private
343
394
  attr_reader :errors, :gem_specs
@@ -366,6 +417,8 @@ module Sentry
366
417
  SERVER_PORT
367
418
  ].freeze
368
419
 
420
+ TRACE_IGNORE_STATUS_CODES_DEFAULT = [(301..303), (305..399), (401..404)]
421
+
369
422
  HEROKU_DYNO_METADATA_MESSAGE = "You are running on Heroku but haven't enabled Dyno Metadata. For Sentry's "\
370
423
  "release detection to work correctly, please run `heroku labs:enable runtime-dyno-metadata`"
371
424
 
@@ -381,6 +434,9 @@ module Sentry
381
434
 
382
435
  APP_DIRS_PATTERN = /(bin|exe|app|config|lib|test|spec)/
383
436
 
437
+ # 101 Hz in microseconds
438
+ DEFAULT_PROFILES_SAMPLE_INTERVAL = 1e6 / 101
439
+
384
440
  class << self
385
441
  # Post initialization callbacks are called at the end of initialization process
386
442
  # allowing extending the configuration of sentry-ruby by multiple extensions
@@ -390,7 +446,23 @@ module Sentry
390
446
 
391
447
  # allow extensions to add their hooks to the Configuration class
392
448
  def add_post_initialization_callback(&block)
393
- post_initialization_callbacks << block
449
+ callbacks[:initialize][:after] << block
450
+ end
451
+
452
+ def before(event, &block)
453
+ callbacks[event.to_sym][:before] << block
454
+ end
455
+
456
+ def after(event, &block)
457
+ callbacks[event.to_sym][:after] << block
458
+ end
459
+
460
+ # @!visibility private
461
+ def callbacks
462
+ @callbacks ||= {
463
+ initialize: { before: [], after: [] },
464
+ configured: { before: [], after: [] }
465
+ }
394
466
  end
395
467
 
396
468
  def validations
@@ -434,6 +506,8 @@ module Sentry
434
506
  validate :profiles_sample_rate, optional: true, type: :numeric
435
507
 
436
508
  def initialize
509
+ run_callbacks(:before, :initialize)
510
+
437
511
  self.app_dirs_pattern = APP_DIRS_PATTERN
438
512
  self.debug = Sentry::Utils::EnvHelper.env_to_bool(ENV["SENTRY_DEBUG"])
439
513
  self.background_worker_threads = (processor_count / 2.0).ceil
@@ -445,7 +519,7 @@ module Sentry
445
519
  self.context_lines = 3
446
520
  self.include_local_variables = false
447
521
  self.environment = environment_from_env
448
- self.enabled_environments = []
522
+ self.enabled_environments = nil
449
523
  self.exclude_loggers = []
450
524
  self.excluded_exceptions = IGNORE_DEFAULT + PUMA_IGNORE_DEFAULT
451
525
  self.inspect_exception_causes_for_exclusion = true
@@ -463,6 +537,9 @@ module Sentry
463
537
  self.enable_backpressure_handling = false
464
538
  self.trusted_proxies = []
465
539
  self.dsn = ENV["SENTRY_DSN"]
540
+ self.capture_queue_time = true
541
+ self.org_id = nil
542
+ self.strict_trace_continuation = false
466
543
 
467
544
  spotlight_env = ENV["SENTRY_SPOTLIGHT"]
468
545
  spotlight_bool = Sentry::Utils::EnvHelper.env_to_bool(spotlight_env, strict: true)
@@ -470,26 +547,36 @@ module Sentry
470
547
  self.server_name = server_name_from_env
471
548
  self.instrumenter = :sentry
472
549
  self.trace_propagation_targets = [PROPAGATION_TARGETS_MATCH_ALL]
550
+ self.trace_ignore_status_codes = TRACE_IGNORE_STATUS_CODES_DEFAULT
473
551
  self.enabled_patches = DEFAULT_PATCHES.dup
474
552
 
475
553
  self.before_send = nil
476
554
  self.before_send_transaction = nil
555
+ self.before_send_check_in = nil
477
556
  self.before_send_log = nil
557
+ self.before_send_metric = nil
558
+ self.std_lib_logger_filter = nil
478
559
  self.rack_env_whitelist = RACK_ENV_WHITELIST_DEFAULT
479
560
  self.traces_sampler = nil
480
- self.enable_tracing = nil
481
561
  self.enable_logs = false
562
+ self.enable_metrics = true
482
563
 
483
564
  self.profiler_class = Sentry::Profiler
565
+ self.profiles_sample_interval = DEFAULT_PROFILES_SAMPLE_INTERVAL
484
566
 
485
567
  @transport = Transport::Configuration.new
486
568
  @cron = Cron::Configuration.new
487
- @metrics = Metrics::Configuration.new
569
+ @structured_logging = StructuredLoggingConfiguration.new
488
570
  @gem_specs = Hash[Gem::Specification.map { |spec| [spec.name, spec.version.to_s] }] if Gem::Specification.respond_to?(:map)
489
571
 
490
- run_post_initialization_callbacks
491
-
492
572
  self.max_log_events = LogEventBuffer::DEFAULT_MAX_EVENTS
573
+ self.max_metric_events = MetricEventBuffer::DEFAULT_MAX_METRICS
574
+
575
+ run_callbacks(:after, :initialize)
576
+
577
+ yield(self) if block_given?
578
+
579
+ run_callbacks(:after, :configured)
493
580
  end
494
581
 
495
582
  def validate
@@ -522,22 +609,6 @@ module Sentry
522
609
  @release = value
523
610
  end
524
611
 
525
- def async=(value)
526
- check_callable!("async", value)
527
-
528
- log_warn <<~MSG
529
-
530
- sentry-ruby now sends events asynchronously by default with its background worker (supported since 4.1.0).
531
- The `config.async` callback has become redundant while continuing to cause issues.
532
- (The problems of `async` are detailed in https://github.com/getsentry/sentry-ruby/issues/1522)
533
-
534
- Therefore, we encourage you to remove it and let the background worker take care of async job sending.
535
- It's deprecation is planned in the next major release (6.0), which is scheduled around the 3rd quarter of 2022.
536
- MSG
537
-
538
- @async = value
539
- end
540
-
541
612
  def breadcrumbs_logger=(logger)
542
613
  loggers =
543
614
  if logger.is_a?(Array)
@@ -563,12 +634,30 @@ module Sentry
563
634
  @before_send_transaction = value
564
635
  end
565
636
 
637
+ def before_send_check_in=(value)
638
+ check_callable!("before_send_check_in", value)
639
+
640
+ @before_send_check_in = value
641
+ end
642
+
643
+ def before_send_metric=(value)
644
+ check_callable!("before_send_metric", value)
645
+
646
+ @before_send_metric = value
647
+ end
648
+
566
649
  def before_breadcrumb=(value)
567
650
  check_callable!("before_breadcrumb", value)
568
651
 
569
652
  @before_breadcrumb = value
570
653
  end
571
654
 
655
+ def std_lib_logger_filter=(value)
656
+ check_callable!("std_lib_logger_filter", value)
657
+
658
+ @std_lib_logger_filter = value
659
+ end
660
+
572
661
  def environment=(environment)
573
662
  @environment = environment.to_s
574
663
  end
@@ -577,15 +666,12 @@ module Sentry
577
666
  @instrumenter = INSTRUMENTERS.include?(instrumenter) ? instrumenter : :sentry
578
667
  end
579
668
 
580
- def enable_tracing=(enable_tracing)
581
- unless enable_tracing.nil?
582
- log_warn <<~MSG
583
- `enable_tracing` is now deprecated in favor of `traces_sample_rate = 1.0`.
584
- MSG
669
+ def trace_ignore_status_codes=(codes)
670
+ unless codes.is_a?(Array) && codes.all? { |code| valid_status_code_entry?(code) }
671
+ raise ArgumentError, "trace_ignore_status_codes must be an Array of integers or ranges between (100-599) where begin <= end"
585
672
  end
586
673
 
587
- @enable_tracing = enable_tracing
588
- @traces_sample_rate ||= 1.0 if enable_tracing
674
+ @trace_ignore_status_codes = codes
589
675
  end
590
676
 
591
677
  def traces_sample_rate=(traces_sample_rate)
@@ -607,6 +693,16 @@ module Sentry
607
693
  @profiler_class = profiler_class
608
694
  end
609
695
 
696
+ def org_id=(value)
697
+ @org_id = value&.to_s
698
+ end
699
+
700
+ # Returns the effective org ID, preferring the explicit config option over the DSN-parsed value.
701
+ # @return [String, nil]
702
+ def effective_org_id
703
+ org_id || dsn&.org_id
704
+ end
705
+
610
706
  def sending_allowed?
611
707
  spotlight || sending_to_dsn_allowed?
612
708
  end
@@ -641,7 +737,7 @@ module Sentry
641
737
  end
642
738
 
643
739
  def enabled_in_current_env?
644
- enabled_environments.empty? || enabled_environments.include?(environment)
740
+ enabled_environments.nil? || enabled_environments.include?(environment)
645
741
  end
646
742
 
647
743
  def valid_sample_rate?(sample_rate)
@@ -652,7 +748,7 @@ module Sentry
652
748
  def tracing_enabled?
653
749
  valid_sampler = !!((valid_sample_rate?(@traces_sample_rate)) || @traces_sampler)
654
750
 
655
- (@enable_tracing != false) && valid_sampler && sending_allowed?
751
+ valid_sampler && sending_allowed?
656
752
  end
657
753
 
658
754
  def profiling_enabled?
@@ -773,8 +869,8 @@ module Sentry
773
869
  File.directory?("/etc/heroku") && !ENV["CI"]
774
870
  end
775
871
 
776
- def run_post_initialization_callbacks
777
- self.class.post_initialization_callbacks.each do |hook|
872
+ def run_callbacks(hook, event)
873
+ self.class.callbacks[event][hook].each do |hook|
778
874
  instance_eval(&hook)
779
875
  end
780
876
  end
@@ -783,5 +879,37 @@ module Sentry
783
879
  available_processor_count = Concurrent.available_processor_count if Concurrent.respond_to?(:available_processor_count)
784
880
  available_processor_count || Concurrent.processor_count
785
881
  end
882
+
883
+ def valid_http_status_code?(code)
884
+ code.is_a?(Integer) && code >= 100 && code <= 599
885
+ end
886
+
887
+ def valid_status_code_entry?(entry)
888
+ case entry
889
+ when Integer
890
+ valid_http_status_code?(entry)
891
+ when Range
892
+ valid_http_status_code?(entry.begin) &&
893
+ valid_http_status_code?(entry.end) &&
894
+ entry.begin <= entry.end
895
+ else
896
+ false
897
+ end
898
+ end
899
+ end
900
+
901
+ class StructuredLoggingConfiguration
902
+ # File path for DebugStructuredLogger to log events to
903
+ # @return [String, Pathname, nil]
904
+ attr_accessor :file_path
905
+
906
+ # The class to use as a structured logger.
907
+ # @return [Class]
908
+ attr_accessor :logger_class
909
+
910
+ def initialize
911
+ @file_path = nil
912
+ @logger_class = Sentry::StructuredLogger
913
+ end
786
914
  end
787
915
  end
@@ -14,12 +14,12 @@ module Sentry
14
14
  :in_progress,
15
15
  monitor_config: monitor_config)
16
16
 
17
- start = Metrics::Timing.duration_start
17
+ start = Process.clock_gettime(Process::CLOCK_MONOTONIC)
18
18
 
19
19
  begin
20
20
  # need to do this on ruby <= 2.6 sadly
21
21
  ret = method(:perform).super_method.arity == 0 ? super() : super
22
- duration = Metrics::Timing.duration_end(start)
22
+ duration = Process.clock_gettime(Process::CLOCK_MONOTONIC) - start
23
23
 
24
24
  Sentry.capture_check_in(slug,
25
25
  :ok,
@@ -29,7 +29,7 @@ module Sentry
29
29
 
30
30
  ret
31
31
  rescue Exception
32
- duration = Metrics::Timing.duration_end(start)
32
+ duration = Process.clock_gettime(Process::CLOCK_MONOTONIC) - start
33
33
 
34
34
  Sentry.capture_check_in(slug,
35
35
  :error,
@@ -40,9 +40,9 @@ module Sentry
40
40
  new(MonitorSchedule::Interval.new(num, unit), **options)
41
41
  end
42
42
 
43
- def to_hash
43
+ def to_h
44
44
  {
45
- schedule: schedule.to_hash,
45
+ schedule: schedule.to_h,
46
46
  checkin_margin: checkin_margin,
47
47
  max_runtime: max_runtime,
48
48
  timezone: timezone
@@ -12,7 +12,7 @@ module Sentry
12
12
  @value = value
13
13
  end
14
14
 
15
- def to_hash
15
+ def to_h
16
16
  { type: :crontab, value: value }
17
17
  end
18
18
  end
@@ -33,7 +33,7 @@ module Sentry
33
33
  @unit = unit
34
34
  end
35
35
 
36
- def to_hash
36
+ def to_h
37
37
  { type: :interval, value: value, unit: unit }
38
38
  end
39
39
  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