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
@@ -1,9 +1,13 @@
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
 
8
12
  # To explain how the entire thing works, we need to know how the original Net::HTTP#request works
9
13
  # Here's part of its definition. As you can see, it usually calls itself inside a #start block
@@ -20,90 +24,67 @@ module Sentry
20
24
  # end
21
25
  # ```
22
26
  #
23
- # So when the entire flow looks like this:
24
- #
25
- # 1. #request is called.
26
- # - But because the request hasn't started yet, it calls #start (which then calls #do_start)
27
- # - At this moment @sentry_span is still nil, so #set_sentry_trace_header returns early
28
- # 2. #do_start then creates a new Span and assigns it to @sentry_span
29
- # 3. #request is called for the second time.
30
- # - This time @sentry_span should present. So #set_sentry_trace_header will set the sentry-trace header on the request object
31
- # 4. Once the request finished, it
32
- # - Records a breadcrumb if http_logger is set
33
- # - Finishes the Span inside @sentry_span and clears the instance variable
34
- #
27
+ # So we're only instrumenting request when `Net::HTTP` is already started
35
28
  def request(req, body = nil, &block)
36
- set_sentry_trace_header(req)
29
+ return super unless started?
30
+
31
+ sentry_span = start_sentry_span
32
+ set_sentry_trace_header(req, sentry_span)
37
33
 
38
34
  super.tap do |res|
39
35
  record_sentry_breadcrumb(req, res)
40
- record_sentry_span(req, res)
41
- end
42
- end
43
-
44
- def do_start
45
- super.tap do
46
- start_sentry_span
47
- end
48
- end
49
-
50
- def do_finish
51
- super.tap do
52
- finish_sentry_span
36
+ record_sentry_span(req, res, sentry_span)
53
37
  end
54
38
  end
55
39
 
56
40
  private
57
41
 
58
- def set_sentry_trace_header(req)
59
- return unless @sentry_span
42
+ def set_sentry_trace_header(req, sentry_span)
43
+ return unless sentry_span
60
44
 
61
- trace = Sentry.get_current_client.generate_sentry_trace(@sentry_span)
45
+ trace = Sentry.get_current_client.generate_sentry_trace(sentry_span)
62
46
  req[SENTRY_TRACE_HEADER_NAME] = trace if trace
63
47
  end
64
48
 
65
49
  def record_sentry_breadcrumb(req, res)
66
- if Sentry.initialized? && Sentry.configuration.breadcrumbs_logger.include?(:http_logger)
67
- return if from_sentry_sdk?
68
-
69
- request_info = extract_request_info(req)
70
- crumb = Sentry::Breadcrumb.new(
71
- level: :info,
72
- category: OP_NAME,
73
- type: :info,
74
- data: {
75
- method: request_info[:method],
76
- url: request_info[:url],
77
- status: res.code.to_i
78
- }
79
- )
80
- Sentry.add_breadcrumb(crumb)
81
- 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)
82
65
  end
83
66
 
84
- def record_sentry_span(req, res)
85
- if Sentry.initialized? && @sentry_span
86
- request_info = extract_request_info(req)
87
- @sentry_span.set_description("#{request_info[:method]} #{request_info[:url]}")
88
- @sentry_span.set_data(:status, res.code.to_i)
89
- 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)
90
74
  end
91
75
 
92
76
  def start_sentry_span
93
- if Sentry.initialized? && transaction = Sentry.get_current_scope.get_transaction
94
- return if from_sentry_sdk?
95
- 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
96
80
 
97
- child_span = transaction.start_child(op: OP_NAME, start_timestamp: Sentry.utc_now.to_f)
98
- @sentry_span = child_span
99
- end
81
+ transaction.start_child(op: OP_NAME, start_timestamp: Sentry.utc_now.to_f)
100
82
  end
101
83
 
102
- def finish_sentry_span
103
- if Sentry.initialized? && @sentry_span
104
- @sentry_span.set_timestamp(Sentry.utc_now.to_f)
105
- @sentry_span = nil
106
- 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)
107
88
  end
108
89
 
109
90
  def from_sentry_sdk?
@@ -112,9 +93,17 @@ module Sentry
112
93
  end
113
94
 
114
95
  def extract_request_info(req)
115
- uri = req.uri
96
+ uri = req.uri || URI.parse("#{use_ssl? ? 'https' : 'http'}://#{address}#{req.path}")
116
97
  url = "#{uri.scheme}://#{uri.host}#{uri.path}" rescue uri.to_s
