sentry-ruby 5.8.0 → 5.9.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 2ced2a96562a5dc27766fb35a62e43f302dd46eeabd16eae2a52106cc2151ee0
4
- data.tar.gz: 14f4b9a532cc0d1f401ee0bcb942787951f107358e9fbfadc2b1c655d00da3b2
3
+ metadata.gz: 7a114a391fe058601b40369376152bee8ba9b1b96ab94710344283cfe1a4490b
4
+ data.tar.gz: 7a17648c7a5d06f22d2d643f6ff1cb25d3fed2348609e372fc5762043ebd538a
5
5
  SHA512:
6
- metadata.gz: 8816a3439fd547d260e538d16c3db3f440bbdae35ec8108e2338ccd712497cbca0413ce1a365862ab510a787f5d3cae8a58eab2273dd06588284d77a5afac898
7
- data.tar.gz: 4d5e8d3fe56644431b33fbec3eeaf4ceec01eb85b21bd2abfa5a0d8bc4c812aef9beb06b89048525186f00170677b160154414d5cb9b8972725532ba35277130
6
+ metadata.gz: dab19469a8e68201380e2be405332bb7c8caef7bf46fd4dae70f4dc3beaac2902ce51b2b2c902b39ca048e1bc5ff47f85b1e7d3a824b5f20ff002dcbdca2c354
7
+ data.tar.gz: 6bf0cba7b42dd9bfd5078e4353ad7f92d932b959a87c748cb30e505ffd8d205899c819ea4f72e5f3099f0b861139864062b870219678abdeda0a0629604963d3
data/Gemfile CHANGED
@@ -10,6 +10,8 @@ gem "rack", "~> #{Gem::Version.new(rack_version)}" unless rack_version == "0"
10
10
  redis_rb_version = ENV.fetch("REDIS_RB_VERSION", "5.0")
11
11
  gem "redis", "~> #{redis_rb_version}"
12
12
 
13
+ gem "puma"
14
+
13
15
  gem "rake", "~> 12.0"
14
16
  gem "rspec", "~> 3.0"
15
17
  gem "rspec-retry"
@@ -17,6 +19,7 @@ gem "timecop"
17
19
  gem "simplecov"
18
20
  gem "simplecov-cobertura", "~> 1.4"
19
21
  gem "rexml"
22
+ gem "stackprof" unless RUBY_PLATFORM == "java"
20
23
 
21
24
  gem "object_tracer"
22
25
  gem "debug", github: "ruby/debug", platform: :ruby if RUBY_VERSION.to_f >= 2.6
data/Rakefile CHANGED
@@ -8,6 +8,13 @@ require "rspec/core/rake_task"
8
8
 
9
9
  RSpec::Core::RakeTask.new(:spec).tap do |task|
10
10
  task.rspec_opts = "--order rand"
11
+ task.exclude_pattern = "spec/isolated/**/*_spec.rb"
11
12
  end
12
13
 
13
- task :default => :spec
14
+ task :isolated_specs do
15
+ Dir["spec/isolated/*"].each do |file|
16
+ sh "bundle exec rspec #{file}"
17
+ end
18
+ end
19
+
20
+ task :default => [:spec, :isolated_specs]
@@ -76,7 +76,7 @@ module Sentry
76
76
  end
77
77
  end
78
78
 
79
- APP_DIRS_PATTERN = /(bin|exe|app|config|lib|test)/.freeze
79
+ APP_DIRS_PATTERN = /(bin|exe|app|config|lib|test|spec)/.freeze
80
80
 
81
81
  # holder for an Array of Backtrace::Line instances
82
82
  attr_reader :lines
@@ -8,17 +8,6 @@ module Sentry
8
8
  SENTRY_PREFIX = 'sentry-'
9
9
  SENTRY_PREFIX_REGEX = /^sentry-/.freeze
10
10
 
11
- DSC_KEYS = %w(
12
- trace_id
13
- public_key
14
- sample_rate
15
- release
16
- environment
17
- transaction
18
- user_id
19
- user_segment
20
- ).freeze
21
-
22
11
  # @return [Hash]
23
12
  attr_reader :items
24
13
 
@@ -68,7 +57,7 @@ module Sentry
68
57
  # hash to be used in the trace envelope header.
