sentry-ruby-core 4.8.0 → 5.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (54) hide show
  1. checksums.yaml +4 -4
  2. data/.yardopts +2 -0
  3. data/Gemfile +3 -0
  4. data/README.md +2 -0
  5. data/lib/sentry/background_worker.rb +33 -3
  6. data/lib/sentry/backtrace.rb +1 -3
  7. data/lib/sentry/breadcrumb/sentry_logger.rb +2 -0
  8. data/lib/sentry/breadcrumb.rb +24 -3
  9. data/lib/sentry/breadcrumb_buffer.rb +16 -0
  10. data/lib/sentry/client.rb +38 -2
  11. data/lib/sentry/configuration.rb +94 -41
  12. data/lib/sentry/core_ext/object/deep_dup.rb +2 -0
  13. data/lib/sentry/core_ext/object/duplicable.rb +1 -0
  14. data/lib/sentry/dsn.rb +2 -0
  15. data/lib/sentry/envelope.rb +45 -0
  16. data/lib/sentry/event.rb +55 -18
  17. data/lib/sentry/exceptions.rb +2 -0
  18. data/lib/sentry/hub.rb +38 -2
  19. data/lib/sentry/integrable.rb +2 -0
  20. data/lib/sentry/interface.rb +3 -10
  21. data/lib/sentry/interfaces/exception.rb +14 -3
  22. data/lib/sentry/interfaces/request.rb +37 -20
  23. data/lib/sentry/interfaces/single_exception.rb +2 -0
  24. data/lib/sentry/interfaces/stacktrace.rb +6 -0
  25. data/lib/sentry/interfaces/stacktrace_builder.rb +39 -10
  26. data/lib/sentry/interfaces/threads.rb +12 -2
  27. data/lib/sentry/linecache.rb +3 -0
  28. data/lib/sentry/net/http.rb +54 -65
  29. data/lib/sentry/rack/capture_exceptions.rb +28 -24
  30. data/lib/sentry/rack.rb +2 -0
  31. data/lib/sentry/rake.rb +16 -6
  32. data/lib/sentry/redis.rb +90 -0
  33. data/lib/sentry/release_detector.rb +3 -0
  34. data/lib/sentry/scope.rb +85 -6
  35. data/lib/sentry/session.rb +35 -0
  36. data/lib/sentry/session_flusher.rb +79 -0
  37. data/lib/sentry/span.rb +84 -8
  38. data/lib/sentry/transaction.rb +48 -14
  39. data/lib/sentry/transaction_event.rb +8 -0
  40. data/lib/sentry/transport/configuration.rb +3 -2
  41. data/lib/sentry/transport/dummy_transport.rb +8 -1
  42. data/lib/sentry/transport/http_transport.rb +55 -42
  43. data/lib/sentry/transport.rb +79 -37
  44. data/lib/sentry/utils/argument_checking_helper.rb +2 -0
  45. data/lib/sentry/utils/custom_inspection.rb +2 -0
  46. data/lib/sentry/utils/exception_cause_chain.rb +2 -0
  47. data/lib/sentry/utils/logging_helper.rb +6 -4
  48. data/lib/sentry/utils/real_ip.rb +2 -0
  49. data/lib/sentry/utils/request_id.rb +2 -0
  50. data/lib/sentry/version.rb +3 -1
  51. data/lib/sentry-ruby.rb +212 -41
  52. data/sentry-ruby-core.gemspec +0 -1
  53. data/sentry-ruby.gemspec +0 -1
  54. metadata +7 -16
