sentry-ruby 5.26.0 → 6.3.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 (67) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile +26 -4
  3. data/README.md +2 -2
  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/breadcrumb.rb +1 -1
  8. data/lib/sentry/breadcrumb_buffer.rb +2 -2
  9. data/lib/sentry/check_in_event.rb +2 -2
  10. data/lib/sentry/client.rb +59 -136
  11. data/lib/sentry/configuration.rb +168 -78
  12. data/lib/sentry/cron/monitor_check_ins.rb +3 -3
  13. data/lib/sentry/cron/monitor_config.rb +2 -2
  14. data/lib/sentry/cron/monitor_schedule.rb +2 -2
  15. data/lib/sentry/debug_structured_logger.rb +94 -0
  16. data/lib/sentry/dsn.rb +32 -0
  17. data/lib/sentry/envelope/item.rb +3 -3
  18. data/lib/sentry/error_event.rb +3 -3
  19. data/lib/sentry/event.rb +4 -10
  20. data/lib/sentry/graphql.rb +1 -1
  21. data/lib/sentry/hub.rb +29 -5
  22. data/lib/sentry/interface.rb +1 -1
  23. data/lib/sentry/interfaces/exception.rb +2 -2
  24. data/lib/sentry/interfaces/request.rb +2 -0
  25. data/lib/sentry/interfaces/single_exception.rb +4 -4
  26. data/lib/sentry/interfaces/stacktrace.rb +3 -3
  27. data/lib/sentry/interfaces/stacktrace_builder.rb +0 -8
  28. data/lib/sentry/interfaces/threads.rb +2 -2
  29. data/lib/sentry/log_event.rb +33 -138
  30. data/lib/sentry/log_event_buffer.rb +13 -60
  31. data/lib/sentry/metric_event.rb +49 -0
  32. data/lib/sentry/metric_event_buffer.rb +28 -0
  33. data/lib/sentry/metrics.rb +47 -42
  34. data/lib/sentry/profiler.rb +4 -5
  35. data/lib/sentry/propagation_context.rb +55 -18
  36. data/lib/sentry/rspec.rb +1 -1
  37. data/lib/sentry/scope.rb +32 -5
  38. data/lib/sentry/sequel.rb +35 -0
  39. data/lib/sentry/span.rb +2 -17
  40. data/lib/sentry/std_lib_logger.rb +10 -1
  41. data/lib/sentry/telemetry_event_buffer.rb +130 -0
  42. data/lib/sentry/test_helper.rb +30 -0
  43. data/lib/sentry/transaction.rb +72 -95
  44. data/lib/sentry/transaction_event.rb +4 -9
  45. data/lib/sentry/transport/debug_transport.rb +70 -0
  46. data/lib/sentry/transport/dummy_transport.rb +1 -0
  47. data/lib/sentry/transport/http_transport.rb +9 -5
  48. data/lib/sentry/transport.rb +3 -5
  49. data/lib/sentry/utils/encoding_helper.rb +6 -0
  50. data/lib/sentry/utils/logging_helper.rb +25 -9
  51. data/lib/sentry/utils/sample_rand.rb +97 -0
  52. data/lib/sentry/utils/telemetry_attributes.rb +30 -0
  53. data/lib/sentry/vernier/profiler.rb +4 -3
  54. data/lib/sentry/version.rb +1 -1
  55. data/lib/sentry-ruby.rb +25 -30
  56. data/sentry-ruby-core.gemspec +1 -1
  57. data/sentry-ruby.gemspec +1 -1
  58. metadata +17 -17
  59. data/lib/sentry/metrics/aggregator.rb +0 -248
  60. data/lib/sentry/metrics/configuration.rb +0 -47
  61. data/lib/sentry/metrics/counter_metric.rb +0 -25
  62. data/lib/sentry/metrics/distribution_metric.rb +0 -25
  63. data/lib/sentry/metrics/gauge_metric.rb +0 -35
  64. data/lib/sentry/metrics/local_aggregator.rb +0 -53
  65. data/lib/sentry/metrics/metric.rb +0 -19
  66. data/lib/sentry/metrics/set_metric.rb +0 -28
  67. data/lib/sentry/metrics/timing.rb +0 -51
