sentry-ruby 5.3.1 → 5.16.1

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 (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,79 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Sentry
4
+ module Rack
5
+ class CaptureExceptions
6
+ ERROR_EVENT_ID_KEY = "sentry.error_event_id"
7
+
8
+ def initialize(app)
9
+ @app = app
10
+ end
11
+
12
+ def call(env)
13
+ return @app.call(env) unless Sentry.initialized?
14
+
15
+ # make sure the current thread has a clean hub
16
+ Sentry.clone_hub_to_current_thread
17
+
18
+ Sentry.with_scope do |scope|
19
+ Sentry.with_session_tracking do
20
+ scope.clear_breadcrumbs
21
+ scope.set_transaction_name(env["PATH_INFO"], source: :url) if env["PATH_INFO"]
22
+ scope.set_rack_env(env)
23
+
24
+ transaction = start_transaction(env, scope)
25
+ scope.set_span(transaction) if transaction
26
+
27
+ begin
28
+ response = @app.call(env)
29
+ rescue Sentry::Error
30
+ finish_transaction(transaction, 500)
31
+ raise # Don't capture Sentry errors
32
+ rescue Exception => e
33
+ capture_exception(e, env)
34
+ finish_transaction(transaction, 500)
35
+ raise
36
+ end
37
+
38
+ exception = collect_exception(env)
39
+ capture_exception(exception, env) if exception
40
+
41
+ finish_transaction(transaction, response[0])
42
+
43
+ response
44
+ end
45
+ end
46
+ end
47
+
48
+ private
49
+
50
+ def collect_exception(env)
51
+ env['rack.exception'] || env['sinatra.error']
52
+ end
53
+
54
+ def transaction_op
55
+ "http.server".freeze
56
+ end
57
+
58
+ def capture_exception(exception, env)
59
+ Sentry.capture_exception(exception).tap do |event|
60
+ env[ERROR_EVENT_ID_KEY] = event.event_id if event
61
+ end
62
+ end
63
+
64
+ def start_transaction(env, scope)
65
+ options = { name: scope.transaction_name, source: scope.transaction_source, op: transaction_op }
66
+ transaction = Sentry.continue_trace(env, **options)
67
+ Sentry.start_transaction(transaction: transaction, custom_sampling_context: { env: env }, **options)
68
+ end
69
+
70
+
71
+ def finish_transaction(transaction, status_code)
72
+ return unless transaction
73
+
74
+ transaction.set_http_status(status_code)
75
+ transaction.finish
76
+ end
77
+ end
78
+ end
79
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'rack'
4
+
5
+ require 'sentry/rack/capture_exceptions'
@@ -0,0 +1,28 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "rake"
4
+ require "rake/task"
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, source: :task)
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
+ end
21
+ end
22
+
23
+ # @api private
24
+ module Rake
25
+ class Application
26
+ prepend(Sentry::Rake::Application)
27
+ end
28
+ end
@@ -0,0 +1,108 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Sentry
4
+ # @api private
5
+ class Redis
6
+ OP_NAME = "db.redis"
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
+ Sentry.with_child_span(op: OP_NAME, start_timestamp: Sentry.utc_now.to_f) do |span|
17
+ yield.tap do
18
+ record_breadcrumb
19
+
20
+ if span
21
+ span.set_description(commands_description)
22
+ span.set_data(Span::DataConventions::DB_SYSTEM, "redis")
23
+ span.set_data(Span::DataConventions::DB_NAME, db)
24
+ span.set_data(Span::DataConventions::SERVER_ADDRESS, host)
25
+ span.set_data(Span::DataConventions::SERVER_PORT, port)
26
+ end
27
+ end
28
+ end
29
+ end
30
+
31
+ private
32
+
33
+ attr_reader :commands, :host, :port, :db
34
+
35
+ def record_breadcrumb
36
+ return unless Sentry.initialized?
37
+ return unless Sentry.configuration.breadcrumbs_logger.include?(LOGGER_NAME)
38
+
39
+ Sentry.add_breadcrumb(
40
+ Sentry::Breadcrumb.new(
41
+ level: :info,
42
+ category: OP_NAME,
43
+ type: :info,
44
+ data: {
45
+ commands: parsed_commands,
46
+ server: server_description
47
+ }
48
+ )
49
+ )
50
+ end
51
+
52
+ def commands_description
53
+ parsed_commands.map do |statement|
54
+ statement.values.join(" ").strip
55
+ end.join(", ")
56
+ end
57
+
58
+ def parsed_commands
59
+ commands.map do |statement|
60
+ command, key, *arguments = statement
61
+ command_set = { command: command.to_s.upcase }
62
+ command_set[:key] = key if Utils::EncodingHelper.valid_utf_8?(key)
63
+
64
+ if Sentry.configuration.send_default_pii
65
+ command_set[:arguments] = arguments
66
+ .select { |a| Utils::EncodingHelper.valid_utf_8?(a) }
67
+ .join(" ")
68
+ end
69
+
70
+ command_set
71
+ end
72
+ end
73
+
74
+ def server_description
75
+ "#{host}:#{port}/#{db}"
76
+ end
77
+
78
+ module OldClientPatch
79
+ def logging(commands, &block)
80
+ Sentry::Redis.new(commands, host, port, db).instrument { super }
81
+ end
82
+ end
83
+
84
+ module GlobalRedisInstrumentation
85
+ def call(command, redis_config)
86
+ Sentry::Redis
87
+ .new([command], redis_config.host, redis_config.port, redis_config.db)
88
+ .instrument { super }
89
+ end
90
+
91
+ def call_pipelined(commands, redis_config)
92
+ Sentry::Redis
93
+ .new(commands, redis_config.host, redis_config.port, redis_config.db)
94
+ .instrument { super }
95
+ end
96
+ end
97
+ end
98
+ end
99
+
100
+ if defined?(::Redis::Client)
101
+ if Gem::Version.new(::Redis::VERSION) < Gem::Version.new("5.0")
102
+ Sentry.register_patch(:redis, Sentry::Redis::OldClientPatch, ::Redis::Client)
103
+ elsif defined?(RedisClient)
104
+ Sentry.register_patch(:redis) do
105
+ RedisClient.register(Sentry::Redis::GlobalRedisInstrumentation)
106
+ end
107
+ end
108
+ 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 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
@@ -0,0 +1,360 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "sentry/breadcrumb_buffer"
4
+ require "sentry/propagation_context"
5
+ require "etc"
6
+
7
+ module Sentry
8
+ class Scope
9
+ include ArgumentCheckingHelper
10
+
11
+ ATTRIBUTES = [
12
+ :transaction_names,
13
+ :transaction_sources,
14
+ :contexts,
15
+ :extra,
16
+ :tags,
17
+ :user,
18
+ :level,
19
+ :breadcrumbs,
20
+ :fingerprint,
21
+ :event_processors,
22
+ :rack_env,
23
+ :span,
24
+ :session,
25
+ :propagation_context
26
+ ]
27
+
28
+ attr_reader(*ATTRIBUTES)
29
+
30
+ # @param max_breadcrumbs [Integer] the maximum number of breadcrumbs to be stored in the scope.
31
+ def initialize(max_breadcrumbs: nil)
32
+ @max_breadcrumbs = max_breadcrumbs
33
+ set_default_value
34
+ end
35
+
36
+ # Resets the scope's attributes to defaults.
37
+ # @return [void]
38
+ def clear
39
+ set_default_value
40
+ end
41
+
42
+ # Applies stored attributes and event processors to the given event.
43
+ # @param event [Event]
44
+ # @param hint [Hash] the hint data that'll be passed to event processors.
45
+ # @return [Event]
46
+ def apply_to_event(event, hint = nil)
47
+ unless event.is_a?(CheckInEvent)
48
+ event.tags = tags.merge(event.tags)
49
+ event.user = user.merge(event.user)
50
+ event.extra = extra.merge(event.extra)
51
+ event.contexts = contexts.merge(event.contexts)
52
+ event.transaction = transaction_name if transaction_name
53
+ event.transaction_info = { source: transaction_source } if transaction_source
54
+ event.fingerprint = fingerprint
55
+ event.level = level
56
+ event.breadcrumbs = breadcrumbs
57
+ event.rack_env = rack_env if rack_env
58
+ end
59
+
60
+ if span
61
+ event.contexts[:trace] ||= span.get_trace_context
62
+ else
63
+ event.contexts[:trace] ||= propagation_context.get_trace_context
64
+ event.dynamic_sampling_context ||= propagation_context.get_dynamic_sampling_context
65
+ end
66
+
67
+ all_event_processors = self.class.global_event_processors + @event_processors
68
+
69
+ unless all_event_processors.empty?
70
+ all_event_processors.each do |processor_block|
71
+ event = processor_block.call(event, hint)
72
+ end
73
+ end
74
+
75
+ event
76
+ end
77
+
78
+ # Adds the breadcrumb to the scope's breadcrumbs buffer.
79
+ # @param breadcrumb [Breadcrumb]
80
+ # @return [void]
81
+ def add_breadcrumb(breadcrumb)
82
+ breadcrumbs.record(breadcrumb)
83
+ end
84
+
85
+ # Clears the scope's breadcrumbs buffer
86
+ # @return [void]
87
+ def clear_breadcrumbs
88
+ set_new_breadcrumb_buffer
89
+ end
90
+
91
+ # @return [Scope]
92
+ def dup
93
+ copy = super
94
+ copy.breadcrumbs = breadcrumbs.dup
95
+ copy.contexts = contexts.deep_dup
96
+ copy.extra = extra.deep_dup
97
+ copy.tags = tags.deep_dup
98
+ copy.user = user.deep_dup
99
+ copy.transaction_names = transaction_names.dup
100
+ copy.transaction_sources = transaction_sources.dup
101
+ copy.fingerprint = fingerprint.deep_dup
102
+ copy.span = span.deep_dup
103
+ copy.session = session.deep_dup
104
+ copy.propagation_context = propagation_context.deep_dup
105
+ copy
106
+ end
107
+
108
+ # Updates the scope's data from a given scope.
109
+ # @param scope [Scope]
110
+ # @return [void]
111
+ def update_from_scope(scope)
112
+ self.breadcrumbs = scope.breadcrumbs
113
+ self.contexts = scope.contexts
114
+ self.extra = scope.extra
115
+ self.tags = scope.tags
116
+ self.user = scope.user
117
+ self.transaction_names = scope.transaction_names
118
+ self.transaction_sources = scope.transaction_sources
119
+ self.fingerprint = scope.fingerprint
120
+ self.span = scope.span
121
+ self.propagation_context = scope.propagation_context
122
+ end
123
+
124
+ # Updates the scope's data from the given options.
125
+ # @param contexts [Hash]
126
+ # @param extras [Hash]
127
+ # @param tags [Hash]
128
+ # @param user [Hash]
129
+ # @param level [String, Symbol]
130
+ # @param fingerprint [Array]
131
+ # @return [void]
132
+ def update_from_options(
133
+ contexts: nil,
134
+ extra: nil,
135
+ tags: nil,
136
+ user: nil,
137
+ level: nil,
138
+ fingerprint: nil
139
+ )
140
+ self.contexts.merge!(contexts) if contexts
141
+ self.extra.merge!(extra) if extra
142
+ self.tags.merge!(tags) if tags
143
+ self.user = user if user
144
+ self.level = level if level
145
+ self.fingerprint = fingerprint if fingerprint
146
+ end
147
+
148
+ # Sets the scope's rack_env attribute.
149
+ # @param env [Hash]
150
+ # @return [Hash]
151
+ def set_rack_env(env)
152
+ env = env || {}
153
+ @rack_env = env
154
+ end
155
+
156
+ # Sets the scope's span attribute.
157
+ # @param span [Span]
158
+ # @return [Span]
159
+ def set_span(span)
160
+ check_argument_type!(span, Span)
161
+ @span = span
162
+ end
163
+
164
+ # @!macro set_user
165
+ def set_user(user_hash)
166
+ check_argument_type!(user_hash, Hash)
167
+ @user = user_hash
168
+ end
169
+
170
+ # @!macro set_extras
171
+ def set_extras(extras_hash)
172
+ check_argument_type!(extras_hash, Hash)
173
+ @extra.merge!(extras_hash)
174
+ end
175
+
176
+ # Adds a new key-value pair to current extras.
177
+ # @param key [String, Symbol]
178
+ # @param value [Object]
179
+ # @return [Hash]
180
+ def set_extra(key, value)
181
+ set_extras(key => value)
182
+ end
183
+
184
+ # @!macro set_tags
185
+ def set_tags(tags_hash)
186
+ check_argument_type!(tags_hash, Hash)
187
+ @tags.merge!(tags_hash)
188
+ end
189
+
190
+ # Adds a new key-value pair to current tags.
191
+ # @param key [String, Symbol]
192
+ # @param value [Object]
193
+ # @return [Hash]
194
+ def set_tag(key, value)
195
+ set_tags(key => value)
196
+ end
197
+
198
+ # Updates the scope's contexts attribute by merging with the old value.
199
+ # @param contexts [Hash]
200
+ # @return [Hash]
201
+ def set_contexts(contexts_hash)
202
+ check_argument_type!(contexts_hash, Hash)
203
+ contexts_hash.values.each do |val|
204
+ check_argument_type!(val, Hash)
205
+ end
206
+
207
+ @contexts.merge!(contexts_hash) do |key, old, new|
208
+ old.merge(new)
209
+ end
210
+ end
211
+
212
+ # @!macro set_context
213
+ def set_context(key, value)
214
+ check_argument_type!(value, Hash)
215
+ set_contexts(key => value)
216
+ end
217
+
218
+ # Sets the scope's level attribute.
219
+ # @param level [String, Symbol]
220
+ # @return [void]
221
+ def set_level(level)
222
+ @level = level
223
+ end
224
+
225
+ # Appends a new transaction name to the scope.
226
+ # The "transaction" here does not refer to `Transaction` objects.
227
+ # @param transaction_name [String]
228
+ # @return [void]
229
+ def set_transaction_name(transaction_name, source: :custom)
230
+ @transaction_names << transaction_name
231
+ @transaction_sources << source
232
+ end
233
+
234
+ # Sets the currently active session on the scope.
235
+ # @param session [Session, nil]
236
+ # @return [void]
237
+ def set_session(session)
238
+ @session = session
239
+ end
240
+
241
+ # Returns current transaction name.
242
+ # The "transaction" here does not refer to `Transaction` objects.
243
+ # @return [String, nil]
244
+ def transaction_name
245
+ @transaction_names.last
246
+ end
247
+
248
+ # Returns current transaction source.
249
+ # The "transaction" here does not refer to `Transaction` objects.
250
+ # @return [String, nil]
251
+ def transaction_source
252
+ @transaction_sources.last
253
+ end
254
+
255
+ # Returns the associated Transaction object.
256
+ # @return [Transaction, nil]
257
+ def get_transaction
258
+ span.transaction if span
259
+ end
260
+
261
+ # Returns the associated Span object.
262
+ # @return [Span, nil]
263
+ def get_span
264
+ span
265
+ end
266
+
267
+ # Sets the scope's fingerprint attribute.
268
+ # @param fingerprint [Array]
269
+ # @return [Array]
270
+ def set_fingerprint(fingerprint)
271
+ check_argument_type!(fingerprint, Array)
272
+
273
+ @fingerprint = fingerprint
274
+ end
275
+
276
+ # Adds a new event processor [Proc] to the scope.
277
+ # @param block [Proc]
278
+ # @return [void]
279
+ def add_event_processor(&block)
280
+ @event_processors << block
281
+ end
282
+
283
+ # Generate a new propagation context either from the incoming env headers or from scratch.
284
+ # @param env [Hash, nil]
285
+ # @return [void]
286
+ def generate_propagation_context(env = nil)
287
+ @propagation_context = PropagationContext.new(self, env)
288
+ end
289
+
290
+ protected
291
+
292
+ # for duplicating scopes internally
293
+ attr_writer(*ATTRIBUTES)
294
+
295
+ private
296
+
297
+ def set_default_value
298
+ @contexts = { :os => self.class.os_context, :runtime => self.class.runtime_context }
299
+ @extra = {}
300
+ @tags = {}
301
+ @user = {}
302
+ @level = :error
303
+ @fingerprint = []
304
+ @transaction_names = []
305
+ @transaction_sources = []
306
+ @event_processors = []
307
+ @rack_env = {}
308
+ @span = nil
309
+ @session = nil
310
+ generate_propagation_context
311
+ set_new_breadcrumb_buffer
312
+ end
313
+
314
+ def set_new_breadcrumb_buffer
315
+ @breadcrumbs = BreadcrumbBuffer.new(@max_breadcrumbs)
316
+ end
317
+
318
+ class << self
319
+ # @return [Hash]
320
+ def os_context
321
+ @os_context ||=
322
+ begin
323
+ uname = Etc.uname
324
+ {
325
+ name: uname[:sysname] || RbConfig::CONFIG["host_os"],
326
+ version: uname[:version],
327
+ build: uname[:release],
328
+ kernel_version: uname[:version],
329
+ machine: uname[:machine]
330
+ }
331
+ end
332
+ end
333
+
334
+ # @return [Hash]
335
+ def runtime_context
336
+ @runtime_context ||= {
337
+ name: RbConfig::CONFIG["ruby_install_name"],
338
+ version: RUBY_DESCRIPTION || Sentry.sys_command("ruby -v")
339
+ }
340
+ end
341
+
342
+ # Returns the global event processors array.
343
+ # @return [Array<Proc>]
344
+ def global_event_processors
345
+ @global_event_processors ||= []
346
+ end
347
+
348
+ # Adds a new global event processor [Proc].
349
+ # Sometimes we need a global event processor without needing to configure scope.
350
+ # These run before scope event processors.
351
+ #
352
+ # @param block [Proc]
353
+ # @return [void]
354
+ def add_global_event_processor(&block)
355
+ global_event_processors << block
356
+ end
357
+ end
358
+
359
+ end
360
+ end
@@ -0,0 +1,33 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Sentry
4
+ class Session
5
+ attr_reader :started, :status, :aggregation_key
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
+
15
+ # truncate seconds from the timestamp since we only care about
16
+ # minute level granularity for aggregation
17
+ @aggregation_key = Time.utc(@started.year, @started.month, @started.day, @started.hour, @started.min)
18
+ end
19
+
20
+ # TODO-neel add :crashed after adding handled mechanism
21
+ def update_from_exception(_exception = nil)
22
+ @status = :errored
23
+ end
24
+
25
+ def close
26
+ @status = :exited if @status == :ok
27
+ end
28
+
29
+ def deep_dup
30
+ dup
31
+ end
32
+ end
33
+ end