sentry-ruby 5.3.1 → 5.16.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (76) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +11 -0
  3. data/.rspec +2 -0
  4. data/.yardopts +2 -0
  5. data/CHANGELOG.md +313 -0
  6. data/Gemfile +26 -0
  7. data/Makefile +4 -0
  8. data/README.md +11 -8
  9. data/Rakefile +20 -0
  10. data/bin/console +18 -0
  11. data/bin/setup +8 -0
  12. data/lib/sentry/background_worker.rb +79 -0
  13. data/lib/sentry/backpressure_monitor.rb +75 -0
  14. data/lib/sentry/backtrace.rb +124 -0
  15. data/lib/sentry/baggage.rb +70 -0
  16. data/lib/sentry/breadcrumb/sentry_logger.rb +90 -0
  17. data/lib/sentry/breadcrumb.rb +76 -0
  18. data/lib/sentry/breadcrumb_buffer.rb +64 -0
  19. data/lib/sentry/check_in_event.rb +60 -0
  20. data/lib/sentry/client.rb +248 -0
  21. data/lib/sentry/configuration.rb +650 -0
  22. data/lib/sentry/core_ext/object/deep_dup.rb +61 -0
  23. data/lib/sentry/core_ext/object/duplicable.rb +155 -0
  24. data/lib/sentry/cron/configuration.rb +23 -0
  25. data/lib/sentry/cron/monitor_check_ins.rb +75 -0
  26. data/lib/sentry/cron/monitor_config.rb +53 -0
  27. data/lib/sentry/cron/monitor_schedule.rb +42 -0
  28. data/lib/sentry/dsn.rb +53 -0
  29. data/lib/sentry/envelope.rb +93 -0
  30. data/lib/sentry/error_event.rb +38 -0
  31. data/lib/sentry/event.rb +156 -0
  32. data/lib/sentry/exceptions.rb +9 -0
  33. data/lib/sentry/hub.rb +316 -0
  34. data/lib/sentry/integrable.rb +32 -0
  35. data/lib/sentry/interface.rb +16 -0
  36. data/lib/sentry/interfaces/exception.rb +43 -0
  37. data/lib/sentry/interfaces/request.rb +134 -0
  38. data/lib/sentry/interfaces/single_exception.rb +67 -0
  39. data/lib/sentry/interfaces/stacktrace.rb +87 -0
  40. data/lib/sentry/interfaces/stacktrace_builder.rb +79 -0
  41. data/lib/sentry/interfaces/threads.rb +42 -0
  42. data/lib/sentry/linecache.rb +47 -0
  43. data/lib/sentry/logger.rb +20 -0
  44. data/lib/sentry/net/http.rb +106 -0
  45. data/lib/sentry/profiler.rb +233 -0
  46. data/lib/sentry/propagation_context.rb +134 -0
  47. data/lib/sentry/puma.rb +32 -0
  48. data/lib/sentry/rack/capture_exceptions.rb +79 -0
  49. data/lib/sentry/rack.rb +5 -0
  50. data/lib/sentry/rake.rb +28 -0
  51. data/lib/sentry/redis.rb +108 -0
  52. data/lib/sentry/release_detector.rb +39 -0
  53. data/lib/sentry/scope.rb +360 -0
  54. data/lib/sentry/session.rb +33 -0
  55. data/lib/sentry/session_flusher.rb +90 -0
  56. data/lib/sentry/span.rb +273 -0
  57. data/lib/sentry/test_helper.rb +84 -0
  58. data/lib/sentry/transaction.rb +359 -0
  59. data/lib/sentry/transaction_event.rb +80 -0
  60. data/lib/sentry/transport/configuration.rb +98 -0
  61. data/lib/sentry/transport/dummy_transport.rb +21 -0
  62. data/lib/sentry/transport/http_transport.rb +206 -0
  63. data/lib/sentry/transport/spotlight_transport.rb +50 -0
  64. data/lib/sentry/transport.rb +225 -0
  65. data/lib/sentry/utils/argument_checking_helper.rb +19 -0
  66. data/lib/sentry/utils/custom_inspection.rb +14 -0
  67. data/lib/sentry/utils/encoding_helper.rb +22 -0
  68. data/lib/sentry/utils/exception_cause_chain.rb +20 -0
  69. data/lib/sentry/utils/logging_helper.rb +26 -0
  70. data/lib/sentry/utils/real_ip.rb +84 -0
  71. data/lib/sentry/utils/request_id.rb +18 -0
  72. data/lib/sentry/version.rb +5 -0
  73. data/lib/sentry-ruby.rb +580 -0
  74. data/sentry-ruby-core.gemspec +23 -0
  75. data/sentry-ruby.gemspec +24 -0
  76. metadata +75 -16
