sentry-ruby 5.16.1 → 5.19.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (62) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile +3 -0
  3. data/README.md +20 -10
  4. data/Rakefile +1 -1
  5. data/bin/console +1 -0
  6. data/lib/sentry/attachment.rb +42 -0
  7. data/lib/sentry/background_worker.rb +1 -1
  8. data/lib/sentry/backpressure_monitor.rb +2 -32
  9. data/lib/sentry/backtrace.rb +7 -3
  10. data/lib/sentry/check_in_event.rb +1 -1
  11. data/lib/sentry/client.rb +59 -9
  12. data/lib/sentry/configuration.rb +25 -12
  13. data/lib/sentry/cron/monitor_schedule.rb +1 -1
  14. data/lib/sentry/dsn.rb +1 -1
  15. data/lib/sentry/envelope.rb +18 -1
  16. data/lib/sentry/error_event.rb +2 -2
  17. data/lib/sentry/event.rb +13 -11
  18. data/lib/sentry/faraday.rb +77 -0
  19. data/lib/sentry/graphql.rb +9 -0
  20. data/lib/sentry/hub.rb +15 -2
  21. data/lib/sentry/integrable.rb +4 -0
  22. data/lib/sentry/interface.rb +1 -0
  23. data/lib/sentry/interfaces/exception.rb +5 -3
  24. data/lib/sentry/interfaces/mechanism.rb +20 -0
  25. data/lib/sentry/interfaces/request.rb +2 -2
  26. data/lib/sentry/interfaces/single_exception.rb +6 -4
  27. data/lib/sentry/interfaces/stacktrace_builder.rb +8 -0
  28. data/lib/sentry/metrics/aggregator.rb +248 -0
  29. data/lib/sentry/metrics/configuration.rb +47 -0
  30. data/lib/sentry/metrics/counter_metric.rb +25 -0
  31. data/lib/sentry/metrics/distribution_metric.rb +25 -0
  32. data/lib/sentry/metrics/gauge_metric.rb +35 -0
  33. data/lib/sentry/metrics/local_aggregator.rb +53 -0
  34. data/lib/sentry/metrics/metric.rb +19 -0
  35. data/lib/sentry/metrics/set_metric.rb +28 -0
  36. data/lib/sentry/metrics/timing.rb +43 -0
  37. data/lib/sentry/metrics.rb +56 -0
  38. data/lib/sentry/net/http.rb +17 -38
  39. data/lib/sentry/propagation_context.rb +9 -8
  40. data/lib/sentry/puma.rb +1 -1
  41. data/lib/sentry/rack/capture_exceptions.rb +14 -2
  42. data/lib/sentry/rake.rb +3 -1
  43. data/lib/sentry/redis.rb +2 -1
  44. data/lib/sentry/scope.rb +35 -26
  45. data/lib/sentry/session.rb +2 -2
  46. data/lib/sentry/session_flusher.rb +6 -38
  47. data/lib/sentry/span.rb +40 -5
  48. data/lib/sentry/test_helper.rb +2 -1
  49. data/lib/sentry/threaded_periodic_worker.rb +39 -0
  50. data/lib/sentry/transaction.rb +16 -14
  51. data/lib/sentry/transaction_event.rb +5 -0
  52. data/lib/sentry/transport/configuration.rb +0 -1
  53. data/lib/sentry/transport.rb +14 -22
  54. data/lib/sentry/utils/argument_checking_helper.rb +6 -0
  55. data/lib/sentry/utils/http_tracing.rb +41 -0
  56. data/lib/sentry/utils/logging_helper.rb +0 -4
  57. data/lib/sentry/utils/real_ip.rb +1 -1
  58. data/lib/sentry/utils/request_id.rb +1 -1
  59. data/lib/sentry/version.rb +1 -1
  60. data/lib/sentry-ruby.rb +34 -3
  61. data/sentry-ruby.gemspec +12 -5
  62. metadata +39 -7
