sentry-ruby 6.5.0 → 6.6.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/README.md +3 -0
- data/lib/sentry/backtrace/line.rb +16 -9
- data/lib/sentry/backtrace.rb +6 -4
- data/lib/sentry/configuration.rb +7 -1
- data/lib/sentry/hub.rb +6 -0
- data/lib/sentry/interfaces/request.rb +13 -4
- data/lib/sentry/interfaces/stacktrace.rb +2 -29
- data/lib/sentry/interfaces/stacktrace_builder.rb +22 -6
- data/lib/sentry/linecache.rb +18 -21
- data/lib/sentry/profiler/helpers.rb +1 -21
- data/lib/sentry/profiler.rb +1 -0
- data/lib/sentry/release_detector.rb +5 -0
- data/lib/sentry/test_helper.rb +41 -8
- data/lib/sentry/transport/dummy_transport.rb +7 -0
- data/lib/sentry/utils/filename_cache.rb +47 -0
- data/lib/sentry/utils/http_tracing.rb +7 -1
- data/lib/sentry/vernier/output.rb +2 -1
- data/lib/sentry/vernier/profiler.rb +3 -1
- data/lib/sentry/version.rb +1 -1
- data/lib/sentry-ruby.rb +1 -0
- metadata +7 -6
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 3ac6a6802b328b59cb636ac1d91b5b1ab5710e9c1427c3d910e7bd05f30b618b
|
|
4
|
+
data.tar.gz: a77ee9a574da2de452d962848dbfca19aef525304e1bdbf65b21cb11ec7e9e3b
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: c1f221cea1a011d2540c666e14823423d12fa37f3ea4ee59f3d68035ad36e33dd62fbc8f23712fee46b8da31e3bbb7c027e664927945b959197c15d10626f4e9
|
|
7
|
+
data.tar.gz: 95f32db537d5fa0a95b3897dcd7fc6d34dff884fe474fc9a43d0567d6a3ef6a84cb181f06c9ef60b0e1e132cb5afa28e65faf427c408c518beeb1305ddf9af1f
|
data/README.md
CHANGED
|
@@ -21,6 +21,7 @@ Sentry SDK for Ruby
|
|
|
21
21
|
| [](https://rubygems.org/gems/sentry-delayed_job) | [](https://github.com/getsentry/sentry-ruby/actions/workflows/tests.yml) | [](https://codecov.io/gh/getsentry/sentry-ruby) | [](https://www.rubydoc.info/gems/sentry-delayed_job) |
|
|
22
22
|
| [](https://rubygems.org/gems/sentry-resque) | [](https://github.com/getsentry/sentry-ruby/actions/workflows/tests.yml) | [](https://codecov.io/gh/getsentry/sentry-ruby) | [](https://www.rubydoc.info/gems/sentry-resque) |
|
|
23
23
|
| [](https://rubygems.org/gems/sentry-opentelemetry) | [](https://github.com/getsentry/sentry-ruby/actions/workflows/tests.yml) | [](https://codecov.io/gh/getsentry/sentry-ruby) | [](https://www.rubydoc.info/gems/sentry-opentelemetry) |
|
|
24
|
+
| [](https://rubygems.org/gems/sentry-yabeda) | [](https://github.com/getsentry/sentry-ruby/actions/workflows/tests.yml) | [](https://codecov.io/gh/getsentry/sentry-ruby) | [](https://www.rubydoc.info/gems/sentry-yabeda) |
|
|
24
25
|
|
|
25
26
|
|
|
26
27
|
|
|
@@ -53,6 +54,7 @@ gem "sentry-sidekiq"
|
|
|
53
54
|
gem "sentry-delayed_job"
|
|
54
55
|
gem "sentry-resque"
|
|
55
56
|
gem "sentry-opentelemetry"
|
|
57
|
+
gem "sentry-yabeda"
|
|
56
58
|
```
|
|
57
59
|
|
|
58
60
|
### Configuration
|
|
@@ -93,6 +95,7 @@ To learn more about sampling transactions, please visit the [official documentat
|
|
|
93
95
|
- [DelayedJob](https://docs.sentry.io/platforms/ruby/guides/delayed_job/)
|
|
94
96
|
- [Resque](https://docs.sentry.io/platforms/ruby/guides/resque/)
|
|
95
97
|
- [OpenTelemetry](https://docs.sentry.io/platforms/ruby/performance/instrumentation/opentelemetry/)
|
|
98
|
+
- [Yabeda](https://docs.sentry.io/platforms/ruby/guides/yabeda/)
|
|
96
99
|
|
|
97
100
|
### Enriching Events
|
|
98
101
|
|
|
@@ -6,6 +6,7 @@ module Sentry
|
|
|
6
6
|
# Handles backtrace parsing line by line
|
|
7
7
|
class Line
|
|
8
8
|
RB_EXTENSION = ".rb"
|
|
9
|
+
CLASS_EXTENSION = ".class"
|
|
9
10
|
# regexp (optional leading X: on windows, or JRuby9000 class-prefix)
|
|
10
11
|
RUBY_INPUT_FORMAT = /
|
|
11
12
|
^ \s* (?: [a-zA-Z]: | uri:classloader: )? ([^:]+ | <.*>):
|
|
@@ -37,12 +38,21 @@ module Sentry
|
|
|
37
38
|
ruby_match = unparsed_line.match(RUBY_INPUT_FORMAT)
|
|
38
39
|
|
|
39
40
|
if ruby_match
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
module_name =
|
|
41
|
+
file = ruby_match[1]
|
|
42
|
+
number = ruby_match[2]
|
|
43
|
+
module_name = ruby_match[4]
|
|
44
|
+
method = ruby_match[5]
|
|
45
|
+
if file.end_with?(CLASS_EXTENSION)
|
|
46
|
+
file.sub!(/\.class$/, RB_EXTENSION)
|
|
47
|
+
end
|
|
43
48
|
else
|
|
44
49
|
java_match = unparsed_line.match(JAVA_INPUT_FORMAT)
|
|
45
|
-
|
|
50
|
+
if java_match
|
|
51
|
+
module_name = java_match[1]
|
|
52
|
+
method = java_match[2]
|
|
53
|
+
file = java_match[3]
|
|
54
|
+
number = java_match[4]
|
|
55
|
+
end
|
|
46
56
|
end
|
|
47
57
|
new(file, number, method, module_name, in_app_pattern)
|
|
48
58
|
end
|
|
@@ -74,12 +84,9 @@ module Sentry
|
|
|
74
84
|
|
|
75
85
|
def in_app
|
|
76
86
|
return false unless in_app_pattern
|
|
87
|
+
return false unless file
|
|
77
88
|
|
|
78
|
-
|
|
79
|
-
true
|
|
80
|
-
else
|
|
81
|
-
false
|
|
82
|
-
end
|
|
89
|
+
file.match?(in_app_pattern)
|
|
83
90
|
end
|
|
84
91
|
|
|
85
92
|
# Reconstructs the line in a readable fashion
|
data/lib/sentry/backtrace.rb
CHANGED
|
@@ -10,14 +10,16 @@ module Sentry
|
|
|
10
10
|
# holder for an Array of Backtrace::Line instances
|
|
11
11
|
attr_reader :lines
|
|
12
12
|
|
|
13
|
-
|
|
13
|
+
# @deprecated project_root, in_app_pattern passed from outside
|
|
14
|
+
# @deprecated app_dirs_pattern, in_app_pattern passed from outside
|
|
15
|
+
def self.parse(backtrace, project_root, app_dirs_pattern, in_app_pattern: nil, &backtrace_cleanup_callback)
|
|
14
16
|
ruby_lines = backtrace.is_a?(Array) ? backtrace : backtrace.split(/\n\s*/)
|
|
15
17
|
|
|
16
18
|
ruby_lines = backtrace_cleanup_callback.call(ruby_lines) if backtrace_cleanup_callback
|
|
17
19
|
|
|
18
|
-
in_app_pattern
|
|
19
|
-
|
|
20
|
-
|
|
20
|
+
# in_app_pattern is now passed in from StacktraceBuilder, so this regex won't be triggered
|
|
21
|
+
# only here for backwards compat and will be deleted
|
|
22
|
+
in_app_pattern ||= Regexp.new("^(#{project_root}/)?#{app_dirs_pattern}")
|
|
21
23
|
|
|
22
24
|
lines = ruby_lines.to_a.map do |unparsed_line|
|
|
23
25
|
Line.parse(unparsed_line, in_app_pattern)
|
data/lib/sentry/configuration.rb
CHANGED
|
@@ -461,7 +461,8 @@ module Sentry
|
|
|
461
461
|
def callbacks
|
|
462
462
|
@callbacks ||= {
|
|
463
463
|
initialize: { before: [], after: [] },
|
|
464
|
-
configured: { before: [], after: [] }
|
|
464
|
+
configured: { before: [], after: [] },
|
|
465
|
+
closed: { before: [], after: [] }
|
|
465
466
|
}
|
|
466
467
|
end
|
|
467
468
|
|
|
@@ -798,6 +799,11 @@ module Sentry
|
|
|
798
799
|
@errors.join(", ")
|
|
799
800
|
end
|
|
800
801
|
|
|
802
|
+
# @api private
|
|
803
|
+
def run_after_close_callbacks
|
|
804
|
+
run_callbacks(:after, :closed)
|
|
805
|
+
end
|
|
806
|
+
|
|
801
807
|
private
|
|
802
808
|
|
|
803
809
|
def init_dsn(dsn_string)
|
data/lib/sentry/hub.rb
CHANGED
|
@@ -54,6 +54,12 @@ module Sentry
|
|
|
54
54
|
current_layer&.client
|
|
55
55
|
end
|
|
56
56
|
|
|
57
|
+
# All clients bound across the hub's scope stack, base layer first.
|
|
58
|
+
# @return [Array<Client>]
|
|
59
|
+
def clients
|
|
60
|
+
@stack.map(&:client).compact
|
|
61
|
+
end
|
|
62
|
+
|
|
57
63
|
def configuration
|
|
58
64
|
current_client.configuration
|
|
59
65
|
end
|
|
@@ -11,6 +11,9 @@ module Sentry
|
|
|
11
11
|
"HTTP_X_FORWARDED_FOR"
|
|
12
12
|
].freeze
|
|
13
13
|
|
|
14
|
+
# Regex to detect lowercase chars — match? is allocation-free (no MatchData/String)
|
|
15
|
+
LOWERCASE_PATTERN = /[a-z]/.freeze
|
|
16
|
+
|
|
14
17
|
# See Sentry server default limits at
|
|
15
18
|
# https://github.com/getsentry/sentry/blob/master/src/sentry/conf/server.py
|
|
16
19
|
MAX_BODY_LIMIT = 4096 * 4
|
|
@@ -93,7 +96,7 @@ module Sentry
|
|
|
93
96
|
next if key == "HTTP_AUTHORIZATION" && !send_default_pii
|
|
94
97
|
|
|
95
98
|
# Rack stores headers as HTTP_WHAT_EVER, we need What-Ever
|
|
96
|
-
key = key.
|
|
99
|
+
key = key.delete_prefix("HTTP_")
|
|
97
100
|
key = key.split("_").map(&:capitalize).join("-")
|
|
98
101
|
|
|
99
102
|
memo[key] = Utils::EncodingHelper.encode_to_utf_8(value.to_s)
|
|
@@ -108,7 +111,7 @@ module Sentry
|
|
|
108
111
|
end
|
|
109
112
|
|
|
110
113
|
def is_skippable_header?(key)
|
|
111
|
-
key.
|
|
114
|
+
key.match?(LOWERCASE_PATTERN) || # lower-case envs aren't real http headers
|
|
112
115
|
key == "HTTP_COOKIE" || # Cookies don't go here, they go somewhere else
|
|
113
116
|
!(key.start_with?("HTTP_") || CONTENT_HEADERS.include?(key))
|
|
114
117
|
end
|
|
@@ -119,12 +122,18 @@ module Sentry
|
|
|
119
122
|
# if the request has legitimately sent a Version header themselves.
|
|
120
123
|
# See: https://github.com/rack/rack/blob/028438f/lib/rack/handler/cgi.rb#L29
|
|
121
124
|
def is_server_protocol?(key, value, protocol_version)
|
|
122
|
-
|
|
123
|
-
return false if rack_version >= Gem::Version.new("3.0")
|
|
125
|
+
return false if self.class.rack_3_or_above?
|
|
124
126
|
|
|
125
127
|
key == "HTTP_VERSION" && value == protocol_version
|
|
126
128
|
end
|
|
127
129
|
|
|
130
|
+
def self.rack_3_or_above?
|
|
131
|
+
return @rack_3_or_above if defined?(@rack_3_or_above)
|
|
132
|
+
|
|
133
|
+
@rack_3_or_above = defined?(::Rack) &&
|
|
134
|
+
Gem::Version.new(::Rack.release) >= Gem::Version.new("3.0")
|
|
135
|
+
end
|
|
136
|
+
|
|
128
137
|
def filter_and_format_env(env, rack_env_whitelist)
|
|
129
138
|
return env if rack_env_whitelist.empty?
|
|
130
139
|
|
|
@@ -27,38 +27,19 @@ 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, strip_backtrace_load_path = true)
|
|
31
|
-
@project_root = project_root
|
|
32
|
-
@strip_backtrace_load_path = strip_backtrace_load_path
|
|
33
|
-
|
|
30
|
+
def initialize(project_root, line, strip_backtrace_load_path = true, filename_cache: nil)
|
|
34
31
|
@abs_path = line.file
|
|
35
32
|
@function = line.method if line.method
|
|
36
33
|
@lineno = line.number
|
|
37
34
|
@in_app = line.in_app
|
|
38
35
|
@module = line.module_name if line.module_name
|
|
39
|
-
@filename = compute_filename
|
|
36
|
+
@filename = filename_cache&.compute_filename(@abs_path, @in_app, strip_backtrace_load_path)
|
|
40
37
|
end
|
|
41
38
|
|
|
42
39
|
def to_s
|
|
43
40
|
"#{@filename}:#{@lineno}"
|
|
44
41
|
end
|
|
45
42
|
|
|
46
|
-
def compute_filename
|
|
47
|
-
return if abs_path.nil?
|
|
48
|
-
return abs_path unless @strip_backtrace_load_path
|
|
49
|
-
|
|
50
|
-
prefix =
|
|
51
|
-
if under_project_root? && in_app
|
|
52
|
-
@project_root
|
|
53
|
-
elsif under_project_root?
|
|
54
|
-
longest_load_path || @project_root
|
|
55
|
-
else
|
|
56
|
-
longest_load_path
|
|
57
|
-
end
|
|
58
|
-
|
|
59
|
-
prefix ? abs_path[prefix.to_s.chomp(File::SEPARATOR).length + 1..-1] : abs_path
|
|
60
|
-
end
|
|
61
|
-
|
|
62
43
|
def set_context(linecache, context_lines)
|
|
63
44
|
return unless abs_path
|
|
64
45
|
|
|
@@ -76,14 +57,6 @@ module Sentry
|
|
|
76
57
|
end
|
|
77
58
|
|
|
78
59
|
private
|
|
79
|
-
|
|
80
|
-
def under_project_root?
|
|
81
|
-
@project_root && abs_path.start_with?(@project_root)
|
|
82
|
-
end
|
|
83
|
-
|
|
84
|
-
def longest_load_path
|
|
85
|
-
$LOAD_PATH.select { |path| abs_path.start_with?(path.to_s) }.max_by(&:size)
|
|
86
|
-
end
|
|
87
60
|
end
|
|
88
61
|
end
|
|
89
62
|
end
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
+
require "sentry/utils/filename_cache"
|
|
4
|
+
|
|
3
5
|
module Sentry
|
|
4
6
|
class StacktraceBuilder
|
|
5
7
|
# @return [String]
|
|
@@ -20,6 +22,9 @@ module Sentry
|
|
|
20
22
|
# @return [Boolean]
|
|
21
23
|
attr_reader :strip_backtrace_load_path
|
|
22
24
|
|
|
25
|
+
# @return [FilenameCache]
|
|
26
|
+
attr_reader :filename_cache
|
|
27
|
+
|
|
23
28
|
# @param project_root [String]
|
|
24
29
|
# @param app_dirs_pattern [Regexp, nil]
|
|
25
30
|
# @param linecache [LineCache]
|
|
@@ -46,6 +51,8 @@ module Sentry
|
|
|
46
51
|
@context_lines = context_lines
|
|
47
52
|
@backtrace_cleanup_callback = backtrace_cleanup_callback
|
|
48
53
|
@strip_backtrace_load_path = strip_backtrace_load_path
|
|
54
|
+
@in_app_pattern = Regexp.new("^(#{project_root}/)?#{app_dirs_pattern}") if app_dirs_pattern
|
|
55
|
+
@filename_cache = FilenameCache.new(project_root)
|
|
49
56
|
end
|
|
50
57
|
|
|
51
58
|
# Generates a StacktraceInterface with the given backtrace.
|
|
@@ -64,13 +71,21 @@ module Sentry
|
|
|
64
71
|
# @yieldparam frame [StacktraceInterface::Frame]
|
|
65
72
|
# @return [StacktraceInterface]
|
|
66
73
|
def build(backtrace:, &frame_callback)
|
|
67
|
-
parsed_lines = parse_backtrace_lines(backtrace)
|
|
74
|
+
parsed_lines = parse_backtrace_lines(backtrace)
|
|
75
|
+
|
|
76
|
+
# Build frames in reverse order, skipping lines without files
|
|
77
|
+
# Single pass instead of select + reverse + map + compact
|
|
78
|
+
frames = []
|
|
79
|
+
i = parsed_lines.size - 1
|
|
80
|
+
while i >= 0
|
|
81
|
+
line = parsed_lines[i]
|
|
82
|
+
i -= 1
|
|
83
|
+
next unless line.file
|
|
68
84
|
|
|
69
|
-
frames = parsed_lines.reverse.map do |line|
|
|
70
85
|
frame = convert_parsed_line_into_frame(line)
|
|
71
86
|
frame = frame_callback.call(frame) if frame_callback
|
|
72
|
-
frame
|
|
73
|
-
end
|
|
87
|
+
frames << frame if frame
|
|
88
|
+
end
|
|
74
89
|
|
|
75
90
|
StacktraceInterface.new(frames: frames)
|
|
76
91
|
end
|
|
@@ -78,14 +93,15 @@ module Sentry
|
|
|
78
93
|
private
|
|
79
94
|
|
|
80
95
|
def convert_parsed_line_into_frame(line)
|
|
81
|
-
frame = StacktraceInterface::Frame.new(project_root, line, strip_backtrace_load_path)
|
|
96
|
+
frame = StacktraceInterface::Frame.new(project_root, line, strip_backtrace_load_path, filename_cache: @filename_cache)
|
|
82
97
|
frame.set_context(linecache, context_lines) if context_lines
|
|
83
98
|
frame
|
|
84
99
|
end
|
|
85
100
|
|
|
86
101
|
def parse_backtrace_lines(backtrace)
|
|
87
102
|
Backtrace.parse(
|
|
88
|
-
backtrace, project_root, app_dirs_pattern,
|
|
103
|
+
backtrace, project_root, app_dirs_pattern,
|
|
104
|
+
in_app_pattern: @in_app_pattern, &backtrace_cleanup_callback
|
|
89
105
|
).lines
|
|
90
106
|
end
|
|
91
107
|
end
|
data/lib/sentry/linecache.rb
CHANGED
|
@@ -12,36 +12,33 @@ module Sentry
|
|
|
12
12
|
# file. The number of lines retrieved is (2 * context) + 1, the middle
|
|
13
13
|
# line should be the line requested by lineno. See specs for more information.
|
|
14
14
|
def get_file_context(filename, lineno, context)
|
|
15
|
-
|
|
15
|
+
lines = getlines(filename)
|
|
16
|
+
return nil, nil, nil unless lines
|
|
16
17
|
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
18
|
+
first_line = lineno - context
|
|
19
|
+
pre = Array.new(context) { |i| line_at(lines, first_line + i) }
|
|
20
|
+
context_line = line_at(lines, lineno)
|
|
21
|
+
post = Array.new(context) { |i| line_at(lines, lineno + 1 + i) }
|
|
22
|
+
|
|
23
|
+
[pre, context_line, post]
|
|
21
24
|
end
|
|
22
25
|
|
|
23
26
|
private
|
|
24
27
|
|
|
25
|
-
def
|
|
26
|
-
|
|
27
|
-
|
|
28
|
+
def line_at(lines, n)
|
|
29
|
+
return nil if n < 1
|
|
30
|
+
|
|
31
|
+
lines[n - 1]
|
|
28
32
|
end
|
|
29
33
|
|
|
30
34
|
def getlines(path)
|
|
31
|
-
@cache
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
+
@cache.fetch(path) do
|
|
36
|
+
@cache[path] = begin
|
|
37
|
+
File.open(path, "r", &:readlines)
|
|
38
|
+
rescue
|
|
39
|
+
nil
|
|
40
|
+
end
|
|
35
41
|
end
|
|
36
42
|
end
|
|
37
|
-
|
|
38
|
-
def getline(path, n)
|
|
39
|
-
return nil if n < 1
|
|
40
|
-
|
|
41
|
-
lines = getlines(path)
|
|
42
|
-
return nil if lines.nil?
|
|
43
|
-
|
|
44
|
-
lines[n - 1]
|
|
45
|
-
end
|
|
46
43
|
end
|
|
47
44
|
end
|
|
@@ -9,28 +9,8 @@ module Sentry
|
|
|
9
9
|
abs_path.match?(@in_app_pattern)
|
|
10
10
|
end
|
|
11
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
12
|
def compute_filename(abs_path, in_app)
|
|
16
|
-
|
|
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
|
|
13
|
+
@filename_cache.compute_filename(abs_path, in_app, true)
|
|
34
14
|
end
|
|
35
15
|
|
|
36
16
|
def split_module(name)
|
data/lib/sentry/profiler.rb
CHANGED
|
@@ -26,6 +26,7 @@ module Sentry
|
|
|
26
26
|
@project_root = configuration.project_root
|
|
27
27
|
@app_dirs_pattern = configuration.app_dirs_pattern
|
|
28
28
|
@in_app_pattern = Regexp.new("^(#{@project_root}/)?#{@app_dirs_pattern}")
|
|
29
|
+
@filename_cache = configuration.stacktrace_builder.filename_cache
|
|
29
30
|
end
|
|
30
31
|
|
|
31
32
|
def start
|
|
@@ -6,6 +6,7 @@ module Sentry
|
|
|
6
6
|
class << self
|
|
7
7
|
def detect_release(project_root:, running_on_heroku:)
|
|
8
8
|
detect_release_from_env ||
|
|
9
|
+
detect_release_from_kamal ||
|
|
9
10
|
detect_release_from_git ||
|
|
10
11
|
detect_release_from_capistrano(project_root) ||
|
|
11
12
|
detect_release_from_heroku(running_on_heroku)
|
|
@@ -31,6 +32,10 @@ module Sentry
|
|
|
31
32
|
Sentry.sys_command("git rev-parse HEAD") if File.directory?(".git")
|
|
32
33
|
end
|
|
33
34
|
|
|
35
|
+
def detect_release_from_kamal
|
|
36
|
+
ENV["KAMAL_VERSION"]
|
|
37
|
+
end
|
|
38
|
+
|
|
34
39
|
def detect_release_from_env
|
|
35
40
|
ENV["SENTRY_RELEASE"]
|
|
36
41
|
end
|
data/lib/sentry/test_helper.rb
CHANGED
|
@@ -37,13 +37,25 @@ module Sentry
|
|
|
37
37
|
# - auto_session_tracking
|
|
38
38
|
block&.call(dummy_config)
|
|
39
39
|
|
|
40
|
+
# Install the testing clients on the *main* hub rather than the current
|
|
41
|
+
# thread's hub. `Sentry.clone_hub_to_current_thread` (used by
|
|
42
|
+
# Sentry::Rack::CaptureExceptions) always clones the main hub, so if we
|
|
43
|
+
# only mutated the thread-local hub a request-time clone would observe a
|
|
44
|
+
# stale transport.
|
|
45
|
+
main_hub = Sentry.get_main_hub
|
|
46
|
+
|
|
40
47
|
# the base layer's client should already use the dummy config so nothing will be sent by accident
|
|
41
48
|
base_client = Sentry::Client.new(dummy_config)
|
|
42
|
-
|
|
49
|
+
main_hub.bind_client(base_client)
|
|
43
50
|
# create a new layer so mutations made to the testing scope or configuration could be simply popped later
|
|
44
|
-
|
|
51
|
+
main_hub.push_scope
|
|
45
52
|
test_client = Sentry::Client.new(dummy_config.dup)
|
|
46
|
-
|
|
53
|
+
main_hub.bind_client(test_client)
|
|
54
|
+
|
|
55
|
+
# Realign the current thread's hub with the main hub so direct
|
|
56
|
+
# `sentry_events` reads and any hub the Rack middleware clones from the
|
|
57
|
+
# main hub all observe the same DummyTransport.
|
|
58
|
+
Thread.current.thread_variable_set(Sentry::THREAD_LOCAL, main_hub)
|
|
47
59
|
end
|
|
48
60
|
|
|
49
61
|
# Clears all stored events and envelopes.
|
|
@@ -54,11 +66,15 @@ module Sentry
|
|
|
54
66
|
|
|
55
67
|
clear_sentry_events
|
|
56
68
|
|
|
57
|
-
# pop testing layer created by `setup_sentry_test`
|
|
58
|
-
#
|
|
69
|
+
# pop the testing layer created by `setup_sentry_test` off the *main*
|
|
70
|
+
# hub (that is where `setup_sentry_test` pushed it), keeping the base
|
|
71
|
+
# layer to avoid nil-pointer errors. Popping the current thread's hub
|
|
72
|
+
# would leave the test layer dangling on the main hub, which the next
|
|
73
|
+
# request-time clone would inherit.
|
|
59
74
|
# TODO: find a way to notify users if they somehow popped the test layer before calling this method
|
|
60
|
-
|
|
61
|
-
|
|
75
|
+
main_hub = Sentry.get_main_hub
|
|
76
|
+
if main_hub.instance_variable_get(:@stack).size > 1
|
|
77
|
+
main_hub.pop_scope
|
|
62
78
|
end
|
|
63
79
|
Sentry::Scope.global_event_processors.clear
|
|
64
80
|
end
|
|
@@ -66,7 +82,13 @@ module Sentry
|
|
|
66
82
|
def clear_sentry_events
|
|
67
83
|
return unless Sentry.initialized?
|
|
68
84
|
|
|
69
|
-
|
|
85
|
+
# Clear every transport reachable from the current thread's hub and the
|
|
86
|
+
# main hub (including its base layer). A request-time clone shares the
|
|
87
|
+
# main hub's base-layer transport, so clearing only the current
|
|
88
|
+
# transport would let stale events survive into the next test.
|
|
89
|
+
sentry_test_transports.each do |transport|
|
|
90
|
+
transport.clear if transport.respond_to?(:clear)
|
|
91
|
+
end
|
|
70
92
|
|
|
71
93
|
if Sentry.configuration.enable_logs && sentry_logger.respond_to?(:clear)
|
|
72
94
|
sentry_logger.clear
|
|
@@ -83,6 +105,17 @@ module Sentry
|
|
|
83
105
|
Sentry.get_current_client.transport
|
|
84
106
|
end
|
|
85
107
|
|
|
108
|
+
# Every transport reachable from the current thread's hub and the main
|
|
109
|
+
# hub, across all stack layers. Used by `clear_sentry_events` so a stale
|
|
110
|
+
# DummyTransport (e.g. the main hub's base layer that a request-time clone
|
|
111
|
+
# shares) cannot carry leftover events into the next test.
|
|
112
|
+
# @return [Array<Transport>]
|
|
113
|
+
def sentry_test_transports
|
|
114
|
+
[Sentry.get_current_hub, Sentry.get_main_hub].compact.uniq.flat_map do |hub|
|
|
115
|
+
hub.clients.map(&:transport)
|
|
116
|
+
end.compact.uniq
|
|
117
|
+
end
|
|
118
|
+
|
|
86
119
|
# Returns the captured event objects.
|
|
87
120
|
# @return [Array<Event>]
|
|
88
121
|
def sentry_events
|
|
@@ -18,5 +18,12 @@ module Sentry
|
|
|
18
18
|
def send_envelope(envelope)
|
|
19
19
|
@envelopes << envelope
|
|
20
20
|
end
|
|
21
|
+
|
|
22
|
+
# Empties the captured events and envelopes so `TestHelper.clear_sentry_events`
|
|
23
|
+
# also clears the dummy transport instance
|
|
24
|
+
def clear
|
|
25
|
+
@events.clear
|
|
26
|
+
@envelopes.clear
|
|
27
|
+
end
|
|
21
28
|
end
|
|
22
29
|
end
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Sentry
|
|
4
|
+
class FilenameCache
|
|
5
|
+
attr_reader :cache
|
|
6
|
+
|
|
7
|
+
def initialize(project_root)
|
|
8
|
+
@project_root = project_root
|
|
9
|
+
@load_paths = $LOAD_PATH.map(&:to_s).sort_by(&:size).reverse.freeze
|
|
10
|
+
@cache = {}
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def compute_filename(abs_path, in_app, strip_backtrace_load_path)
|
|
14
|
+
return unless abs_path
|
|
15
|
+
return abs_path unless strip_backtrace_load_path
|
|
16
|
+
|
|
17
|
+
@cache.fetch(abs_path) do
|
|
18
|
+
under_root = @project_root && abs_path.start_with?(@project_root)
|
|
19
|
+
prefix =
|
|
20
|
+
if under_root && in_app
|
|
21
|
+
@project_root
|
|
22
|
+
elsif under_root
|
|
23
|
+
longest_load_path(abs_path) || @project_root
|
|
24
|
+
else
|
|
25
|
+
longest_load_path(abs_path)
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
@cache[abs_path] = if prefix
|
|
29
|
+
offset = if prefix.end_with?(File::SEPARATOR)
|
|
30
|
+
prefix.bytesize
|
|
31
|
+
else
|
|
32
|
+
prefix.bytesize + 1
|
|
33
|
+
end
|
|
34
|
+
abs_path.byteslice(offset, abs_path.bytesize - offset)
|
|
35
|
+
else
|
|
36
|
+
abs_path
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
private
|
|
42
|
+
|
|
43
|
+
def longest_load_path(abs_path)
|
|
44
|
+
@load_paths.find { |path| abs_path.start_with?(path) }
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
end
|
|
@@ -12,7 +12,13 @@ module Sentry
|
|
|
12
12
|
end
|
|
13
13
|
|
|
14
14
|
def set_propagation_headers(req)
|
|
15
|
-
Sentry.get_trace_propagation_headers&.each
|
|
15
|
+
Sentry.get_trace_propagation_headers&.each do |k, v|
|
|
16
|
+
if k == BAGGAGE_HEADER_NAME && req[k]
|
|
17
|
+
req[k] = "#{v},#{req[k]}"
|
|
18
|
+
else
|
|
19
|
+
req[k] = v
|
|
20
|
+
end
|
|
21
|
+
end
|
|
16
22
|
end
|
|
17
23
|
|
|
18
24
|
def record_sentry_breadcrumb(request_info, response_status)
|
|
@@ -10,11 +10,12 @@ module Sentry
|
|
|
10
10
|
|
|
11
11
|
attr_reader :profile
|
|
12
12
|
|
|
13
|
-
def initialize(profile, project_root:, in_app_pattern:, app_dirs_pattern:)
|
|
13
|
+
def initialize(profile, project_root:, in_app_pattern:, app_dirs_pattern:, filename_cache:)
|
|
14
14
|
@profile = profile
|
|
15
15
|
@project_root = project_root
|
|
16
16
|
@in_app_pattern = in_app_pattern
|
|
17
17
|
@app_dirs_pattern = app_dirs_pattern
|
|
18
|
+
@filename_cache = filename_cache
|
|
18
19
|
end
|
|
19
20
|
|
|
20
21
|
def to_h
|
|
@@ -24,6 +24,7 @@ module Sentry
|
|
|
24
24
|
@project_root = configuration.project_root
|
|
25
25
|
@app_dirs_pattern = configuration.app_dirs_pattern
|
|
26
26
|
@in_app_pattern = Regexp.new("^(#{@project_root}/)?#{@app_dirs_pattern}")
|
|
27
|
+
@filename_cache = configuration.stacktrace_builder.filename_cache
|
|
27
28
|
end
|
|
28
29
|
|
|
29
30
|
def set_initial_sample_decision(transaction_sampled)
|
|
@@ -125,7 +126,8 @@ module Sentry
|
|
|
125
126
|
result,
|
|
126
127
|
project_root: @project_root,
|
|
127
128
|
app_dirs_pattern: @app_dirs_pattern,
|
|
128
|
-
in_app_pattern: @in_app_pattern
|
|
129
|
+
in_app_pattern: @in_app_pattern,
|
|
130
|
+
filename_cache: @filename_cache
|
|
129
131
|
)
|
|
130
132
|
end
|
|
131
133
|
end
|
data/lib/sentry/version.rb
CHANGED
data/lib/sentry-ruby.rb
CHANGED
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: sentry-ruby
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 6.
|
|
4
|
+
version: 6.6.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Sentry Team
|
|
@@ -157,6 +157,7 @@ files:
|
|
|
157
157
|
- lib/sentry/utils/encoding_helper.rb
|
|
158
158
|
- lib/sentry/utils/env_helper.rb
|
|
159
159
|
- lib/sentry/utils/exception_cause_chain.rb
|
|
160
|
+
- lib/sentry/utils/filename_cache.rb
|
|
160
161
|
- lib/sentry/utils/http_tracing.rb
|
|
161
162
|
- lib/sentry/utils/logging_helper.rb
|
|
162
163
|
- lib/sentry/utils/real_ip.rb
|
|
@@ -169,15 +170,15 @@ files:
|
|
|
169
170
|
- lib/sentry/version.rb
|
|
170
171
|
- sentry-ruby-core.gemspec
|
|
171
172
|
- sentry-ruby.gemspec
|
|
172
|
-
homepage: https://github.com/getsentry/sentry-ruby/tree/6.
|
|
173
|
+
homepage: https://github.com/getsentry/sentry-ruby/tree/6.6.0/sentry-ruby
|
|
173
174
|
licenses:
|
|
174
175
|
- MIT
|
|
175
176
|
metadata:
|
|
176
|
-
homepage_uri: https://github.com/getsentry/sentry-ruby/tree/6.
|
|
177
|
-
source_code_uri: https://github.com/getsentry/sentry-ruby/tree/6.
|
|
178
|
-
changelog_uri: https://github.com/getsentry/sentry-ruby/blob/6.
|
|
177
|
+
homepage_uri: https://github.com/getsentry/sentry-ruby/tree/6.6.0/sentry-ruby
|
|
178
|
+
source_code_uri: https://github.com/getsentry/sentry-ruby/tree/6.6.0/sentry-ruby
|
|
179
|
+
changelog_uri: https://github.com/getsentry/sentry-ruby/blob/6.6.0/CHANGELOG.md
|
|
179
180
|
bug_tracker_uri: https://github.com/getsentry/sentry-ruby/issues
|
|
180
|
-
documentation_uri: http://www.rubydoc.info/gems/sentry-ruby/6.
|
|
181
|
+
documentation_uri: http://www.rubydoc.info/gems/sentry-ruby/6.6.0
|
|
181
182
|
rdoc_options: []
|
|
182
183
|
require_paths:
|
|
183
184
|
- lib
|