sentry-ruby-core 4.4.0 → 5.1.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (58) hide show
  1. checksums.yaml +4 -4
  2. data/.yardopts +2 -0
  3. data/CHANGELOG.md +12 -0
  4. data/Gemfile +9 -5
  5. data/LICENSE.txt +1 -1
  6. data/README.md +29 -175
  7. data/bin/console +5 -1
  8. data/lib/sentry/background_worker.rb +33 -3
  9. data/lib/sentry/backtrace.rb +1 -3
  10. data/lib/sentry/breadcrumb/sentry_logger.rb +3 -1
  11. data/lib/sentry/breadcrumb.rb +28 -2
  12. data/lib/sentry/breadcrumb_buffer.rb +16 -0
  13. data/lib/sentry/client.rb +66 -7
  14. data/lib/sentry/configuration.rb +156 -112
  15. data/lib/sentry/core_ext/object/deep_dup.rb +4 -0
  16. data/lib/sentry/core_ext/object/duplicable.rb +2 -0
  17. data/lib/sentry/dsn.rb +6 -1
  18. data/lib/sentry/envelope.rb +49 -0
  19. data/lib/sentry/event.rb +65 -23
  20. data/lib/sentry/exceptions.rb +2 -0
  21. data/lib/sentry/hub.rb +37 -6
  22. data/lib/sentry/integrable.rb +2 -0
  23. data/lib/sentry/interface.rb +3 -10
  24. data/lib/sentry/interfaces/exception.rb +13 -3
  25. data/lib/sentry/interfaces/request.rb +52 -21
  26. data/lib/sentry/interfaces/single_exception.rb +31 -0
  27. data/lib/sentry/interfaces/stacktrace.rb +14 -0
  28. data/lib/sentry/interfaces/stacktrace_builder.rb +39 -10
  29. data/lib/sentry/interfaces/threads.rb +12 -2
  30. data/lib/sentry/linecache.rb +3 -0
  31. data/lib/sentry/net/http.rb +79 -51
  32. data/lib/sentry/rack/capture_exceptions.rb +2 -0
  33. data/lib/sentry/rack.rb +2 -1
  34. data/lib/sentry/rake.rb +33 -9
  35. data/lib/sentry/redis.rb +88 -0
  36. data/lib/sentry/release_detector.rb +39 -0
  37. data/lib/sentry/scope.rb +76 -6
  38. data/lib/sentry/span.rb +84 -8
  39. data/lib/sentry/transaction.rb +50 -13
  40. data/lib/sentry/transaction_event.rb +19 -6
  41. data/lib/sentry/transport/configuration.rb +4 -2
  42. data/lib/sentry/transport/dummy_transport.rb +2 -0
  43. data/lib/sentry/transport/http_transport.rb +55 -42
  44. data/lib/sentry/transport.rb +101 -32
  45. data/lib/sentry/utils/argument_checking_helper.rb +2 -0
  46. data/lib/sentry/utils/custom_inspection.rb +14 -0
  47. data/lib/sentry/utils/exception_cause_chain.rb +10 -10
  48. data/lib/sentry/utils/logging_helper.rb +6 -4
  49. data/lib/sentry/utils/real_ip.rb +9 -1
  50. data/lib/sentry/utils/request_id.rb +2 -0
  51. data/lib/sentry/version.rb +3 -1
  52. data/lib/sentry-ruby.rb +247 -47
  53. data/sentry-ruby-core.gemspec +2 -3
  54. data/sentry-ruby.gemspec +2 -3
  55. metadata +10 -22
  56. data/.craft.yml +0 -29
  57. data/lib/sentry/benchmarks/benchmark_transport.rb +0 -14
  58. data/lib/sentry/rack/deprecations.rb +0 -19
@@ -1,5 +1,9 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Sentry
2
4
  class ThreadsInterface
5
+ # @param crashed [Boolean]
6
+ # @param stacktrace [Array]
3
7
  def initialize(crashed: false, stacktrace: nil)
4
8
  @id = Thread.current.object_id
5
9
  @name = Thread.current.name
@@ -8,6 +12,7 @@ module Sentry
8
12
  @stacktrace = stacktrace
9
13
  end
10
14
 
15
+ # @return [Hash]
11
16
  def to_hash