@@ -9,11 +9,12 @@ require "sentry/dsn"
9
9
  require "sentry/release_detector"
10
10
  require "sentry/transport/configuration"
11
11
  require "sentry/cron/configuration"
12
- require "sentry/metrics/configuration"
13
12
  require "sentry/linecache"
14
13
  require "sentry/interfaces/stacktrace_builder"
15
14
  require "sentry/logger"
15
+ require "sentry/structured_logger"
16
16
  require "sentry/log_event_buffer"
17
+ require "sentry/metric_event_buffer"
17
18
 
18
19
  module Sentry
19
20
  class Configuration
@@ -30,13 +31,6 @@ module Sentry
30
31
  # @return [Regexp, nil]
31
32
  attr_accessor :app_dirs_pattern
32
33
 
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
34
  # to send events in a non-blocking way, sentry-ruby has its own background worker
41
35
  # by default, the worker holds a thread pool that has [the number of processors] threads
42
36
  # but you can configure it with this configuration option
@@ -74,11 +68,10 @@ module Sentry
74
68
  # @return [Proc]
75
69
  attr_reader :before_breadcrumb
76
70
 
77
- # Optional Proc, called before sending an event to the server
71
+ # Optional Proc, called before sending an error event to the server
78
72
  # @example
79
73
  # config.before_send = lambda do |event, hint|
80
74
  # # skip ZeroDivisionError exceptions
81
- # # note: hint[:exception] would be a String if you use async callback
82
75
  # if hint[:exception].is_a?(ZeroDivisionError)
83
76
  # nil
84
77
  # else
@@ -88,7 +81,7 @@ module Sentry
88
81
  # @return [Proc]
89
82
  attr_reader :before_send
90
83
 
91
- # Optional Proc, called before sending an event to the server
84
+ # Optional Proc, called before sending a transaction event to the server
92
85
  # @example
93
86
  # config.before_send_transaction = lambda do |event, hint|
94
87
  # # skip unimportant transactions or strip sensitive data
@@ -101,6 +94,18 @@ module Sentry
101
94
  # @return [Proc]
102
95
  attr_reader :before_send_transaction
103
96
 
97
+ # Optional Proc, called before sending a check-in event to the server
98
+ # @example
99
+ # config.before_send_check_in = lambda do |event, hint|
100
+ # if event.monitor_slug == "unimportant_job"
101
+ # nil
102
+ # else
103
+ # event
104
+ # end
105
+ # end
106
+ # @return [Proc]
107
+ attr_reader :before_send_check_in
108
+
104
109
  # Optional Proc, called before sending an event to the server
105
110
  # @example
106
111
  # config.before_send_log = lambda do |log|
@@ -117,7 +122,6 @@ module Sentry
117
122
  #
118
123
  # And if you also use sentry-rails:
119
124
  # - :active_support_logger
120
- # - :monotonic_active_support_logger
121
125
  #
122
126
  # @return [Array<Symbol>]
123
127
  attr_reader :breadcrumbs_logger
@@ -144,7 +148,7 @@ module Sentry
144
148
  attr_reader :dsn
145
149
 
146
150
  # Whitelist of enabled_environments that will send notifications to Sentry. Array of Strings.
147
- # @return [Array<String>]
151
+ # @return [Array<String>, nil]
148
152
  attr_accessor :enabled_environments
149
153
 
150
154
  # Logger 'progname's to exclude from breadcrumbs
@@ -173,18 +177,6 @@ module Sentry
173
177
  # @return [Boolean, String]
174
178
  attr_accessor :spotlight