69
58
  # @return [Hash]
70
59
  def dynamic_sampling_context
71
- @items.select { |k, _v| DSC_KEYS.include?(k) }
60
+ @items
72
61
  end
73
62
 
74
63
  # Serialize the Baggage object back to a string.
data/lib/sentry/client.rb CHANGED
@@ -76,7 +76,10 @@ module Sentry
76
76
  # @param hint [Hash] the hint data that'll be passed to `before_send` callback and the scope's event processors.
77
77
  # @return [Event, nil]
78
78
  def event_from_exception(exception, hint = {})
79
- return unless @configuration.sending_allowed? && @configuration.exception_class_allowed?(exception)
79
+ return unless @configuration.sending_allowed?
80
+
81
+ ignore_exclusions = hint.delete(:ignore_exclusions) { false }
82
+ return if !ignore_exclusions && !@configuration.exception_class_allowed?(exception)
80
83
 
81
84
  integration_meta = Sentry.integrations[hint[:integration]]
82
85
 
@@ -227,6 +227,11 @@ module Sentry
227
227
  # @return [Proc]
228
228
  attr_accessor :traces_sampler
229
229
 
230
+ # Easier way to use performance tracing
231
+ # If set to true, will set traces_sample_rate to 1.0
232
+ # @return [Boolean, nil]
233
+ attr_reader :enable_tracing
234
+
230
235
  # Send diagnostic client reports about dropped events, true by default
231
236
  # tries to attach to an existing envelope max once every 30s
232
237
  # @return [Boolean]
@@ -240,6 +245,12 @@ module Sentry
240
245
  # @return [Symbol]
241
246
  attr_reader :instrumenter
242
247
 
248
+ # Take a float between 0.0 and 1.0 as the sample rate for capturing profiles.
249
+ # Note that this rate is relative to traces_sample_rate / traces_sampler,
250
+ # i.e. the profile is sampled by this rate after the transaction is sampled.
251
+ # @return [Float, nil]
252
+ attr_reader :profiles_sample_rate
253
+
243
254
  # these are not config options
244
255
  # @!visibility private
245
256
  attr_reader :errors, :gem_specs
@@ -316,6 +327,7 @@ module Sentry
316
327
  self.rack_env_whitelist = RACK_ENV_WHITELIST_DEFAULT
317
328
  self.traces_sample_rate = nil
318
329
  self.traces_sampler = nil
330
+ self.enable_tracing = nil
319
331
 
320
332
  @transport = Transport::Configuration.new
321
333
  @gem_specs = Hash[Gem::Specification.map { |spec| [spec.name, spec.version.to_s] }] if Gem::Specification.respond_to?(:map)
@@ -384,6 +396,16 @@ module Sentry
384
396
  @instrumenter = INSTRUMENTERS.include?(instrumenter) ? instrumenter : :sentry
385
397
  end
386
398
 
399
+ def enable_tracing=(enable_tracing)
400
+ @enable_tracing = enable_tracing
401
+ @traces_sample_rate ||= 1.0 if enable_tracing
402
+ end
403
+
404
+ def profiles_sample_rate=(profiles_sample_rate)
405
+ log_info("Please make sure to include the 'stackprof' gem in your Gemfile to use Profiling with Sentry.") unless defined?(StackProf)
406
+ @profiles_sample_rate = profiles_sample_rate
407
+ end
408
+
387
409
  def sending_allowed?
388
410
  @errors = []
389
411
 
@@ -414,7 +436,20 @@ module Sentry
414
436
  end
415
437
 
416
438
  def tracing_enabled?
417
- !!((@traces_sample_rate && @traces_sample_rate >= 0.0 && @traces_sample_rate <= 1.0) || @traces_sampler) && sending_allowed?
439
+ valid_sampler = !!((@traces_sample_rate &&
440
+ @traces_sample_rate >= 0.0 &&
441
+ @traces_sample_rate <= 1.0) ||
442
+ @traces_sampler)
443
+
444
+ (@enable_tracing != false) && valid_sampler && sending_allowed?
445
+ end
446
+
447
+ def profiling_enabled?
448
+ valid_sampler = !!(@profiles_sample_rate &&
449
+ @profiles_sample_rate >= 0.0 &&
450
+ @profiles_sample_rate <= 1.0)
451
+
452
+ tracing_enabled? && valid_sampler && sending_allowed?
418
453
  end
