sentry-ruby-core 4.8.1 → 5.1.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/.yardopts +2 -0
- data/Gemfile +3 -0
- data/README.md +2 -0
- data/lib/sentry/background_worker.rb +3 -1
- data/lib/sentry/backtrace.rb +1 -3
- data/lib/sentry/breadcrumb.rb +22 -3
- data/lib/sentry/breadcrumb_buffer.rb +14 -0
- data/lib/sentry/client.rb +36 -2
- data/lib/sentry/configuration.rb +87 -41
- data/lib/sentry/envelope.rb +1 -0
- data/lib/sentry/event.rb +54 -18
- data/lib/sentry/hub.rb +7 -1
- data/lib/sentry/interface.rb +1 -10
- data/lib/sentry/interfaces/exception.rb +11 -3
- data/lib/sentry/interfaces/request.rb +37 -20
- data/lib/sentry/interfaces/stacktrace.rb +4 -0
- data/lib/sentry/interfaces/stacktrace_builder.rb +37 -10
- data/lib/sentry/interfaces/threads.rb +10 -2
- data/lib/sentry/linecache.rb +1 -0
- data/lib/sentry/net/http.rb +52 -65
- data/lib/sentry/rake.rb +12 -2
- data/lib/sentry/redis.rb +88 -0
- data/lib/sentry/release_detector.rb +1 -0
- data/lib/sentry/scope.rb +67 -1
- data/lib/sentry/span.rb +83 -8
- data/lib/sentry/transaction.rb +46 -14
- data/lib/sentry/transaction_event.rb +8 -0
- data/lib/sentry/transport/configuration.rb +1 -2
- data/lib/sentry/transport/http_transport.rb +50 -39
- data/lib/sentry/transport.rb +4 -1
- data/lib/sentry/utils/logging_helper.rb +4 -4
- data/lib/sentry/version.rb +1 -1
- data/lib/sentry-ruby.rb +165 -41
- data/sentry-ruby-core.gemspec +0 -1
- data/sentry-ruby.gemspec +0 -1
- metadata +4 -16
data/lib/sentry/event.rb
CHANGED
@@ -10,31 +10,40 @@ require 'sentry/utils/custom_inspection'
|
|
10
10
|
|
11
11
|
module Sentry
|
12
12
|
class Event
|
13
|
+
# These are readable attributes.
|
13
14
|
SERIALIZEABLE_ATTRIBUTES = %i(
|
14
15
|
event_id level timestamp
|
15
16
|
release environment server_name modules
|
16
17
|
message user tags contexts extra
|
17
|
-
fingerprint breadcrumbs
|
18
|
+
fingerprint breadcrumbs transaction
|
18
19
|
platform sdk type
|
19
20
|
)
|
20
21
|
|
22
|
+
# These are writable attributes.
|
21
23
|
WRITER_ATTRIBUTES = SERIALIZEABLE_ATTRIBUTES - %i(type timestamp level)
|
22
24
|
|
23
25
|
MAX_MESSAGE_SIZE_IN_BYTES = 1024 * 8
|
24
26
|
|
25
|
-
SKIP_INSPECTION_ATTRIBUTES = [:@
|
27
|
+
SKIP_INSPECTION_ATTRIBUTES = [:@modules, :@stacktrace_builder, :@send_default_pii, :@trusted_proxies, :@rack_env_whitelist]
|
26
28
|
|
27
29
|
include CustomInspection
|
28
30
|
|
29
31
|
attr_writer(*WRITER_ATTRIBUTES)
|
30
32
|
attr_reader(*SERIALIZEABLE_ATTRIBUTES)
|
31
33
|
|
32
|
-
|
34
|
+
# @return [RequestInterface]
|
35
|
+
attr_reader :request
|
33
36
|
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
+
# @return [ExceptionInterface]
|
38
|
+
attr_reader :exception
|
39
|
+
|
40
|
+
# @return [ThreadsInterface]
|
41
|
+
attr_reader :threads
|
37
42
|
|
43
|
+
# @param configuration [Configuration]
|
44
|
+
# @param integration_meta [Hash, nil]
|
45
|
+
# @param message [String, nil]
|
46
|
+
def initialize(configuration:, integration_meta: nil, message: nil)
|
38
47
|
# Set some simple default values
|
39
48
|
@event_id = SecureRandom.uuid.delete("-")
|
40
49
|
@timestamp = Sentry.utc_now.iso8601
|
@@ -48,17 +57,25 @@ module Sentry
|
|
48
57
|
|
49
58
|
@fingerprint = []
|
50
59
|
|
60
|
+
# configuration data that's directly used by events
|
51
61
|
@server_name = configuration.server_name
|
52
62
|
@environment = configuration.environment
|
53
63
|
@release = configuration.release
|
54
64
|
@modules = configuration.gem_specs if configuration.send_modules
|
55
65
|
|
66
|
+
# configuration options to help events process data
|
67
|
+
@send_default_pii = configuration.send_default_pii
|
68
|
+
@trusted_proxies = configuration.trusted_proxies
|
69
|
+
@stacktrace_builder = configuration.stacktrace_builder
|
70
|
+
@rack_env_whitelist = configuration.rack_env_whitelist
|
71
|
+
|
56
72
|
@message = (message || "").byteslice(0..MAX_MESSAGE_SIZE_IN_BYTES)
|
57
73
|
|
58
74
|
self.level = :error
|
59
75
|
end
|
60
76
|
|
61
77
|
class << self
|
78
|
+
# @!visibility private
|
62
79
|
def get_log_message(event_hash)
|
63
80
|
message = event_hash[:message] || event_hash['message']
|
64
81
|
|
@@ -75,6 +92,7 @@ module Sentry
|
|
75
92
|
'<no message value>'
|
76
93
|
end
|
77
94
|
|
95
|
+
# @!visibility private
|
78
96
|
def get_message_from_exception(event_hash)
|
79
97
|
if exception = event_hash.dig(:exception, :values, 0)
|
80
98
|
"#{exception[:type]}: #{exception[:value]}"
|
@@ -84,21 +102,35 @@ module Sentry
|
|
84
102
|
end
|
85
103
|
end
|
86
104
|
|
105
|
+
# @deprecated This method will be removed in v5.0.0. Please just use Sentry.configuration
|
106
|
+
# @return [Configuration]
|
107
|
+
def configuration
|
108
|
+
Sentry.configuration
|
109
|
+
end
|
110
|
+
|
111
|
+
# Sets the event's timestamp.
|
112
|
+
# @param time [Time, Float]
|
113
|
+
# @return [void]
|
87
114
|
def timestamp=(time)
|
88
115
|
@timestamp = time.is_a?(Time) ? time.to_f : time
|
89
116
|
end
|
90
117
|
|
91
|
-
|
92
|
-
|
118
|
+
# Sets the event's level.
|
119
|
+
# @param level [String, Symbol]
|
120
|
+
# @return [void]
|
121
|
+
def level=(level) # needed to meet the Sentry spec
|
122
|
+
@level = level.to_s == "warn" ? :warning : level
|
93
123
|
end
|
94
124
|
|
125
|
+
# Sets the event's request environment data with RequestInterface.
|
126
|
+
# @see RequestInterface
|
127
|
+
# @param env [Hash]
|
128
|
+
# @return [void]
|
95
129
|
def rack_env=(env)
|
96
130
|
unless request || env.empty?
|
97
|
-
env = env.dup
|
98
|
-
|
99
131
|
add_request_interface(env)
|
100
132
|
|
101
|
-
if
|
133
|
+
if @send_default_pii
|
102
134
|
user[:ip_address] = calculate_real_ip_from_rack(env)
|
103
135
|
end
|
104
136
|
|
@@ -108,6 +140,7 @@ module Sentry
|
|
108
140
|
end
|
109
141
|
end
|
110
142
|
|
143
|
+
# @return [Hash]
|
111
144
|
def to_hash
|
112
145
|
data = serialize_attributes
|
113
146
|
data[:breadcrumbs] = breadcrumbs.to_hash if breadcrumbs
|
@@ -118,32 +151,35 @@ module Sentry
|
|
118
151
|
data
|
119
152
|
end
|
120
153
|
|
154
|
+
# @return [Hash]
|
121
155
|
def to_json_compatible
|
122
156
|
JSON.parse(JSON.generate(to_hash))
|
123
157
|
end
|
124
158
|
|
125
|
-
|
126
|
-
@request = Sentry::RequestInterface.build(env: env)
|
127
|
-
end
|
128
|
-
|
159
|
+
# @!visibility private
|
129
160
|
def add_threads_interface(backtrace: nil, **options)
|
130
161
|
@threads = ThreadsInterface.build(
|
131
162
|
backtrace: backtrace,
|
132
|
-
stacktrace_builder:
|
163
|
+
stacktrace_builder: @stacktrace_builder,
|
133
164
|
**options
|
134
165
|
)
|
135
166
|
end
|
136
167
|
|
168
|
+
# @!visibility private
|
137
169
|
def add_exception_interface(exception)
|
138
170
|
if exception.respond_to?(:sentry_context)
|
139
171
|
@extra.merge!(exception.sentry_context)
|
140
172
|
end
|
141
173
|
|
142
|
-
@exception = Sentry::ExceptionInterface.build(exception: exception, stacktrace_builder:
|
174
|
+
@exception = Sentry::ExceptionInterface.build(exception: exception, stacktrace_builder: @stacktrace_builder)
|
143
175
|
end
|
144
176
|
|
145
177
|
private
|
146
178
|
|
179
|
+
def add_request_interface(env)
|
180
|
+
@request = Sentry::RequestInterface.new(env: env, send_default_pii: @send_default_pii, rack_env_whitelist: @rack_env_whitelist)
|
181
|
+
end
|
182
|
+
|
147
183
|
def serialize_attributes
|
148
184
|
self.class::SERIALIZEABLE_ATTRIBUTES.each_with_object({}) do |att, memo|
|
149
185
|
if value = public_send(att)
|
@@ -160,7 +196,7 @@ module Sentry
|
|
160
196
|
:client_ip => env["HTTP_CLIENT_IP"],
|
161
197
|
:real_ip => env["HTTP_X_REAL_IP"],
|
162
198
|
:forwarded_for => env["HTTP_X_FORWARDED_FOR"],
|
163
|
-
:trusted_proxies =>
|
199
|
+
:trusted_proxies => @trusted_proxies
|
164
200
|
).calculate_ip
|
165
201
|
end
|
166
202
|
end
|
data/lib/sentry/hub.rb
CHANGED
@@ -102,7 +102,10 @@ module Sentry
|
|
102
102
|
|
103
103
|
return unless event
|
104
104
|
|
105
|
-
capture_event(event, **options, &block)
|
105
|
+
capture_event(event, **options, &block).tap do
|
106
|
+
# mark the exception as captured so we can use this information to avoid duplicated capturing
|
107
|
+
exception.instance_variable_set(:@__sentry_captured, true)
|
108
|
+
end
|
106
109
|
end
|
107
110
|
|
108
111
|
def capture_message(message, **options, &block)
|
@@ -114,6 +117,9 @@ module Sentry
|
|
114
117
|
options[:hint][:message] = message
|
115
118
|
backtrace = options.delete(:backtrace)
|
116
119
|
event = current_client.event_from_message(message, options[:hint], backtrace: backtrace)
|
120
|
+
|
121
|
+
return unless event
|
122
|
+
|
117
123
|
capture_event(event, **options, &block)
|
118
124
|
end
|
119
125
|
|
data/lib/sentry/interface.rb
CHANGED
@@ -2,16 +2,7 @@
|
|
2
2
|
|
3
3
|
module Sentry
|
4
4
|
class Interface
|
5
|
-
|
6
|
-
name = klass.name.split("::").last.downcase.gsub("interface", "")
|
7
|
-
registered[name.to_sym] = klass
|
8
|
-
super
|
9
|
-
end
|
10
|
-
|
11
|
-
def self.registered
|
12
|
-
@@registered ||= {} # rubocop:disable Style/ClassVars
|
13
|
-
end
|
14
|
-
|
5
|
+
# @return [Hash]
|
15
6
|
def to_hash
|
16
7
|
Hash[instance_variables.map { |name| [name[1..-1].to_sym, instance_variable_get(name)] }]
|
17
8
|
end
|
@@ -2,16 +2,24 @@
|
|
2
2
|
|
3
3
|
module Sentry
|
4
4
|
class ExceptionInterface < Interface
|
5
|
-
|
6
|
-
|
5
|
+
# @param exceptions [Array<SingleExceptionInterface>]
|
6
|
+
def initialize(exceptions:)
|
7
|
+
@values = exceptions
|
7
8
|
end
|
8
9
|
|
10
|
+
# @return [Hash]
|
9
11
|
def to_hash
|
10
12
|
data = super
|
11
13
|
data[:values] = data[:values].map(&:to_hash) if data[:values]
|
12
14
|
data
|
13
15
|
end
|
14
16
|
|
17
|
+
# Builds ExceptionInterface with given exception and stacktrace_builder.
|
18
|
+
# @param exception [Exception]
|
19
|
+
# @param stacktrace_builder [StacktraceBuilder]
|
20
|
+
# @see SingleExceptionInterface#build_with_stacktrace
|
21
|
+
# @see SingleExceptionInterface#initialize
|
22
|
+
# @return [ExceptionInterface]
|
15
23
|
def self.build(exception:, stacktrace_builder:)
|
16
24
|
exceptions = Sentry::Utils::ExceptionCauseChain.exception_to_array(exception).reverse
|
17
25
|
processed_backtrace_ids = Set.new
|
@@ -25,7 +33,7 @@ module Sentry
|
|
25
33
|
end
|
26
34
|
end
|
27
35
|
|
28
|
-
new(
|
36
|
+
new(exceptions: exceptions)
|
29
37
|
end
|
30
38
|
end
|
31
39
|
end
|
@@ -15,29 +15,45 @@ module Sentry
|
|
15
15
|
# https://github.com/getsentry/sentry/blob/master/src/sentry/conf/server.py
|
16
16
|
MAX_BODY_LIMIT = 4096 * 4
|
17
17
|
|
18
|
-
|
18
|
+
# @return [String]
|
19
|
+
attr_accessor :url
|
19
20
|
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
21
|
+
# @return [String]
|
22
|
+
attr_accessor :method
|
23
|
+
|
24
|
+
# @return [Hash]
|
25
|
+
attr_accessor :data
|
26
|
+
|
27
|
+
# @return [String]
|
28
|
+
attr_accessor :query_string
|
29
|
+
|
30
|
+
# @return [String]
|
31
|
+
attr_accessor :cookies
|
32
|
+
|
33
|
+
# @return [Hash]
|
34
|
+
attr_accessor :headers
|
25
35
|
|
26
|
-
|
27
|
-
|
36
|
+
# @return [Hash]
|
37
|
+
attr_accessor :env
|
38
|
+
|
39
|
+
# @param env [Hash]
|
40
|
+
# @param send_default_pii [Boolean]
|
41
|
+
# @param rack_env_whitelist [Array]
|
42
|
+
# @see Configuration#send_default_pii
|
43
|
+
# @see Configuration#rack_env_whitelist
|
44
|
+
def initialize(env:, send_default_pii:, rack_env_whitelist:)
|
45
|
+
env = env.dup
|
46
|
+
|
47
|
+
unless send_default_pii
|
28
48
|
# need to completely wipe out ip addresses
|
29
49
|
RequestInterface::IP_HEADERS.each do |header|
|
30
50
|
env.delete(header)
|
31
51
|
end
|
32
52
|
end
|
33
53
|
|
34
|
-
env
|
35
|
-
end
|
36
|
-
|
37
|
-
def initialize(request:)
|
38
|
-
env = request.env
|
54
|
+
request = ::Rack::Request.new(env)
|
39
55
|
|
40
|
-
if
|
56
|
+
if send_default_pii
|
41
57
|
self.data = read_data_from(request)
|
42
58
|
self.cookies = request.cookies
|
43
59
|
self.query_string = request.query_string
|
@@ -46,8 +62,8 @@ module Sentry
|
|
46
62
|
self.url = request.scheme && request.url.split('?').first
|
47
63
|
self.method = request.request_method
|
48
64
|
|
49
|
-
self.headers = filter_and_format_headers(env)
|
50
|
-
self.env = filter_and_format_env(env)
|
65
|
+
self.headers = filter_and_format_headers(env, send_default_pii)
|
66
|
+
self.env = filter_and_format_env(env, rack_env_whitelist)
|
51
67
|
end
|
52
68
|
|
53
69
|
private
|
@@ -65,13 +81,14 @@ module Sentry
|
|
65
81
|
e.message
|
66
82
|
end
|
67
83
|
|
68
|
-
def filter_and_format_headers(env)
|
84
|
+
def filter_and_format_headers(env, send_default_pii)
|
69
85
|
env.each_with_object({}) do |(key, value), memo|
|
70
86
|
begin
|
71
87
|
key = key.to_s # rack env can contain symbols
|
72
88
|
next memo['X-Request-Id'] ||= Utils::RequestId.read_from(env) if Utils::RequestId::REQUEST_ID_HEADERS.include?(key)
|
73
89
|
next if is_server_protocol?(key, value, env["SERVER_PROTOCOL"])
|
74
90
|
next if is_skippable_header?(key)
|
91
|
+
next if key == "HTTP_AUTHORIZATION" && !send_default_pii
|
75
92
|
|
76
93
|
# Rack stores headers as HTTP_WHAT_EVER, we need What-Ever
|
77
94
|
key = key.sub(/^HTTP_/, "")
|
@@ -116,11 +133,11 @@ module Sentry
|
|
116
133
|
key == 'HTTP_VERSION' && value == protocol_version
|
117
134
|
end
|
118
135
|
|
119
|
-
def filter_and_format_env(env)
|
120
|
-
return env if
|
136
|
+
def filter_and_format_env(env, rack_env_whitelist)
|
137
|
+
return env if rack_env_whitelist.empty?
|
121
138
|
|
122
139
|
env.select do |k, _v|
|
123
|
-
|
140
|
+
rack_env_whitelist.include? k.to_s
|
124
141
|
end
|
125
142
|
end
|
126
143
|
end
|
@@ -2,16 +2,20 @@
|
|
2
2
|
|
3
3
|
module Sentry
|
4
4
|
class StacktraceInterface
|
5
|
+
# @return [<Array[Frame]>]
|
5
6
|
attr_reader :frames
|
6
7
|
|
8
|
+
# @param frames [<Array[Frame]>]
|
7
9
|
def initialize(frames:)
|
8
10
|
@frames = frames
|
9
11
|
end
|
10
12
|
|
13
|
+
# @return [Hash]
|
11
14
|
def to_hash
|
12
15
|
{ frames: @frames.map(&:to_hash) }
|
13
16
|
end
|
14
17
|
|
18
|
+
# @return [String]
|
15
19
|
def inspect
|
16
20
|
@frames.map(&:to_s)
|
17
21
|
end
|
@@ -2,8 +2,31 @@
|
|
2
2
|
|
3
3
|
module Sentry
|
4
4
|
class StacktraceBuilder
|
5
|
-
|
5
|
+
# @return [String]
|
6
|
+
attr_reader :project_root
|
6
7
|
|
8
|
+
# @return [Regexp, nil]
|
9
|
+
attr_reader :app_dirs_pattern
|
10
|
+
|
11
|
+
# @return [LineCache]
|
12
|
+
attr_reader :linecache
|
13
|
+
|
14
|
+
# @return [Integer, nil]
|
15
|
+
attr_reader :context_lines
|
16
|
+
|
17
|
+
# @return [Proc, nil]
|
18
|
+
attr_reader :backtrace_cleanup_callback
|
19
|
+
|
20
|
+
# @param project_root [String]
|
21
|
+
# @param app_dirs_pattern [Regexp, nil]
|
22
|
+
# @param linecache [LineCache]
|
23
|
+
# @param context_lines [Integer, nil]
|
24
|
+
# @param backtrace_cleanup_callback [Proc, nil]
|
25
|
+
# @see Configuration#project_root
|
26
|
+
# @see Configuration#app_dirs_pattern
|
27
|
+
# @see Configuration#linecache
|
28
|
+
# @see Configuration#context_lines
|
29
|
+
# @see Configuration#backtrace_cleanup_callback
|
7
30
|
def initialize(project_root:, app_dirs_pattern:, linecache:, context_lines:, backtrace_cleanup_callback: nil)
|
8
31
|
@project_root = project_root
|
9
32
|
@app_dirs_pattern = app_dirs_pattern
|
@@ -12,17 +35,21 @@ module Sentry
|
|
12
35
|
@backtrace_cleanup_callback = backtrace_cleanup_callback
|
13
36
|
end
|
14
37
|
|
15
|
-
#
|
38
|
+
# Generates a StacktraceInterface with the given backtrace.
|
39
|
+
# You can pass a block to customize/exclude frames:
|
16
40
|
#
|
17
|
-
#
|
18
|
-
#
|
19
|
-
#
|
20
|
-
#
|
21
|
-
#
|
22
|
-
#
|
41
|
+
# @example
|
42
|
+
# builder.build(backtrace) do |frame|
|
43
|
+
# if frame.module.match?(/a_gem/)
|
44
|
+
# nil
|
45
|
+
# else
|
46
|
+
# frame
|
47
|
+
# end
|
23
48
|
# end
|
24
|
-
#
|
25
|
-
#
|
49
|
+
# @param backtrace [Array<String>]
|
50
|
+
# @param frame_callback [Proc]
|
51
|
+
# @yieldparam frame [StacktraceInterface::Frame]
|
52
|
+
# @return [StacktraceInterface]
|
26
53
|
def build(backtrace:, &frame_callback)
|
27
54
|
parsed_lines = parse_backtrace_lines(backtrace).select(&:file)
|
28
55
|
|
@@ -2,6 +2,8 @@
|
|
2
2
|
|
3
3
|
module Sentry
|
4
4
|
class ThreadsInterface
|
5
|
+
# @param crashed [Boolean]
|
6
|
+
# @param stacktrace [Array]
|
5
7
|
def initialize(crashed: false, stacktrace: nil)
|
6
8
|
@id = Thread.current.object_id
|
7
9
|
@name = Thread.current.name
|
@@ -10,6 +12,7 @@ module Sentry
|
|
10
12
|
@stacktrace = stacktrace
|
11
13
|
end
|
12
14
|
|
15
|
+
# @return [Hash]
|
13
16
|
def to_hash
|
14
17
|
{
|
15
18
|
values: [
|
@@ -24,8 +27,13 @@ module Sentry
|
|
24
27
|
}
|
25
28
|
end
|
26
29
|
|
27
|
-
#
|
28
|
-
#
|
30
|
+
# Builds the ThreadsInterface with given backtrace and stacktrace_builder.
|
31
|
+
# Patch this method if you want to change a threads interface's stacktrace frames.
|
32
|
+
# @see StacktraceBuilder.build
|
33
|
+
# @param backtrace [Array]
|
34
|
+
# @param stacktrace_builder [StacktraceBuilder]
|
35
|
+
# @param crashed [Hash]
|
36
|
+
# @return [ThreadsInterface]
|
29
37
|
def self.build(backtrace:, stacktrace_builder:, **options)
|
30
38
|
stacktrace = stacktrace_builder.build(backtrace: backtrace) if backtrace
|
31
39
|
new(**options, stacktrace: stacktrace)
|
data/lib/sentry/linecache.rb
CHANGED
data/lib/sentry/net/http.rb
CHANGED
@@ -3,9 +3,11 @@
|
|
3
3
|
require "net/http"
|
4
4
|
|
5
5
|
module Sentry
|
6
|
+
# @api private
|
6
7
|
module Net
|
7
8
|
module HTTP
|
8
|
-
OP_NAME = "
|
9
|
+
OP_NAME = "http.client"
|
10
|
+
BREADCRUMB_CATEGORY = "net.http"
|
9
11
|
|
10
12
|
# To explain how the entire thing works, we need to know how the original Net::HTTP#request works
|
11
13
|
# Here's part of its definition. As you can see, it usually calls itself inside a #start block
|
@@ -22,90 +24,67 @@ module Sentry
|
|
22
24
|
# end
|
23
25
|
# ```
|
24
26
|
#
|
25
|
-
# So
|
26
|
-
#
|
27
|
-
# 1. #request is called.
|
28
|
-
# - But because the request hasn't started yet, it calls #start (which then calls #do_start)
|
29
|
-
# - At this moment @sentry_span is still nil, so #set_sentry_trace_header returns early
|
30
|
-
# 2. #do_start then creates a new Span and assigns it to @sentry_span
|
31
|
-
# 3. #request is called for the second time.
|
32
|
-
# - This time @sentry_span should present. So #set_sentry_trace_header will set the sentry-trace header on the request object
|
33
|
-
# 4. Once the request finished, it
|
34
|
-
# - Records a breadcrumb if http_logger is set
|
35
|
-
# - Finishes the Span inside @sentry_span and clears the instance variable
|
36
|
-
#
|
27
|
+
# So we're only instrumenting request when `Net::HTTP` is already started
|
37
28
|
def request(req, body = nil, &block)
|
38
|
-
|
29
|
+
return super unless started?
|
30
|
+
|
31
|
+
sentry_span = start_sentry_span
|
32
|
+
set_sentry_trace_header(req, sentry_span)
|
39
33
|
|
40
34
|
super.tap do |res|
|
41
35
|
record_sentry_breadcrumb(req, res)
|
42
|
-
record_sentry_span(req, res)
|
43
|
-
end
|
44
|
-
end
|
45
|
-
|
46
|
-
def do_start
|
47
|
-
super.tap do
|
48
|
-
start_sentry_span
|
49
|
-
end
|
50
|
-
end
|
51
|
-
|
52
|
-
def do_finish
|
53
|
-
super.tap do
|
54
|
-
finish_sentry_span
|
36
|
+
record_sentry_span(req, res, sentry_span)
|
55
37
|
end
|
56
38
|
end
|
57
39
|
|
58
40
|
private
|
59
41
|
|
60
|
-
def set_sentry_trace_header(req)
|
61
|
-
return unless
|
42
|
+
def set_sentry_trace_header(req, sentry_span)
|
43
|
+
return unless sentry_span
|
62
44
|
|
63
|
-
trace = Sentry.get_current_client.generate_sentry_trace(
|
45
|
+
trace = Sentry.get_current_client.generate_sentry_trace(sentry_span)
|
64
46
|
req[SENTRY_TRACE_HEADER_NAME] = trace if trace
|
65
47
|
end
|
66
48
|
|
67
49
|
def record_sentry_breadcrumb(req, res)
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
end
|
50
|
+
return unless Sentry.initialized? && Sentry.configuration.breadcrumbs_logger.include?(:http_logger)
|
51
|
+
return if from_sentry_sdk?
|
52
|
+
|
53
|
+
request_info = extract_request_info(req)
|
54
|
+
|
55
|
+
crumb = Sentry::Breadcrumb.new(
|
56
|
+
level: :info,
|
57
|
+
category: BREADCRUMB_CATEGORY,
|
58
|
+
type: :info,
|
59
|
+
data: {
|
60
|
+
status: res.code.to_i,
|
61
|
+
**request_info
|
62
|
+
}
|
63
|
+
)
|
64
|
+
Sentry.add_breadcrumb(crumb)
|
84
65
|
end
|
85
66
|
|
86
|
-
def record_sentry_span(req, res)
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
67
|
+
def record_sentry_span(req, res, sentry_span)
|
68
|
+
return unless Sentry.initialized? && sentry_span
|
69
|
+
|
70
|
+
request_info = extract_request_info(req)
|
71
|
+
sentry_span.set_description("#{request_info[:method]} #{request_info[:url]}")
|
72
|
+
sentry_span.set_data(:status, res.code.to_i)
|
73
|
+
finish_sentry_span(sentry_span)
|
92
74
|
end
|
93
75
|
|
94
76
|
def start_sentry_span
|
95
|
-
|
96
|
-
|
97
|
-
|
77
|
+
return unless Sentry.initialized? && transaction = Sentry.get_current_scope.get_transaction
|
78
|
+
return if from_sentry_sdk?
|
79
|
+
return if transaction.sampled == false
|
98
80
|
|
99
|
-
|
100
|
-
@sentry_span = child_span
|
101
|
-
end
|
81
|
+
transaction.start_child(op: OP_NAME, start_timestamp: Sentry.utc_now.to_f)
|
102
82
|
end
|
103
83
|
|
104
|
-
def finish_sentry_span
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
end
|
84
|
+
def finish_sentry_span(sentry_span)
|
85
|
+
return unless Sentry.initialized? && sentry_span
|
86
|
+
|
87
|
+
sentry_span.set_timestamp(Sentry.utc_now.to_f)
|
109
88
|
end
|
110
89
|
|
111
90
|
def from_sentry_sdk?
|
@@ -114,9 +93,17 @@ module Sentry
|
|
114
93
|
end
|
115
94
|
|
116
95
|
def extract_request_info(req)
|
117
|
-
uri = req.uri
|
96
|
+
uri = req.uri || URI.parse("#{use_ssl? ? 'https' : 'http'}://#{address}#{req.path}")
|
118
97
|
url = "#{uri.scheme}://#{uri.host}#{uri.path}" rescue uri.to_s
|
119
|
-
|
98
|
+
|
99
|
+
result = { method: req.method, url: url }
|
100
|
+
|
101
|
+
if Sentry.configuration.send_default_pii
|
102
|
+
result[:url] = result[:url] + "?#{uri.query}"
|
103
|
+
result[:body] = req.body
|
104
|
+
end
|
105
|
+
|
106
|
+
result
|
120
107
|
end
|
121
108
|
end
|
122
109
|
end
|
data/lib/sentry/rake.rb
CHANGED
@@ -6,6 +6,7 @@ require "rake/task"
|
|
6
6
|
module Sentry
|
7
7
|
module Rake
|
8
8
|
module Application
|
9
|
+
# @api private
|
9
10
|
def display_error_message(ex)
|
10
11
|
Sentry.capture_exception(ex) do |scope|
|
11
12
|
task_name = top_level_tasks.join(' ')
|
@@ -18,6 +19,7 @@ module Sentry
|
|
18
19
|
end
|
19
20
|
|
20
21
|
module Task
|
22
|
+
# @api private
|
21
23
|
def execute(args=nil)
|
22
24
|
return super unless Sentry.initialized? && Sentry.get_current_hub
|
23
25
|
|
@@ -27,5 +29,13 @@ module Sentry
|
|
27
29
|
end
|
28
30
|
end
|
29
31
|
|
30
|
-
|
31
|
-
Rake
|
32
|
+
# @api private
|
33
|
+
module Rake
|
34
|
+
class Application
|
35
|
+
prepend(Sentry::Rake::Application)
|
36
|
+
end
|
37
|
+
|
38
|
+
class Task
|
39
|
+
prepend(Sentry::Rake::Task)
|
40
|
+
end
|
41
|
+
end
|