175
179
 
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
180
  # You may provide your own LineCache for matching paths with source files.
189
181
  # This may be useful if you need to get source code from places other than the disk.
190
182
  # @see LineCache
@@ -196,17 +188,10 @@ module Sentry
196
188
  # @return [Logger]
197
189
  attr_accessor :sdk_logger
198
190
 
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
191
+ # File path for DebugTransport to log events to. If not set, defaults to a temporary file.
192
+ # This is useful for debugging and testing purposes.
193
+ # @return [String, nil]
194
+ attr_accessor :sdk_debug_transport_log_file
210
195
 
211
196
  # Project directory root for in_app detection. Could be Rails root, etc.
212
197
  # Set automatically for Rails.
@@ -267,10 +252,6 @@ module Sentry
267
252
  # @return [Cron::Configuration]
268
253
  attr_reader :cron
269
254
 
270
- # Metrics related configuration.
271
- # @return [Metrics::Configuration]
272
- attr_reader :metrics
273
-
274
255
  # Take a float between 0.0 and 1.0 as the sample rate for tracing events (transactions).
275
256
  # @return [Float, nil]
276
257
  attr_reader :traces_sample_rate
@@ -289,11 +270,9 @@ module Sentry
289
270
  # @return [Boolean]
290
271
  attr_accessor :enable_logs
291
272
 
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
273
+ # Structured logging configuration.
274
+ # @return [StructuredLoggingConfiguration]
275
+ attr_reader :structured_logging
297
276
 
298
277
  # Send diagnostic client reports about dropped events, true by default
299
278
  # tries to attach to an existing envelope max once every 30s
@@ -315,6 +294,18 @@ module Sentry
315
294
  # @return [Array<String, Regexp>]
316
295
  attr_accessor :trace_propagation_targets
317
296
 
297
+ # Collection of HTTP status codes or ranges of codes to ignore when tracing incoming requests.
298
+ # If a transaction's http.response.status_code matches one of these values,
299
+ # the transaction will be dropped and marked as not sampled.
300
+ # Defaults to TRACE_IGNORE_STATUS_CODES_DEFAULT.
301
+ #
302
+ # @example
303
+ # # ignore 404 and 502 <= status_code <= 511
304
+ # config.trace_ignore_status_codes = [404, (502..511)]
305
+ #
306
+ # @return [Array<Integer>, Array<Range>]
307
+ attr_reader :trace_ignore_status_codes
308
+
318
309
  # The instrumenter to use, :sentry or :otel
319
310
  # @return [Symbol]
320
311
  attr_reader :instrumenter
@@ -329,6 +320,15 @@ module Sentry
329
320
  # @return [Float, nil]
330
321
  attr_reader :profiles_sample_rate
331
322
 
323
+ # Interval in microseconds at which to take samples.
324
+ # The default is 1e6 / 101, or 101Hz.
325
+ # Note that the 101 is intentional to avoid lockstep sampling.
326
+ #
327
+ # @example
328
+ # config.profiles_sample_interval = 1e5 / 101
329
+ # @return [Float]
330
+ attr_accessor :profiles_sample_interval
331
+
332
332
  # Array of patches to apply.
333
333
  # Default is {DEFAULT_PATCHES}
334
334
  # @return [Array<Symbol>]
@@ -338,6 +338,32 @@ module Sentry
338
338
  # @return [Integer]
339
339
  attr_accessor :max_log_events
340
340
 
341
+ # Enable metrics collection, defaults to true
342
+ # @return [Boolean]
343
+ attr_accessor :enable_metrics
344
+
345
+ # Maximum number of metric events to buffer before sending
346
+ # @return [Integer]
347
+ attr_accessor :max_metric_events
348
+
349
+ # Optional Proc, called before sending a metric
350
+ # @example
351
+ # config.before_send_metric = lambda do |metric|
352
+ # # return nil to drop the metric
353
+ # metric
354
+ # end
355
+ # @return [Proc, nil]
356
+ attr_reader :before_send_metric
357
+
358
+ # Optional Proc, called to filter log messages before sending to Sentry
359
+ # @example
360
+ # config.std_lib_logger_filter = lambda do |logger, message, level|
361
+ # # Only send error and fatal logs to Sentry
362
+ # [:error, :fatal].include?(level)
363
+ # end
364
+ # @return [Proc, nil]
365
+ attr_reader :std_lib_logger_filter
366
+
341
367
  # these are not config options
