sentry-ruby-core 5.20.1 → 5.22.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.
- checksums.yaml +4 -4
- data/Gemfile +4 -0
- data/Rakefile +2 -0
- data/bin/console +1 -0
- data/lib/sentry/backtrace.rb +2 -2
- data/lib/sentry/baggage.rb +1 -1
- data/lib/sentry/configuration.rb +27 -9
- data/lib/sentry/cron/monitor_check_ins.rb +2 -0
- data/lib/sentry/envelope/item.rb +88 -0
- data/lib/sentry/envelope.rb +2 -85
- data/lib/sentry/excon/middleware.rb +77 -0
- data/lib/sentry/excon.rb +10 -0
- data/lib/sentry/interfaces/single_exception.rb +2 -2
- data/lib/sentry/profiler/helpers.rb +46 -0
- data/lib/sentry/profiler.rb +7 -37
- data/lib/sentry/rack/capture_exceptions.rb +1 -1
- data/lib/sentry/rspec.rb +91 -0
- data/lib/sentry/session_flusher.rb +8 -4
- data/lib/sentry/test_helper.rb +2 -0
- data/lib/sentry/transaction.rb +2 -2
- data/lib/sentry/transaction_event.rb +1 -2
- data/lib/sentry/utils/http_tracing.rb +20 -1
- data/lib/sentry/vernier/output.rb +89 -0
- data/lib/sentry/vernier/profiler.rb +130 -0
- data/lib/sentry/version.rb +1 -1
- data/lib/sentry-ruby.rb +12 -6
- data/sentry-ruby-core.gemspec +2 -0
- data/sentry-ruby.gemspec +2 -0
- metadata +12 -5
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: d6ea6ea7aa1656256faf78dd295dddffdea94bac58ff263f49d3a67433a35fd6
|
4
|
+
data.tar.gz: 9f8b2d74f31fc7a313d4170bdbbc0707b61c52ae658102c6d353a09f84df81e9
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 312560c682b07cee10690c180e7e2780daf616574dfcb792bebf1315feeb9a47ad428dbb7c7627439e781b8db85c9bc9166feb77dfa14974373df12982ae6324
|
7
|
+
data.tar.gz: 7f2d782aa3b58c473c0c06efbcb1e8ff8ff039a5d396d8066187d2ff863008145fb08a7ad97eedc69841b685d09da01a56c67938bb19032d65fab7ddbce05e7f
|
data/Gemfile
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
source "https://rubygems.org"
|
2
4
|
git_source(:github) { |name| "https://github.com/#{name}.git" }
|
3
5
|
|
@@ -14,6 +16,7 @@ gem "puma"
|
|
14
16
|
|
15
17
|
gem "timecop"
|
16
18
|
gem "stackprof" unless RUBY_PLATFORM == "java"
|
19
|
+
gem "vernier", platforms: :ruby if RUBY_VERSION >= "3.2.1"
|
17
20
|
|
18
21
|
gem "graphql", ">= 2.2.6" if RUBY_VERSION.to_f >= 2.7
|
19
22
|
|
@@ -25,5 +28,6 @@ gem "benchmark-memory"
|
|
25
28
|
gem "yard", github: "lsegal/yard"
|
26
29
|
gem "webrick"
|
27
30
|
gem "faraday"
|
31
|
+
gem "excon"
|
28
32
|
|
29
33
|
eval_gemfile File.expand_path("../Gemfile", __dir__)
|
data/Rakefile
CHANGED
data/bin/console
CHANGED
data/lib/sentry/backtrace.rb
CHANGED
@@ -13,10 +13,10 @@ module Sentry
|
|
13
13
|
^ \s* (?: [a-zA-Z]: | uri:classloader: )? ([^:]+ | <.*>):
|
14
14
|
(\d+)
|
15
15
|
(?: :in\s('|`)([^']+)')?$
|
16
|
-
/x
|
16
|
+
/x
|
17
17
|
|
18
18
|
# org.jruby.runtime.callsite.CachingCallSite.call(CachingCallSite.java:170)
|
19
|
-
JAVA_INPUT_FORMAT = /^(.+)\.([^\.]+)\(([^\:]+)\:(\d+)\)
|
19
|
+
JAVA_INPUT_FORMAT = /^(.+)\.([^\.]+)\(([^\:]+)\:(\d+)\)$/
|
20
20
|
|
21
21
|
# The file portion of the line (such as app/models/user.rb)
|
22
22
|
attr_reader :file
|
data/lib/sentry/baggage.rb
CHANGED
data/lib/sentry/configuration.rb
CHANGED
@@ -3,8 +3,8 @@
|
|
3
3
|
require "concurrent/utility/processor_counter"
|
4
4
|
|
5
5
|
require "sentry/utils/exception_cause_chain"
|
6
|
-
require
|
7
|
-
require
|
6
|
+
require "sentry/utils/custom_inspection"
|
7
|
+
require "sentry/utils/env_helper"
|
8
8
|
require "sentry/dsn"
|
9
9
|
require "sentry/release_detector"
|
10
10
|
require "sentry/transport/configuration"
|
@@ -291,6 +291,10 @@ module Sentry
|
|
291
291
|
# @return [Symbol]
|
292
292
|
attr_reader :instrumenter
|
293
293
|
|
294
|
+
# The profiler class
|
295
|
+
# @return [Class]
|
296
|
+
attr_reader :profiler_class
|
297
|
+
|
294
298
|
# Take a float between 0.0 and 1.0 as the sample rate for capturing profiles.
|
295
299
|
# Note that this rate is relative to traces_sample_rate / traces_sampler,
|
296
300
|
# i.e. the profile is sampled by this rate after the transaction is sampled.
|
@@ -331,19 +335,19 @@ module Sentry
|
|
331
335
|
].freeze
|
332
336
|
|
333
337
|
HEROKU_DYNO_METADATA_MESSAGE = "You are running on Heroku but haven't enabled Dyno Metadata. For Sentry's "\
|
334
|
-
"release detection to work correctly, please run `heroku labs:enable runtime-dyno-metadata`"
|
338
|
+
"release detection to work correctly, please run `heroku labs:enable runtime-dyno-metadata`"
|
335
339
|
|
336
|
-
LOG_PREFIX = "** [Sentry] "
|
337
|
-
MODULE_SEPARATOR = "::"
|
340
|
+
LOG_PREFIX = "** [Sentry] "
|
341
|
+
MODULE_SEPARATOR = "::"
|
338
342
|
SKIP_INSPECTION_ATTRIBUTES = [:@linecache, :@stacktrace_builder]
|
339
343
|
|
340
344
|
INSTRUMENTERS = [:sentry, :otel]
|
341
345
|
|
342
|
-
PROPAGATION_TARGETS_MATCH_ALL =
|
346
|
+
PROPAGATION_TARGETS_MATCH_ALL = /.*/
|
343
347
|
|
344
348
|
DEFAULT_PATCHES = %i[redis puma http].freeze
|
345
349
|
|
346
|
-
APP_DIRS_PATTERN = /(bin|exe|app|config|lib|test|spec)
|
350
|
+
APP_DIRS_PATTERN = /(bin|exe|app|config|lib|test|spec)/
|
347
351
|
|
348
352
|
class << self
|
349
353
|
# Post initialization callbacks are called at the end of initialization process
|
@@ -387,9 +391,9 @@ module Sentry
|
|
387
391
|
self.auto_session_tracking = true
|
388
392
|
self.enable_backpressure_handling = false
|
389
393
|
self.trusted_proxies = []
|
390
|
-
self.dsn = ENV[
|
394
|
+
self.dsn = ENV["SENTRY_DSN"]
|
391
395
|
|
392
|
-
spotlight_env = ENV[
|
396
|
+
spotlight_env = ENV["SENTRY_SPOTLIGHT"]
|
393
397
|
spotlight_bool = Sentry::Utils::EnvHelper.env_to_bool(spotlight_env, strict: true)
|
394
398
|
self.spotlight = spotlight_bool.nil? ? (spotlight_env || false) : spotlight_bool
|
395
399
|
self.server_name = server_name_from_env
|
@@ -403,6 +407,8 @@ module Sentry
|
|
403
407
|
self.traces_sampler = nil
|
404
408
|
self.enable_tracing = nil
|
405
409
|
|
410
|
+
self.profiler_class = Sentry::Profiler
|
411
|
+
|
406
412
|
@transport = Transport::Configuration.new
|
407
413
|
@cron = Cron::Configuration.new
|
408
414
|
@metrics = Metrics::Configuration.new
|
@@ -498,6 +504,18 @@ module Sentry
|
|
498
504
|
@profiles_sample_rate = profiles_sample_rate
|
499
505
|
end
|
500
506
|
|
507
|
+
def profiler_class=(profiler_class)
|
508
|
+
if profiler_class == Sentry::Vernier::Profiler
|
509
|
+
begin
|
510
|
+
require "vernier"
|
511
|
+
rescue LoadError
|
512
|
+
raise ArgumentError, "Please add the 'vernier' gem to your Gemfile to use the Vernier profiler with Sentry."
|
513
|
+
end
|
514
|
+
end
|
515
|
+
|
516
|
+
@profiler_class = profiler_class
|
517
|
+
end
|
518
|
+
|
501
519
|
def sending_allowed?
|
502
520
|
spotlight || sending_to_dsn_allowed?
|
503
521
|
end
|
@@ -0,0 +1,88 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Sentry
|
4
|
+
# @api private
|
5
|
+
class Envelope::Item
|
6
|
+
STACKTRACE_FRAME_LIMIT_ON_OVERSIZED_PAYLOAD = 500
|
7
|
+
MAX_SERIALIZED_PAYLOAD_SIZE = 1024 * 1000
|
8
|
+
|
9
|
+
SIZE_LIMITS = Hash.new(MAX_SERIALIZED_PAYLOAD_SIZE).update(
|
10
|
+
"profile" => 1024 * 1000 * 50
|
11
|
+
)
|
12
|
+
|
13
|
+
attr_reader :size_limit, :headers, :payload, :type, :data_category
|
14
|
+
|
15
|
+
# rate limits and client reports use the data_category rather than envelope item type
|
16
|
+
def self.data_category(type)
|
17
|
+
case type
|
18
|
+
when "session", "attachment", "transaction", "profile", "span" then type
|
19
|
+
when "sessions" then "session"
|
20
|
+
when "check_in" then "monitor"
|
21
|
+
when "statsd", "metric_meta" then "metric_bucket"
|
22
|
+
when "event" then "error"
|
23
|
+
when "client_report" then "internal"
|
24
|
+
else "default"
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
def initialize(headers, payload)
|
29
|
+
@headers = headers
|
30
|
+
@payload = payload
|
31
|
+
@type = headers[:type] || "event"
|
32
|
+
@data_category = self.class.data_category(type)
|
33
|
+
@size_limit = SIZE_LIMITS[type]
|
34
|
+
end
|
35
|
+
|
36
|
+
def to_s
|
37
|
+
[JSON.generate(@headers), @payload.is_a?(String) ? @payload : JSON.generate(@payload)].join("\n")
|
38
|
+
end
|
39
|
+
|
40
|
+
def serialize
|
41
|
+
result = to_s
|
42
|
+
|
43
|
+
if result.bytesize > size_limit
|
44
|
+
remove_breadcrumbs!
|
45
|
+
result = to_s
|
46
|
+
end
|
47
|
+
|
48
|
+
if result.bytesize > size_limit
|
49
|
+
reduce_stacktrace!
|
50
|
+
result = to_s
|
51
|
+
end
|
52
|
+
|
53
|
+
[result, result.bytesize > size_limit]
|
54
|
+
end
|
55
|
+
|
56
|
+
def size_breakdown
|
57
|
+
payload.map do |key, value|
|
58
|
+
"#{key}: #{JSON.generate(value).bytesize}"
|
59
|
+
end.join(", ")
|
60
|
+
end
|
61
|
+
|
62
|
+
private
|
63
|
+
|
64
|
+
def remove_breadcrumbs!
|
65
|
+
if payload.key?(:breadcrumbs)
|
66
|
+
payload.delete(:breadcrumbs)
|
67
|
+
elsif payload.key?("breadcrumbs")
|
68
|
+
payload.delete("breadcrumbs")
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
def reduce_stacktrace!
|
73
|
+
if exceptions = payload.dig(:exception, :values) || payload.dig("exception", "values")
|
74
|
+
exceptions.each do |exception|
|
75
|
+
# in most cases there is only one exception (2 or 3 when have multiple causes), so we won't loop through this double condition much
|
76
|
+
traces = exception.dig(:stacktrace, :frames) || exception.dig("stacktrace", "frames")
|
77
|
+
|
78
|
+
if traces && traces.size > STACKTRACE_FRAME_LIMIT_ON_OVERSIZED_PAYLOAD
|
79
|
+
size_on_both_ends = STACKTRACE_FRAME_LIMIT_ON_OVERSIZED_PAYLOAD / 2
|
80
|
+
traces.replace(
|
81
|
+
traces[0..(size_on_both_ends - 1)] + traces[-size_on_both_ends..-1],
|
82
|
+
)
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
data/lib/sentry/envelope.rb
CHANGED
@@ -3,91 +3,6 @@
|
|
3
3
|
module Sentry
|
4
4
|
# @api private
|
5
5
|
class Envelope
|
6
|
-
class Item
|
7
|
-
STACKTRACE_FRAME_LIMIT_ON_OVERSIZED_PAYLOAD = 500
|
8
|
-
MAX_SERIALIZED_PAYLOAD_SIZE = 1024 * 1000
|
9
|
-
|
10
|
-
attr_accessor :headers, :payload
|
11
|
-
|
12
|
-
def initialize(headers, payload)
|
13
|
-
@headers = headers
|
14
|
-
@payload = payload
|
15
|
-
end
|
16
|
-
|
17
|
-
def type
|
18
|
-
@headers[:type] || "event"
|
19
|
-
end
|
20
|
-
|
21
|
-
# rate limits and client reports use the data_category rather than envelope item type
|
22
|
-
def self.data_category(type)
|
23
|
-
case type
|
24
|
-
when "session", "attachment", "transaction", "profile", "span" then type
|
25
|
-
when "sessions" then "session"
|
26
|
-
when "check_in" then "monitor"
|
27
|
-
when "statsd", "metric_meta" then "metric_bucket"
|
28
|
-
when "event" then "error"
|
29
|
-
when "client_report" then "internal"
|
30
|
-
else "default"
|
31
|
-
end
|
32
|
-
end
|
33
|
-
|
34
|
-
def data_category
|
35
|
-
self.class.data_category(type)
|
36
|
-
end
|
37
|
-
|
38
|
-
def to_s
|
39
|
-
[JSON.generate(@headers), @payload.is_a?(String) ? @payload : JSON.generate(@payload)].join("\n")
|
40
|
-
end
|
41
|
-
|
42
|
-
def serialize
|
43
|
-
result = to_s
|
44
|
-
|
45
|
-
if result.bytesize > MAX_SERIALIZED_PAYLOAD_SIZE
|
46
|
-
remove_breadcrumbs!
|
47
|
-
result = to_s
|
48
|
-
end
|
49
|
-
|
50
|
-
if result.bytesize > MAX_SERIALIZED_PAYLOAD_SIZE
|
51
|
-
reduce_stacktrace!
|
52
|
-
result = to_s
|
53
|
-
end
|
54
|
-
|
55
|
-
[result, result.bytesize > MAX_SERIALIZED_PAYLOAD_SIZE]
|
56
|
-
end
|
57
|
-
|
58
|
-
def size_breakdown
|
59
|
-
payload.map do |key, value|
|
60
|
-
"#{key}: #{JSON.generate(value).bytesize}"
|
61
|
-
end.join(", ")
|
62
|
-
end
|
63
|
-
|
64
|
-
private
|
65
|
-
|
66
|
-
def remove_breadcrumbs!
|
67
|
-
if payload.key?(:breadcrumbs)
|
68
|
-
payload.delete(:breadcrumbs)
|
69
|
-
elsif payload.key?("breadcrumbs")
|
70
|
-
payload.delete("breadcrumbs")
|
71
|
-
end
|
72
|
-
end
|
73
|
-
|
74
|
-
def reduce_stacktrace!
|
75
|
-
if exceptions = payload.dig(:exception, :values) || payload.dig("exception", "values")
|
76
|
-
exceptions.each do |exception|
|
77
|
-
# in most cases there is only one exception (2 or 3 when have multiple causes), so we won't loop through this double condition much
|
78
|
-
traces = exception.dig(:stacktrace, :frames) || exception.dig("stacktrace", "frames")
|
79
|
-
|
80
|
-
if traces && traces.size > STACKTRACE_FRAME_LIMIT_ON_OVERSIZED_PAYLOAD
|
81
|
-
size_on_both_ends = STACKTRACE_FRAME_LIMIT_ON_OVERSIZED_PAYLOAD / 2
|
82
|
-
traces.replace(
|
83
|
-
traces[0..(size_on_both_ends - 1)] + traces[-size_on_both_ends..-1],
|
84
|
-
)
|
85
|
-
end
|
86
|
-
end
|
87
|
-
end
|
88
|
-
end
|
89
|
-
end
|
90
|
-
|
91
6
|
attr_accessor :headers, :items
|
92
7
|
|
93
8
|
def initialize(headers = {})
|
@@ -108,3 +23,5 @@ module Sentry
|
|
108
23
|
end
|
109
24
|
end
|
110
25
|
end
|
26
|
+
|
27
|
+
require_relative "envelope/item"
|
@@ -0,0 +1,77 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Sentry
|
4
|
+
module Excon
|
5
|
+
OP_NAME = "http.client"
|
6
|
+
|
7
|
+
class Middleware < ::Excon::Middleware::Base
|
8
|
+
def initialize(stack)
|
9
|
+
super
|
10
|
+
@instrumenter = Instrumenter.new
|
11
|
+
end
|
12
|
+
|
13
|
+
def request_call(datum)
|
14
|
+
@instrumenter.start_transaction(datum)
|
15
|
+
@stack.request_call(datum)
|
16
|
+
end
|
17
|
+
|
18
|
+
def response_call(datum)
|
19
|
+
@instrumenter.finish_transaction(datum)
|
20
|
+
@stack.response_call(datum)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
class Instrumenter
|
25
|
+
SPAN_ORIGIN = "auto.http.excon"
|
26
|
+
BREADCRUMB_CATEGORY = "http"
|
27
|
+
|
28
|
+
include Utils::HttpTracing
|
29
|
+
|
30
|
+
def start_transaction(env)
|
31
|
+
return unless Sentry.initialized?
|
32
|
+
|
33
|
+
current_span = Sentry.get_current_scope&.span
|
34
|
+
@span = current_span&.start_child(op: OP_NAME, start_timestamp: Sentry.utc_now.to_f, origin: SPAN_ORIGIN)
|
35
|
+
|
36
|
+
request_info = extract_request_info(env)
|
37
|
+
|
38
|
+
if propagate_trace?(request_info[:url])
|
39
|
+
set_propagation_headers(env[:headers])
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
def finish_transaction(response)
|
44
|
+
return unless @span
|
45
|
+
|
46
|
+
response_status = response[:response][:status]
|
47
|
+
request_info = extract_request_info(response)
|
48
|
+
|
49
|
+
if record_sentry_breadcrumb?
|
50
|
+
record_sentry_breadcrumb(request_info, response_status)
|
51
|
+
end
|
52
|
+
|
53
|
+
set_span_info(@span, request_info, response_status)
|
54
|
+
ensure
|
55
|
+
@span&.finish
|
56
|
+
end
|
57
|
+
|
58
|
+
private
|
59
|
+
|
60
|
+
def extract_request_info(env)
|
61
|
+
url = env[:scheme] + "://" + env[:hostname] + env[:path]
|
62
|
+
result = { method: env[:method].to_s.upcase, url: url }
|
63
|
+
|
64
|
+
if Sentry.configuration.send_default_pii
|
65
|
+
result[:query] = env[:query]
|
66
|
+
|
67
|
+
# Handle excon 1.0.0+
|
68
|
+
result[:query] = build_nested_query(result[:query]) unless result[:query].is_a?(String)
|
69
|
+
|
70
|
+
result[:body] = env[:body]
|
71
|
+
end
|
72
|
+
|
73
|
+
result
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
data/lib/sentry/excon.rb
ADDED
@@ -0,0 +1,10 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
Sentry.register_patch(:excon) do
|
4
|
+
if defined?(::Excon)
|
5
|
+
require "sentry/excon/middleware"
|
6
|
+
if Excon.defaults[:middlewares]
|
7
|
+
Excon.defaults[:middlewares] << Sentry::Excon::Middleware unless Excon.defaults[:middlewares].include?(Sentry::Excon::Middleware)
|
8
|
+
end
|
9
|
+
end
|
10
|
+
end
|
@@ -7,8 +7,8 @@ module Sentry
|
|
7
7
|
include CustomInspection
|
8
8
|
|
9
9
|
SKIP_INSPECTION_ATTRIBUTES = [:@stacktrace]
|
10
|
-
PROBLEMATIC_LOCAL_VALUE_REPLACEMENT = "[ignored due to error]"
|
11
|
-
OMISSION_MARK = "..."
|
10
|
+
PROBLEMATIC_LOCAL_VALUE_REPLACEMENT = "[ignored due to error]"
|
11
|
+
OMISSION_MARK = "..."
|
12
12
|
MAX_LOCAL_BYTES = 1024
|
13
13
|
|
14
14
|
attr_reader :type, :module, :thread_id, :stacktrace, :mechanism
|
@@ -0,0 +1,46 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "securerandom"
|
4
|
+
|
5
|
+
module Sentry
|
6
|
+
class Profiler
|
7
|
+
module Helpers
|
8
|
+
def in_app?(abs_path)
|
9
|
+
abs_path.match?(@in_app_pattern)
|
10
|
+
end
|
11
|
+
|
12
|
+
# copied from stacktrace.rb since I don't want to touch existing code
|
13
|
+
# TODO-neel-profiler try to fetch this from stackprof once we patch
|
14
|
+
# the native extension
|
15
|
+
def compute_filename(abs_path, in_app)
|
16
|
+
return nil if abs_path.nil?
|
17
|
+
|
18
|
+
under_project_root = @project_root && abs_path.start_with?(@project_root)
|
19
|
+
|
20
|
+
prefix =
|
21
|
+
if under_project_root && in_app
|
22
|
+
@project_root
|
23
|
+
else
|
24
|
+
longest_load_path = $LOAD_PATH.select { |path| abs_path.start_with?(path.to_s) }.max_by(&:size)
|
25
|
+
|
26
|
+
if under_project_root
|
27
|
+
longest_load_path || @project_root
|
28
|
+
else
|
29
|
+
longest_load_path
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
prefix ? abs_path[prefix.to_s.chomp(File::SEPARATOR).length + 1..-1] : abs_path
|
34
|
+
end
|
35
|
+
|
36
|
+
def split_module(name)
|
37
|
+
# last module plus class/instance method
|
38
|
+
i = name.rindex("::")
|
39
|
+
function = i ? name[(i + 2)..-1] : name
|
40
|
+
mod = i ? name[0...i] : nil
|
41
|
+
|
42
|
+
[function, mod]
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
data/lib/sentry/profiler.rb
CHANGED
@@ -1,9 +1,12 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require "securerandom"
|
4
|
+
require_relative "profiler/helpers"
|
4
5
|
|
5
6
|
module Sentry
|
6
7
|
class Profiler
|
8
|
+
include Profiler::Helpers
|
9
|
+
|
7
10
|
VERSION = "1"
|
8
11
|
PLATFORM = "ruby"
|
9
12
|
# 101 Hz in microseconds
|
@@ -44,6 +47,10 @@ module Sentry
|
|
44
47
|
log("Stopped")
|
45
48
|
end
|
46
49
|
|
50
|
+
def active_thread_id
|
51
|
+
"0"
|
52
|
+
end
|
53
|
+
|
47
54
|
# Sets initial sampling decision of the profile.
|
48
55
|
# @return [void]
|
49
56
|
def set_initial_sample_decision(transaction_sampled)
|
@@ -188,43 +195,6 @@ module Sentry
|
|
188
195
|
Sentry.logger.debug(LOGGER_PROGNAME) { "[Profiler] #{message}" }
|
189
196
|
end
|
190
197
|
|
191
|
-
def in_app?(abs_path)
|
192
|
-
abs_path.match?(@in_app_pattern)
|
193
|
-
end
|
194
|
-
|
195
|
-
# copied from stacktrace.rb since I don't want to touch existing code
|
196
|
-
# TODO-neel-profiler try to fetch this from stackprof once we patch
|
197
|
-
# the native extension
|
198
|
-
def compute_filename(abs_path, in_app)
|
199
|
-
return nil if abs_path.nil?
|
200
|
-
|
201
|
-
under_project_root = @project_root && abs_path.start_with?(@project_root)
|
202
|
-
|
203
|
-
prefix =
|
204
|
-
if under_project_root && in_app
|
205
|
-
@project_root
|
206
|
-
else
|
207
|
-
longest_load_path = $LOAD_PATH.select { |path| abs_path.start_with?(path.to_s) }.max_by(&:size)
|
208
|
-
|
209
|
-
if under_project_root
|
210
|
-
longest_load_path || @project_root
|
211
|
-
else
|
212
|
-
longest_load_path
|
213
|
-
end
|
214
|
-
end
|
215
|
-
|
216
|
-
prefix ? abs_path[prefix.to_s.chomp(File::SEPARATOR).length + 1..-1] : abs_path
|
217
|
-
end
|
218
|
-
|
219
|
-
def split_module(name)
|
220
|
-
# last module plus class/instance method
|
221
|
-
i = name.rindex("::")
|
222
|
-
function = i ? name[(i + 2)..-1] : name
|
223
|
-
mod = i ? name[0...i] : nil
|
224
|
-
|
225
|
-
[function, mod]
|
226
|
-
end
|
227
|
-
|
228
198
|
def record_lost_event(reason)
|
229
199
|
Sentry.get_current_client&.transport&.record_lost_event(reason, "profile")
|
230
200
|
end
|
data/lib/sentry/rspec.rb
ADDED
@@ -0,0 +1,91 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
RSpec::Matchers.define :include_sentry_event do |event_message = "", **opts|
|
4
|
+
match do |sentry_events|
|
5
|
+
@expected_exception = expected_exception(**opts)
|
6
|
+
@context = context(**opts)
|
7
|
+
@tags = tags(**opts)
|
8
|
+
|
9
|
+
@expected_event = expected_event(event_message)
|
10
|
+
@matched_event = find_matched_event(event_message, sentry_events)
|
11
|
+
|
12
|
+
return false unless @matched_event
|
13
|
+
|
14
|
+
[verify_context(), verify_tags()].all?
|
15
|
+
end
|
16
|
+
|
17
|
+
chain :with_context do |context|
|
18
|
+
@context = context
|
19
|
+
end
|
20
|
+
|
21
|
+
chain :with_tags do |tags|
|
22
|
+
@tags = tags
|
23
|
+
end
|
24
|
+
|
25
|
+
failure_message do |sentry_events|
|
26
|
+
info = ["Failed to find event matching:\n"]
|
27
|
+
info << " message: #{@expected_event.message.inspect}"
|
28
|
+
info << " exception: #{@expected_exception.inspect}"
|
29
|
+
info << " context: #{@context.inspect}"
|
30
|
+
info << " tags: #{@tags.inspect}"
|
31
|
+
info << "\n"
|
32
|
+
info << "Captured events:\n"
|
33
|
+
info << dump_events(sentry_events)
|
34
|
+
info.join("\n")
|
35
|
+
end
|
36
|
+
|
37
|
+
def expected_event(event_message)
|
38
|
+
if @expected_exception
|
39
|
+
Sentry.get_current_client.event_from_exception(@expected_exception)
|
40
|
+
else
|
41
|
+
Sentry.get_current_client.event_from_message(event_message)
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
def expected_exception(**opts)
|
46
|
+
opts[:exception].new(opts[:message]) if opts[:exception]
|
47
|
+
end
|
48
|
+
|
49
|
+
def context(**opts)
|
50
|
+
opts.fetch(:context, @context || {})
|
51
|
+
end
|
52
|
+
|
53
|
+
def tags(**opts)
|
54
|
+
opts.fetch(:tags, @tags || {})
|
55
|
+
end
|
56
|
+
|
57
|
+
def find_matched_event(event_message, sentry_events)
|
58
|
+
@matched_event ||= sentry_events
|
59
|
+
.find { |event|
|
60
|
+
if @expected_exception
|
61
|
+
# Is it OK that we only compare the first exception?
|
62
|
+
event_exception = event.exception.values.first
|
63
|
+
expected_event_exception = @expected_event.exception.values.first
|
64
|
+
|
65
|
+
event_exception.type == expected_event_exception.type && event_exception.value == expected_event_exception.value
|
66
|
+
else
|
67
|
+
event.message == @expected_event.message
|
68
|
+
end
|
69
|
+
}
|
70
|
+
end
|
71
|
+
|
72
|
+
def dump_events(sentry_events)
|
73
|
+
sentry_events.map(&Kernel.method(:Hash)).map do |hash|
|
74
|
+
hash.select { |k, _| [:message, :contexts, :tags, :exception].include?(k) }
|
75
|
+
end.map do |hash|
|
76
|
+
JSON.pretty_generate(hash)
|
77
|
+
end.join("\n\n")
|
78
|
+
end
|
79
|
+
|
80
|
+
def verify_context
|
81
|
+
return true if @context.empty?
|
82
|
+
|
83
|
+
@matched_event.contexts.any? { |key, value| value == @context[key] }
|
84
|
+
end
|
85
|
+
|
86
|
+
def verify_tags
|
87
|
+
return true if @tags.empty?
|
88
|
+
|
89
|
+
@tags.all? { |key, value| @matched_event.tags.include?(key) && @matched_event.tags[key] == value }
|
90
|
+
end
|
91
|
+
end
|
@@ -10,6 +10,7 @@ module Sentry
|
|
10
10
|
@pending_aggregates = {}
|
11
11
|
@release = configuration.release
|
12
12
|
@environment = configuration.environment
|
13
|
+
@mutex = Mutex.new
|
13
14
|
|
14
15
|
log_debug("[Sessions] Sessions won't be captured without a valid release") unless @release
|
15
16
|
end
|
@@ -18,7 +19,6 @@ module Sentry
|
|
18
19
|
return if @pending_aggregates.empty?
|
19
20
|
|
20
21
|
@client.capture_envelope(pending_envelope)
|
21
|
-
@pending_aggregates = {}
|
22
22
|
end
|
23
23
|
|
24
24
|
alias_method :run, :flush
|
@@ -42,11 +42,15 @@ module Sentry
|
|
42
42
|
end
|
43
43
|
|
44
44
|
def pending_envelope
|
45
|
-
|
45
|
+
aggregates = @mutex.synchronize do
|
46
|
+
aggregates = @pending_aggregates.values
|
47
|
+
@pending_aggregates = {}
|
48
|
+
aggregates
|
49
|
+
end
|
46
50
|
|
51
|
+
envelope = Envelope.new
|
47
52
|
header = { type: "sessions" }
|
48
|
-
payload = { attrs: attrs, aggregates:
|
49
|
-
|
53
|
+
payload = { attrs: attrs, aggregates: aggregates }
|
50
54
|
envelope.add_item(header, payload)
|
51
55
|
envelope
|
52
56
|
end
|
data/lib/sentry/test_helper.rb
CHANGED
data/lib/sentry/transaction.rb
CHANGED
@@ -9,7 +9,7 @@ module Sentry
|
|
9
9
|
# @deprecated Use Sentry::PropagationContext::SENTRY_TRACE_REGEXP instead.
|
10
10
|
SENTRY_TRACE_REGEXP = PropagationContext::SENTRY_TRACE_REGEXP
|
11
11
|
|
12
|
-
UNLABELD_NAME = "<unlabeled transaction>"
|
12
|
+
UNLABELD_NAME = "<unlabeled transaction>"
|
13
13
|
MESSAGE_PREFIX = "[Tracing]"
|
14
14
|
|
15
15
|
# https://develop.sentry.dev/sdk/event-payloads/transaction/#transaction-annotations
|
@@ -85,7 +85,7 @@ module Sentry
|
|
85
85
|
@effective_sample_rate = nil
|
86
86
|
@contexts = {}
|
87
87
|
@measurements = {}
|
88
|
-
@profiler =
|
88
|
+
@profiler = @configuration.profiler_class.new(@configuration)
|
89
89
|
init_span_recorder
|
90
90
|
end
|
91
91
|
|
@@ -74,8 +74,7 @@ module Sentry
|
|
74
74
|
id: event_id,
|
75
75
|
name: transaction.name,
|
76
76
|
trace_id: transaction.trace_id,
|
77
|
-
|
78
|
-
active_thead_id: "0"
|
77
|
+
active_thread_id: transaction.profiler.active_thread_id.to_s
|
79
78
|
}
|
80
79
|
)
|
81
80
|
|
@@ -19,7 +19,7 @@ module Sentry
|
|
19
19
|
crumb = Sentry::Breadcrumb.new(
|
20
20
|
level: :info,
|
21
21
|
category: self.class::BREADCRUMB_CATEGORY,
|
22
|
-
type:
|
22
|
+
type: "info",
|
23
23
|
data: { status: response_status, **request_info }
|
24
24
|
)
|
25
25
|
|
@@ -36,6 +36,25 @@ module Sentry
|
|
36
36
|
Sentry.configuration.propagate_traces &&
|
37
37
|
Sentry.configuration.trace_propagation_targets.any? { |target| url.match?(target) }
|
38
38
|
end
|
39
|
+
|
40
|
+
# Kindly borrowed from Rack::Utils
|
41
|
+
def build_nested_query(value, prefix = nil)
|
42
|
+
case value
|
43
|
+
when Array
|
44
|
+
value.map { |v|
|
45
|
+
build_nested_query(v, "#{prefix}[]")
|
46
|
+
}.join("&")
|
47
|
+
when Hash
|
48
|
+
value.map { |k, v|
|
49
|
+
build_nested_query(v, prefix ? "#{prefix}[#{k}]" : k)
|
50
|
+
}.delete_if(&:empty?).join("&")
|
51
|
+
when nil
|
52
|
+
URI.encode_www_form_component(prefix)
|
53
|
+
else
|
54
|
+
raise ArgumentError, "value must be a Hash" if prefix.nil?
|
55
|
+
"#{URI.encode_www_form_component(prefix)}=#{URI.encode_www_form_component(value)}"
|
56
|
+
end
|
57
|
+
end
|
39
58
|
end
|
40
59
|
end
|
41
60
|
end
|
@@ -0,0 +1,89 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "json"
|
4
|
+
require "rbconfig"
|
5
|
+
|
6
|
+
module Sentry
|
7
|
+
module Vernier
|
8
|
+
class Output
|
9
|
+
include Profiler::Helpers
|
10
|
+
|
11
|
+
attr_reader :profile
|
12
|
+
|
13
|
+
def initialize(profile, project_root:, in_app_pattern:, app_dirs_pattern:)
|
14
|
+
@profile = profile
|
15
|
+
@project_root = project_root
|
16
|
+
@in_app_pattern = in_app_pattern
|
17
|
+
@app_dirs_pattern = app_dirs_pattern
|
18
|
+
end
|
19
|
+
|
20
|
+
def to_h
|
21
|
+
@to_h ||= {
|
22
|
+
frames: frames,
|
23
|
+
stacks: stacks,
|
24
|
+
samples: samples,
|
25
|
+
thread_metadata: thread_metadata
|
26
|
+
}
|
27
|
+
end
|
28
|
+
|
29
|
+
private
|
30
|
+
|
31
|
+
def thread_metadata
|
32
|
+
profile.threads.map { |thread_id, thread_info|
|
33
|
+
[thread_id, { name: thread_info[:name] }]
|
34
|
+
}.to_h
|
35
|
+
end
|
36
|
+
|
37
|
+
def samples
|
38
|
+
profile.threads.flat_map { |thread_id, thread_info|
|
39
|
+
started_at = thread_info[:started_at]
|
40
|
+
samples, timestamps = thread_info.values_at(:samples, :timestamps)
|
41
|
+
|
42
|
+
samples.zip(timestamps).map { |stack_id, timestamp|
|
43
|
+
elapsed_since_start_ns = timestamp - started_at
|
44
|
+
|
45
|
+
next if elapsed_since_start_ns < 0
|
46
|
+
|
47
|
+
{
|
48
|
+
thread_id: thread_id.to_s,
|
49
|
+
stack_id: stack_id,
|
50
|
+
elapsed_since_start_ns: elapsed_since_start_ns.to_s
|
51
|
+
}
|
52
|
+
}.compact
|
53
|
+
}
|
54
|
+
end
|
55
|
+
|
56
|
+
def frames
|
57
|
+
funcs = stack_table_hash[:frame_table].fetch(:func)
|
58
|
+
lines = stack_table_hash[:func_table].fetch(:first_line)
|
59
|
+
|
60
|
+
funcs.map do |idx|
|
61
|
+
function, mod = split_module(stack_table_hash[:func_table][:name][idx])
|
62
|
+
|
63
|
+
abs_path = stack_table_hash[:func_table][:filename][idx]
|
64
|
+
in_app = in_app?(abs_path)
|
65
|
+
filename = compute_filename(abs_path, in_app)
|
66
|
+
|
67
|
+
{
|
68
|
+
function: function,
|
69
|
+
module: mod,
|
70
|
+
filename: filename,
|
71
|
+
abs_path: abs_path,
|
72
|
+
lineno: (lineno = lines[idx]) > 0 ? lineno : nil,
|
73
|
+
in_app: in_app
|
74
|
+
}.compact
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
def stacks
|
79
|
+
profile._stack_table.stack_count.times.map do |stack_id|
|
80
|
+
profile.stack(stack_id).frames.map(&:idx)
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
def stack_table_hash
|
85
|
+
@stack_table_hash ||= profile._stack_table.to_h
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
@@ -0,0 +1,130 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "securerandom"
|
4
|
+
require_relative "../profiler/helpers"
|
5
|
+
require_relative "output"
|
6
|
+
|
7
|
+
module Sentry
|
8
|
+
module Vernier
|
9
|
+
class Profiler
|
10
|
+
EMPTY_RESULT = {}.freeze
|
11
|
+
|
12
|
+
attr_reader :started, :event_id, :result
|
13
|
+
|
14
|
+
def initialize(configuration)
|
15
|
+
@event_id = SecureRandom.uuid.delete("-")
|
16
|
+
|
17
|
+
@started = false
|
18
|
+
@sampled = nil
|
19
|
+
|
20
|
+
@profiling_enabled = defined?(Vernier) && 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
|
24
|
+
@in_app_pattern = Regexp.new("^(#{@project_root}/)?#{@app_dirs_pattern}")
|
25
|
+
end
|
26
|
+
|
27
|
+
def set_initial_sample_decision(transaction_sampled)
|
28
|
+
unless @profiling_enabled
|
29
|
+
@sampled = false
|
30
|
+
return
|
31
|
+
end
|
32
|
+
|
33
|
+
unless transaction_sampled
|
34
|
+
@sampled = false
|
35
|
+
log("Discarding profile because transaction not sampled")
|
36
|
+
return
|
37
|
+
end
|
38
|
+
|
39
|
+
case @profiles_sample_rate
|
40
|
+
when 0.0
|
41
|
+
@sampled = false
|
42
|
+
log("Discarding profile because sample_rate is 0")
|
43
|
+
return
|
44
|
+
when 1.0
|
45
|
+
@sampled = true
|
46
|
+
return
|
47
|
+
else
|
48
|
+
@sampled = Random.rand < @profiles_sample_rate
|
49
|
+
end
|
50
|
+
|
51
|
+
log("Discarding profile due to sampling decision") unless @sampled
|
52
|
+
end
|
53
|
+
|
54
|
+
def start
|
55
|
+
return unless @sampled
|
56
|
+
return if @started
|
57
|
+
|
58
|
+
@started = ::Vernier.start_profile
|
59
|
+
|
60
|
+
log("Started")
|
61
|
+
|
62
|
+
@started
|
63
|
+
rescue RuntimeError => e
|
64
|
+
# TODO: once Vernier raises something more dedicated, we should catch that instead
|
65
|
+
if e.message.include?("Profile already started")
|
66
|
+
log("Not started since running elsewhere")
|
67
|
+
else
|
68
|
+
log("Failed to start: #{e.message}")
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
def stop
|
73
|
+
return unless @sampled
|
74
|
+
return unless @started
|
75
|
+
|
76
|
+
@result = ::Vernier.stop_profile
|
77
|
+
|
78
|
+
log("Stopped")
|
79
|
+
rescue RuntimeError => e
|
80
|
+
if e.message.include?("Profile not started")
|
81
|
+
log("Not stopped since not started")
|
82
|
+
else
|
83
|
+
log("Failed to stop Vernier: #{e.message}")
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
def active_thread_id
|
88
|
+
Thread.current.object_id
|
89
|
+
end
|
90
|
+
|
91
|
+
def to_hash
|
92
|
+
return EMPTY_RESULT unless @started
|
93
|
+
|
94
|
+
unless @sampled
|
95
|
+
record_lost_event(:sample_rate)
|
96
|
+
return EMPTY_RESULT
|
97
|
+
end
|
98
|
+
|
99
|
+
{ **profile_meta, profile: output.to_h }
|
100
|
+
end
|
101
|
+
|
102
|
+
private
|
103
|
+
|
104
|
+
def log(message)
|
105
|
+
Sentry.logger.debug(LOGGER_PROGNAME) { "[Profiler::Vernier] #{message}" }
|
106
|
+
end
|
107
|
+
|
108
|
+
def record_lost_event(reason)
|
109
|
+
Sentry.get_current_client&.transport&.record_lost_event(reason, "profile")
|
110
|
+
end
|
111
|
+
|
112
|
+
def profile_meta
|
113
|
+
{
|
114
|
+
event_id: @event_id,
|
115
|
+
version: "1",
|
116
|
+
platform: "ruby"
|
117
|
+
}
|
118
|
+
end
|
119
|
+
|
120
|
+
def output
|
121
|
+
@output ||= Output.new(
|
122
|
+
result,
|
123
|
+
project_root: @project_root,
|
124
|
+
app_dirs_pattern: @app_dirs_pattern,
|
125
|
+
in_app_pattern: @in_app_pattern
|
126
|
+
)
|
127
|
+
end
|
128
|
+
end
|
129
|
+
end
|
130
|
+
end
|
data/lib/sentry/version.rb
CHANGED
data/lib/sentry-ruby.rb
CHANGED
@@ -25,6 +25,7 @@ require "sentry/session_flusher"
|
|
25
25
|
require "sentry/backpressure_monitor"
|
26
26
|
require "sentry/cron/monitor_check_ins"
|
27
27
|
require "sentry/metrics"
|
28
|
+
require "sentry/vernier/profiler"
|
28
29
|
|
29
30
|
[
|
30
31
|
"sentry/rake",
|
@@ -41,14 +42,16 @@ module Sentry
|
|
41
42
|
|
42
43
|
CAPTURED_SIGNATURE = :@__sentry_captured
|
43
44
|
|
44
|
-
LOGGER_PROGNAME = "sentry"
|
45
|
+
LOGGER_PROGNAME = "sentry"
|
45
46
|
|
46
|
-
SENTRY_TRACE_HEADER_NAME = "sentry-trace"
|
47
|
+
SENTRY_TRACE_HEADER_NAME = "sentry-trace"
|
47
48
|
|
48
|
-
BAGGAGE_HEADER_NAME = "baggage"
|
49
|
+
BAGGAGE_HEADER_NAME = "baggage"
|
49
50
|
|
50
51
|
THREAD_LOCAL = :sentry_hub
|
51
52
|
|
53
|
+
MUTEX = Mutex.new
|
54
|
+
|
52
55
|
class << self
|
53
56
|
# @!visibility private
|
54
57
|
def exception_locals_tp
|
@@ -274,8 +277,10 @@ module Sentry
|
|
274
277
|
|
275
278
|
@background_worker.shutdown
|
276
279
|
|
277
|
-
|
278
|
-
|
280
|
+
MUTEX.synchronize do
|
281
|
+
@main_hub = nil
|
282
|
+
Thread.current.thread_variable_set(THREAD_LOCAL, nil)
|
283
|
+
end
|
279
284
|
end
|
280
285
|
|
281
286
|
# Returns true if the SDK is initialized.
|
@@ -302,7 +307,7 @@ module Sentry
|
|
302
307
|
#
|
303
308
|
# @return [Hub]
|
304
309
|
def get_main_hub
|
305
|
-
@main_hub
|
310
|
+
MUTEX.synchronize { @main_hub }
|
306
311
|
end
|
307
312
|
|
308
313
|
# Takes an instance of Sentry::Breadcrumb and stores it to the current active scope.
|
@@ -609,3 +614,4 @@ require "sentry/redis"
|
|
609
614
|
require "sentry/puma"
|
610
615
|
require "sentry/graphql"
|
611
616
|
require "sentry/faraday"
|
617
|
+
require "sentry/excon"
|
data/sentry-ruby-core.gemspec
CHANGED
data/sentry-ruby.gemspec
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: sentry-ruby-core
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 5.
|
4
|
+
version: 5.22.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: 2024-
|
11
|
+
date: 2024-12-04 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: sentry-ruby
|
@@ -16,14 +16,14 @@ dependencies:
|
|
16
16
|
requirements:
|
17
17
|
- - '='
|
18
18
|
- !ruby/object:Gem::Version
|
19
|
-
version: 5.
|
19
|
+
version: 5.22.0
|
20
20
|
type: :runtime
|
21
21
|
prerelease: false
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
23
23
|
requirements:
|
24
24
|
- - '='
|
25
25
|
- !ruby/object:Gem::Version
|
26
|
-
version: 5.
|
26
|
+
version: 5.22.0
|
27
27
|
- !ruby/object:Gem::Dependency
|
28
28
|
name: concurrent-ruby
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
@@ -77,9 +77,12 @@ files:
|
|
77
77
|
- lib/sentry/cron/monitor_schedule.rb
|
78
78
|
- lib/sentry/dsn.rb
|
79
79
|
- lib/sentry/envelope.rb
|
80
|
+
- lib/sentry/envelope/item.rb
|
80
81
|
- lib/sentry/error_event.rb
|
81
82
|
- lib/sentry/event.rb
|
82
83
|
- lib/sentry/exceptions.rb
|
84
|
+
- lib/sentry/excon.rb
|
85
|
+
- lib/sentry/excon/middleware.rb
|
83
86
|
- lib/sentry/faraday.rb
|
84
87
|
- lib/sentry/graphql.rb
|
85
88
|
- lib/sentry/hub.rb
|
@@ -106,6 +109,7 @@ files:
|
|
106
109
|
- lib/sentry/metrics/timing.rb
|
107
110
|
- lib/sentry/net/http.rb
|
108
111
|
- lib/sentry/profiler.rb
|
112
|
+
- lib/sentry/profiler/helpers.rb
|
109
113
|
- lib/sentry/propagation_context.rb
|
110
114
|
- lib/sentry/puma.rb
|
111
115
|
- lib/sentry/rack.rb
|
@@ -113,6 +117,7 @@ files:
|
|
113
117
|
- lib/sentry/rake.rb
|
114
118
|
- lib/sentry/redis.rb
|
115
119
|
- lib/sentry/release_detector.rb
|
120
|
+
- lib/sentry/rspec.rb
|
116
121
|
- lib/sentry/scope.rb
|
117
122
|
- lib/sentry/session.rb
|
118
123
|
- lib/sentry/session_flusher.rb
|
@@ -135,6 +140,8 @@ files:
|
|
135
140
|
- lib/sentry/utils/logging_helper.rb
|
136
141
|
- lib/sentry/utils/real_ip.rb
|
137
142
|
- lib/sentry/utils/request_id.rb
|
143
|
+
- lib/sentry/vernier/output.rb
|
144
|
+
- lib/sentry/vernier/profiler.rb
|
138
145
|
- lib/sentry/version.rb
|
139
146
|
- sentry-ruby-core.gemspec
|
140
147
|
- sentry-ruby.gemspec
|
@@ -160,7 +167,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
160
167
|
- !ruby/object:Gem::Version
|
161
168
|
version: '0'
|
162
169
|
requirements: []
|
163
|
-
rubygems_version: 3.5.
|
170
|
+
rubygems_version: 3.5.22
|
164
171
|
signing_key:
|
165
172
|
specification_version: 4
|
166
173
|
summary: A gem that provides a client interface for the Sentry error logger
|