@@ -0,0 +1,45 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Sentry
4
+ # @api private
5
+ class Envelope
6
+ class Item
7
+ attr_accessor :headers, :payload
8
+
9
+ def initialize(headers, payload)
10
+ @headers = headers
11
+ @payload = payload
12
+ end
13
+
14
+ def type
15
+ @headers[:type] || 'event'
16
+ end
17
+
18
+ def to_s
19
+ <<~ITEM
20
+ #{JSON.generate(@headers)}
21
+ #{JSON.generate(@payload)}
22
+ ITEM
23
+ end
24
+ end
25
+
26
+ attr_accessor :headers, :items
27
+
28
+ def initialize(headers = {})
29
+ @headers = headers
30
+ @items = []
31
+ end
32
+
33
+ def add_item(headers, payload)
34
+ @items << Item.new(headers, payload)
35
+ end
36
+
37
+ def item_types
38
+ @items.map(&:type)
39
+ end
40
+
41
+ def event_id
42
+ @headers[:event_id]
43
+ end
44
+ end
45
+ end
data/lib/sentry/event.rb CHANGED
@@ -10,31 +10,41 @@ require 'sentry/utils/custom_inspection'
10
10
 
11
11
  module Sentry
12
12
  class Event
13
+ # These are readable attributes.
13
14
  SERIALIZEABLE_ATTRIBUTES = %i(
14
15
  event_id level timestamp
15
16
  release environment server_name modules
16
17
  message user tags contexts extra
17
- fingerprint breadcrumbs backtrace transaction
18
+ fingerprint breadcrumbs transaction
18
19
  platform sdk type
19
20
  )
20
21
 
22
+ # These are writable attributes.
21
23
  WRITER_ATTRIBUTES = SERIALIZEABLE_ATTRIBUTES - %i(type timestamp level)
22
24
 
23
25
  MAX_MESSAGE_SIZE_IN_BYTES = 1024 * 8
26
+ MAX_SERIALIZED_PAYLOAD_SIZE = 1024 * 200
24
27
 
25
- SKIP_INSPECTION_ATTRIBUTES = [:@configuration, :@modules, :@backtrace]
28
+ SKIP_INSPECTION_ATTRIBUTES = [:@modules, :@stacktrace_builder, :@send_default_pii, :@trusted_proxies, :@rack_env_whitelist]
26
29
 
27
30
  include CustomInspection
28
31
 
29
32
  attr_writer(*WRITER_ATTRIBUTES)
30
33
  attr_reader(*SERIALIZEABLE_ATTRIBUTES)
31
34
 
32
- attr_reader :configuration, :request, :exception, :threads
35
+ # @return [RequestInterface]
36
+ attr_reader :request
33
37
 
34
- def initialize(configuration:, integration_meta: nil, message: nil)
35
- # this needs to go first because some setters rely on configuration
36
- @configuration = configuration
38
+ # @return [ExceptionInterface]
39
+ attr_reader :exception
40
+
41
+ # @return [ThreadsInterface]
42
+ attr_reader :threads
37
43
 
44
+ # @param configuration [Configuration]
45
+ # @param integration_meta [Hash, nil]
46
+ # @param message [String, nil]
47
+ def initialize(configuration:, integration_meta: nil, message: nil)
38
48
  # Set some simple default values
39
49
  @event_id = SecureRandom.uuid.delete("-")
40
50
  @timestamp = Sentry.utc_now.iso8601
@@ -48,17 +58,25 @@ module Sentry
48
58
 
49
59
  @fingerprint = []
50
60
 
61
+ # configuration data that's directly used by events
51
62
  @server_name = configuration.server_name
52
63
  @environment = configuration.environment
53
64
  @release = configuration.release
54
65
  @modules = configuration.gem_specs if configuration.send_modules
55
66
 
67
+ # configuration options to help events process data
68
+ @send_default_pii = configuration.send_default_pii
69
+ @trusted_proxies = configuration.trusted_proxies
70
+ @stacktrace_builder = configuration.stacktrace_builder
71
+ @rack_env_whitelist = configuration.rack_env_whitelist
72
+
56
73
  @message = (message || "").byteslice(0..MAX_MESSAGE_SIZE_IN_BYTES)
57
74
 
58
75
  self.level = :error
59
76
  end
60
77
 
61
78
  class << self
79
+ # @!visibility private
62
80
  def get_log_message(event_hash)
63
81
  message = event_hash[:message] || event_hash['message']
64
82
 
@@ -75,6 +93,7 @@ module Sentry
75
93
  '<no message value>'
76
94
  end
77
95
 
96
+ # @!visibility private
78
97
  def get_message_from_exception(event_hash)