342
368
  # @!visibility private
343
369
  attr_reader :errors, :gem_specs
@@ -366,6 +392,8 @@ module Sentry
366
392
  SERVER_PORT
367
393
  ].freeze
368
394
 
395
+ TRACE_IGNORE_STATUS_CODES_DEFAULT = [(301..303), (305..399), (401..404)]
396
+
369
397
  HEROKU_DYNO_METADATA_MESSAGE = "You are running on Heroku but haven't enabled Dyno Metadata. For Sentry's "\
370
398
  "release detection to work correctly, please run `heroku labs:enable runtime-dyno-metadata`"
371
399
 
@@ -381,6 +409,9 @@ module Sentry
381
409
 
382
410
  APP_DIRS_PATTERN = /(bin|exe|app|config|lib|test|spec)/
383
411
 
412
+ # 101 Hz in microseconds
413
+ DEFAULT_PROFILES_SAMPLE_INTERVAL = 1e6 / 101
414
+
384
415
  class << self
385
416
  # Post initialization callbacks are called at the end of initialization process
386
417
  # allowing extending the configuration of sentry-ruby by multiple extensions
@@ -390,7 +421,23 @@ module Sentry
390
421
 
391
422
  # allow extensions to add their hooks to the Configuration class
392
423
  def add_post_initialization_callback(&block)
393
- post_initialization_callbacks << block
424
+ callbacks[:initialize][:after] << block
425
+ end
426
+
427
+ def before(event, &block)
428
+ callbacks[event.to_sym][:before] << block
429
+ end
430
+
431
+ def after(event, &block)
432
+ callbacks[event.to_sym][:after] << block
433
+ end
434
+
435
+ # @!visibility private
436
+ def callbacks
437
+ @callbacks ||= {
438
+ initialize: { before: [], after: [] },
439
+ configured: { before: [], after: [] }
440
+ }
394
441
  end
395
442
 
396
443
  def validations
@@ -434,6 +481,8 @@ module Sentry
434
481
  validate :profiles_sample_rate, optional: true, type: :numeric
435
482
 
436
483
  def initialize
484
+ run_callbacks(:before, :initialize)
485
+
437
486
  self.app_dirs_pattern = APP_DIRS_PATTERN
438
487
  self.debug = Sentry::Utils::EnvHelper.env_to_bool(ENV["SENTRY_DEBUG"])
439
488
  self.background_worker_threads = (processor_count / 2.0).ceil
@@ -445,7 +494,7 @@ module Sentry
445
494
  self.context_lines = 3
446
495
  self.include_local_variables = false
447
496
  self.environment = environment_from_env
448
- self.enabled_environments = []
497
+ self.enabled_environments = nil
449
498
  self.exclude_loggers = []
450
499
  self.excluded_exceptions = IGNORE_DEFAULT + PUMA_IGNORE_DEFAULT
451
500
  self.inspect_exception_causes_for_exclusion = true
@@ -470,26 +519,36 @@ module Sentry
470
519
  self.server_name = server_name_from_env
471
520
  self.instrumenter = :sentry
472
521
  self.trace_propagation_targets = [PROPAGATION_TARGETS_MATCH_ALL]
522
+ self.trace_ignore_status_codes = TRACE_IGNORE_STATUS_CODES_DEFAULT
473
523
  self.enabled_patches = DEFAULT_PATCHES.dup
474
524
 