117
- { 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
118
107
  end
119
108
  end
120
109
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Sentry
2
4
  module Rack
3
5
  class CaptureExceptions
@@ -12,30 +14,32 @@ module Sentry
12
14
  Sentry.clone_hub_to_current_thread
13
15
 
14
16
  Sentry.with_scope do |scope|
15
- scope.clear_breadcrumbs
16
- scope.set_transaction_name(env["PATH_INFO"]) if env["PATH_INFO"]
17
- scope.set_rack_env(env)
18
-
19
- transaction = start_transaction(env, scope)
20
- scope.set_span(transaction) if transaction
21
-
22
- begin
23
- response = @app.call(env)
24
- rescue Sentry::Error
25
- finish_transaction(transaction, 500)
26
- raise # Don't capture Sentry errors
27
- rescue Exception => e
28
- capture_exception(e)
29
- finish_transaction(transaction, 500)
30
- raise
17
+ Sentry.with_session_tracking do
18
+ scope.clear_breadcrumbs
19
+ scope.set_transaction_name(env["PATH_INFO"]) if env["PATH_INFO"]
20
+ scope.set_rack_env(env)
21
+
22
+ transaction = start_transaction(env, scope)
23
+ scope.set_span(transaction) if transaction
24
+
25
+ begin
26
+ response = @app.call(env)
27
+ rescue Sentry::Error
28
+ finish_transaction(transaction, 500)
29
+ raise # Don't capture Sentry errors
30
+ rescue Exception => e
31
+ capture_exception(e)
32
+ finish_transaction(transaction, 500)
33
+ raise
34
+ end
35
+
36
+ exception = collect_exception(env)
37
+ capture_exception(exception) if exception
38
+
39
+ finish_transaction(transaction, response[0])
40
+
41
+ response
31
42
  end
32
-
33
- exception = collect_exception(env)
34
- capture_exception(exception) if exception
35
-
36
- finish_transaction(transaction, response[0])
37
-
38
- response
39
43
  end
40
44
  end
41
45
 
@@ -57,7 +61,7 @@ module Sentry
57
61
  sentry_trace = env["HTTP_SENTRY_TRACE"]
58
62
  options = { name: scope.transaction_name, op: transaction_op }
59
63
  transaction = Sentry::Transaction.from_sentry_trace(sentry_trace, **options) if sentry_trace
60
- Sentry.start_transaction(transaction: transaction, **options)
64
+ Sentry.start_transaction(transaction: transaction, custom_sampling_context: { env: env }, **options)
61
65
  end
62
66
 
63
67
 
data/lib/sentry/rack.rb CHANGED
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'rack'
2
4
 
3
5
  require 'sentry/rack/capture_exceptions'
data/lib/sentry/rake.rb CHANGED
@@ -1,11 +1,14 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require "rake"
2
4
  require "rake/task"
3
5
 
4
6
  module Sentry
5
7
  module Rake
6
8
  module Application
9
+ # @api private
7
10
  def display_error_message(ex)
8
- Sentry.capture_exception(ex, hint: { background: false }) do |scope|
11
+ Sentry.capture_exception(ex) do |scope|
9
12
  task_name = top_level_tasks.join(' ')
10
13
  scope.set_transaction_name(task_name)
11
14
  scope.set_tag("rake_task", task_name)
@@ -16,16 +19,23 @@ module Sentry
16
19
  end
17
20
 
18
21
  module Task
22
+ # @api private
19
23
  def execute(args=nil)
20
24
  return super unless Sentry.initialized? && Sentry.get_current_hub
21
25
 
22
- Sentry.get_current_hub.with_background_worker_disabled do
23
- super
24
- end
26
+ super
25
27
  end
26
28
  end
27
29
  end
28
30
  end
29
31
 
30
- Rake::Application.prepend(Sentry::Rake::Application)
31
- Rake::Task.prepend(Sentry::Rake::Task)
32
+ # @api private
33
+ module Rake
34
+ class Application
35
+ prepend(Sentry::Rake::Application)
36
+ end
37
+
38
+ class Task
39
+ prepend(Sentry::Rake::Task)
40
+ end
41
+ end
@@ -0,0 +1,90 @@
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, *arguments = statement
64
+
65
+ { command: command.to_s.upcase, key: key }.tap do |command_set|
66
+ command_set[:arguments] = arguments.join(" ") if Sentry.configuration.send_default_pii
67
+ end
68
+ end
69
+ end
70
+
71
+ def server_description
72
+ "#{host}:#{port}/#{db}"
73
+ end
74
+
75
+ module Client
76
+ def logging(commands, &block)
77
+ Sentry::Redis.new(commands, host, port, db).instrument do
78
+ super
79
+ end
80
+ end
81
+ end
82
+ end
83
+ end
84
+
85
+ if defined?(::Redis::Client)
86
+ Sentry.register_patch do
87
+ patch = Sentry::Redis::Client
88
+ Redis::Client.prepend(patch) unless Redis::Client.ancestors.include?(patch)
89
+ end
90
+ end
@@ -1,4 +1,7 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Sentry
4
+ # @api private
2
5
  class ReleaseDetector
3
6
  class << self
4
7
  def detect_release(project_root:, running_on_heroku:)
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
 
@@ -5,19 +7,26 @@ module Sentry
5
7
  class Scope
6
8
  include ArgumentCheckingHelper
7
9
 
8
- ATTRIBUTES = [:transaction_names, :contexts, :extra, :tags, :user, :level, :breadcrumbs, :fingerprint, :event_processors, :rack_env, :span]
10
+ ATTRIBUTES = [:transaction_names, :contexts, :extra, :tags, :user, :level, :breadcrumbs, :fingerprint, :event_processors, :rack_env, :span, :session]
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)
@@ -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
@@ -61,9 +76,13 @@ module Sentry
61
76
  copy.transaction_names = transaction_names.deep_dup