@@ -0,0 +1,53 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Sentry
4
+ module Metrics
5
+ class LocalAggregator
6
+ # exposed only for testing
7
+ attr_reader :buckets
8
+
9
+ def initialize
10
+ @buckets = {}
11
+ end
12
+
13
+ def add(key, value)
14
+ if @buckets[key]
15
+ @buckets[key].add(value)
16
+ else
17
+ @buckets[key] = GaugeMetric.new(value)
18
+ end
19
+ end
20
+
21
+ def to_hash
22
+ return nil if @buckets.empty?
23
+
24
+ @buckets.map do |bucket_key, metric|
25
+ type, key, unit, tags = bucket_key
26
+
27
+ payload_key = "#{type}:#{key}@#{unit}"
28
+ payload_value = {
29
+ tags: deserialize_tags(tags),
30
+ min: metric.min,
31
+ max: metric.max,
32
+ count: metric.count,
33
+ sum: metric.sum
34
+ }
35
+
36
+ [payload_key, payload_value]
37
+ end.to_h
38
+ end
39
+
40
+ private
41
+
42
+ def deserialize_tags(tags)
43
+ tags.inject({}) do |h, tag|
44
+ k, v = tag
45
+ old = h[k]
46
+ # make it an array if key repeats
47
+ h[k] = old ? (old.is_a?(Array) ? old << v : [old, v]) : v
48
+ h
49
+ end
50
+ end
51
+ end
52
+ end
53
+ end
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Sentry
4
+ module Metrics
5
+ class Metric
6
+ def add(value)
7
+ raise NotImplementedError
8
+ end
9
+
10
+ def serialize
11
+ raise NotImplementedError
12
+ end
13
+
14
+ def weight
15
+ raise NotImplementedError
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,28 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'set'
4
+ require 'zlib'
5
+
6
+ module Sentry
7
+ module Metrics
8
+ class SetMetric < Metric
9
+ attr_reader :value
10
+
11
+ def initialize(value)
12
+ @value = Set[value]
13
+ end
14
+
15
+ def add(value)
16
+ @value << value
17
+ end
18
+
19
+ def serialize
20
+ value.map { |x| x.is_a?(String) ? Zlib.crc32(x) : x.to_i }
21
+ end
22
+
23
+ def weight
24
+ value.size
25
+ end
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,43 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Sentry
4
+ module Metrics
5
+ module Timing
6
+ class << self
7
+ def nanosecond
8
+ time = Sentry.utc_now
9
+ time.to_i * (10 ** 9) + time.nsec
10
+ end
11
+
12
+ def microsecond
13
+ time = Sentry.utc_now
14
+ time.to_i * (10 ** 6) + time.usec
15
+ end
16
+
17
+ def millisecond
18
+ Sentry.utc_now.to_i * (10 ** 3)
19
+ end
20
+
21
+ def second
22
+ Sentry.utc_now.to_i
23
+ end
24
+
25
+ def minute
26
+ Sentry.utc_now.to_i / 60.0
27
+ end
28
+
29
+ def hour
30
+ Sentry.utc_now.to_i / 3600.0
31
+ end
32
+
33
+ def day
34
+ Sentry.utc_now.to_i / (3600.0 * 24.0)
35
+ end
36
+
37
+ def week
38
+ Sentry.utc_now.to_i / (3600.0 * 24.0 * 7.0)
39
+ end
40
+ end
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,56 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'sentry/metrics/metric'
4
+ require 'sentry/metrics/counter_metric'
5
+ require 'sentry/metrics/distribution_metric'
6
+ require 'sentry/metrics/gauge_metric'
7
+ require 'sentry/metrics/set_metric'
8
+ require 'sentry/metrics/timing'
9
+ require 'sentry/metrics/aggregator'
10
+
11
+ module Sentry
12
+ module Metrics
13
+ DURATION_UNITS = %w[nanosecond microsecond millisecond second minute hour day week]
14
+ INFORMATION_UNITS = %w[bit byte kilobyte kibibyte megabyte mebibyte gigabyte gibibyte terabyte tebibyte petabyte pebibyte exabyte exbibyte]
15
+ FRACTIONAL_UNITS = %w[ratio percent]
16
+
17
+ OP_NAME = 'metric.timing'
18
+ SPAN_ORIGIN = 'auto.metric.timing'
19
+
20
+ class << self
21
+ def increment(key, value = 1.0, unit: 'none', tags: {}, timestamp: nil)
22
+ Sentry.metrics_aggregator&.add(:c, key, value, unit: unit, tags: tags, timestamp: timestamp)
23
+ end
24
+
25
+ def distribution(key, value, unit: 'none', tags: {}, timestamp: nil)
26
+ Sentry.metrics_aggregator&.add(:d, key, value, unit: unit, tags: tags, timestamp: timestamp)
27
+ end
28
+
29
+ def set(key, value, unit: 'none', tags: {}, timestamp: nil)
30
+ Sentry.metrics_aggregator&.add(:s, key, value, unit: unit, tags: tags, timestamp: timestamp)
31
+ end
32
+
33
+ def gauge(key, value, unit: 'none', tags: {}, timestamp: nil)
34
+ Sentry.metrics_aggregator&.add(:g, key, value, unit: unit, tags: tags, timestamp: timestamp)
35
+ end
36
+
37
+ def timing(key, unit: 'second', tags: {}, timestamp: nil, &block)
38
+ return unless block_given?
39
+ return yield unless DURATION_UNITS.include?(unit)
40
+
41
+ result, value = Sentry.with_child_span(op: OP_NAME, description: key, origin: SPAN_ORIGIN) do |span|
42
+ tags.each { |k, v| span.set_tag(k, v.is_a?(Array) ? v.join(', ') : v.to_s) } if span
43
+
44
+ start = Timing.send(unit.to_sym)
45
+ result = yield
46
+ value = Timing.send(unit.to_sym) - start
47
+
48
+ [result, value]
49
+ end
50
+
51
+ Sentry.metrics_aggregator&.add(:d, key, value, unit: unit, tags: tags, timestamp: timestamp)
52
+ result
53
+ end
54
+ end
55
+ end
56
+ end
@@ -2,12 +2,16 @@
2
2
 