475
525
  self.before_send = nil
476
526
  self.before_send_transaction = nil
527
+ self.before_send_check_in = nil
477
528
  self.before_send_log = nil
529
+ self.before_send_metric = nil
530
+ self.std_lib_logger_filter = nil
478
531
  self.rack_env_whitelist = RACK_ENV_WHITELIST_DEFAULT
479
532
  self.traces_sampler = nil
480
- self.enable_tracing = nil
481
533
  self.enable_logs = false
534
+ self.enable_metrics = true
482
535
 
483
536
  self.profiler_class = Sentry::Profiler
537
+ self.profiles_sample_interval = DEFAULT_PROFILES_SAMPLE_INTERVAL
484
538
 
485
539
  @transport = Transport::Configuration.new
486
540
  @cron = Cron::Configuration.new
487
- @metrics = Metrics::Configuration.new
541
+ @structured_logging = StructuredLoggingConfiguration.new
488
542
  @gem_specs = Hash[Gem::Specification.map { |spec| [spec.name, spec.version.to_s] }] if Gem::Specification.respond_to?(:map)
489
543
 
490
- run_post_initialization_callbacks
491
-
492
544
  self.max_log_events = LogEventBuffer::DEFAULT_MAX_EVENTS
545
+ self.max_metric_events = MetricEventBuffer::DEFAULT_MAX_METRICS
546
+
547
+ run_callbacks(:after, :initialize)
548
+
549
+ yield(self) if block_given?
550
+
551
+ run_callbacks(:after, :configured)
493
552
  end
494
553
 
495
554
  def validate
@@ -522,22 +581,6 @@ module Sentry
522
581
  @release = value
523
582
  end
524
583
 
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
584
  def breadcrumbs_logger=(logger)
542
585
  loggers =
543
586
  if logger.is_a?(Array)
@@ -563,12 +606,30 @@ module Sentry
563
606
  @before_send_transaction = value
564
607
  end
565
608
 
609
+ def before_send_check_in=(value)
610
+ check_callable!("before_send_check_in", value)
611
+
612
+ @before_send_check_in = value
613
+ end
614
+
615
+ def before_send_metric=(value)
616
+ check_callable!("before_send_metric", value)
617
+
618
+ @before_send_metric = value
619
+ end
620
+
566
621
  def before_breadcrumb=(value)
567
622
  check_callable!("before_breadcrumb", value)
568
623
 
569
624
  @before_breadcrumb = value
570
625
  end
571
626
 
627
+ def std_lib_logger_filter=(value)
628
+ check_callable!("std_lib_logger_filter", value)
629
+
630
+ @std_lib_logger_filter = value
631
+ end
632
+
572
633
  def environment=(environment)
573
634
  @environment = environment.to_s
574
635
  end
@@ -577,15 +638,12 @@ module Sentry
577
638
  @instrumenter = INSTRUMENTERS.include?(instrumenter) ? instrumenter : :sentry
578
639
  end
579
640
 
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
641
+ def trace_ignore_status_codes=(codes)
642
+ unless codes.is_a?(Array) && codes.all? { |code| valid_status_code_entry?(code) }
643
+ raise ArgumentError, "trace_ignore_status_codes must be an Array of integers or ranges between (100-599) where begin <= end"
585
644
  end
586
645
 
587
- @enable_tracing = enable_tracing
588
- @traces_sample_rate ||= 1.0 if enable_tracing
646
+ @trace_ignore_status_codes = codes
589
647
  end
590
648
 
591
649
  def traces_sample_rate=(traces_sample_rate)
@@ -641,7 +699,7 @@ module Sentry
641
699
  end
642
700
 
643
701
  def enabled_in_current_env?
644
- enabled_environments.empty? || enabled_environments.include?(environment)
702
+ enabled_environments.nil? || enabled_environments.include?(environment)
645
703
  end
646
704
 
647
705
  def valid_sample_rate?(sample_rate)