79
98
  if exception = event_hash.dig(:exception, :values, 0)
80
99
  "#{exception[:type]}: #{exception[:value]}"
@@ -84,21 +103,35 @@ module Sentry
84
103
  end
85
104
  end
86
105
 
106
+ # @deprecated This method will be removed in v5.0.0. Please just use Sentry.configuration
107
+ # @return [Configuration]
108
+ def configuration
109
+ Sentry.configuration
110
+ end
111
+
112
+ # Sets the event's timestamp.
113
+ # @param time [Time, Float]
114
+ # @return [void]
87
115
  def timestamp=(time)
88
116
  @timestamp = time.is_a?(Time) ? time.to_f : time
89
117
  end
90
118
 
91
- def level=(new_level) # needed to meet the Sentry spec
92
- @level = new_level.to_s == "warn" ? :warning : new_level
119
+ # Sets the event's level.
120
+ # @param level [String, Symbol]
121
+ # @return [void]
122
+ def level=(level) # needed to meet the Sentry spec
123
+ @level = level.to_s == "warn" ? :warning : level
93
124
  end
94
125
 
126
+ # Sets the event's request environment data with RequestInterface.
127
+ # @see RequestInterface
128
+ # @param env [Hash]
129
+ # @return [void]
95
130
  def rack_env=(env)
96
131
  unless request || env.empty?
97
- env = env.dup
98
-
99
132
  add_request_interface(env)
100
133
 
101
- if configuration.send_default_pii
134
+ if @send_default_pii
102
135
  user[:ip_address] = calculate_real_ip_from_rack(env)
103
136
  end
104
137
 
@@ -108,6 +141,7 @@ module Sentry
108
141
  end
109
142
  end
110
143
 
144
+ # @return [Hash]
111
145
  def to_hash
112
146
  data = serialize_attributes
113
147
  data[:breadcrumbs] = breadcrumbs.to_hash if breadcrumbs
@@ -118,32 +152,35 @@ module Sentry
118
152
  data
119
153
  end
120
154
 
155
+ # @return [Hash]
121
156
  def to_json_compatible
122
157
  JSON.parse(JSON.generate(to_hash))
123
158
  end
124
159
 
125
- def add_request_interface(env)
126
- @request = Sentry::RequestInterface.build(env: env)
127
- end
128
-
160
+ # @!visibility private
129
161
  def add_threads_interface(backtrace: nil, **options)
130
162
  @threads = ThreadsInterface.build(
131
163
  backtrace: backtrace,
132
- stacktrace_builder: configuration.stacktrace_builder,
164
+ stacktrace_builder: @stacktrace_builder,
133
165
  **options
134
166
  )
135
167
  end
136
168
 
169
+ # @!visibility private
137
170
  def add_exception_interface(exception)
138
171
  if exception.respond_to?(:sentry_context)
139
172
  @extra.merge!(exception.sentry_context)
140
173
  end
141
174
 
142
- @exception = Sentry::ExceptionInterface.build(exception: exception, stacktrace_builder: configuration.stacktrace_builder)
175
+ @exception = Sentry::ExceptionInterface.build(exception: exception, stacktrace_builder: @stacktrace_builder)
143
176
  end
144
177
 
145
178
  private
146
179
 
180
+ def add_request_interface(env)
181
+ @request = Sentry::RequestInterface.new(env: env, send_default_pii: @send_default_pii, rack_env_whitelist: @rack_env_whitelist)
182
+ end
183
+
147
184
  def serialize_attributes
148
185
  self.class::SERIALIZEABLE_ATTRIBUTES.each_with_object({}) do |att, memo|
149
186
  if value = public_send(att)
@@ -160,7 +197,7 @@ module Sentry
160
197
  :client_ip => env["HTTP_CLIENT_IP"],
161
198
  :real_ip => env["HTTP_X_REAL_IP"],
162
199
  :forwarded_for => env["HTTP_X_FORWARDED_FOR"],
163
- :trusted_proxies => configuration.trusted_proxies
200
+ :trusted_proxies => @trusted_proxies
164
201
  ).calculate_ip