419
454
 
420
455
  # @return [String, nil]
@@ -19,10 +19,7 @@ module Sentry
19
19
  end
20
20
 
21
21
  def to_s
22
- <<~ITEM
23
- #{JSON.generate(@headers)}
24
- #{JSON.generate(@payload)}
25
- ITEM
22
+ [JSON.generate(@headers), JSON.generate(@payload)].join("\n")
26
23
  end
27
24
 
28
25
  def serialize
data/lib/sentry/hub.rb CHANGED
@@ -88,8 +88,10 @@ module Sentry
88
88
  }
89
89
 
90
90
  sampling_context.merge!(custom_sampling_context)
91
-
92
91
  transaction.set_initial_sample_decision(sampling_context: sampling_context)
92
+
93
+ transaction.start_profiler!
94
+
93
95
  transaction
94
96
  end
95
97
 
@@ -122,6 +124,7 @@ module Sentry
122
124
 
123
125
  options[:hint] ||= {}
124
126
  options[:hint][:exception] = exception
127
+
125
128
  event = current_client.event_from_exception(exception, options[:hint])
126
129
 
127
130
  return unless event
@@ -97,7 +97,4 @@ module Sentry
97
97
  end
98
98
  end
99
99
 
100
- Sentry.register_patch do
101
- patch = Sentry::Net::HTTP
102
- Net::HTTP.send(:prepend, patch) unless Net::HTTP.ancestors.include?(patch)
103
- end
100
+ Sentry.register_patch(Sentry::Net::HTTP, Net::HTTP)
@@ -0,0 +1,222 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'securerandom'
4
+
5
+ module Sentry
6
+ class Profiler
7
+ VERSION = '1'
8
+ PLATFORM = 'ruby'
9
+ # 101 Hz in microseconds
10
+ DEFAULT_INTERVAL = 1e6 / 101
11
+ MICRO_TO_NANO_SECONDS = 1e3
12
+
13
+ attr_reader :sampled, :started, :event_id
14
+
15
+ def initialize(configuration)
16
+ @event_id = SecureRandom.uuid.delete('-')
17
+ @started = false
18
+ @sampled = nil
19
+
20
+ @profiling_enabled = defined?(StackProf) && configuration.profiling_enabled?
21
+ @profiles_sample_rate = configuration.profiles_sample_rate
22
+ @project_root = configuration.project_root
23
+ @app_dirs_pattern = configuration.app_dirs_pattern || Backtrace::APP_DIRS_PATTERN
24
+ @in_app_pattern = Regexp.new("^(#{@project_root}/)?#{@app_dirs_pattern}")
25
+ end
26
+
27
+ def start
28
+ return unless @sampled
29
+
30
+ @started = StackProf.start(interval: DEFAULT_INTERVAL,
31
+ mode: :wall,
32
+ raw: true,
33
+ aggregate: false)
34
+
35
+ @started ? log('Started') : log('Not started since running elsewhere')
36
+ end
37
+
38
+ def stop
39
+ return unless @sampled
40
+ return unless @started
41
+
42
+ StackProf.stop
43
+ log('Stopped')
44
+ end
45
+
46
+ # Sets initial sampling decision of the profile.
47
+ # @return [void]
48
+ def set_initial_sample_decision(transaction_sampled)
49
+ unless @profiling_enabled
50
+ @sampled = false
51
+ return
52
+ end
53
+
54
+ unless transaction_sampled
55
+ @sampled = false
56
+ log('Discarding profile because transaction not sampled')
57
+ return
58
+ end
59
+
60
+ case @profiles_sample_rate
61
+ when 0.0
62
+ @sampled = false
63
+ log('Discarding profile because sample_rate is 0')
64
+ return
65
+ when 1.0
66
+ @sampled = true
67
+ return
68
+ else
69
+ @sampled = Random.rand < @profiles_sample_rate
70
+ end
71
+
72
+ log('Discarding profile due to sampling decision') unless @sampled
73
+ end
74
+
75
+ def to_hash
76
+ return {} unless @sampled
77
+ return {} unless @started
78
+
79
+ results = StackProf.results
80
+ return {} unless results
81
+ return {} if results.empty?
82
+ return {} if results[:samples] == 0
83
+ return {} unless results[:raw]
84
+
85
+ frame_map = {}
86
+
87
+ frames = results[:frames].to_enum.with_index.map do |frame, idx|
88
+ frame_id, frame_data = frame
89
+
90
+ # need to map over stackprof frame ids to ours
91
+ frame_map[frame_id] = idx
92
+
93
+ file_path = frame_data[:file]
94
+ in_app = in_app?(file_path)
95
+ filename = compute_filename(file_path, in_app)
96
+ function, mod = split_module(frame_data[:name])
97
+
98
+ frame_hash = {
99
+ abs_path: file_path,
100
+ function: function,
101
+ filename: filename,
102
+ in_app: in_app
103
+ }
104
+
105
+ frame_hash[:module] = mod if mod
106
+ frame_hash[:lineno] = frame_data[:line] if frame_data[:line]
107
+
108
+ frame_hash
109
+ end
110
+
111
+ idx = 0
112
+ stacks = []
113
+ num_seen = []
114
+
115
+ # extract stacks from raw
116
+ # raw is a single array of [.., len_stack, *stack_frames(len_stack), num_stack_seen , ..]
117
+ while (len = results[:raw][idx])
118
+ idx += 1
119
+
120
+ # our call graph is reversed
121
+ stack = results[:raw].slice(idx, len).map { |id| frame_map[id] }.compact.reverse
122
+ stacks << stack
123
+
124
+ num_seen << results[:raw][idx + len]
125
+ idx += len + 1
126
+
127
+ log('Unknown frame in stack') if stack.size != len
128
+ end
129
+
130
+ idx = 0
131
+ elapsed_since_start_ns = 0
132
+ samples = []
133
+
134
+ num_seen.each_with_index do |n, i|
135
+ n.times do
136
+ # stackprof deltas are in microseconds
137
+ delta = results[:raw_timestamp_deltas][idx]
138
+ elapsed_since_start_ns += (delta * MICRO_TO_NANO_SECONDS).to_i
139
+ idx += 1
140
+
141
+ # Not sure why but some deltas are very small like 0/1 values,
142
+ # they pollute our flamegraph so just ignore them for now.
143
+ # Open issue at https://github.com/tmm1/stackprof/issues/201
144
+ next if delta < 10
145
+
146
+ samples << {
147
+ stack_id: i,
148
+ # TODO-neel-profiler we need to patch rb_profile_frames and write our own C extension to enable threading info.
149
+ # Till then, on multi-threaded servers like puma, we will get frames from other active threads when the one
150
+ # we're profiling is idle/sleeping/waiting for IO etc.
151
+ # https://bugs.ruby-lang.org/issues/10602
152
+ thread_id: '0',
153
+ elapsed_since_start_ns: elapsed_since_start_ns.to_s
154
+ }
155
+ end
156
+ end
157
+
158
+ log('Some samples thrown away') if samples.size != results[:samples]
159
+
160
+ if samples.size <= 2
161
+ log('Not enough samples, discarding profiler')
162
+ return {}
163
+ end
164
+
165
+ profile = {
166
+ frames: frames,
167
+ stacks: stacks,
168
+ samples: samples
169
+ }
170
+
171
+ {
172
+ event_id: @event_id,
173
+ platform: PLATFORM,
174
+ version: VERSION,
175
+ profile: profile
176
+ }
177
+ end
178
+
179
+ private
180
+
181
+ def log(message)
182
+ Sentry.logger.debug(LOGGER_PROGNAME) { "[Profiler] #{message}" }
183
+ end
184
+
185
+ def in_app?(abs_path)
186
+ abs_path.match?(@in_app_pattern)
187
+ end
188
+
189
+ # copied from stacktrace.rb since I don't want to touch existing code
190
+ # TODO-neel-profiler try to fetch this from stackprof once we patch
191
+ # the native extension
192
+ def compute_filename(abs_path, in_app)
193
+ return nil if abs_path.nil?
194
+
195
+ under_project_root = @project_root && abs_path.start_with?(@project_root)
196
+
197
+ prefix =
198
+ if under_project_root && in_app
199
+ @project_root
200
+ else
201
+ longest_load_path = $LOAD_PATH.select { |path| abs_path.start_with?(path.to_s) }.max_by(&:size)
202
+
203
+ if under_project_root
204
+ longest_load_path || @project_root
205
+ else
206
+ longest_load_path
207
+ end
208
+ end
209
+
210
+ prefix ? abs_path[prefix.to_s.chomp(File::SEPARATOR).length + 1..-1] : abs_path
211
+ end
212
+
213
+ def split_module(name)
214
+ # last module plus class/instance method
215
+ i = name.rindex('::')
216
+ function = i ? name[(i + 2)..-1] : name
217
+ mod = i ? name[0...i] : nil
218
+
219
+ [function, mod]
220
+ end
221
+ end
222
+ end
@@ -0,0 +1,25 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Sentry
4
+ module Puma
5
+ module Server
6
+ def lowlevel_error(e, env, status=500)
7
+ result = super
8
+
9
+ begin
10
+ Sentry.capture_exception(e) do |scope|
11
+ scope.set_rack_env(env)
12
+ end
13
+ rescue
14
+ # if anything happens, we don't want to break the app
15
+ end
16
+
17
+ result
18
+ end
19
+ end
20
+ end
21
+ end
22
+
23
+ if defined?(Puma::Server)
24
+ Sentry.register_patch(Sentry::Puma::Server, Puma::Server)
25
+ end
data/lib/sentry/redis.rb CHANGED
@@ -95,12 +95,7 @@ end
95
95
 