12
17
  {
13
18
  values: [
@@ -22,8 +27,13 @@ module Sentry
22
27
  }
23
28
  end
24
29
 
25
- # patch this method if you want to change a threads interface's stacktrace frames
26
- # also see `StacktraceBuilder.build`.
30
+ # Builds the ThreadsInterface with given backtrace and stacktrace_builder.
31
+ # Patch this method if you want to change a threads interface's stacktrace frames.
32
+ # @see StacktraceBuilder.build
33
+ # @param backtrace [Array]
34
+ # @param stacktrace_builder [StacktraceBuilder]
35
+ # @param crashed [Hash]
36
+ # @return [ThreadsInterface]
27
37
  def self.build(backtrace:, stacktrace_builder:, **options)
28
38
  stacktrace = stacktrace_builder.build(backtrace: backtrace) if backtrace
29
39
  new(**options, stacktrace: stacktrace)
@@ -1,4 +1,7 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Sentry
4
+ # @api private
2
5
  class LineCache
3
6
  def initialize
4
7
  @cache = {}
@@ -1,87 +1,115 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require "net/http"
2
4
 
3
5
  module Sentry
6
+ # @api private
4
7
  module Net
5
8
  module HTTP
6
- OP_NAME = "net.http"
9
+ OP_NAME = "http.client"
10
+ BREADCRUMB_CATEGORY = "net.http"
7
11
 
12
+ # To explain how the entire thing works, we need to know how the original Net::HTTP#request works
13
+ # Here's part of its definition. As you can see, it usually calls itself inside a #start block
14
+ #
15
+ # ```
16
+ # def request(req, body = nil, &block)
17
+ # unless started?
18
+ # start {
19
+ # req['connection'] ||= 'close'
20
+ # return request(req, body, &block) # <- request will be called for the second time from the first call
21
+ # }
22
+ # end
23
+ # # .....
24
+ # end
25
+ # ```
26
+ #
27
+ # So we're only instrumenting request when `Net::HTTP` is already started
8
28
  def request(req, body = nil, &block)
29
+ return super unless started?
30
+
31
+ sentry_span = start_sentry_span
32
+ set_sentry_trace_header(req, sentry_span)
33
+
9
34
  super.tap do |res|
10
35
  record_sentry_breadcrumb(req, res)
11
- record_sentry_span(req, res)
36
+ record_sentry_span(req, res, sentry_span)
12
37
  end
13
38
  end
14
39
 
15
- def do_start
16
- super.tap do
17
- start_sentry_span
18
- end
19
- end
40
+ private
20
41
 
21
- def do_finish
22
- super.tap do
23
- finish_sentry_span
24
- end
25
- end
42
+ def set_sentry_trace_header(req, sentry_span)
43
+ return unless sentry_span
26
44
 
27
- private
45
+ trace = Sentry.get_current_client.generate_sentry_trace(sentry_span)
46
+ req[SENTRY_TRACE_HEADER_NAME] = trace if trace
47
+ end
28
48
 
29
49
  def record_sentry_breadcrumb(req, res)
30
- if Sentry.initialized? && Sentry.configuration.breadcrumbs_logger.include?(:http_logger)
31
- return if from_sentry_sdk?
32
-
33
- request_info = extract_request_info(req)
34
- crumb = Sentry::Breadcrumb.new(
35
- level: :info,
36
- category: OP_NAME,
37
- type: :info,
38
- data: {
39
- method: request_info[:method],
40
- url: request_info[:url],
41
- status: res.code.to_i
42
- }
43
- )
44
- Sentry.add_breadcrumb(crumb)
45
- end
50
+ return unless Sentry.initialized? && Sentry.configuration.breadcrumbs_logger.include?(:http_logger)
51
+ return if from_sentry_sdk?
52
+
53
+ request_info = extract_request_info(req)
54
+
55
+ crumb = Sentry::Breadcrumb.new(
56
+ level: :info,
57
+ category: BREADCRUMB_CATEGORY,
58
+ type: :info,
59
+ data: {
60
+ status: res.code.to_i,
61
+ **request_info
62
+ }
63
+ )
64
+ Sentry.add_breadcrumb(crumb)
46
65
  end
47
66
 
48
- def record_sentry_span(req, res)
49
- if Sentry.initialized? && @sentry_span
50
- request_info = extract_request_info(req)
51
- @sentry_span.set_description("#{request_info[:method]} #{request_info[:url]}")
52
- @sentry_span.set_data(:status, res.code.to_i)
53
- end
67
+ def record_sentry_span(req, res, sentry_span)
68
+ return unless Sentry.initialized? && sentry_span
69
+
70
+ request_info = extract_request_info(req)
71
+ sentry_span.set_description("#{request_info[:method]} #{request_info[:url]}")
72
+ sentry_span.set_data(:status, res.code.to_i)
73
+ finish_sentry_span(sentry_span)
54
74
  end
55
75
 
56
76
  def start_sentry_span
57
- if Sentry.initialized? && transaction = Sentry.get_current_scope.get_transaction
58
- return if from_sentry_sdk?
59
- return if transaction.sampled == false
77
+ return unless Sentry.initialized? && transaction = Sentry.get_current_scope.get_transaction
78
+ return if from_sentry_sdk?
79
+ return if transaction.sampled == false
60
80
 
61
- child_span = transaction.start_child(op: OP_NAME, start_timestamp: Sentry.utc_now.to_f)
62
- @sentry_span = child_span
63
- end
81
+ transaction.start_child(op: OP_NAME, start_timestamp: Sentry.utc_now.to_f)
64
82
  end
65
83
 
66
- def finish_sentry_span
67
- if Sentry.initialized? && @sentry_span
68
- @sentry_span.set_timestamp(Sentry.utc_now.to_f)
69
- @sentry_span = nil
70
- end
84
+ def finish_sentry_span(sentry_span)
85
+ return unless Sentry.initialized? && sentry_span
86
+
87
+ sentry_span.set_timestamp(Sentry.utc_now.to_f)
71
88
  end
72
89
 
73
90
  def from_sentry_sdk?
74
- dsn_host = Sentry.configuration.dsn.host
75
- dsn_host == self.address
91
+ dsn = Sentry.configuration.dsn
92
+ dsn && dsn.host == self.address
76
93
  end
77
94
 
78
95
  def extract_request_info(req)
79
- uri = req.uri
96
+ uri = req.uri || URI.parse("#{use_ssl? ? 'https' : 'http'}://#{address}#{req.path}")
80
97
  url = "#{uri.scheme}://#{uri.host}#{uri.path}" rescue uri.to_s
81
- { method: req.method, url: url }
98
+
99
+ result = { method: req.method, url: url }
100
+
101
+ if Sentry.configuration.send_default_pii
102
+ result[:url] = result[:url] + "?#{uri.query}"
103
+ result[:body] = req.body
104
+ end
105
+
106
+ result
82
107
  end
83
108
  end
84
109
  end
85
110
  end
86
111
 
87
- Net::HTTP.send(:prepend, Sentry::Net::HTTP)
112
+ Sentry.register_patch do
113
+ patch = Sentry::Net::HTTP
114
+ Net::HTTP.send(:prepend, patch) unless Net::HTTP.ancestors.include?(patch)
115
+ end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Sentry
2
4
  module Rack
3
5
  class CaptureExceptions
data/lib/sentry/rack.rb CHANGED
@@ -1,4 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'rack'
2
4
 
3
5
  require 'sentry/rack/capture_exceptions'
4
- require 'sentry/rack/deprecations'
data/lib/sentry/rake.rb CHANGED
@@ -1,17 +1,41 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require "rake"
2
4
  require "rake/task"
3
5
 
6
+ module Sentry
7
+ module Rake
8
+ module Application
9
+ # @api private
10
+ def display_error_message(ex)
11
+ Sentry.capture_exception(ex) do |scope|
12
+ task_name = top_level_tasks.join(' ')
13
+ scope.set_transaction_name(task_name)
14
+ scope.set_tag("rake_task", task_name)
15
+ end if Sentry.initialized? && !Sentry.configuration.skip_rake_integration
16
+
17
+ super
18
+ end
19
+ end
20
+
21
+ module Task
22
+ # @api private
23
+ def execute(args=nil)
24
+ return super unless Sentry.initialized? && Sentry.get_current_hub
25
+
26
+ super
27
+ end
28
+ end
29
+ end
30
+ end
31
+
32
+ # @api private
4
33
  module Rake
5
34
  class Application
6
- alias orig_display_error_messsage display_error_message
7
- def display_error_message(ex)
8
- Sentry.capture_exception(ex, hint: { background: false }) do |scope|
9
- task_name = top_level_tasks.join(' ')
10
- scope.set_transaction_name(task_name)
11
- scope.set_tag("rake_task", task_name)
12
- end if Sentry.initialized?
35
+ prepend(Sentry::Rake::Application)
36
+ end
13
37
 
14
- orig_display_error_messsage(ex)
15
- end
38
+ class Task
39
+ prepend(Sentry::Rake::Task)
16
40
  end
17
41
  end
@@ -0,0 +1,88 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Sentry
4
+ # @api private
5
+ class Redis
6
+ OP_NAME ||= "db.redis.command"
7
+ LOGGER_NAME ||= :redis_logger
8
+
9
+ def initialize(commands, host, port, db)
10
+ @commands, @host, @port, @db = commands, host, port, db
11
+ end
12
+
13
+ def instrument
14
+ return yield unless Sentry.initialized?
15
+
16
+ record_span do
17
+ yield.tap do
18
+ record_breadcrumb
19
+ end
20
+ end
21
+ end
22
+
23
+ private
24
+
25
+ attr_reader :commands, :host, :port, :db
26
+
27
+ def record_span
28
+ return yield unless (transaction = Sentry.get_current_scope.get_transaction) && transaction.sampled
29
+
30
+ sentry_span = transaction.start_child(op: OP_NAME, start_timestamp: Sentry.utc_now.to_f)
31
+
32
+ yield.tap do
33
+ sentry_span.set_description(commands_description)
34
+ sentry_span.set_data(:server, server_description)
35
+ sentry_span.set_timestamp(Sentry.utc_now.to_f)
36
+ end
37
+ end
38
+
39
+ def record_breadcrumb
40
+ return unless Sentry.configuration.breadcrumbs_logger.include?(LOGGER_NAME)
41
+
42
+ Sentry.add_breadcrumb(
43
+ Sentry::Breadcrumb.new(
44
+ level: :info,
45
+ category: OP_NAME,
46
+ type: :info,
47
+ data: {
48
+ commands: parsed_commands,
49
+ server: server_description
50
+ }
51
+ )
52
+ )
53
+ end
54
+
55
+ def commands_description
56
+ parsed_commands.map do |statement|
57
+ statement.values.join(" ").strip
58
+ end.join(", ")
59
+ end
60
+
61
+ def parsed_commands
62
+ commands.map do |statement|
63
+ command, key, *_values = statement
64
+
65
+ { command: command.to_s.upcase, key: key }
66
+ end
67
+ end
68
+
69
+ def server_description
70
+ "#{host}:#{port}/#{db}"
71
+ end
72
+
73
+ module Client
74
+ def logging(commands, &block)
75
+ Sentry::Redis.new(commands, host, port, db).instrument do
76
+ super
77
+ end
78
+ end
79
+ end
80
+ end
81
+ end
82
+
83
+ if defined?(::Redis::Client)
84
+ Sentry.register_patch do
85
+ patch = Sentry::Redis::Client
86
+ Redis::Client.prepend(patch) unless Redis::Client.ancestors.include?(patch)
87
+ end
88
+ end
@@ -0,0 +1,39 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Sentry
4
+ # @api private
5
+ class ReleaseDetector
6
+ class << self
7
+ def detect_release(project_root:, running_on_heroku:)
8
+ detect_release_from_env ||
9
+ detect_release_from_git ||
10
+ detect_release_from_capistrano(project_root) ||
11
+ detect_release_from_heroku(running_on_heroku)
12
+ end
13
+
14
+ def detect_release_from_heroku(running_on_heroku)
15
+ return unless running_on_heroku
16
+ ENV['HEROKU_SLUG_COMMIT']
17
+ end
18
+
19
+ def detect_release_from_capistrano(project_root)
20
+ revision_file = File.join(project_root, 'REVISION')
21
+ revision_log = File.join(project_root, '..', 'revisions.log')
22
+
23
+ if File.exist?(revision_file)
24
+ File.read(revision_file).strip
25
+ elsif File.exist?(revision_log)
26
+ File.open(revision_log).to_a.last.strip.sub(/.*as release ([0-9]+).*/, '\1')
27
+ end
28
+ end
29
+
30
+ def detect_release_from_git
31
+ Sentry.sys_command("git rev-parse --short HEAD") if File.directory?(".git")
32
+ end
33
+
34
+ def detect_release_from_env
35
+ ENV['SENTRY_RELEASE']
36
+ end
37
+ end
38
+ end
39
+ end
data/lib/sentry/scope.rb CHANGED
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require "sentry/breadcrumb_buffer"
2
4
  require "etc"
3
5
 
@@ -9,20 +11,28 @@ module Sentry
9
11
 
10
12
  attr_reader(*ATTRIBUTES)
11
13
 
14
+ # @param max_breadcrumbs [Integer] the maximum number of breadcrumbs to be stored in the scope.
12
15
  def initialize(max_breadcrumbs: nil)
13
16
  @max_breadcrumbs = max_breadcrumbs
14
17
  set_default_value
15
18
  end
16
19
 
20
+ # Resets the scope's attributes to defaults.
21
+ # @return [void]
17
22
  def clear
18
23
  set_default_value
19
24
  end
20
25
 
26
+ # Applies stored attributes and event processors to the given event.
27
+ # @param event [Event]
28
+ # @param hint [Hash] the hint data that'll be passed to event processors.
29
+ # @return [Event]
21
30
  def apply_to_event(event, hint = nil)
22
31
  event.tags = tags.merge(event.tags)
23
32
  event.user = user.merge(event.user)
24
33
  event.extra = extra.merge(event.extra)
25
34
  event.contexts = contexts.merge(event.contexts)
35
+ event.transaction = transaction_name if transaction_name
26
36
 
27
37
  if span
28
38
  event.contexts[:trace] = span.get_trace_context
@@ -30,7 +40,6 @@ module Sentry
30
40
 
31
41
  event.fingerprint = fingerprint
32
42
  event.level = level
33
- event.transaction = transaction_names.last
34
43
  event.breadcrumbs = breadcrumbs
35
44
  event.rack_env = rack_env if rack_env
36
45
 
@@ -43,14 +52,20 @@ module Sentry
43
52
  event
44
53
  end
45
54
 
55
+ # Adds the breadcrumb to the scope's breadcrumbs buffer.
56
+ # @param breadcrumb [Breadcrumb]
57
+ # @return [void]
46
58
  def add_breadcrumb(breadcrumb)
47
59
  breadcrumbs.record(breadcrumb)
48
60
  end
49
61
 
62
+ # Clears the scope's breadcrumbs buffer
63
+ # @return [void]
50
64
  def clear_breadcrumbs
51
65
  set_new_breadcrumb_buffer
52
66
  end
53
67
 
68
+ # @return [Scope]
54
69
  def dup
55
70
  copy = super
56
71
  copy.breadcrumbs = breadcrumbs.dup
@@ -64,6 +79,9 @@ module Sentry
64
79
  copy
65
80
  end
66
81
 
82
+ # Updates the scope's data from a given scope.
83
+ # @param scope [Scope]
84
+ # @return [void]
67
85
  def update_from_scope(scope)
68
86
  self.breadcrumbs = scope.breadcrumbs
69
87
  self.contexts = scope.contexts
@@ -75,6 +93,14 @@ module Sentry
75
93
  self.span = scope.span
76
94
  end
77
95
 
96
+ # Updates the scope's data from the given options.
97
+ # @param contexts [Hash]
98
+ # @param extras [Hash]
99
+ # @param tags [Hash]
100
+ # @param user [Hash]
101
+ # @param level [String, Symbol]
102
+ # @param fingerprint [Array]
103
+ # @return [void]
78
104
  def update_from_options(
79
105
  contexts: nil,
80
106
  extra: nil,
@@ -91,75 +117,118 @@ module Sentry
91
117
  self.fingerprint = fingerprint if fingerprint
92
118
  end
93
119
 
120
+ # Sets the scope's rack_env attribute.
121
+ # @param env [Hash]
122
+ # @return [Hash]
94
123
  def set_rack_env(env)
95
124
  env = env || {}
96
125
  @rack_env = env
97
126
  end
98
127
 
128
+ # Sets the scope's span attribute.
129
+ # @param span [Span]
130
+ # @return [Span]
99
131
  def set_span(span)
100
132
  check_argument_type!(span, Span)
101
133
  @span = span
102
134
  end
103
135
 
136
+ # @!macro set_user
104
137
  def set_user(user_hash)
105
138
  check_argument_type!(user_hash, Hash)
106
139
  @user = user_hash
107
140
  end
108
141
 
142
+ # @!macro set_extras
109
143
  def set_extras(extras_hash)
110
144
  check_argument_type!(extras_hash, Hash)
111
145
  @extra.merge!(extras_hash)
112
146
  end
113
147
 
148
+ # Adds a new key-value pair to current extras.
149
+ # @param key [String, Symbol]
150
+ # @param value [Object]
151
+ # @return [Hash]
114
152
  def set_extra(key, value)
115
- @extra.merge!(key => value)
153
+ set_extras(key => value)
116
154
  end
117
155
 
156
+ # @!macro set_tags
118
157
  def set_tags(tags_hash)
119
158
  check_argument_type!(tags_hash, Hash)
120
159
  @tags.merge!(tags_hash)
121
160
  end
122
161
 
162
+ # Adds a new key-value pair to current tags.
163
+ # @param key [String, Symbol]
164
+ # @param value [Object]
165
+ # @return [Hash]
123
166
  def set_tag(key, value)
124
- @tags.merge!(key => value)
167
+ set_tags(key => value)
125
168
  end
126
169
 
170
+ # Updates the scope's contexts attribute by merging with the old value.
171
+ # @param contexts [Hash]
172
+ # @return [Hash]
127
173
  def set_contexts(contexts_hash)
128
174
  check_argument_type!(contexts_hash, Hash)
129
- @contexts.merge!(contexts_hash)
175
+ @contexts.merge!(contexts_hash) do |key, old, new|
176
+ old.merge(new)
177
+ end
130
178
  end
131
179
 
180
+ # @!macro set_context
132
181
  def set_context(key, value)
133
182
  check_argument_type!(value, Hash)
134
- @contexts.merge!(key => value)
183
+ set_contexts(key => value)
135
184
  end
136
185
 
186
+ # Sets the scope's level attribute.
187
+ # @param level [String, Symbol]
188
+ # @return [void]
137
189
  def set_level(level)
138
190
  @level = level
139
191
  end
140
192
 
193
+ # Appends a new transaction name to the scope.
194
+ # The "transaction" here does not refer to `Transaction` objects.
195
+ # @param transaction_name [String]
196
+ # @return [void]
141
197
  def set_transaction_name(transaction_name)
142
198
  @transaction_names << transaction_name
143
199
  end
144
200
 
201
+ # Returns current transaction name.
202
+ # The "transaction" here does not refer to `Transaction` objects.
203
+ # @return [String, nil]
145
204
  def transaction_name
146
205
  @transaction_names.last
147
206
  end
148
207
 
208
+ # Returns the associated Transaction object.
209
+ # @return [Transaction, nil]
149
210
  def get_transaction
150
211
  span.transaction if span
151
212
  end
152
213
 
214
+ # Returns the associated Span object.
215
+ # @return [Span, nil]
153
216
  def get_span
154
217
  span
155
218
  end
156
219
 
220
+ # Sets the scope's fingerprint attribute.
221
+ # @param fingerprint [Array]
222
+ # @return [Array]
157
223
  def set_fingerprint(fingerprint)
158
224
  check_argument_type!(fingerprint, Array)
159
225
 
160
226
  @fingerprint = fingerprint
161
227
  end
162
228
 
229
+ # Adds a new event processor [Proc] to the scope.
230
+ # @param block [Proc]
231
+ # @return [void]
163
232
  def add_event_processor(&block)
164
233
  @event_processors << block
165
234
  end
@@ -189,8 +258,8 @@ module Sentry
189
258
  @breadcrumbs = BreadcrumbBuffer.new(@max_breadcrumbs)
190
259
  end
191
260
 
192
-
193
261
  class << self
262
+ # @return [Hash]
194
263
  def os_context
195
264
  @os_context ||=
196
265
  begin
@@ -204,6 +273,7 @@ module Sentry
204
273
  end
205
274
  end
206
275
 
276
+ # @return [Hash]
207
277
  def runtime_context
208
278
  @runtime_context ||= {
209
279
  name: RbConfig::CONFIG["ruby_install_name"],