3
3
  require "net/http"
4
4
  require "resolv"
5
+ require "sentry/utils/http_tracing"
5
6
 
6
7
  module Sentry
7
8
  # @api private
8
9
  module Net
9
10
  module HTTP
11
+ include Utils::HttpTracing
12
+
10
13
  OP_NAME = "http.client"
14
+ SPAN_ORIGIN = "auto.http.net_http"
11
15
  BREADCRUMB_CATEGORY = "net.http"
12
16
 
13
17
  # To explain how the entire thing works, we need to know how the original Net::HTTP#request works
@@ -20,8 +24,7 @@ module Sentry
20
24
  # req['connection'] ||= 'close'
21
25
  # return request(req, body, &block) # <- request will be called for the second time from the first call
22
26
  # }
23
- # end
24
- # # .....
27
+ # end # .....
25
28
  # end
26
29
  # ```
27
30
  #
@@ -30,47 +33,29 @@ module Sentry
30
33
  return super unless started? && Sentry.initialized?
31
34
  return super if from_sentry_sdk?
32
35
 
33
- Sentry.with_child_span(op: OP_NAME, start_timestamp: Sentry.utc_now.to_f) do |sentry_span|
36
+ Sentry.with_child_span(op: OP_NAME, start_timestamp: Sentry.utc_now.to_f, origin: SPAN_ORIGIN) do |sentry_span|
34
37
  request_info = extract_request_info(req)
35
38
 
36
- if propagate_trace?(request_info[:url], Sentry.configuration)
39
+ if propagate_trace?(request_info[:url])
37
40
  set_propagation_headers(req)
38
41
  end
39
42
 