@@ -652,7 +710,7 @@ module Sentry
652
710
  def tracing_enabled?
653
711
  valid_sampler = !!((valid_sample_rate?(@traces_sample_rate)) || @traces_sampler)
654
712
 
655
- (@enable_tracing != false) && valid_sampler && sending_allowed?
713
+ valid_sampler && sending_allowed?
656
714
  end
657
715
 
658
716
  def profiling_enabled?
@@ -773,8 +831,8 @@ module Sentry
773
831
  File.directory?("/etc/heroku") && !ENV["CI"]
774
832
  end
775
833
 
776
- def run_post_initialization_callbacks
777
- self.class.post_initialization_callbacks.each do |hook|
834
+ def run_callbacks(hook, event)
835
+ self.class.callbacks[event][hook].each do |hook|
778
836
  instance_eval(&hook)
779
837
  end
780
838
  end
@@ -783,5 +841,37 @@ module Sentry
783
841
  available_processor_count = Concurrent.available_processor_count if Concurrent.respond_to?(:available_processor_count)
784
842
  available_processor_count || Concurrent.processor_count
785
843
  end
844
+
845
+ def valid_http_status_code?(code)
846
+ code.is_a?(Integer) && code >= 100 && code <= 599
847
+ end
848
+
849
+ def valid_status_code_entry?(entry)
850
+ case entry
851
+ when Integer
852
+ valid_http_status_code?(entry)
853
+ when Range
854
+ valid_http_status_code?(entry.begin) &&
855
+ valid_http_status_code?(entry.end) &&
856
+ entry.begin <= entry.end
857
+ else
858
+ false
859
+ end
860
+ end
861
+ end
862
+
863
+ class StructuredLoggingConfiguration
864
+ # File path for DebugStructuredLogger to log events to
865
+ # @return [String, Pathname, nil]
866
+ attr_accessor :file_path
867
+
868
+ # The class to use as a structured logger.
869
+ # @return [Class]
870
+ attr_accessor :logger_class
871
+
872
+ def initialize
873
+ @file_path = nil
874
+ @logger_class = Sentry::StructuredLogger
875
+ end
786
876
  end
787
877
  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
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
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
 
@@ -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
@@ -3,7 +3,7 @@
3
3
  module Sentry
4
4
  # @api private
5
5
  class Envelope::Item
6
- STACKTRACE_FRAME_LIMIT_ON_OVERSIZED_PAYLOAD = 500
6
+ STACKTRACE_FRAME_LIMIT_ON_OVERSIZED_PAYLOAD = 1000
7
7
  MAX_SERIALIZED_PAYLOAD_SIZE = 1024 * 1000
8
8
 
9
9
  SIZE_LIMITS = Hash.new(MAX_SERIALIZED_PAYLOAD_SIZE).update(
@@ -15,10 +15,10 @@ module Sentry
15
15
  # rate limits and client reports use the data_category rather than envelope item type
16
16
  def self.data_category(type)
17
17
  case type
18
- when "session", "attachment", "transaction", "profile", "span", "log" then type
18
+ when "session", "attachment", "transaction", "profile", "span", "trace_metric" then type
19
+ when "log" then "log_item"
19
20
  when "sessions" then "session"
20
21
  when "check_in" then "monitor"
21
- when "statsd", "metric_meta" then "metric_bucket"
22
22
  when "event" then "error"
23
23
  when "client_report" then "internal"
24
24
  else "default"
@@ -10,10 +10,10 @@ module Sentry
10
10
  attr_reader :threads
11
11
 
12
12
  # @return [Hash]
13
- def to_hash
13
+ def to_h
14
14
  data = super
15
- data[:threads] = threads.to_hash if threads
16
- data[:exception] = exception.to_hash if exception
15
+ data[:threads] = threads.to_h if threads
16
+ data[:exception] = exception.to_h if exception
17
17
  data
18
18
  end
19
19