96
96
  if defined?(::Redis::Client)
97
97
  if Gem::Version.new(::Redis::VERSION) < Gem::Version.new("5.0")
98
- Sentry.register_patch do
99
- patch = Sentry::Redis::OldClientPatch
100
- unless Redis::Client.ancestors.include?(patch)
101
- Redis::Client.prepend(patch)
102
- end
103
- end
98
+ Sentry.register_patch(Sentry::Redis::OldClientPatch, ::Redis::Client)
104
99
  elsif defined?(RedisClient)
105
100
  RedisClient.register(Sentry::Redis::GlobalRedisInstrumentation)
106
101
  end
data/lib/sentry/scope.rb CHANGED
@@ -192,6 +192,10 @@ module Sentry
192
192
  # @return [Hash]
193
193
  def set_contexts(contexts_hash)
194
194
  check_argument_type!(contexts_hash, Hash)
195
+ contexts_hash.values.each do |val|
196
+ check_argument_type!(val, Hash)
197
+ end
198
+
195
199
  @contexts.merge!(contexts_hash) do |key, old, new|
196
200
  old.merge(new)
197
201
  end
@@ -305,7 +309,8 @@ module Sentry
305
309
  name: uname[:sysname] || RbConfig::CONFIG["host_os"],
306
310
  version: uname[:version],