165
202
  end
166
203
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Sentry
2
4
  class Error < StandardError
3
5
  end
data/lib/sentry/hub.rb CHANGED
@@ -1,5 +1,8 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require "sentry/scope"
2
4
  require "sentry/client"
5
+ require "sentry/session"
3
6
 
4
7
  module Sentry
5
8
  class Hub
@@ -92,6 +95,8 @@ module Sentry
92
95
  def capture_exception(exception, **options, &block)
93
96
  check_argument_type!(exception, ::Exception)
94
97
 
98
+ return if Sentry.exception_captured?(exception)
99
+
95
100
  return unless current_client
96
101
 
97
102
  options[:hint] ||= {}
@@ -100,7 +105,12 @@ module Sentry
100
105
 
101
106
  return unless event
102
107
 
103
- capture_event(event, **options, &block)
108
+ current_scope.session&.update_from_exception(event.exception)
109
+
110
+ capture_event(event, **options, &block).tap do
111
+ # mark the exception as captured so we can use this information to avoid duplicated capturing
112
+ exception.instance_variable_set(Sentry::CAPTURED_SIGNATURE, true)
113
+ end
104
114
  end
105
115
 
106
116
  def capture_message(message, **options, &block)
@@ -112,6 +122,9 @@ module Sentry
112
122
  options[:hint][:message] = message
113
123
  backtrace = options.delete(:backtrace)
114
124
  event = current_client.event_from_message(message, options[:hint], backtrace: backtrace)
125
+
126
+ return unless event
127
+
115
128
  capture_event(event, **options, &block)
116
129
  end
117
130
 
@@ -133,7 +146,6 @@ module Sentry
133
146
 
134
147
  event = current_client.capture_event(event, scope, hint)
135
148
 
136
-
137
149
  if event && configuration.debug
138
150
  configuration.log_debug(event.to_json_compatible)
139
151
  end
@@ -165,6 +177,30 @@ module Sentry
165
177
  configuration.background_worker_threads = original_background_worker_threads
166
178
  end
167
179
 
180
+ def start_session
181
+ return unless current_scope
182
+ current_scope.set_session(Session.new)
183
+ end
184
+
185
+ def end_session
186
+ return unless current_scope
187
+ session = current_scope.session
188
+ current_scope.set_session(nil)
189
+
190
+ return unless session
191
+ session.close
192
+ Sentry.session_flusher.add_session(session)
193
+ end
194
+
195
+ def with_session_tracking(&block)
196
+ return yield unless configuration.auto_session_tracking
197
+
198
+ start_session
199
+ yield
200
+ ensure
201
+ end_session
202
+ end
203
+
168
204
  private
169
205
 
170
206
  def current_layer
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Sentry
2
4
  module Integrable
3
5
  def register_integration(name:, version:)
@@ -1,15 +1,8 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Sentry
2
4
  class Interface
3
- def self.inherited(klass)
4
- name = klass.name.split("::").last.downcase.gsub("interface", "")
5
- registered[name.to_sym] = klass
6
- super
7
- end
8
-
9
- def self.registered
10
- @@registered ||= {} # rubocop:disable Style/ClassVars
11
- end
12
-
5
+ # @return [Hash]
13
6
  def to_hash
14
7
  Hash[instance_variables.map { |name| [name[1..-1].to_sym, instance_variable_get(name)] }]
15
8
  end
@@ -1,15 +1,26 @@
1
+ # frozen_string_literal: true
2
+ require "set"
3
+
1
4
  module Sentry
2
5
  class ExceptionInterface < Interface
3
- def initialize(values:)
4
- @values = values
6
+ # @param exceptions [Array<SingleExceptionInterface>]
7
+ def initialize(exceptions:)
8
+ @values = exceptions
5
9
  end
6
10
 
11
+ # @return [Hash]
7
12
  def to_hash
8
13
  data = super
9
14
  data[:values] = data[:values].map(&:to_hash) if data[:values]
10
15
  data
11
16
  end
12
17
 