@@ -0,0 +1,155 @@
1
+ # frozen_string_literal: true
2
+
3
+ return if Object.method_defined?(:duplicable?)
4
+
5
+ #########################################
6
+ # This file was copied from Rails 5.2 #
7
+ #########################################
8
+
9
+ #--
10
+ # Most objects are cloneable, but not all. For example you can't dup methods:
11
+ #
12
+ # method(:puts).dup # => TypeError: allocator undefined for Method
13
+ #
14
+ # Classes may signal their instances are not duplicable removing +dup+/+clone+
15
+ # or raising exceptions from them. So, to dup an arbitrary object you normally
16
+ # use an optimistic approach and are ready to catch an exception, say:
17
+ #
18
+ # arbitrary_object.dup rescue object
19
+ #
20
+ # Rails dups objects in a few critical spots where they are not that arbitrary.
21
+ # That rescue is very expensive (like 40 times slower than a predicate), and it
22
+ # is often triggered.
23
+ #
24
+ # That's why we hardcode the following cases and check duplicable? instead of
25
+ # using that rescue idiom.
26
+ #++
27
+ class Object
28
+ # Can you safely dup this object?
29
+ #
30
+ # False for method objects;
31
+ # true otherwise.
32
+ def duplicable?
33
+ true
34
+ end
35
+ end
36
+
37
+ class NilClass
38
+ begin
39
+ nil.dup
40
+ rescue TypeError
41
+ # +nil+ is not duplicable:
42
+ #
43
+ # nil.duplicable? # => false
44
+ # nil.dup # => TypeError: can't dup NilClass
45
+ def duplicable?
46
+ false
47
+ end
48
+ end
49
+ end
50
+
51
+ class FalseClass
52
+ begin
53
+ false.dup
54
+ rescue TypeError
55
+ # +false+ is not duplicable:
56
+ #
57
+ # false.duplicable? # => false
58
+ # false.dup # => TypeError: can't dup FalseClass
59
+ def duplicable?
60
+ false
61
+ end
62
+ end
63
+ end
64
+
65
+ class TrueClass
66
+ begin
67
+ true.dup
68
+ rescue TypeError
69
+ # +true+ is not duplicable:
70
+ #
71
+ # true.duplicable? # => false
72
+ # true.dup # => TypeError: can't dup TrueClass
73
+ def duplicable?
74
+ false
75
+ end
76
+ end
77
+ end
78
+
79
+ class Symbol
80
+ begin
81
+ :symbol.dup # Ruby 2.4.x.
82
+ "symbol_from_string".to_sym.dup # Some symbols can't `dup` in Ruby 2.4.0.
83
+ rescue TypeError
84
+ # Symbols are not duplicable:
85
+ #
86
+ # :my_symbol.duplicable? # => false
87
+ # :my_symbol.dup # => TypeError: can't dup Symbol
88
+ def duplicable?
89
+ false
90
+ end
91
+ end
92
+ end
93
+
94
+ class Numeric
95
+ begin
96
+ 1.dup
97
+ rescue TypeError
98
+ # Numbers are not duplicable:
99
+ #
100
+ # 3.duplicable? # => false
101
+ # 3.dup # => TypeError: can't dup Integer
102
+ def duplicable?
103
+ false
104
+ end
105
+ end
106
+ end
107
+
108
+ require "bigdecimal"
109
+ class BigDecimal
110
+ # BigDecimals are duplicable:
111
+ #
112
+ # BigDecimal("1.2").duplicable? # => true
113
+ # BigDecimal("1.2").dup # => #<BigDecimal:...,'0.12E1',18(18)>
114
+ def duplicable?
115
+ true
116
+ end
117
+ end
118
+
119
+ class Method
120
+ # Methods are not duplicable:
121
+ #
122
+ # method(:puts).duplicable? # => false
123
+ # method(:puts).dup # => TypeError: allocator undefined for Method
124
+ def duplicable?
125
+ false
126
+ end
127
+ end
128
+
129
+ class Complex
130
+ begin
131
+ Complex(1).dup
132
+ rescue TypeError
133
+ # Complexes are not duplicable:
134
+ #
135
+ # Complex(1).duplicable? # => false
136
+ # Complex(1).dup # => TypeError: can't copy Complex
137
+ def duplicable?
138
+ false
139
+ end
140
+ end
141
+ end
142
+
143
+ class Rational
144
+ begin
145
+ Rational(1).dup
146
+ rescue TypeError
147
+ # Rationals are not duplicable:
148
+ #
149
+ # Rational(1).duplicable? # => false
150
+ # Rational(1).dup # => TypeError: can't copy Rational
151
+ def duplicable?
152
+ false
153
+ end
154
+ end
155
+ end
@@ -0,0 +1,23 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Sentry
4
+ module Cron
5
+ class Configuration
6
+ # Defaults set here will apply to all {Cron::MonitorConfig} objects unless overwritten.
7
+
8
+ # How long (in minutes) after the expected checkin time will we wait
9
+ # until we consider the checkin to have been missed.
10
+ # @return [Integer, nil]
11
+ attr_accessor :default_checkin_margin
12
+
13
+ # How long (in minutes) is the checkin allowed to run for in in_progress
14
+ # before it is considered failed.
15
+ # @return [Integer, nil]
16
+ attr_accessor :default_max_runtime
17
+
18
+ # tz database style timezone string
19
+ # @return [String, nil]
20
+ attr_accessor :default_timezone
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,75 @@
1
+ module Sentry
2
+ module Cron
3
+ module MonitorCheckIns
4
+ MAX_SLUG_LENGTH = 50
5
+
6
+ module Patch
7
+ def perform(*args, **opts)
8
+ slug = self.class.sentry_monitor_slug
9
+ monitor_config = self.class.sentry_monitor_config
10
+
11
+ check_in_id = Sentry.capture_check_in(slug,
12
+ :in_progress,
13
+ monitor_config: monitor_config)
14
+
15
+ start = Sentry.utc_now.to_i
16
+
17
+ begin
18
+ # need to do this on ruby <= 2.6 sadly
19
+ ret = method(:perform).super_method.arity == 0 ? super() : super
20
+ duration = Sentry.utc_now.to_i - start
21
+
22
+ Sentry.capture_check_in(slug,
23
+ :ok,
24
+ check_in_id: check_in_id,
25
+ duration: duration,
26
+ monitor_config: monitor_config)
27
+
28
+ ret
29
+ rescue Exception
30
+ duration = Sentry.utc_now.to_i - start
31
+
32
+ Sentry.capture_check_in(slug,
33
+ :error,
34
+ check_in_id: check_in_id,
35
+ duration: duration,
36
+ monitor_config: monitor_config)
37
+
38
+ raise
39
+ end
40
+ end
41
+ end
42
+
43
+ module ClassMethods
44
+ def sentry_monitor_check_ins(slug: nil, monitor_config: nil)
45
+ if monitor_config && Sentry.configuration
46
+ cron_config = Sentry.configuration.cron
47
+ monitor_config.checkin_margin ||= cron_config.default_checkin_margin
48
+ monitor_config.max_runtime ||= cron_config.default_max_runtime
49
+ monitor_config.timezone ||= cron_config.default_timezone
50
+ end
51
+
52
+ @sentry_monitor_slug = slug
53
+ @sentry_monitor_config = monitor_config
54
+
55
+ prepend Patch
56
+ end
57
+
58
+ def sentry_monitor_slug(name: self.name)
59
+ @sentry_monitor_slug ||= begin
60
+ slug = name.gsub('::', '-').downcase
61
+ slug[-MAX_SLUG_LENGTH..-1] || slug
62
+ end
63
+ end
64
+
65
+ def sentry_monitor_config
66
+ @sentry_monitor_config
67
+ end
68
+ end
69
+
70
+ def self.included(base)
71
+ base.extend(ClassMethods)
72
+ end
73
+ end
74
+ end
75
+ end
@@ -0,0 +1,53 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'sentry/cron/monitor_schedule'
4
+
5
+ module Sentry
6
+ module Cron
7
+ class MonitorConfig
8
+ # The monitor schedule configuration
9
+ # @return [MonitorSchedule::Crontab, MonitorSchedule::Interval]
10
+ attr_accessor :schedule
11
+
12
+ # How long (in minutes) after the expected checkin time will we wait
13
+ # until we consider the checkin to have been missed.
14
+ # @return [Integer, nil]
15
+ attr_accessor :checkin_margin
16
+
17
+ # How long (in minutes) is the checkin allowed to run for in in_progress
18
+ # before it is considered failed.
19
+ # @return [Integer, nil]
20
+ attr_accessor :max_runtime
21
+
22
+ # tz database style timezone string
23
+ # @return [String, nil]
24
+ attr_accessor :timezone
25
+
26
+ def initialize(schedule, checkin_margin: nil, max_runtime: nil, timezone: nil)
27
+ @schedule = schedule
28
+ @checkin_margin = checkin_margin
29
+ @max_runtime = max_runtime
30
+ @timezone = timezone
31
+ end
32
+
33
+ def self.from_crontab(crontab, **options)
34
+ new(MonitorSchedule::Crontab.new(crontab), **options)
35
+ end
36
+
37
+ def self.from_interval(num, unit, **options)
38
+ return nil unless MonitorSchedule::Interval::VALID_UNITS.include?(unit)
39
+
40
+ new(MonitorSchedule::Interval.new(num, unit), **options)
41
+ end
42
+
43
+ def to_hash
44
+ {
45
+ schedule: schedule.to_hash,
46
+ checkin_margin: checkin_margin,
47
+ max_runtime: max_runtime,
48
+ timezone: timezone
49
+ }.compact
50
+ end
51
+ end
52
+ end
53
+ end
@@ -0,0 +1,42 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Sentry
4
+ module Cron
5
+ module MonitorSchedule
6
+ class Crontab
7
+ # A crontab formatted string such as "0 * * * *".
8
+ # @return [String]
9
+ attr_accessor :value
10
+
11
+ def initialize(value)
12
+ @value = value
13
+ end
14
+
15
+ def to_hash
16
+ { type: :crontab, value: value }
17
+ end
18
+ end
19
+
20
+ class Interval
21
+ # The number representing duration of the interval.
22
+ # @return [Integer]
23
+ attr_accessor :value
24
+
25
+ # The unit representing duration of the interval.
26
+ # @return [Symbol]
27
+ attr_accessor :unit
28
+
29
+ VALID_UNITS = %i(year month week day hour minute)
30
+
31
+ def initialize(value, unit)
32
+ @value = value
33
+ @unit = unit
34
+ end
35
+
36
+ def to_hash
37
+ { type: :interval, value: value, unit: unit }
38
+ end
39
+ end
40
+ end
41
+ end
42
+ end
data/lib/sentry/dsn.rb ADDED
@@ -0,0 +1,53 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "uri"
4
+
5
+ module Sentry
6
+ class DSN
7
+ PORT_MAP = { 'http' => 80, 'https' => 443 }.freeze
8
+ REQUIRED_ATTRIBUTES = %w(host path public_key project_id).freeze
9
+
10
+ attr_reader :scheme, :secret_key, :port, *REQUIRED_ATTRIBUTES
11
+
12
+ def initialize(dsn_string)
13
+ @raw_value = dsn_string
14
+
15
+ uri = URI.parse(dsn_string)
16
+ uri_path = uri.path.split('/')
17
+
18
+ if uri.user
19
+ # DSN-style string
20
+ @project_id = uri_path.pop
21
+ @public_key = uri.user
22
+ @secret_key = !(uri.password.nil? || uri.password.empty?) ? uri.password : nil
23
+ end
24
+
25
+ @scheme = uri.scheme
26
+ @host = uri.host
27
+ @port = uri.port if uri.port
28
+ @path = uri_path.join('/')
29
+ end
30
+
31
+ def valid?
32
+ REQUIRED_ATTRIBUTES.all? { |k| public_send(k) }
33
+ end
34
+
35
+ def to_s
36
+ @raw_value
37
+ end
38
+
39
+ def server
40
+ server = "#{scheme}://#{host}"
41
+ server += ":#{port}" unless port == PORT_MAP[scheme]
42
+ server
43
+ end
44
+
45
+ def csp_report_uri
46
+ "#{server}/api/#{project_id}/security/?sentry_key=#{public_key}"
47
+ end
48
+
49
+ def envelope_endpoint
50
+ "#{path}/api/#{project_id}/envelope/"
51
+ end
52
+ end
53
+ end
@@ -0,0 +1,93 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Sentry
4
+ # @api private
5
+ class Envelope
6
+ class Item
7
+ STACKTRACE_FRAME_LIMIT_ON_OVERSIZED_PAYLOAD = 500
8
+ MAX_SERIALIZED_PAYLOAD_SIZE = 1024 * 1000
9
+
10
+ attr_accessor :headers, :payload
11
+
12
+ def initialize(headers, payload)
13
+ @headers = headers
14
+ @payload = payload
15
+ end
16
+
17
+ def type
18
+ @headers[:type] || 'event'
19
+ end
20
+
21
+ def to_s
22
+ [JSON.generate(@headers), JSON.generate(@payload)].join("\n")
23
+ end
24
+
25
+ def serialize
26
+ result = to_s
27
+
28
+ if result.bytesize > MAX_SERIALIZED_PAYLOAD_SIZE
29
+ remove_breadcrumbs!
30
+ result = to_s
31
+ end
32
+
33
+ if result.bytesize > MAX_SERIALIZED_PAYLOAD_SIZE
34
+ reduce_stacktrace!
35
+ result = to_s
36
+ end
37
+
38
+ [result, result.bytesize > MAX_SERIALIZED_PAYLOAD_SIZE]
39
+ end
40
+
41
+ def size_breakdown
42
+ payload.map do |key, value|
43
+ "#{key}: #{JSON.generate(value).bytesize}"
44
+ end.join(", ")
45
+ end
46
+
47
+ private
48
+
49
+ def remove_breadcrumbs!
50
+ if payload.key?(:breadcrumbs)
51
+ payload.delete(:breadcrumbs)
52
+ elsif payload.key?("breadcrumbs")
53
+ payload.delete("breadcrumbs")
54
+ end
55
+ end
56
+
57
+ def reduce_stacktrace!
58
+ if exceptions = payload.dig(:exception, :values) || payload.dig("exception", "values")
59
+ exceptions.each do |exception|
60
+ # in most cases there is only one exception (2 or 3 when have multiple causes), so we won't loop through this double condition much
61
+ traces = exception.dig(:stacktrace, :frames) || exception.dig("stacktrace", "frames")
62
+
63
+ if traces && traces.size > STACKTRACE_FRAME_LIMIT_ON_OVERSIZED_PAYLOAD
64
+ size_on_both_ends = STACKTRACE_FRAME_LIMIT_ON_OVERSIZED_PAYLOAD / 2
65
+ traces.replace(
66
+ traces[0..(size_on_both_ends - 1)] + traces[-size_on_both_ends..-1],
67
+ )
68
+ end
69
+ end
70
+ end
71
+ end
72
+ end
73
+
74
+ attr_accessor :headers, :items
75
+
76
+ def initialize(headers = {})
77
+ @headers = headers
78
+ @items = []
79
+ end
80
+
81
+ def add_item(headers, payload)
82
+ @items << Item.new(headers, payload)
83
+ end
84
+
85
+ def item_types
86
+ @items.map(&:type)
87
+ end
88
+
89
+ def event_id
90
+ @headers[:event_id]
91
+ end
92
+ end
93
+ end
@@ -0,0 +1,38 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Sentry
4
+ # ErrorEvent represents error or normal message events.
5
+ class ErrorEvent < Event
6
+ # @return [ExceptionInterface]
7
+ attr_reader :exception
8
+
9
+ # @return [ThreadsInterface]
10
+ attr_reader :threads
11
+
12
+ # @return [Hash]
13
+ def to_hash
14
+ data = super
15
+ data[:threads] = threads.to_hash if threads
16
+ data[:exception] = exception.to_hash if exception
17
+ data
18
+ end
19
+
20
+ # @!visibility private
21
+ def add_threads_interface(backtrace: nil, **options)
22
+ @threads = ThreadsInterface.build(
23
+ backtrace: backtrace,
24
+ stacktrace_builder: @stacktrace_builder,
25
+ **options
26
+ )
27
+ end
28
+
29
+ # @!visibility private
30
+ def add_exception_interface(exception)
31
+ if exception.respond_to?(:sentry_context)
32
+ @extra.merge!(exception.sentry_context)
33
+ end
34
+
35
+ @exception = Sentry::ExceptionInterface.build(exception: exception, stacktrace_builder: @stacktrace_builder)
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,156 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'socket'
4
+ require 'securerandom'
5
+ require 'sentry/interface'
6
+ require 'sentry/backtrace'
7
+ require 'sentry/utils/real_ip'
8
+ require 'sentry/utils/request_id'
9
+ require 'sentry/utils/custom_inspection'
10
+
11
+ module Sentry
12
+ # This is an abstract class that defines the shared attributes of an event.
13
+ # Please don't use it directly. The user-facing classes are its child classes.
14
+ class Event
15
+ TYPE = "event"
16
+ # These are readable attributes.
17
+ SERIALIZEABLE_ATTRIBUTES = %i(
18
+ event_id level timestamp
19
+ release environment server_name modules
20
+ message user tags contexts extra
21
+ fingerprint breadcrumbs transaction transaction_info
22
+ platform sdk type
23
+ )
24
+
25
+ # These are writable attributes.
26
+ WRITER_ATTRIBUTES = SERIALIZEABLE_ATTRIBUTES - %i(type timestamp level)
27
+
28
+ MAX_MESSAGE_SIZE_IN_BYTES = 1024 * 8
29
+
30
+ SKIP_INSPECTION_ATTRIBUTES = [:@modules, :@stacktrace_builder, :@send_default_pii, :@trusted_proxies, :@rack_env_whitelist]
31
+
32
+ include CustomInspection
33
+
34
+ attr_writer(*WRITER_ATTRIBUTES)
35
+ attr_reader(*SERIALIZEABLE_ATTRIBUTES)
36
+
37
+ # @return [RequestInterface]
38
+ attr_reader :request
39
+
40
+ # Dynamic Sampling Context (DSC) that gets attached
41
+ # as the trace envelope header in the transport.
42
+ # @return [Hash, nil]
43
+ attr_accessor :dynamic_sampling_context
44
+
45
+ # @param configuration [Configuration]
46
+ # @param integration_meta [Hash, nil]
47
+ # @param message [String, nil]
48
+ def initialize(configuration:, integration_meta: nil, message: nil)
49
+ # Set some simple default values
50
+ @event_id = SecureRandom.uuid.delete("-")
51
+ @timestamp = Sentry.utc_now.iso8601
52
+ @platform = :ruby
53
+ @type = self.class::TYPE
54
+ @sdk = integration_meta || Sentry.sdk_meta
55
+
56
+ @user = {}
57
+ @extra = {}
58
+ @contexts = {}
59
+ @tags = {}
60
+
61
+ @fingerprint = []
62
+ @dynamic_sampling_context = nil
63
+
64
+ # configuration data that's directly used by events
65
+ @server_name = configuration.server_name
66
+ @environment = configuration.environment
67
+ @release = configuration.release
68
+ @modules = configuration.gem_specs if configuration.send_modules
69
+
70
+ # configuration options to help events process data
71
+ @send_default_pii = configuration.send_default_pii
72
+ @trusted_proxies = configuration.trusted_proxies
73
+ @stacktrace_builder = configuration.stacktrace_builder
74
+ @rack_env_whitelist = configuration.rack_env_whitelist
75
+
76
+ @message = (message || "").byteslice(0..MAX_MESSAGE_SIZE_IN_BYTES)
77
+ end
78
+
79
+ # @deprecated This method will be removed in v5.0.0. Please just use Sentry.configuration
80
+ # @return [Configuration]
81
+ def configuration
82
+ Sentry.configuration
83
+ end
84
+
85
+ # Sets the event's timestamp.
86
+ # @param time [Time, Float]
87
+ # @return [void]
88
+ def timestamp=(time)
89
+ @timestamp = time.is_a?(Time) ? time.to_f : time
90
+ end
91
+
92
+ # Sets the event's level.
93
+ # @param level [String, Symbol]
94
+ # @return [void]
95
+ def level=(level) # needed to meet the Sentry spec
96
+ @level = level.to_s == "warn" ? :warning : level
97
+ end
98
+
99
+ # Sets the event's request environment data with RequestInterface.
100
+ # @see RequestInterface
101
+ # @param env [Hash]
102
+ # @return [void]
103
+ def rack_env=(env)
104
+ unless request || env.empty?
105
+ add_request_interface(env)
106
+
107
+ if @send_default_pii
108
+ user[:ip_address] = calculate_real_ip_from_rack(env)
109
+ end
110
+
111
+ if request_id = Utils::RequestId.read_from(env)
112
+ tags[:request_id] = request_id
113
+ end
114
+ end
115
+ end
116
+
117
+ # @return [Hash]
118
+ def to_hash
119
+ data = serialize_attributes
120
+ data[:breadcrumbs] = breadcrumbs.to_hash if breadcrumbs
121
+ data[:request] = request.to_hash if request
122
+ data
123
+ end
124
+
125
+ # @return [Hash]
126
+ def to_json_compatible
127
+ JSON.parse(JSON.generate(to_hash))
128
+ end
129
+
130
+ private
131
+
132
+ def add_request_interface(env)
133
+ @request = Sentry::RequestInterface.new(env: env, send_default_pii: @send_default_pii, rack_env_whitelist: @rack_env_whitelist)
134
+ end
135
+
136
+ def serialize_attributes
137
+ self.class::SERIALIZEABLE_ATTRIBUTES.each_with_object({}) do |att, memo|
138
+ if value = public_send(att)
139
+ memo[att] = value
140
+ end
141
+ end
142
+ end
143
+
144
+ # When behind a proxy (or if the user is using a proxy), we can't use
145
+ # REMOTE_ADDR to determine the Event IP, and must use other headers instead.
146
+ def calculate_real_ip_from_rack(env)
147
+ Utils::RealIp.new(
148
+ :remote_addr => env["REMOTE_ADDR"],
149
+ :client_ip => env["HTTP_CLIENT_IP"],
150
+ :real_ip => env["HTTP_X_REAL_IP"],
151
+ :forwarded_for => env["HTTP_X_FORWARDED_FOR"],
152
+ :trusted_proxies => @trusted_proxies
153
+ ).calculate_ip
154
+ end
155
+ end
156
+ end
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Sentry
4
+ class Error < StandardError
5
+ end
6
+
7
+ class ExternalError < Error
8
+ end
9
+ end