307
311
  build: uname[:release],
308
- kernel_version: uname[:version]
312
+ kernel_version: uname[:version],
313
+ machine: uname[:machine]
309
314
  }
310
315
  end
311
316
  end
@@ -1,6 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "sentry/baggage"
4
+ require "sentry/profiler"
4
5
 
5
6
  module Sentry
6
7
  class Transaction < Span
@@ -58,6 +59,10 @@ module Sentry
58
59
  # @return [Hash]
59
60
  attr_reader :contexts
60
61
 
62
+ # The Profiler instance for this transaction.
63
+ # @return [Profiler]
64
+ attr_reader :profiler
65
+
61
66
  def initialize(
62
67
  hub:,
63
68
  name: nil,
@@ -83,6 +88,7 @@ module Sentry
83
88
  @effective_sample_rate = nil
84
89
  @contexts = {}
85
90
  @measurements = {}
91
+ @profiler = Profiler.new(@configuration)
86
92
  init_span_recorder
87
93
  end
88
94
 
@@ -254,6 +260,8 @@ module Sentry
254
260
  @name = UNLABELD_NAME
255
261
  end
256
262
 
263
+ @profiler.stop
264
+
257
265
  if @sampled
258
266
  event = hub.current_client.event_from_transaction(self)
259
267
  hub.capture_event(event)
@@ -288,6 +296,13 @@ module Sentry
288
296
  @contexts[key] = value
289
297
  end
290
298
 
299
+ # Start the profiler.
300
+ # @return [void]
301
+ def start_profiler!
302
+ profiler.set_initial_sample_decision(sampled)
303
+ profiler.start
304
+ end
305
+
291
306
  protected
292
307
 
293
308
  def init_span_recorder(limit = 1000)
@@ -17,6 +17,9 @@ module Sentry
17
17
  # @return [Float, nil]
18
18
  attr_reader :start_timestamp
19
19
 
20
+ # @return [Hash, nil]
21
+ attr_accessor :profile
22
+
20
23
  def initialize(transaction:, **options)
21
24
  super(**options)
22
25
 
@@ -32,6 +35,8 @@ module Sentry
32
35
 
33
36
  finished_spans = transaction.span_recorder.spans.select { |span| span.timestamp && span != transaction }
34
37
  self.spans = finished_spans.map(&:to_hash)
38
+
39
+ populate_profile(transaction)
35
40
  end
36
41
 
37
42
  # Sets the event's start_timestamp.
@@ -49,5 +54,30 @@ module Sentry
49
54
  data[:measurements] = @measurements
50
55
  data
51
56
  end
57
+
58
+ private
59
+
60
+ def populate_profile(transaction)
61
+ profile_hash = transaction.profiler.to_hash
62
+ return if profile_hash.empty?
63
+
64
+ profile_hash.merge!(
65
+ environment: environment,
66
+ release: release,
67
+ timestamp: Time.at(start_timestamp).iso8601,
68
+ device: { architecture: Scope.os_context[:machine] },
69
+ os: { name: Scope.os_context[:name], version: Scope.os_context[:version] },
70
+ runtime: Scope.runtime_context,
71
+ transaction: {
72
+ id: event_id,
73
+ name: transaction.name,
74
+ trace_id: transaction.trace_id,
75
+ # TODO-neel-profiler stubbed for now, see thread_id note in profiler.rb
76
+ active_thead_id: '0'
77
+ }
78
+ )
79
+
80
+ self.profile = profile_hash
81
+ end
52
82
  end
53
83
  end
@@ -154,6 +154,13 @@ module Sentry
154
154
  event_payload
155
155
  )