40
- super.tap do |res|
41
- record_sentry_breadcrumb(request_info, res)
43
+ res = super
44
+ response_status = res.code.to_i
42
45
 
43
- if sentry_span
44
- sentry_span.set_description("#{request_info[:method]} #{request_info[:url]}")
45
- sentry_span.set_data(Span::DataConventions::URL, request_info[:url])
46
- sentry_span.set_data(Span::DataConventions::HTTP_METHOD, request_info[:method])
47
- sentry_span.set_data(Span::DataConventions::HTTP_QUERY, request_info[:query]) if request_info[:query]
48
- sentry_span.set_data(Span::DataConventions::HTTP_STATUS_CODE, res.code.to_i)
49
- end
46
+ if record_sentry_breadcrumb?
47
+ record_sentry_breadcrumb(request_info, response_status)
50
48
  end
51
- end
52
- end
53
49
 
54
- private
50
+ if sentry_span
51
+ set_span_info(sentry_span, request_info, response_status)
52
+ end
55
53
 
56
- def set_propagation_headers(req)
57
- Sentry.get_trace_propagation_headers&.each { |k, v| req[k] = v }
54
+ res
55
+ end
58
56
  end
59
57
 
60
- def record_sentry_breadcrumb(request_info, res)
61
- return unless Sentry.initialized? && Sentry.configuration.breadcrumbs_logger.include?(:http_logger)
62
-
63
- crumb = Sentry::Breadcrumb.new(
64
- level: :info,
65
- category: BREADCRUMB_CATEGORY,
66
- type: :info,
67
- data: {
68
- status: res.code.to_i,
69
- **request_info
70
- }
71
- )
72
- Sentry.add_breadcrumb(crumb)
73
- end
58
+ private
74
59
 
75
60
  def from_sentry_sdk?
76
61
  dsn = Sentry.configuration.dsn
@@ -93,12 +78,6 @@ module Sentry
93
78
 
94
79
  result
95
80
  end
96
-
97
- def propagate_trace?(url, configuration)
98
- url &&
99
- configuration.propagate_traces &&
100
- configuration.trace_propagation_targets.any? { |target| url.match?(target) }
101
- end
102
81
  end
103
82
  end
104
83
  end
@@ -50,14 +50,15 @@ module Sentry
50
50
  if sentry_trace_data
51
51
  @trace_id, @parent_span_id, @parent_sampled = sentry_trace_data
52
52
 
53
- @baggage = if baggage_header && !baggage_header.empty?
54
- Baggage.from_incoming_header(baggage_header)
55
- else
56
- # If there's an incoming sentry-trace but no incoming baggage header,
57
- # for instance in traces coming from older SDKs,
58
- # baggage will be empty and frozen and won't be populated as head SDK.
59
- Baggage.new({})
60
- end
53
+ @baggage =
54
+ if baggage_header && !baggage_header.empty?
55
+ Baggage.from_incoming_header(baggage_header)
56
+ else
57
+ # If there's an incoming sentry-trace but no incoming baggage header,
58
+ # for instance in traces coming from older SDKs,
59
+ # baggage will be empty and frozen and won't be populated as head SDK.
60
+ Baggage.new({})
61
+ end
61
62
 
62
63
  @baggage.freeze!
63
64
  @incoming_trace = true
data/lib/sentry/puma.rb CHANGED
@@ -7,7 +7,7 @@ module Sentry
7
7
  module Server
8
8
  PUMA_4_AND_PRIOR = Gem::Version.new(::Puma::Const::PUMA_VERSION) < Gem::Version.new("5.0.0")
9
9
 
10
- def lowlevel_error(e, env, status=500)
10
+ def lowlevel_error(e, env, status = 500)
11
11
  result =
12
12
  if PUMA_4_AND_PRIOR
13
13
  super(e, env)
@@ -4,6 +4,8 @@ module Sentry
4
4
  module Rack
5
5
  class CaptureExceptions
6
6
  ERROR_EVENT_ID_KEY = "sentry.error_event_id"
