sentry-ruby-core 4.8.0 → 5.2.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 (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 = {}