18
+ # Builds ExceptionInterface with given exception and stacktrace_builder.
19
+ # @param exception [Exception]
20
+ # @param stacktrace_builder [StacktraceBuilder]
21
+ # @see SingleExceptionInterface#build_with_stacktrace
22
+ # @see SingleExceptionInterface#initialize
23
+ # @return [ExceptionInterface]
13
24
  def self.build(exception:, stacktrace_builder:)
14
25
  exceptions = Sentry::Utils::ExceptionCauseChain.exception_to_array(exception).reverse
15
26
  processed_backtrace_ids = Set.new
@@ -23,7 +34,7 @@ module Sentry
23
34
  end
24
35
  end
25
36
 
26
- new(values: exceptions)
37
+ new(exceptions: exceptions)
27
38
  end
28
39
  end
29
40
  end
@@ -15,29 +15,45 @@ module Sentry
15
15
  # https://github.com/getsentry/sentry/blob/master/src/sentry/conf/server.py
16
16
  MAX_BODY_LIMIT = 4096 * 4
17
17
 
18
- attr_accessor :url, :method, :data, :query_string, :cookies, :headers, :env
18
+ # @return [String]
19
+ attr_accessor :url
19
20
 
20
- def self.build(env:)
21
- env = clean_env(env)
22
- request = ::Rack::Request.new(env)
23
- self.new(request: request)
24
- end
21
+ # @return [String]
22
+ attr_accessor :method
23
+
24
+ # @return [Hash]
25
+ attr_accessor :data
26
+
27
+ # @return [String]
28
+ attr_accessor :query_string
29
+
30
+ # @return [String]
31
+ attr_accessor :cookies
32
+
33
+ # @return [Hash]
34
+ attr_accessor :headers
25
35
 
26
- def self.clean_env(env)
27
- unless Sentry.configuration.send_default_pii
36
+ # @return [Hash]
37
+ attr_accessor :env
38
+
39
+ # @param env [Hash]
40
+ # @param send_default_pii [Boolean]
41
+ # @param rack_env_whitelist [Array]
42
+ # @see Configuration#send_default_pii
43
+ # @see Configuration#rack_env_whitelist
44
+ def initialize(env:, send_default_pii:, rack_env_whitelist:)
45
+ env = env.dup
46
+
47
+ unless send_default_pii
28
48
  # need to completely wipe out ip addresses
29
49
  RequestInterface::IP_HEADERS.each do |header|
30
50
  env.delete(header)
31
51
  end
32
52
  end
33
53
 
34
- env
35
- end
36
-
37
- def initialize(request:)
38
- env = request.env
54
+ request = ::Rack::Request.new(env)
39
55
 
40
- if Sentry.configuration.send_default_pii
56
+ if send_default_pii
41
57
  self.data = read_data_from(request)
42
58
  self.cookies = request.cookies
43
59
  self.query_string = request.query_string
@@ -46,8 +62,8 @@ module Sentry
46
62
  self.url = request.scheme && request.url.split('?').first
47
63
  self.method = request.request_method
48
64
 
49
- self.headers = filter_and_format_headers(env)
50
- self.env = filter_and_format_env(env)
65
+ self.headers = filter_and_format_headers(env, send_default_pii)
66
+ self.env = filter_and_format_env(env, rack_env_whitelist)
51
67
  end
52
68
 
53
69
  private
@@ -65,13 +81,14 @@ module Sentry
65
81
  e.message
66
82
  end
67
83
 
68
- def filter_and_format_headers(env)
84
+ def filter_and_format_headers(env, send_default_pii)
69
85
  env.each_with_object({}) do |(key, value), memo|
70
86
  begin
71
87
  key = key.to_s # rack env can contain symbols
72
88
  next memo['X-Request-Id'] ||= Utils::RequestId.read_from(env) if Utils::RequestId::REQUEST_ID_HEADERS.include?(key)
73
89
  next if is_server_protocol?(key, value, env["SERVER_PROTOCOL"])
74
90
  next if is_skippable_header?(key)
91
+ next if key == "HTTP_AUTHORIZATION" && !send_default_pii
75
92
 
