sentry-ruby 5.3.1 → 5.16.1

Sign up to get free protection for your applications and to get access to all the features.
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