7
+ MECHANISM_TYPE = "rack"
8
+ SPAN_ORIGIN = "auto.http.rack"
7
9
 
8
10
  def initialize(app)
9
11
  @app = app
@@ -56,13 +58,19 @@ module Sentry
56
58
  end
57
59
 
58
60
  def capture_exception(exception, env)
59
- Sentry.capture_exception(exception).tap do |event|
61
+ Sentry.capture_exception(exception, hint: { mechanism: mechanism }).tap do |event|
60
62
  env[ERROR_EVENT_ID_KEY] = event.event_id if event
61
63
  end
62
64
  end
63
65
 
64
66
  def start_transaction(env, scope)
65
- options = { name: scope.transaction_name, source: scope.transaction_source, op: transaction_op }
67
+ options = {
68
+ name: scope.transaction_name,
69
+ source: scope.transaction_source,
70
+ op: transaction_op,
71
+ origin: SPAN_ORIGIN
72
+ }
73
+
66
74
  transaction = Sentry.continue_trace(env, **options)
67
75
  Sentry.start_transaction(transaction: transaction, custom_sampling_context: { env: env }, **options)
68
76
  end
@@ -74,6 +82,10 @@ module Sentry
74
82
  transaction.set_http_status(status_code)
75
83
  transaction.finish
76
84
  end
85
+
86
+ def mechanism
87
+ Sentry::Mechanism.new(type: MECHANISM_TYPE, handled: false)
88
+ end
77
89
  end
78
90
  end
79
91
  end
data/lib/sentry/rake.rb CHANGED
@@ -8,7 +8,9 @@ module Sentry
8
8
  module Application
9
9
  # @api private
10
10
  def display_error_message(ex)
11
- Sentry.capture_exception(ex) do |scope|
11
+ mechanism = Sentry::Mechanism.new(type: 'rake', handled: false)
12
+
13
+ Sentry.capture_exception(ex, hint: { mechanism: mechanism }) do |scope|
12
14
  task_name = top_level_tasks.join(' ')
13
15
  scope.set_transaction_name(task_name, source: :task)
14
16
  scope.set_tag("rake_task", task_name)
data/lib/sentry/redis.rb CHANGED
@@ -4,6 +4,7 @@ module Sentry
4
4
  # @api private
5
5
  class Redis
6
6
  OP_NAME = "db.redis"
7
+ SPAN_ORIGIN = "auto.db.redis"
7
8
  LOGGER_NAME = :redis_logger
8
9
 
9
10
  def initialize(commands, host, port, db)
@@ -13,7 +14,7 @@ module Sentry
13
14
  def instrument
14
15
  return yield unless Sentry.initialized?
15
16
 
16
- Sentry.with_child_span(op: OP_NAME, start_timestamp: Sentry.utc_now.to_f) do |span|
17
+ Sentry.with_child_span(op: OP_NAME, start_timestamp: Sentry.utc_now.to_f, origin: SPAN_ORIGIN) do |span|
17
18
  yield.tap do
18
19
  record_breadcrumb
19
20
 
data/lib/sentry/scope.rb CHANGED
@@ -2,6 +2,7 @@
2
2
 
3
3
  require "sentry/breadcrumb_buffer"
4
4
  require "sentry/propagation_context"
5
+ require "sentry/attachment"
5
6
  require "etc"
6
7
 
7
8
  module Sentry
@@ -9,8 +10,8 @@ module Sentry
9
10
  include ArgumentCheckingHelper
10
11
 
11
12
  ATTRIBUTES = [
12
- :transaction_names,
13
- :transaction_sources,
13
+ :transaction_name,
14
+ :transaction_source,
14
15
  :contexts,
15
16
  :extra,
16
17
  :tags,
@@ -22,6 +23,7 @@ module Sentry
22
23
  :rack_env,
23
24
  :span,
24
25
  :session,
26
+ :attachments,
25
27
  :propagation_context
26
28
  ]
27
29
 