62
77
  copy.fingerprint = fingerprint.deep_dup
63
78
  copy.span = span.deep_dup
79
+ copy.session = session.deep_dup
64
80
  copy
65
81
  end
66
82
 
83
+ # Updates the scope's data from a given scope.
84
+ # @param scope [Scope]
85
+ # @return [void]
67
86
  def update_from_scope(scope)
68
87
  self.breadcrumbs = scope.breadcrumbs
69
88
  self.contexts = scope.contexts
@@ -75,6 +94,14 @@ module Sentry
75
94
  self.span = scope.span
76
95
  end
77
96
 
97
+ # Updates the scope's data from the given options.
98
+ # @param contexts [Hash]
99
+ # @param extras [Hash]
100
+ # @param tags [Hash]
101
+ # @param user [Hash]
102
+ # @param level [String, Symbol]
103
+ # @param fingerprint [Array]
104
+ # @return [void]
78
105
  def update_from_options(
79
106
  contexts: nil,
80
107
  extra: nil,
@@ -91,75 +118,125 @@ module Sentry
91
118
  self.fingerprint = fingerprint if fingerprint
92
119
  end
93
120
 
121
+ # Sets the scope's rack_env attribute.
122
+ # @param env [Hash]
123
+ # @return [Hash]
94
124
  def set_rack_env(env)
95
125
  env = env || {}
96
126
  @rack_env = env
97
127
  end
98
128
 
129
+ # Sets the scope's span attribute.
130
+ # @param span [Span]
131
+ # @return [Span]
99
132
  def set_span(span)
100
133
  check_argument_type!(span, Span)
101
134
  @span = span
102
135
  end
103
136
 
137
+ # @!macro set_user
104
138
  def set_user(user_hash)
105
139
  check_argument_type!(user_hash, Hash)
106
140
  @user = user_hash
107
141
  end
108
142
 
143
+ # @!macro set_extras
109
144
  def set_extras(extras_hash)
110
145
  check_argument_type!(extras_hash, Hash)
111
146
  @extra.merge!(extras_hash)
112
147
  end
113
148
 
149
+ # Adds a new key-value pair to current extras.
150
+ # @param key [String, Symbol]
151
+ # @param value [Object]
152
+ # @return [Hash]
114
153
  def set_extra(key, value)
115
- @extra.merge!(key => value)
154
+ set_extras(key => value)
116
155
  end
117
156
 
157
+ # @!macro set_tags
118
158
  def set_tags(tags_hash)
119
159
  check_argument_type!(tags_hash, Hash)
120
160
  @tags.merge!(tags_hash)
121
161
  end
122
162
 
163
+ # Adds a new key-value pair to current tags.
164
+ # @param key [String, Symbol]
165
+ # @param value [Object]
166
+ # @return [Hash]
123
167
  def set_tag(key, value)
124
- @tags.merge!(key => value)
168
+ set_tags(key => value)
125
169
  end
126
170
 
171
+ # Updates the scope's contexts attribute by merging with the old value.
172
+ # @param contexts [Hash]
173
+ # @return [Hash]
127
174
  def set_contexts(contexts_hash)
128
175
  check_argument_type!(contexts_hash, Hash)
129
- @contexts.merge!(contexts_hash)
176
+ @contexts.merge!(contexts_hash) do |key, old, new|
177
+ old.merge(new)
178
+ end
130
179
  end
131
180
 
181
+ # @!macro set_context
132
182
  def set_context(key, value)
133
183
  check_argument_type!(value, Hash)
134
- @contexts.merge!(key => value)
184
+ set_contexts(key => value)
135
185
  end
136
186
 
187
+ # Sets the scope's level attribute.
188
+ # @param level [String, Symbol]
189
+ # @return [void]
137
190
  def set_level(level)
138
191
  @level = level
139
192
  end
140
193
 
194
+ # Appends a new transaction name to the scope.
195
+ # The "transaction" here does not refer to `Transaction` objects.
196
+ # @param transaction_name [String]
197
+ # @return [void]
141
198
  def set_transaction_name(transaction_name)
142
199
  @transaction_names << transaction_name
143
200
  end
144
201
 
202
+ # Sets the currently active session on the scope.
203
+ # @param session [Session, nil]
204
+ # @return [void]
205
+ def set_session(session)
206
+ @session = session
207
+ end
208
+
209
+ # Returns current transaction name.
210
+ # The "transaction" here does not refer to `Transaction` objects.
211
+ # @return [String, nil]
145
212
  def transaction_name
146
213
  @transaction_names.last
147
214
  end
148
215
 
216
+ # Returns the associated Transaction object.
217
+ # @return [Transaction, nil]
149
218
  def get_transaction
150
219
  span.transaction if span
151
220
  end
152
221
 
222
+ # Returns the associated Span object.
223
+ # @return [Span, nil]
153
224
  def get_span
154
225
  span
155
226
  end
156
227
 
228
+ # Sets the scope's fingerprint attribute.
229
+ # @param fingerprint [Array]
230
+ # @return [Array]
157
231
  def set_fingerprint(fingerprint)
158
232
  check_argument_type!(fingerprint, Array)
159
233
 
160
234
  @fingerprint = fingerprint
161
235
  end
162
236
 
237
+ # Adds a new event processor [Proc] to the scope.
238
+ # @param block [Proc]
239
+ # @return [void]
163
240
  def add_event_processor(&block)
164
241
  @event_processors << block
165
242
  end
@@ -182,6 +259,7 @@ module Sentry
182
259
  @event_processors = []
183
260
  @rack_env = {}
184
261
  @span = nil
262
+ @session = nil
185
263
  set_new_breadcrumb_buffer
186
264
  end
187
265
 
@@ -189,8 +267,8 @@ module Sentry
189
267
  @breadcrumbs = BreadcrumbBuffer.new(@max_breadcrumbs)
190
268
  end
191
269
 
192
-
193
270
  class << self
271
+ # @return [Hash]
194
272
  def os_context
195
273
  @os_context ||=
196
274
  begin
@@ -204,6 +282,7 @@ module Sentry
204
282
  end
205
283
  end
206
284
 
285
+ # @return [Hash]
207
286
  def runtime_context
208
287
  @runtime_context ||= {
209
288
  name: RbConfig::CONFIG["ruby_install_name"],
@@ -0,0 +1,35 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Sentry
4
+ class Session
5
+ attr_reader :started, :status
6
+
7
+ # TODO-neel add :crashed after adding handled mechanism
8
+ STATUSES = %i(ok errored exited)
9
+ AGGREGATE_STATUSES = %i(errored exited)
10
+
11
+ def initialize
12
+ @started = Sentry.utc_now
13
+ @status = :ok
14
+ end
15
+
16
+ # TODO-neel add :crashed after adding handled mechanism
17
+ def update_from_exception(_exception = nil)
18
+ @status = :errored
19
+ end
20
+
21
+ def close
22
+ @status = :exited if @status == :ok
23
+ end
24
+
25
+ # truncate seconds from the timestamp since we only care about
26
+ # minute level granularity for aggregation
27
+ def aggregation_key
28
+ Time.utc(started.year, started.month, started.day, started.hour, started.min)
29
+ end
30
+
31
+ def deep_dup
32
+ dup
33
+ end
34
+ end
35
+ end