76
93
  # Rack stores headers as HTTP_WHAT_EVER, we need What-Ever
77
94
  key = key.sub(/^HTTP_/, "")
@@ -116,11 +133,11 @@ module Sentry
116
133
  key == 'HTTP_VERSION' && value == protocol_version
117
134
  end
118
135
 
119
- def filter_and_format_env(env)
120
- return env if Sentry.configuration.rack_env_whitelist.empty?
136
+ def filter_and_format_env(env, rack_env_whitelist)
137
+ return env if rack_env_whitelist.empty?
121
138
 
122
139
  env.select do |k, _v|
123
- Sentry.configuration.rack_env_whitelist.include? k.to_s
140
+ rack_env_whitelist.include? k.to_s
124
141
  end
125
142
  end
126
143
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require "sentry/utils/exception_cause_chain"
2
4
 
3
5
  module Sentry
@@ -1,15 +1,21 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Sentry
2
4
  class StacktraceInterface
5
+ # @return [<Array[Frame]>]
3
6
  attr_reader :frames
4
7
 
8
+ # @param frames [<Array[Frame]>]
5
9
  def initialize(frames:)
6
10
  @frames = frames
7
11
  end
8
12
 
13
+ # @return [Hash]
9
14
  def to_hash
10
15
  { frames: @frames.map(&:to_hash) }
11
16
  end
12
17
 
18
+ # @return [String]
13
19
  def inspect
14
20
  @frames.map(&:to_s)
15
21
  end
@@ -1,7 +1,32 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Sentry
2
4
  class StacktraceBuilder
3
- attr_reader :project_root, :app_dirs_pattern, :linecache, :context_lines, :backtrace_cleanup_callback
5
+ # @return [String]
6
+ attr_reader :project_root
7
+
8
+ # @return [Regexp, nil]
9
+ attr_reader :app_dirs_pattern
10
+
11
+ # @return [LineCache]
12
+ attr_reader :linecache
13
+
14
+ # @return [Integer, nil]
15
+ attr_reader :context_lines
16
+
17
+ # @return [Proc, nil]
18
+ attr_reader :backtrace_cleanup_callback
4
19
 
20
+ # @param project_root [String]
21
+ # @param app_dirs_pattern [Regexp, nil]
22
+ # @param linecache [LineCache]
23
+ # @param context_lines [Integer, nil]
24
+ # @param backtrace_cleanup_callback [Proc, nil]
25
+ # @see Configuration#project_root
26
+ # @see Configuration#app_dirs_pattern
27
+ # @see Configuration#linecache
28
+ # @see Configuration#context_lines
29
+ # @see Configuration#backtrace_cleanup_callback
5
30
  def initialize(project_root:, app_dirs_pattern:, linecache:, context_lines:, backtrace_cleanup_callback: nil)
6
31
  @project_root = project_root
7
32
  @app_dirs_pattern = app_dirs_pattern
@@ -10,17 +35,21 @@ module Sentry
10
35
  @backtrace_cleanup_callback = backtrace_cleanup_callback
11
36
  end
12
37
 
13
- # you can pass a block to customize/exclude frames:
38
+ # Generates a StacktraceInterface with the given backtrace.
39
+ # You can pass a block to customize/exclude frames:
14
40
  #
15
- # ```ruby
16
- # builder.build(backtrace) do |frame|
17
- # if frame.module.match?(/a_gem/)
18
- # nil
19
- # else
20
- # frame
41
+ # @example
42
+ # builder.build(backtrace) do |frame|
43
+ # if frame.module.match?(/a_gem/)
44
+ # nil
45
+ # else
46
+ # frame
47
+ # end
21
48
  # end
22
- # end
23
- # ```
49
+ # @param backtrace [Array<String>]
50
+ # @param frame_callback [Proc]
51
+ # @yieldparam frame [StacktraceInterface::Frame]
52
+ # @return [StacktraceInterface]
24
53
  def build(backtrace:, &frame_callback)
25
54
  parsed_lines = parse_backtrace_lines(backtrace).select(&:file)
26
55
 
@@ -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 = {}