@@ -55,6 +57,7 @@ module Sentry
55
57
  event.level = level
56
58
  event.breadcrumbs = breadcrumbs
57
59
  event.rack_env = rack_env if rack_env
60
+ event.attachments = attachments
58
61
  end
59
62
 
60
63
  if span
@@ -96,12 +99,13 @@ module Sentry
96
99
  copy.extra = extra.deep_dup
97
100
  copy.tags = tags.deep_dup
98
101
  copy.user = user.deep_dup
99
- copy.transaction_names = transaction_names.dup
100
- copy.transaction_sources = transaction_sources.dup
102
+ copy.transaction_name = transaction_name.dup
103
+ copy.transaction_source = transaction_source.dup
101
104
  copy.fingerprint = fingerprint.deep_dup
102
105
  copy.span = span.deep_dup
103
106
  copy.session = session.deep_dup
104
107
  copy.propagation_context = propagation_context.deep_dup
108
+ copy.attachments = attachments.dup
105
109
  copy
106
110
  end
107
111
 
@@ -114,11 +118,12 @@ module Sentry
114
118
  self.extra = scope.extra
115
119
  self.tags = scope.tags
116
120
  self.user = scope.user
117
- self.transaction_names = scope.transaction_names
118
- self.transaction_sources = scope.transaction_sources
121
+ self.transaction_name = scope.transaction_name
122
+ self.transaction_source = scope.transaction_source
119
123
  self.fingerprint = scope.fingerprint
120
124
  self.span = scope.span
121
125
  self.propagation_context = scope.propagation_context
126
+ self.attachments = scope.attachments
122
127
  end
123
128
 
124
129
  # Updates the scope's data from the given options.
@@ -128,14 +133,17 @@ module Sentry
128
133
  # @param user [Hash]
129
134
  # @param level [String, Symbol]
130
135
  # @param fingerprint [Array]
131
- # @return [void]
136
+ # @param attachments [Array<Attachment>]
137
+ # @return [Array]
132
138
  def update_from_options(
133
139
  contexts: nil,
134
140
  extra: nil,
135
141
  tags: nil,
136
142
  user: nil,
137
143
  level: nil,
138
- fingerprint: nil
144
+ fingerprint: nil,
145
+ attachments: nil,
146
+ **options
139
147
  )
140
148
  self.contexts.merge!(contexts) if contexts
141
149
  self.extra.merge!(extra) if extra
@@ -143,6 +151,9 @@ module Sentry
143
151
  self.user = user if user
144
152
  self.level = level if level
145
153
  self.fingerprint = fingerprint if fingerprint
154
+
155
+ # Returns unsupported option keys so we can notify users.
156
+ options.keys
146
157
  end
147
158
 
148
159
  # Sets the scope's rack_env attribute.
@@ -227,8 +238,8 @@ module Sentry
227
238
  # @param transaction_name [String]
228
239
  # @return [void]
229
240
  def set_transaction_name(transaction_name, source: :custom)
230
- @transaction_names << transaction_name
231
- @transaction_sources << source
241
+ @transaction_name = transaction_name
242
+ @transaction_source = source
232
243
  end
233
244
 
234
245
  # Sets the currently active session on the scope.
@@ -238,18 +249,10 @@ module Sentry
238
249
  @session = session
239
250
  end
240
251
 
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
252
+ # These are high cardinality and thus bad.
253
+ # @return [Boolean]
254
+ def transaction_source_low_quality?
255
+ transaction_source == :url
253
256
  end
254
257
 
255
258
  # Returns the associated Transaction object.
@@ -287,6 +290,12 @@ module Sentry
287
290
  @propagation_context = PropagationContext.new(self, env)
288
291
  end
289
292
 
293
+ # Add a new attachment to the scope.
294
+ def add_attachment(**opts)
295
+ attachments << (attachment = Attachment.new(**opts))
296
+ attachment
297
+ end
298
+
290
299
  protected
291
300
 
292
301
  # for duplicating scopes internally