156
156
 
157
+ if event.is_a?(TransactionEvent) && event.profile
158
+ envelope.add_item(
159
+ { type: 'profile', content_type: 'application/json' },
160
+ event.profile
161
+ )
162
+ end
163
+
157
164
  client_report_headers, client_report_payload = fetch_pending_client_report
158
165
  envelope.add_item(client_report_headers, client_report_payload) if client_report_headers
159
166
 
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Sentry
4
- VERSION = "5.8.0"
4
+ VERSION = "5.9.0"
5
5
  end
data/lib/sentry-ruby.rb CHANGED
@@ -73,8 +73,18 @@ module Sentry
73
73
  ##### Patch Registration #####
74
74
 
75
75
  # @!visibility private
76
- def register_patch(&block)
77
- registered_patches << block
76
+ def register_patch(patch = nil, target = nil, &block)
77
+ if patch && block
78
+ raise ArgumentError.new("Please provide either a patch and its target OR a block, but not both")
79
+ end
80
+
81
+ if block
82
+ registered_patches << block
83
+ else
84
+ registered_patches << proc do
85
+ target.send(:prepend, patch) unless target.ancestors.include?(patch)
86
+ end
87
+ end
78
88
  end
79
89
 
80
90
  # @!visibility private
@@ -509,3 +519,4 @@ end
509
519
  # patches
510
520
  require "sentry/net/http"
511
521
  require "sentry/redis"
522
+ require "sentry/puma"
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: sentry-ruby
3
3
  version: !ruby/object:Gem::Version
4
- version: 5.8.0
4
+ version: 5.9.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Sentry Team
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2023-02-06 00:00:00.000000000 Z
11
+ date: 2023-04-19 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: concurrent-ruby
@@ -78,6 +78,8 @@ files:
78
78
  - lib/sentry/linecache.rb
79
79
  - lib/sentry/logger.rb
80
80
  - lib/sentry/net/http.rb
81
+ - lib/sentry/profiler.rb
82
+ - lib/sentry/puma.rb
81
83
  - lib/sentry/rack.rb
82
84
  - lib/sentry/rack/capture_exceptions.rb
83
85
  - lib/sentry/rake.rb