sentry-ruby 5.19.0 → 5.21.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 +3 -0
- data/Rakefile +2 -0
- data/bin/console +1 -0
- data/lib/sentry/attachment.rb +3 -5
- data/lib/sentry/backtrace.rb +3 -5
- data/lib/sentry/baggage.rb +7 -7
- data/lib/sentry/breadcrumb/sentry_logger.rb +6 -6
- data/lib/sentry/check_in_event.rb +4 -4
- data/lib/sentry/client.rb +9 -9
- data/lib/sentry/configuration.rb +52 -19
- data/lib/sentry/core_ext/object/deep_dup.rb +1 -1
- data/lib/sentry/cron/monitor_check_ins.rb +3 -1
- data/lib/sentry/cron/monitor_config.rb +1 -1
- data/lib/sentry/dsn.rb +3 -3
- data/lib/sentry/envelope/item.rb +88 -0
- data/lib/sentry/envelope.rb +2 -85
- data/lib/sentry/event.rb +7 -7
- data/lib/sentry/graphql.rb +1 -1
- data/lib/sentry/hub.rb +8 -1
- data/lib/sentry/interfaces/mechanism.rb +1 -1
- data/lib/sentry/interfaces/request.rb +5 -5
- data/lib/sentry/interfaces/single_exception.rb +3 -3
- data/lib/sentry/interfaces/stacktrace.rb +3 -1
- data/lib/sentry/interfaces/stacktrace_builder.rb +15 -2
- data/lib/sentry/logger.rb +1 -1
- data/lib/sentry/metrics/aggregator.rb +12 -12
- data/lib/sentry/metrics/set_metric.rb +2 -2
- data/lib/sentry/metrics.rb +15 -15
- data/lib/sentry/net/http.rb +1 -1
- data/lib/sentry/profiler/helpers.rb +46 -0
- data/lib/sentry/profiler.rb +25 -56
- data/lib/sentry/propagation_context.rb +1 -1
- data/lib/sentry/rack/capture_exceptions.rb +2 -2
- data/lib/sentry/rack.rb +2 -2
- data/lib/sentry/rake.rb +2 -2
- data/lib/sentry/release_detector.rb +4 -4
- data/lib/sentry/scope.rb +1 -0
- data/lib/sentry/session_flusher.rb +1 -1
- data/lib/sentry/span.rb +6 -0
- data/lib/sentry/test_helper.rb +3 -1
- data/lib/sentry/transaction.rb +4 -4
- data/lib/sentry/transaction_event.rb +1 -2
- data/lib/sentry/transport/http_transport.rb +12 -12
- data/lib/sentry/transport.rb +4 -4
- data/lib/sentry/utils/env_helper.rb +21 -0
- data/lib/sentry/utils/real_ip.rb +1 -1
- data/lib/sentry/vernier/output.rb +89 -0
- data/lib/sentry/vernier/profiler.rb +125 -0
- data/lib/sentry/version.rb +1 -1
- data/lib/sentry-ruby.rb +5 -4
- data/sentry-ruby-core.gemspec +3 -1
- data/sentry-ruby.gemspec +3 -1
- metadata +13 -8
data/lib/sentry/event.rb
CHANGED
@@ -1,12 +1,12 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require
|
4
|
-
require
|
5
|
-
require
|
6
|
-
require
|
7
|
-
require
|
8
|
-
require
|
9
|
-
require
|
3
|
+
require "socket"
|
4
|
+
require "securerandom"
|
5
|
+
require "sentry/interface"
|
6
|
+
require "sentry/backtrace"
|
7
|
+
require "sentry/utils/real_ip"
|
8
|
+
require "sentry/utils/request_id"
|
9
|
+
require "sentry/utils/custom_inspection"
|
10
10
|
|
11
11
|
module Sentry
|
12
12
|
# This is an abstract class that defines the shared attributes of an event.
|
data/lib/sentry/graphql.rb
CHANGED
@@ -4,6 +4,6 @@ Sentry.register_patch(:graphql) do |config|
|
|
4
4
|
if defined?(::GraphQL::Schema) && defined?(::GraphQL::Tracing::SentryTrace) && ::GraphQL::Schema.respond_to?(:trace_with)
|
5
5
|
::GraphQL::Schema.trace_with(::GraphQL::Tracing::SentryTrace, set_transaction_name: true)
|
6
6
|
else
|
7
|
-
config.logger.warn(Sentry::LOGGER_PROGNAME) {
|
7
|
+
config.logger.warn(Sentry::LOGGER_PROGNAME) { "You tried to enable the GraphQL integration but no GraphQL gem was detected. Make sure you have the `graphql` gem (>= 2.2.6) in your Gemfile." }
|
8
8
|
end
|
9
9
|
end
|
data/lib/sentry/hub.rb
CHANGED
@@ -73,7 +73,13 @@ module Sentry
|
|
73
73
|
end
|
74
74
|
|
75
75
|
def pop_scope
|
76
|
-
@stack.
|
76
|
+
if @stack.size > 1
|
77
|
+
@stack.pop
|
78
|
+
else
|
79
|
+
# We never want to enter a situation where we have no scope and no client
|
80
|
+
client = current_client
|
81
|
+
@stack = [Layer.new(client, Scope.new)]
|
82
|
+
end
|
77
83
|
end
|
78
84
|
|
79
85
|
def start_transaction(transaction: nil, custom_sampling_context: {}, instrumenter: :sentry, **options)
|
@@ -214,6 +220,7 @@ module Sentry
|
|
214
220
|
end
|
215
221
|
|
216
222
|
def add_breadcrumb(breadcrumb, hint: {})
|
223
|
+
return unless current_client
|
217
224
|
return unless configuration.enabled_in_current_env?
|
218
225
|
|
219
226
|
if before_breadcrumb = current_client.configuration.before_breadcrumb
|
@@ -59,7 +59,7 @@ module Sentry
|
|
59
59
|
self.query_string = request.query_string
|
60
60
|
end
|
61
61
|
|
62
|
-
self.url = request.scheme && request.url.split(
|
62
|
+
self.url = request.scheme && request.url.split("?").first
|
63
63
|
self.method = request.request_method
|
64
64
|
|
65
65
|
self.headers = filter_and_format_headers(env, send_default_pii)
|
@@ -85,14 +85,14 @@ module Sentry
|
|
85
85
|
env.each_with_object({}) do |(key, value), memo|
|
86
86
|
begin
|
87
87
|
key = key.to_s # rack env can contain symbols
|
88
|
-
next memo[
|
88
|
+
next memo["X-Request-Id"] ||= Utils::RequestId.read_from(env) if Utils::RequestId::REQUEST_ID_HEADERS.include?(key)
|
89
89
|
next if is_server_protocol?(key, value, env["SERVER_PROTOCOL"])
|
90
90
|
next if is_skippable_header?(key)
|
91
91
|
next if key == "HTTP_AUTHORIZATION" && !send_default_pii
|
92
92
|
|
93
93
|
# Rack stores headers as HTTP_WHAT_EVER, we need What-Ever
|
94
94
|
key = key.sub(/^HTTP_/, "")
|
95
|
-
key = key.split(
|
95
|
+
key = key.split("_").map(&:capitalize).join("-")
|
96
96
|
|
97
97
|
memo[key] = Utils::EncodingHelper.encode_to_utf_8(value.to_s)
|
98
98
|
rescue StandardError => e
|
@@ -108,7 +108,7 @@ module Sentry
|
|
108
108
|
def is_skippable_header?(key)
|
109
109
|
key.upcase != key || # lower-case envs aren't real http headers
|
110
110
|
key == "HTTP_COOKIE" || # Cookies don't go here, they go somewhere else
|
111
|
-
!(key.start_with?(
|
111
|
+
!(key.start_with?("HTTP_") || CONTENT_HEADERS.include?(key))
|
112
112
|
end
|
113
113
|
|
114
114
|
# In versions < 3, Rack adds in an incorrect HTTP_VERSION key, which causes downstream
|
@@ -120,7 +120,7 @@ module Sentry
|
|
120
120
|
rack_version = Gem::Version.new(::Rack.release)
|
121
121
|
return false if rack_version >= Gem::Version.new("3.0")
|
122
122
|
|
123
|
-
key ==
|
123
|
+
key == "HTTP_VERSION" && value == protocol_version
|
124
124
|
end
|
125
125
|
|
126
126
|
def filter_and_format_env(env, rack_env_whitelist)
|
@@ -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
|
@@ -26,7 +26,7 @@ module Sentry
|
|
26
26
|
|
27
27
|
@value = Utils::EncodingHelper.encode_to_utf_8(exception_message.byteslice(0..Event::MAX_MESSAGE_SIZE_IN_BYTES))
|
28
28
|
|
29
|
-
@module = exception.class.to_s.split(
|
29
|
+
@module = exception.class.to_s.split("::")[0...-1].join("::")
|
30
30
|
@thread_id = Thread.current.object_id
|
31
31
|
@stacktrace = stacktrace
|
32
32
|
@mechanism = mechanism
|
@@ -27,8 +27,9 @@ module Sentry
|
|
27
27
|
attr_accessor :abs_path, :context_line, :function, :in_app, :filename,
|
28
28
|
:lineno, :module, :pre_context, :post_context, :vars
|
29
29
|
|
30
|
-
def initialize(project_root, line)
|
30
|
+
def initialize(project_root, line, strip_backtrace_load_path = true)
|
31
31
|
@project_root = project_root
|
32
|
+
@strip_backtrace_load_path = strip_backtrace_load_path
|
32
33
|
|
33
34
|
@abs_path = line.file
|
34
35
|
@function = line.method if line.method
|
@@ -44,6 +45,7 @@ module Sentry
|
|
44
45
|
|
45
46
|
def compute_filename
|
46
47
|
return if abs_path.nil?
|
48
|
+
return abs_path unless @strip_backtrace_load_path
|
47
49
|
|
48
50
|
prefix =
|
49
51
|
if under_project_root? && in_app
|
@@ -17,22 +17,35 @@ module Sentry
|
|
17
17
|
# @return [Proc, nil]
|
18
18
|
attr_reader :backtrace_cleanup_callback
|
19
19
|
|
20
|
+
# @return [Boolean]
|
21
|
+
attr_reader :strip_backtrace_load_path
|
22
|
+
|
20
23
|
# @param project_root [String]
|
21
24
|
# @param app_dirs_pattern [Regexp, nil]
|
22
25
|
# @param linecache [LineCache]
|
23
26
|
# @param context_lines [Integer, nil]
|
24
27
|
# @param backtrace_cleanup_callback [Proc, nil]
|
28
|
+
# @param strip_backtrace_load_path [Boolean]
|
25
29
|
# @see Configuration#project_root
|
26
30
|
# @see Configuration#app_dirs_pattern
|
27
31
|
# @see Configuration#linecache
|
28
32
|
# @see Configuration#context_lines
|
29
33
|
# @see Configuration#backtrace_cleanup_callback
|
30
|
-
|
34
|
+
# @see Configuration#strip_backtrace_load_path
|
35
|
+
def initialize(
|
36
|
+
project_root:,
|
37
|
+
app_dirs_pattern:,
|
38
|
+
linecache:,
|
39
|
+
context_lines:,
|
40
|
+
backtrace_cleanup_callback: nil,
|
41
|
+
strip_backtrace_load_path: true
|
42
|
+
)
|
31
43
|
@project_root = project_root
|
32
44
|
@app_dirs_pattern = app_dirs_pattern
|
33
45
|
@linecache = linecache
|
34
46
|
@context_lines = context_lines
|
35
47
|
@backtrace_cleanup_callback = backtrace_cleanup_callback
|
48
|
+
@strip_backtrace_load_path = strip_backtrace_load_path
|
36
49
|
end
|
37
50
|
|
38
51
|
# Generates a StacktraceInterface with the given backtrace.
|
@@ -73,7 +86,7 @@ module Sentry
|
|
73
86
|
private
|
74
87
|
|
75
88
|
def convert_parsed_line_into_frame(line)
|
76
|
-
frame = StacktraceInterface::Frame.new(project_root, line)
|
89
|
+
frame = StacktraceInterface::Frame.new(project_root, line, strip_backtrace_load_path)
|
77
90
|
frame.set_context(linecache, context_lines) if context_lines
|
78
91
|
frame
|
79
92
|
end
|
data/lib/sentry/logger.rb
CHANGED
@@ -41,8 +41,8 @@ module Sentry
|
|
41
41
|
@stacktrace_builder = configuration.stacktrace_builder
|
42
42
|
|
43
43
|
@default_tags = {}
|
44
|
-
@default_tags[
|
45
|
-
@default_tags[
|
44
|
+
@default_tags["release"] = configuration.release if configuration.release
|
45
|
+
@default_tags["environment"] = configuration.environment if configuration.environment
|
46
46
|
|
47
47
|
@mutex = Mutex.new
|
48
48
|
|
@@ -59,7 +59,7 @@ module Sentry
|
|
59
59
|
def add(type,
|
60
60
|
key,
|
61
61
|
value,
|
62
|
-
unit:
|
62
|
+
unit: "none",
|
63
63
|
tags: {},
|
64
64
|
timestamp: nil,
|
65
65
|
stacklevel: nil)
|
@@ -98,7 +98,7 @@ module Sentry
|
|
98
98
|
unless flushable_buckets.empty?
|
99
99
|
payload = serialize_buckets(flushable_buckets)
|
100
100
|
envelope.add_item(
|
101
|
-
{ type:
|
101
|
+
{ type: "statsd", length: payload.bytesize },
|
102
102
|
payload
|
103
103
|
)
|
104
104
|
end
|
@@ -107,7 +107,7 @@ module Sentry
|
|
107
107
|
code_locations.each do |timestamp, locations|
|
108
108
|
payload = serialize_locations(timestamp, locations)
|
109
109
|
envelope.add_item(
|
110
|
-
{ type:
|
110
|
+
{ type: "metric_meta", content_type: "application/json" },
|
111
111
|
payload
|
112
112
|
)
|
113
113
|
end
|
@@ -161,8 +161,8 @@ module Sentry
|
|
161
161
|
buckets.map do |timestamp, timestamp_buckets|
|
162
162
|
timestamp_buckets.map do |metric_key, metric|
|
163
163
|
type, key, unit, tags = metric_key
|
164
|
-
values = metric.serialize.join(
|
165
|
-
sanitized_tags = tags.map { |k, v| "#{sanitize_tag_key(k)}:#{sanitize_tag_value(v)}" }.join(
|
164
|
+
values = metric.serialize.join(":")
|
165
|
+
sanitized_tags = tags.map { |k, v| "#{sanitize_tag_key(k)}:#{sanitize_tag_value(v)}" }.join(",")
|
166
166
|
|
167
167
|
"#{sanitize_key(key)}@#{sanitize_unit(unit)}:#{values}|#{type}|\##{sanitized_tags}|T#{timestamp}"
|
168
168
|
end
|
@@ -175,22 +175,22 @@ module Sentry
|
|
175
175
|
mri = "#{type}:#{sanitize_key(key)}@#{sanitize_unit(unit)}"
|
176
176
|
|
177
177
|
# note this needs to be an array but it really doesn't serve a purpose right now
|
178
|
-
[mri, [location.merge(type:
|
178
|
+
[mri, [location.merge(type: "location")]]
|
179
179
|
end.to_h
|
180
180
|
|
181
181
|
{ timestamp: timestamp, mapping: mapping }
|
182
182
|
end
|
183
183
|
|
184
184
|
def sanitize_key(key)
|
185
|
-
key.gsub(KEY_SANITIZATION_REGEX,
|
185
|
+
key.gsub(KEY_SANITIZATION_REGEX, "_")
|
186
186
|
end
|
187
187
|
|
188
188
|
def sanitize_unit(unit)
|
189
|
-
unit.gsub(UNIT_SANITIZATION_REGEX,
|
189
|
+
unit.gsub(UNIT_SANITIZATION_REGEX, "")
|
190
190
|
end
|
191
191
|
|
192
192
|
def sanitize_tag_key(key)
|
193
|
-
key.gsub(TAG_KEY_SANITIZATION_REGEX,
|
193
|
+
key.gsub(TAG_KEY_SANITIZATION_REGEX, "")
|
194
194
|
end
|
195
195
|
|
196
196
|
def sanitize_tag_value(value)
|
@@ -209,7 +209,7 @@ module Sentry
|
|
209
209
|
updated_tags = @default_tags.merge(tags)
|
210
210
|
|
211
211
|
transaction_name = get_transaction_name
|
212
|
-
updated_tags[
|
212
|
+
updated_tags["transaction"] = transaction_name if transaction_name
|
213
213
|
|
214
214
|
updated_tags
|
215
215
|
end
|
data/lib/sentry/metrics.rb
CHANGED
@@ -1,12 +1,12 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require
|
4
|
-
require
|
5
|
-
require
|
6
|
-
require
|
7
|
-
require
|
8
|
-
require
|
9
|
-
require
|
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
10
|
|
11
11
|
module Sentry
|
12
12
|
module Metrics
|
@@ -14,32 +14,32 @@ module Sentry
|
|
14
14
|
INFORMATION_UNITS = %w[bit byte kilobyte kibibyte megabyte mebibyte gigabyte gibibyte terabyte tebibyte petabyte pebibyte exabyte exbibyte]
|
15
15
|
FRACTIONAL_UNITS = %w[ratio percent]
|
16
16
|
|
17
|
-
OP_NAME =
|
18
|
-
SPAN_ORIGIN =
|
17
|
+
OP_NAME = "metric.timing"
|
18
|
+
SPAN_ORIGIN = "auto.metric.timing"
|
19
19
|
|
20
20
|
class << self
|
21
|
-
def increment(key, value = 1.0, unit:
|
21
|
+
def increment(key, value = 1.0, unit: "none", tags: {}, timestamp: nil)
|
22
22
|
Sentry.metrics_aggregator&.add(:c, key, value, unit: unit, tags: tags, timestamp: timestamp)
|
23
23
|
end
|
24
24
|
|
25
|
-
def distribution(key, value, unit:
|
25
|
+
def distribution(key, value, unit: "none", tags: {}, timestamp: nil)
|
26
26
|
Sentry.metrics_aggregator&.add(:d, key, value, unit: unit, tags: tags, timestamp: timestamp)
|
27
27
|
end
|
28
28
|
|
29
|
-
def set(key, value, unit:
|
29
|
+
def set(key, value, unit: "none", tags: {}, timestamp: nil)
|
30
30
|
Sentry.metrics_aggregator&.add(:s, key, value, unit: unit, tags: tags, timestamp: timestamp)
|
31
31
|
end
|
32
32
|
|
33
|
-
def gauge(key, value, unit:
|
33
|
+
def gauge(key, value, unit: "none", tags: {}, timestamp: nil)
|
34
34
|
Sentry.metrics_aggregator&.add(:g, key, value, unit: unit, tags: tags, timestamp: timestamp)
|
35
35
|
end
|
36
36
|
|
37
|
-
def timing(key, unit:
|
37
|
+
def timing(key, unit: "second", tags: {}, timestamp: nil, &block)
|
38
38
|
return unless block_given?
|
39
39
|
return yield unless DURATION_UNITS.include?(unit)
|
40
40
|
|
41
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(
|
42
|
+
tags.each { |k, v| span.set_tag(k, v.is_a?(Array) ? v.join(", ") : v.to_s) } if span
|
43
43
|
|
44
44
|
start = Timing.send(unit.to_sym)
|
45
45
|
result = yield
|
data/lib/sentry/net/http.rb
CHANGED
@@ -66,7 +66,7 @@ module Sentry
|
|
66
66
|
# IPv6 url could look like '::1/path', and that won't parse without
|
67
67
|
# wrapping it in square brackets.
|
68
68
|
hostname = address =~ Resolv::IPv6::Regex ? "[#{address}]" : address
|
69
|
-
uri = req.uri || URI.parse("#{use_ssl? ? 'https' : 'http'}://#{hostname}#{req.path}")
|
69
|
+
uri = req.uri || URI.parse(URI::DEFAULT_PARSER.escape("#{use_ssl? ? 'https' : 'http'}://#{hostname}#{req.path}"))
|
70
70
|
url = "#{uri.scheme}://#{uri.host}#{uri.path}" rescue uri.to_s
|
71
71
|
|
72
72
|
result = { method: req.method, url: url }
|
@@ -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,11 +1,14 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require
|
3
|
+
require "securerandom"
|
4
|
+
require_relative "profiler/helpers"
|
4
5
|
|
5
6
|
module Sentry
|
6
7
|
class Profiler
|
7
|
-
|
8
|
-
|
8
|
+
include Profiler::Helpers
|
9
|
+
|
10
|
+
VERSION = "1"
|
11
|
+
PLATFORM = "ruby"
|
9
12
|
# 101 Hz in microseconds
|
10
13
|
DEFAULT_INTERVAL = 1e6 / 101
|
11
14
|
MICRO_TO_NANO_SECONDS = 1e3
|
@@ -14,14 +17,14 @@ module Sentry
|
|
14
17
|
attr_reader :sampled, :started, :event_id
|
15
18
|
|
16
19
|
def initialize(configuration)
|
17
|
-
@event_id = SecureRandom.uuid.delete(
|
20
|
+
@event_id = SecureRandom.uuid.delete("-")
|
18
21
|
@started = false
|
19
22
|
@sampled = nil
|
20
23
|
|
21
24
|
@profiling_enabled = defined?(StackProf) && configuration.profiling_enabled?
|
22
25
|
@profiles_sample_rate = configuration.profiles_sample_rate
|
23
26
|
@project_root = configuration.project_root
|
24
|
-
@app_dirs_pattern = configuration.app_dirs_pattern
|
27
|
+
@app_dirs_pattern = configuration.app_dirs_pattern
|
25
28
|
@in_app_pattern = Regexp.new("^(#{@project_root}/)?#{@app_dirs_pattern}")
|
26
29
|
end
|
27
30
|
|
@@ -33,7 +36,7 @@ module Sentry
|
|
33
36
|
raw: true,
|
34
37
|
aggregate: false)
|
35
38
|
|
36
|
-
@started ? log(
|
39
|
+
@started ? log("Started") : log("Not started since running elsewhere")
|
37
40
|
end
|
38
41
|
|
39
42
|
def stop
|
@@ -41,7 +44,11 @@ module Sentry
|
|
41
44
|
return unless @started
|
42
45
|
|
43
46
|
StackProf.stop
|
44
|
-
log(
|
47
|
+
log("Stopped")
|
48
|
+
end
|
49
|
+
|
50
|
+
def active_thread_id
|
51
|
+
"0"
|
45
52
|
end
|
46
53
|
|
47
54
|
# Sets initial sampling decision of the profile.
|
@@ -54,14 +61,14 @@ module Sentry
|
|
54
61
|
|
55
62
|
unless transaction_sampled
|
56
63
|
@sampled = false
|
57
|
-
log(
|
64
|
+
log("Discarding profile because transaction not sampled")
|
58
65
|
return
|
59
66
|
end
|
60
67
|
|
61
68
|
case @profiles_sample_rate
|
62
69
|
when 0.0
|
63
70
|
@sampled = false
|
64
|
-
log(
|
71
|
+
log("Discarding profile because sample_rate is 0")
|
65
72
|
return
|
66
73
|
when 1.0
|
67
74
|
@sampled = true
|
@@ -70,7 +77,7 @@ module Sentry
|
|
70
77
|
@sampled = Random.rand < @profiles_sample_rate
|
71
78
|
end
|
72
79
|
|
73
|
-
log(
|
80
|
+
log("Discarding profile due to sampling decision") unless @sampled
|
74
81
|
end
|
75
82
|
|
76
83
|
def to_hash
|
@@ -90,13 +97,12 @@ module Sentry
|
|
90
97
|
|
91
98
|
frame_map = {}
|
92
99
|
|
93
|
-
frames = results[:frames].
|
94
|
-
frame_id, frame_data = frame
|
95
|
-
|
100
|
+
frames = results[:frames].map.with_index do |(frame_id, frame_data), idx|
|
96
101
|
# need to map over stackprof frame ids to ours
|
97
102
|
frame_map[frame_id] = idx
|
98
103
|
|
99
104
|
file_path = frame_data[:file]
|
105
|
+
lineno = frame_data[:line]
|
100
106
|
in_app = in_app?(file_path)
|
101
107
|
filename = compute_filename(file_path, in_app)
|
102
108
|
function, mod = split_module(frame_data[:name])
|
@@ -109,7 +115,7 @@ module Sentry
|
|
109
115
|
}
|
110
116
|
|
111
117
|
frame_hash[:module] = mod if mod
|
112
|
-
frame_hash[:lineno] =
|
118
|
+
frame_hash[:lineno] = lineno if lineno && lineno >= 0
|
113
119
|
|
114
120
|
frame_hash
|
115
121
|
end
|
@@ -130,7 +136,7 @@ module Sentry
|
|
130
136
|
num_seen << results[:raw][idx + len]
|
131
137
|
idx += len + 1
|
132
138
|
|
133
|
-
log(
|
139
|
+
log("Unknown frame in stack") if stack.size != len
|
134
140
|
end
|
135
141
|
|
136
142
|
idx = 0
|
@@ -155,16 +161,16 @@ module Sentry
|
|
155
161
|
# Till then, on multi-threaded servers like puma, we will get frames from other active threads when the one
|
156
162
|
# we're profiling is idle/sleeping/waiting for IO etc.
|
157
163
|
# https://bugs.ruby-lang.org/issues/10602
|
158
|
-
thread_id:
|
164
|
+
thread_id: "0",
|
159
165
|
elapsed_since_start_ns: elapsed_since_start_ns.to_s
|
160
166
|
}
|
161
167
|
end
|
162
168
|
end
|
163
169
|
|
164
|
-
log(
|
170
|
+
log("Some samples thrown away") if samples.size != results[:samples]
|
165
171
|
|
166
172
|
if samples.size <= MIN_SAMPLES_REQUIRED
|
167
|
-
log(
|
173
|
+
log("Not enough samples, discarding profiler")
|
168
174
|
record_lost_event(:insufficient_data)
|
169
175
|
return {}
|
170
176
|
end
|
@@ -189,45 +195,8 @@ module Sentry
|
|
189
195
|
Sentry.logger.debug(LOGGER_PROGNAME) { "[Profiler] #{message}" }
|
190
196
|
end
|
191
197
|
|
192
|
-
def in_app?(abs_path)
|
193
|
-
abs_path.match?(@in_app_pattern)
|
194
|
-
end
|
195
|
-
|
196
|
-
# copied from stacktrace.rb since I don't want to touch existing code
|
197
|
-
# TODO-neel-profiler try to fetch this from stackprof once we patch
|
198
|
-
# the native extension
|
199
|
-
def compute_filename(abs_path, in_app)
|
200
|
-
return nil if abs_path.nil?
|
201
|
-
|
202
|
-
under_project_root = @project_root && abs_path.start_with?(@project_root)
|
203
|
-
|
204
|
-
prefix =
|
205
|
-
if under_project_root && in_app
|
206
|
-
@project_root
|
207
|
-
else
|
208
|
-
longest_load_path = $LOAD_PATH.select { |path| abs_path.start_with?(path.to_s) }.max_by(&:size)
|
209
|
-
|
210
|
-
if under_project_root
|
211
|
-
longest_load_path || @project_root
|
212
|
-
else
|
213
|
-
longest_load_path
|
214
|
-
end
|
215
|
-
end
|
216
|
-
|
217
|
-
prefix ? abs_path[prefix.to_s.chomp(File::SEPARATOR).length + 1..-1] : abs_path
|
218
|
-
end
|
219
|
-
|
220
|
-
def split_module(name)
|
221
|
-
# last module plus class/instance method
|
222
|
-
i = name.rindex('::')
|
223
|
-
function = i ? name[(i + 2)..-1] : name
|
224
|
-
mod = i ? name[0...i] : nil
|
225
|
-
|
226
|
-
[function, mod]
|
227
|
-
end
|
228
|
-
|
229
198
|
def record_lost_event(reason)
|
230
|
-
Sentry.get_current_client&.transport&.record_lost_event(reason,
|
199
|
+
Sentry.get_current_client&.transport&.record_lost_event(reason, "profile")
|
231
200
|
end
|
232
201
|
end
|
233
202
|
end
|
@@ -50,11 +50,11 @@ module Sentry
|
|
50
50
|
private
|
51
51
|
|
52
52
|
def collect_exception(env)
|
53
|
-
env[
|
53
|
+
env["rack.exception"] || env["sinatra.error"]
|
54
54
|
end
|
55
55
|
|
56
56
|
def transaction_op
|
57
|
-
"http.server"
|
57
|
+
"http.server"
|
58
58
|
end
|
59
59
|
|
60
60
|
def capture_exception(exception, env)
|
data/lib/sentry/rack.rb
CHANGED
data/lib/sentry/rake.rb
CHANGED
@@ -8,10 +8,10 @@ module Sentry
|
|
8
8
|
module Application
|
9
9
|
# @api private
|
10
10
|
def display_error_message(ex)
|
11
|
-
mechanism = Sentry::Mechanism.new(type:
|
11
|
+
mechanism = Sentry::Mechanism.new(type: "rake", handled: false)
|
12
12
|
|
13
13
|
Sentry.capture_exception(ex, hint: { mechanism: mechanism }) do |scope|
|
14
|
-
task_name = top_level_tasks.join(
|
14
|
+
task_name = top_level_tasks.join(" ")
|
15
15
|
scope.set_transaction_name(task_name, source: :task)
|
16
16
|
scope.set_tag("rake_task", task_name)
|
17
17
|
end if Sentry.initialized? && !Sentry.configuration.skip_rake_integration
|