@@ -295,18 +304,19 @@ module Sentry
295
304
  private
296
305
 
297
306
  def set_default_value
298
- @contexts = { :os => self.class.os_context, :runtime => self.class.runtime_context }
307
+ @contexts = { os: self.class.os_context, runtime: self.class.runtime_context }
299
308
  @extra = {}
300
309
  @tags = {}
301
310
  @user = {}
302
311
  @level = :error
303
312
  @fingerprint = []
304
- @transaction_names = []
305
- @transaction_sources = []
313
+ @transaction_name = nil
314
+ @transaction_source = nil
306
315
  @event_processors = []
307
316
  @rack_env = {}
308
317
  @span = nil
309
318
  @session = nil
319
+ @attachments = []
310
320
  generate_propagation_context
311
321
  set_new_breadcrumb_buffer
312
322
  end
@@ -355,6 +365,5 @@ module Sentry
355
365
  global_event_processors << block
356
366
  end
357
367
  end
358
-
359
368
  end
360
369
  end
@@ -5,8 +5,8 @@ module Sentry
5
5
  attr_reader :started, :status, :aggregation_key
6
6
 
7
7
  # TODO-neel add :crashed after adding handled mechanism
8
- STATUSES = %i(ok errored exited)
9
- AGGREGATE_STATUSES = %i(errored exited)
8
+ STATUSES = %i[ok errored exited]
9
+ AGGREGATE_STATUSES = %i[errored exited]
10
10
 
11
11
  def initialize
12
12
  @started = Sentry.utc_now
@@ -1,58 +1,38 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Sentry
4
- class SessionFlusher
5
- include LoggingHelper
6
-
4
+ class SessionFlusher < ThreadedPeriodicWorker
7
5
  FLUSH_INTERVAL = 60
8
6
 
9
7
  def initialize(configuration, client)
10
- @thread = nil
11
- @exited = false
8
+ super(configuration.logger, FLUSH_INTERVAL)
12
9
  @client = client
13
10
  @pending_aggregates = {}
14
11
  @release = configuration.release
15
12
  @environment = configuration.environment
16
- @logger = configuration.logger
17
13
 
18
14
  log_debug("[Sessions] Sessions won't be captured without a valid release") unless @release
19
15
  end
20
16
 
21
17
  def flush
22
18
  return if @pending_aggregates.empty?
23
- envelope = pending_envelope
24
-
25
- Sentry.background_worker.perform do
26
- @client.transport.send_envelope(envelope)
27
- end
28
19
 
20
+ @client.capture_envelope(pending_envelope)
29
21
  @pending_aggregates = {}
30
22
  end
31
23
 
24
+ alias_method :run, :flush
25
+
32
26
  def add_session(session)
33
- return if @exited
34
27
  return unless @release
35
28
 
36
- begin
37
- ensure_thread
38
- rescue ThreadError
39
- log_debug("Session flusher thread creation failed")
40
- @exited = true
41
- return
42
- end
29
+ return unless ensure_thread
43
30
 
44
31
  return unless Session::AGGREGATE_STATUSES.include?(session.status)
45
32
  @pending_aggregates[session.aggregation_key] ||= init_aggregates(session.aggregation_key)
46
33
  @pending_aggregates[session.aggregation_key][session.status] += 1
47
34
  end
48
35
 
49
- def kill
50
- log_debug("Killing session flusher")
51
-
52
- @exited = true
53
- @thread&.kill
54
- end
55
-
56
36
  private
57
37
 
58
38
  def init_aggregates(aggregation_key)
@@ -74,17 +54,5 @@ module Sentry
74
54
  def attrs
75
55
  { release: @release, environment: @environment }
76
56
  end
77
-
78
- def ensure_thread
79
- return if @thread&.alive?
80
-
81
- @thread = Thread.new do
82
- loop do
83
- sleep(FLUSH_INTERVAL)
84
- flush
85
- end
86
- end
87
- end
88
-
89
57
  end
90
58
  end