sentry-ruby 6.7.0 → 7.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/gemfiles/ruby-2.7_rack-2_redis-4.gemfile.lock +9 -9
- data/gemfiles/ruby-2.7_rack-3.1_redis-4.gemfile.lock +9 -9
- data/gemfiles/ruby-2.7_rack-3_redis-4.gemfile.lock +9 -9
- data/gemfiles/ruby-3.0_rack-2_redis-4.gemfile.lock +12 -12
- data/gemfiles/ruby-3.0_rack-3.1_redis-4.gemfile.lock +12 -12
- data/gemfiles/ruby-3.0_rack-3_redis-4.gemfile.lock +12 -12
- data/gemfiles/ruby-3.1_rack-2_redis-4.gemfile.lock +24 -24
- data/gemfiles/ruby-3.1_rack-3.1_redis-4.gemfile.lock +24 -24
- data/gemfiles/ruby-3.1_rack-3_redis-4.gemfile.lock +24 -24
- data/gemfiles/ruby-3.2_rack-0_redis-5.gemfile.lock +24 -24
- data/gemfiles/ruby-3.2_rack-2_redis-4.gemfile.lock +26 -26
- data/gemfiles/ruby-3.2_rack-2_redis-5.gemfile.lock +26 -26
- data/gemfiles/ruby-3.2_rack-3.1_redis-4.gemfile.lock +26 -26
- data/gemfiles/ruby-3.2_rack-3_redis-4.gemfile.lock +26 -26
- data/gemfiles/ruby-3.2_rack-3_redis-5.gemfile.lock +26 -26
- data/gemfiles/ruby-3.3_rack-2_redis-4.gemfile.lock +30 -30
- data/gemfiles/ruby-3.3_rack-3.1_redis-4.gemfile.lock +30 -30
- data/gemfiles/ruby-3.3_rack-3.1_redis-5.3.gemfile.lock +30 -30
- data/gemfiles/ruby-3.3_rack-3_redis-4.gemfile.lock +30 -30
- data/gemfiles/ruby-3.4_rack-2_redis-4.gemfile.lock +30 -30
- data/gemfiles/ruby-3.4_rack-3.1_redis-4.gemfile.lock +30 -30
- data/gemfiles/ruby-3.4_rack-3.1_redis-5.3.gemfile.lock +30 -30
- data/gemfiles/ruby-3.4_rack-3_redis-4.gemfile.lock +30 -30
- data/gemfiles/ruby-4.0_rack-2_redis-4.gemfile.lock +30 -30
- data/gemfiles/ruby-4.0_rack-3.1_redis-4.gemfile.lock +30 -30
- data/gemfiles/ruby-4.0_rack-3_redis-4.gemfile.lock +30 -30
- data/gemfiles/ruby-jruby-9.4.14.0_rack-2_redis-4.gemfile.lock +24 -24
- data/gemfiles/ruby-jruby-9.4.14.0_rack-3.1_redis-4.gemfile.lock +24 -24
- data/gemfiles/ruby-jruby-9.4.14.0_rack-3_redis-4.gemfile.lock +24 -24
- data/lib/sentry/backtrace.rb +10 -1
- data/lib/sentry/client.rb +21 -12
- data/lib/sentry/configuration.rb +35 -13
- data/lib/sentry/data_collection/key_value_collection.rb +136 -0
- data/lib/sentry/data_collection.rb +203 -0
- data/lib/sentry/debug_structured_logger.rb +2 -15
- data/lib/sentry/error_event.rb +6 -1
- data/lib/sentry/event.rb +8 -4
- data/lib/sentry/excon/middleware.rb +3 -8
- data/lib/sentry/faraday.rb +7 -5
- data/lib/sentry/hub.rb +11 -4
- data/lib/sentry/integrable.rb +12 -0
- data/lib/sentry/interfaces/exception.rb +8 -2
- data/lib/sentry/interfaces/request.rb +42 -46
- data/lib/sentry/interfaces/single_exception.rb +2 -2
- data/lib/sentry/log_event_buffer.rb +1 -1
- data/lib/sentry/metric_event.rb +4 -2
- data/lib/sentry/metric_event_buffer.rb +1 -1
- data/lib/sentry/metrics.rb +12 -6
- data/lib/sentry/net/http.rb +3 -4
- data/lib/sentry/redis.rb +1 -1
- data/lib/sentry/scope.rb +7 -3
- data/lib/sentry/session_flusher.rb +7 -0
- data/lib/sentry/std_lib_logger.rb +10 -8
- data/lib/sentry/telemetry_event_buffer.rb +57 -31
- data/lib/sentry/test_helper.rb +6 -4
- data/lib/sentry/threaded_periodic_worker.rb +90 -13
- data/lib/sentry/transaction.rb +7 -1
- data/lib/sentry/utils/callback_helper.rb +27 -0
- data/lib/sentry/utils/http_tracing.rb +42 -26
- data/lib/sentry/version.rb +1 -1
- data/lib/sentry-ruby.rb +7 -18
- metadata +9 -6
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Sentry
|
|
4
|
+
class DataCollection
|
|
5
|
+
# Configuration for key-value data collection.
|
|
6
|
+
class KeyValueCollection
|
|
7
|
+
FILTERED_VALUE = "[Filtered]"
|
|
8
|
+
|
|
9
|
+
# Keys from this list are ALWAYS filtered, regardless of :mode
|
|
10
|
+
SENSITIVE_DENY_LIST = %w[
|
|
11
|
+
auth
|
|
12
|
+
token
|
|
13
|
+
secret
|
|
14
|
+
session
|
|
15
|
+
password
|
|
16
|
+
passwd
|
|
17
|
+
pwd
|
|
18
|
+
key
|
|
19
|
+
jwt
|
|
20
|
+
bearer
|
|
21
|
+
sso
|
|
22
|
+
saml
|
|
23
|
+
csrf
|
|
24
|
+
xsrf
|
|
25
|
+
credentials
|
|
26
|
+
sid
|
|
27
|
+
identity
|
|
28
|
+
cookie
|
|
29
|
+
set-cookie
|
|
30
|
+
].freeze
|
|
31
|
+
|
|
32
|
+
# Additional terms applied to cookie names only. These cover common
|
|
33
|
+
# opaque session, identity-provider, and load-balancer cookies without
|
|
34
|
+
# making the general header denylist overly broad.
|
|
35
|
+
SENSITIVE_COOKIE_NAME_DENY_LIST = %w[
|
|
36
|
+
.sid
|
|
37
|
+
sessid
|
|
38
|
+
remember
|
|
39
|
+
oidc
|
|
40
|
+
pkce
|
|
41
|
+
nonce
|
|
42
|
+
__secure-
|
|
43
|
+
__host-
|
|
44
|
+
awsalb
|
|
45
|
+
awselb
|
|
46
|
+
akamai
|
|
47
|
+
__stripe
|
|
48
|
+
cognito
|
|
49
|
+
firebase
|
|
50
|
+
supabase
|
|
51
|
+
sb-
|
|
52
|
+
mfa
|
|
53
|
+
2fa
|
|
54
|
+
].freeze
|
|
55
|
+
|
|
56
|
+
# `mode` controls whether values are collected:
|
|
57
|
+
# - `:off` disables collection.
|
|
58
|
+
# - `:deny_list` collects values except those matching `terms`.
|
|
59
|
+
# - `:allow_list` collects only values matching `terms`.
|
|
60
|
+
# Boolean values are accepted as shorthand for `:deny_list` and `:off`.
|
|
61
|
+
# @return [:off, :deny_list, :allow_list]
|
|
62
|
+
attr_reader :mode
|
|
63
|
+
|
|
64
|
+
# `terms` contains the keys or patterns used by the selected mode.
|
|
65
|
+
# @return [Array<String, Regexp>, nil]
|
|
66
|
+
attr_reader :terms
|
|
67
|
+
|
|
68
|
+
def initialize(mode:, terms:)
|
|
69
|
+
self.mode = mode
|
|
70
|
+
self.terms = terms
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
def mode=(mode)
|
|
74
|
+
@mode = case mode
|
|
75
|
+
when true then :deny_list
|
|
76
|
+
when false, nil then :off
|
|
77
|
+
else mode
|
|
78
|
+
end
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
# Converts the boolean shorthand into a collection configuration.
|
|
82
|
+
def self.from(value)
|
|
83
|
+
return new(mode: value, terms: nil) if value.equal?(true) || value.equal?(false) || value.nil?
|
|
84
|
+
|
|
85
|
+
value
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
def terms=(terms)
|
|
89
|
+
@terms = terms&.map { |term| term.is_a?(Regexp) ? term : term.to_s.downcase }
|
|
90
|
+
&.reject { |term| !term.is_a?(Regexp) && term.strip.empty? }
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
# Applies this collection configuration without changing the input hash.
|
|
94
|
+
# Keys are retained whenever the category is collected; values that are not
|
|
95
|
+
# safe to send are replaced with FILTERED_VALUE.
|
|
96
|
+
#
|
|
97
|
+
# @param values [Hash] key-value data to filter
|
|
98
|
+
# @return [Hash] a new filtered hash, or an empty hash when collection is off
|
|
99
|
+
def filter(values, cookie: false)
|
|
100
|
+
return {} if mode == :off
|
|
101
|
+
|
|
102
|
+
values.each_with_object({}) do |(key, value), filtered|
|
|
103
|
+
filtered[key] = safe_value?(key, cookie: cookie) ? value : FILTERED_VALUE
|
|
104
|
+
end
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
private
|
|
108
|
+
|
|
109
|
+
def safe_value?(key, cookie: false)
|
|
110
|
+
key_string = key.to_s
|
|
111
|
+
key_downcase = key_string.downcase
|
|
112
|
+
return false if sensitive?(key_downcase, cookie: cookie)
|
|
113
|
+
|
|
114
|
+
case mode
|
|
115
|
+
when :deny_list
|
|
116
|
+
!matches_any_term?(key_string, key_downcase)
|
|
117
|
+
when :allow_list
|
|
118
|
+
matches_any_term?(key_string, key_downcase)
|
|
119
|
+
else
|
|
120
|
+
false
|
|
121
|
+
end
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
def sensitive?(key, cookie: false)
|
|
125
|
+
SENSITIVE_DENY_LIST.any? { |term| key.include?(term) } ||
|
|
126
|
+
(cookie && SENSITIVE_COOKIE_NAME_DENY_LIST.any? { |term| key.include?(term) })
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
def matches_any_term?(key, key_downcase)
|
|
130
|
+
@terms&.any? do |term|
|
|
131
|
+
term.is_a?(Regexp) ? term.match?(key) : key_downcase.include?(term)
|
|
132
|
+
end
|
|
133
|
+
end
|
|
134
|
+
end
|
|
135
|
+
end
|
|
136
|
+
end
|
|
@@ -0,0 +1,203 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "sentry/data_collection/key_value_collection"
|
|
4
|
+
|
|
5
|
+
module Sentry
|
|
6
|
+
class DataCollection
|
|
7
|
+
# Configuration for the categories of data collected by the SDK.
|
|
8
|
+
# Replacement for send_default_pii.
|
|
9
|
+
# Spec: https://develop.sentry.dev/sdk/foundations/client/data-collection/
|
|
10
|
+
#
|
|
11
|
+
# @example Configure data collection
|
|
12
|
+
# Sentry.init do |config|
|
|
13
|
+
# data_collection = config.data_collection
|
|
14
|
+
# data_collection.user_info = true
|
|
15
|
+
# data_collection.cookies.mode = :deny_list
|
|
16
|
+
# data_collection.cookies.terms = ["session", "token"]
|
|
17
|
+
# data_collection.http_headers.request.mode = :deny_list
|
|
18
|
+
# data_collection.http_headers.request.terms = nil
|
|
19
|
+
# data_collection.http_headers.response.mode = :allow_list
|
|
20
|
+
# data_collection.http_headers.response.terms = ["my_special_header"]
|
|
21
|
+
# data_collection.http_bodies = [:incoming_request]
|
|
22
|
+
# data_collection.url_query_params.mode = :allow_list
|
|
23
|
+
# data_collection.url_query_params.terms = ["page", "limit"]
|
|
24
|
+
# data_collection.graphql.document = true
|
|
25
|
+
# data_collection.graphql.variables = true
|
|
26
|
+
# data_collection.database_query_data = true
|
|
27
|
+
# data_collection.queues = true
|
|
28
|
+
# data_collection.stack_frame_variables = true
|
|
29
|
+
# data_collection.frame_context_lines = 5
|
|
30
|
+
# end
|
|
31
|
+
|
|
32
|
+
MODES = %i[off deny_list allow_list].freeze
|
|
33
|
+
|
|
34
|
+
PII_HEADER_SNIPPETS = %w[forwarded -ip _ip remote via _user -user].freeze
|
|
35
|
+
|
|
36
|
+
BODY_TYPES = %i[
|
|
37
|
+
incoming_request
|
|
38
|
+
outgoing_request
|
|
39
|
+
incoming_response
|
|
40
|
+
outgoing_response
|
|
41
|
+
].freeze
|
|
42
|
+
|
|
43
|
+
class HttpHeaders
|
|
44
|
+
# @return [KeyValueCollection]
|
|
45
|
+
attr_reader :request
|
|
46
|
+
|
|
47
|
+
# @return [KeyValueCollection]
|
|
48
|
+
attr_reader :response
|
|
49
|
+
|
|
50
|
+
def initialize(request:, response:)
|
|
51
|
+
self.request = request
|
|
52
|
+
self.response = response
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
def request=(value)
|
|
56
|
+
@request = KeyValueCollection.from(value)
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
def response=(value)
|
|
60
|
+
@response = KeyValueCollection.from(value)
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
class GraphQL
|
|
65
|
+
# @return [Boolean]
|
|
66
|
+
attr_accessor :document
|
|
67
|
+
|
|
68
|
+
# @return [Boolean]
|
|
69
|
+
attr_accessor :variables
|
|
70
|
+
|
|
71
|
+
def initialize(document:, variables:)
|
|
72
|
+
@document = document
|
|
73
|
+
@variables = variables
|
|
74
|
+
end
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
# @return [Boolean]
|
|
78
|
+
# @default `true`
|
|
79
|
+
attr_accessor :user_info
|
|
80
|
+
|
|
81
|
+
# @return [Boolean, KeyValueCollection]
|
|
82
|
+
# A boolean is shorthand for `mode: :deny_list` (`true`) or `mode: :off` (`false`).
|
|
83
|
+
# @default `mode: :deny_list, terms: nil`
|
|
84
|
+
attr_reader :cookies
|
|
85
|
+
|
|
86
|
+
# @return [HttpHeaders]
|
|
87
|
+
# @default request and response use `mode: :deny_list, terms: nil`
|
|
88
|
+
attr_accessor :http_headers
|
|
89
|
+
|
|
90
|
+
# @return [Array<Symbol>] containing values from BODY_TYPES
|
|
91
|
+
# @default `nil` (all valid body types)
|
|
92
|
+
attr_accessor :http_bodies
|
|
93
|
+
|
|
94
|
+
# @return [Boolean, KeyValueCollection]
|
|
95
|
+
# A boolean is shorthand for `mode: :deny_list` (`true`) or `mode: :off` (`false`).
|
|
96
|
+
# @default `mode: :deny_list, terms: nil`
|
|
97
|
+
attr_reader :url_query_params
|
|
98
|
+
|
|
99
|
+
# @return [Boolean]
|
|
100
|
+
# @default `true`
|
|
101
|
+
attr_accessor :database_query_data
|
|
102
|
+
|
|
103
|
+
# @return [GraphQL]
|
|
104
|
+
# @default `document: true, variables: true`
|
|
105
|
+
attr_accessor :graphql
|
|
106
|
+
|
|
107
|
+
# @return [Boolean]
|
|
108
|
+
# @default `true`
|
|
109
|
+
attr_accessor :queues
|
|
110
|
+
|
|
111
|
+
# @return [Boolean, KeyValueCollection]
|
|
112
|
+
# A boolean is shorthand for `mode: :deny_list` (`true`) or `mode: :off` (`false`).
|
|
113
|
+
# @default `false` (Ruby-specific to avoid TracePoint overhead)
|
|
114
|
+
attr_reader :stack_frame_variables
|
|
115
|
+
|
|
116
|
+
# @return [Integer]
|
|
117
|
+
# @default `3`
|
|
118
|
+
attr_accessor :frame_context_lines
|
|
119
|
+
|
|
120
|
+
# Filters key-value data using the default sensitive denylist.
|
|
121
|
+
def self.filter(values)
|
|
122
|
+
default_filter.filter(values, cookie: false)
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
def self.default_filter
|
|
126
|
+
@default_filter ||= KeyValueCollection.new(mode: :deny_list, terms: nil)
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
# Builds data collection settings compatible with the legacy send_default_pii
|
|
130
|
+
# configuration.
|
|
131
|
+
def self.backfill(configuration)
|
|
132
|
+
data_collection = new
|
|
133
|
+
data_collection.backfill_stack_frame_variables(configuration)
|
|
134
|
+
return data_collection if configuration.send_default_pii
|
|
135
|
+
|
|
136
|
+
# TODO-neel-data map to exact ruby behaviour for backwards compat behavior
|
|
137
|
+
data_collection.user_info = false
|
|
138
|
+
data_collection.cookies.mode = :off
|
|
139
|
+
data_collection.http_headers.request.mode = :deny_list
|
|
140
|
+
data_collection.http_headers.request.terms = PII_HEADER_SNIPPETS
|
|
141
|
+
data_collection.http_headers.response.mode = :deny_list
|
|
142
|
+
data_collection.http_headers.response.terms = PII_HEADER_SNIPPETS
|
|
143
|
+
data_collection.http_bodies = []
|
|
144
|
+
data_collection.url_query_params.mode = :off
|
|
145
|
+
data_collection.graphql.document = false
|
|
146
|
+
data_collection.graphql.variables = false
|
|
147
|
+
data_collection.database_query_data = false
|
|
148
|
+
data_collection.queues = false
|
|
149
|
+
data_collection.frame_context_lines = configuration.context_lines
|
|
150
|
+
data_collection
|
|
151
|
+
end
|
|
152
|
+
|
|
153
|
+
def initialize
|
|
154
|
+
@user_info = true
|
|
155
|
+
self.cookies = KeyValueCollection.new(mode: :deny_list, terms: nil)
|
|
156
|
+
@http_headers = HttpHeaders.new(
|
|
157
|
+
request: KeyValueCollection.new(mode: :deny_list, terms: nil),
|
|
158
|
+
response: KeyValueCollection.new(mode: :deny_list, terms: nil)
|
|
159
|
+
)
|
|
160
|
+
@http_bodies = BODY_TYPES.dup
|
|
161
|
+
self.url_query_params = KeyValueCollection.new(mode: :deny_list, terms: nil)
|
|
162
|
+
@database_query_data = true
|
|
163
|
+
@graphql = GraphQL.new(document: true, variables: true)
|
|
164
|
+
@queues = true
|
|
165
|
+
self.stack_frame_variables = false
|
|
166
|
+
@frame_context_lines = 3
|
|
167
|
+
end
|
|
168
|
+
|
|
169
|
+
# Copies the legacy stack frame variable configuration.
|
|
170
|
+
def backfill_stack_frame_variables(configuration)
|
|
171
|
+
self.stack_frame_variables = configuration.include_local_variables
|
|
172
|
+
end
|
|
173
|
+
|
|
174
|
+
# Returns whether stack frame local variables should be captured.
|
|
175
|
+
def collect_stack_frame_variables?
|
|
176
|
+
stack_frame_variables.mode != :off
|
|
177
|
+
end
|
|
178
|
+
|
|
179
|
+
def stack_frame_variables=(value)
|
|
180
|
+
@stack_frame_variables = KeyValueCollection.from(value)
|
|
181
|
+
end
|
|
182
|
+
|
|
183
|
+
def cookies=(value)
|
|
184
|
+
@cookies = KeyValueCollection.from(value)
|
|
185
|
+
end
|
|
186
|
+
|
|
187
|
+
def url_query_params=(value)
|
|
188
|
+
@url_query_params = KeyValueCollection.from(value)
|
|
189
|
+
end
|
|
190
|
+
|
|
191
|
+
# Returns whether incoming HTTP request bodies should be collected.
|
|
192
|
+
# nil implies all BODY_TYPES according to spec
|
|
193
|
+
def collect_incoming_http_body?
|
|
194
|
+
http_bodies.nil? || http_bodies.include?(:incoming_request)
|
|
195
|
+
end
|
|
196
|
+
|
|
197
|
+
# Returns whether outgoing HTTP request bodies should be collected.
|
|
198
|
+
# nil implies all BODY_TYPES according to spec
|
|
199
|
+
def collect_outgoing_http_body?
|
|
200
|
+
http_bodies.nil? || http_bodies.include?(:outgoing_request)
|
|
201
|
+
end
|
|
202
|
+
end
|
|
203
|
+
end
|
|
@@ -8,8 +8,7 @@ require "delegate"
|
|
|
8
8
|
module Sentry
|
|
9
9
|
# DebugStructuredLogger is a logger that captures structured log events to a file for debugging purposes.
|
|
10
10
|
#
|
|
11
|
-
# It
|
|
12
|
-
# is enabled.
|
|
11
|
+
# It also sends log events to Sentry via the normal structured logger.
|
|
13
12
|
class DebugStructuredLogger < SimpleDelegator
|
|
14
13
|
DEFAULT_LOG_FILE_PATH = File.join("log", "sentry_debug_logs.log")
|
|
15
14
|
|
|
@@ -68,12 +67,7 @@ module Sentry
|
|
|
68
67
|
private
|
|
69
68
|
|
|
70
69
|
def initialize_backend(configuration)
|
|
71
|
-
|
|
72
|
-
StructuredLogger.new(configuration)
|
|
73
|
-
else
|
|
74
|
-
# Create a no-op logger if logging is disabled
|
|
75
|
-
NoOpLogger.new
|
|
76
|
-
end
|
|
70
|
+
StructuredLogger.new(configuration)
|
|
77
71
|
end
|
|
78
72
|
|
|
79
73
|
def initialize_log_file(log_file_path)
|
|
@@ -83,12 +77,5 @@ module Sentry
|
|
|
83
77
|
|
|
84
78
|
log_file
|
|
85
79
|
end
|
|
86
|
-
|
|
87
|
-
# No-op logger for when structured logging is disabled
|
|
88
|
-
class NoOpLogger
|
|
89
|
-
%i[trace debug info warn error fatal log].each do |method|
|
|
90
|
-
define_method(method) { |*args, **kwargs| nil }
|
|
91
|
-
end
|
|
92
|
-
end
|
|
93
80
|
end
|
|
94
81
|
end
|
data/lib/sentry/error_event.rb
CHANGED
|
@@ -32,7 +32,12 @@ module Sentry
|
|
|
32
32
|
@extra.merge!(exception.sentry_context)
|
|
33
33
|
end
|
|
34
34
|
|
|
35
|
-
@exception = Sentry::ExceptionInterface.build(
|
|
35
|
+
@exception = Sentry::ExceptionInterface.build(
|
|
36
|
+
exception: exception,
|
|
37
|
+
stacktrace_builder: @stacktrace_builder,
|
|
38
|
+
mechanism: mechanism,
|
|
39
|
+
data_collection: @data_collection
|
|
40
|
+
)
|
|
36
41
|
end
|
|
37
42
|
end
|
|
38
43
|
end
|
data/lib/sentry/event.rb
CHANGED
|
@@ -28,7 +28,7 @@ module Sentry
|
|
|
28
28
|
|
|
29
29
|
MAX_MESSAGE_SIZE_IN_BYTES = 1024 * 8
|
|
30
30
|
|
|
31
|
-
SKIP_INSPECTION_ATTRIBUTES = [:@modules, :@stacktrace_builder, :@
|
|
31
|
+
SKIP_INSPECTION_ATTRIBUTES = [:@modules, :@stacktrace_builder, :@data_collection, :@trusted_proxies, :@rack_env_whitelist]
|
|
32
32
|
|
|
33
33
|
include CustomInspection
|
|
34
34
|
|
|
@@ -73,7 +73,7 @@ module Sentry
|
|
|
73
73
|
@modules = configuration.gem_specs if configuration.send_modules
|
|
74
74
|
|
|
75
75
|
# configuration options to help events process data
|
|
76
|
-
@
|
|
76
|
+
@data_collection = configuration.data_collection
|
|
77
77
|
@trusted_proxies = configuration.trusted_proxies
|
|
78
78
|
@stacktrace_builder = configuration.stacktrace_builder
|
|
79
79
|
@rack_env_whitelist = configuration.rack_env_whitelist
|
|
@@ -103,7 +103,7 @@ module Sentry
|
|
|
103
103
|
unless request || env.empty?
|
|
104
104
|
add_request_interface(env)
|
|
105
105
|
|
|
106
|
-
user[:ip_address] ||= calculate_real_ip_from_rack(env) if @
|
|
106
|
+
user[:ip_address] ||= calculate_real_ip_from_rack(env) if @data_collection.user_info
|
|
107
107
|
|
|
108
108
|
if request_id = Utils::RequestId.read_from(env)
|
|
109
109
|
tags[:request_id] = request_id
|
|
@@ -127,7 +127,11 @@ module Sentry
|
|
|
127
127
|
private
|
|
128
128
|
|
|
129
129
|
def add_request_interface(env)
|
|
130
|
-
@request = Sentry::RequestInterface.new(
|
|
130
|
+
@request = Sentry::RequestInterface.new(
|
|
131
|
+
env: env,
|
|
132
|
+
data_collection: @data_collection,
|
|
133
|
+
rack_env_whitelist: @rack_env_whitelist
|
|
134
|
+
)
|
|
131
135
|
end
|
|
132
136
|
|
|
133
137
|
def serialize_attributes
|
|
@@ -61,14 +61,9 @@ module Sentry
|
|
|
61
61
|
url = env[:scheme] + "://" + env[:hostname] + env[:path]
|
|
62
62
|
result = { method: env[:method].to_s.upcase, url: url }
|
|
63
63
|
|
|
64
|
-
|
|
65
|
-
|
|
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
|
|
64
|
+
query = filter_query_params(env[:query])
|
|
65
|
+
result[:query] = query if query
|
|
66
|
+
result[:body] = env[:body] if Sentry.configuration.data_collection.collect_outgoing_http_body?
|
|
72
67
|
|
|
73
68
|
result
|
|
74
69
|
end
|
data/lib/sentry/faraday.rb
CHANGED
|
@@ -14,10 +14,13 @@ module Sentry
|
|
|
14
14
|
|
|
15
15
|
# Ensure that we attach instrumentation only if the adapter is not net/http
|
|
16
16
|
# because if is is, then the net/http instrumentation will take care of it
|
|
17
|
-
if builder.adapter.name != "Faraday::Adapter::NetHttp"
|
|
17
|
+
if builder.adapter.name != "Faraday::Adapter::NetHttp" &&
|
|
18
|
+
!builder.locked? &&
|
|
19
|
+
!builder.instance_variable_defined?(:@sentry_patched)
|
|
18
20
|
# Make sure that it's going to be the first middleware so that it can capture
|
|
19
21
|
# the entire request processing involving other middlewares
|
|
20
22
|
builder.insert(0, ::Faraday::Request::Instrumentation, name: OP_NAME, instrumenter: Instrumenter.new)
|
|
23
|
+
builder.instance_variable_set(:@sentry_patched, true)
|
|
21
24
|
end
|
|
22
25
|
end
|
|
23
26
|
end
|
|
@@ -59,10 +62,9 @@ module Sentry
|
|
|
59
62
|
url = env[:url].scheme + "://" + env[:url].host + env[:url].path
|
|
60
63
|
result = { method: env[:method].to_s.upcase, url: url }
|
|
61
64
|
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
end
|
|
65
|
+
query = filter_query_params(env[:url].query)
|
|
66
|
+
result[:query] = query if query
|
|
67
|
+
result[:body] = env[:body] if Sentry.configuration.data_collection.collect_outgoing_http_body?
|
|
66
68
|
|
|
67
69
|
result
|
|
68
70
|
end
|
data/lib/sentry/hub.rb
CHANGED
|
@@ -7,6 +7,7 @@ require "sentry/session"
|
|
|
7
7
|
module Sentry
|
|
8
8
|
class Hub
|
|
9
9
|
include ArgumentCheckingHelper
|
|
10
|
+
include CallbackHelper
|
|
10
11
|
|
|
11
12
|
MUTEX = Mutex.new
|
|
12
13
|
|
|
@@ -224,7 +225,7 @@ module Sentry
|
|
|
224
225
|
end
|
|
225
226
|
|
|
226
227
|
def capture_log_event(message, **options)
|
|
227
|
-
return unless current_client
|
|
228
|
+
return unless current_client
|
|
228
229
|
|
|
229
230
|
event = current_client.event_from_log(message, **options)
|
|
230
231
|
|
|
@@ -240,9 +241,10 @@ module Sentry
|
|
|
240
241
|
# @param value [Numeric] the metric value
|
|
241
242
|
# @param unit [String, nil] (optional) the metric unit
|
|
242
243
|
# @param attributes [Hash, nil] (optional) additional attributes for the metric
|
|
244
|
+
# @param integration [String, Symbol, nil] (optional) registered integration emitting the metric
|
|
243
245
|
# @return [void]
|
|
244
|
-
def capture_metric(name:, type:, value:, unit: nil, attributes: nil)
|
|
245
|
-
return unless current_client
|
|
246
|
+
def capture_metric(name:, type:, value:, unit: nil, attributes: nil, integration: nil)
|
|
247
|
+
return unless current_client
|
|
246
248
|
|
|
247
249
|
metric = MetricEvent.new(
|
|
248
250
|
name: name,
|
|
@@ -250,6 +252,7 @@ module Sentry
|
|
|
250
252
|
type: type,
|
|
251
253
|
unit: unit,
|
|
252
254
|
attributes: attributes&.dup,
|
|
255
|
+
integration_meta: Sentry.integrations[integration.to_s]
|
|
253
256
|
)
|
|
254
257
|
|
|
255
258
|
current_client.buffer_metric_event(metric, current_scope)
|
|
@@ -294,7 +297,11 @@ module Sentry
|
|
|
294
297
|
return unless configuration.enabled_in_current_env?
|
|
295
298
|
|
|
296
299
|
if before_breadcrumb = current_client.configuration.before_breadcrumb
|
|
297
|
-
breadcrumb =
|
|
300
|
+
breadcrumb = safe_dispatch_callback(
|
|
301
|
+
"before_breadcrumb",
|
|
302
|
+
before_breadcrumb,
|
|
303
|
+
[breadcrumb, hint]
|
|
304
|
+
)
|
|
298
305
|
end
|
|
299
306
|
|
|
300
307
|
return unless breadcrumb
|
data/lib/sentry/integrable.rb
CHANGED
|
@@ -32,5 +32,17 @@ module Sentry
|
|
|
32
32
|
options[:hint][:integration] = integration_name
|
|
33
33
|
Sentry.capture_check_in(slug, status, **options, &block)
|
|
34
34
|
end
|
|
35
|
+
|
|
36
|
+
def count(name, value: 1, attributes: nil)
|
|
37
|
+
Sentry.metrics.count(name, value: value, attributes: attributes, integration: integration_name)
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def gauge(name, value, unit: nil, attributes: nil)
|
|
41
|
+
Sentry.metrics.gauge(name, value, unit: unit, attributes: attributes, integration: integration_name)
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def distribution(name, value, unit: nil, attributes: nil)
|
|
45
|
+
Sentry.metrics.distribution(name, value, unit: unit, attributes: attributes, integration: integration_name)
|
|
46
|
+
end
|
|
35
47
|
end
|
|
36
48
|
end
|
|
@@ -25,15 +25,21 @@ module Sentry
|
|
|
25
25
|
# @see SingleExceptionInterface#build_with_stacktrace
|
|
26
26
|
# @see SingleExceptionInterface#initialize
|
|
27
27
|
# @param mechanism [Mechanism]
|
|
28
|
+
# @param data_collection [DataCollection]
|
|
28
29
|
# @return [ExceptionInterface]
|
|
29
|
-
def self.build(exception:, stacktrace_builder:, mechanism:)
|
|
30
|
+
def self.build(exception:, stacktrace_builder:, mechanism:, data_collection:)
|
|
30
31
|
exceptions = Sentry::Utils::ExceptionCauseChain.exception_to_array(exception).reverse
|
|
31
32
|
processed_backtrace_ids = Set.new
|
|
32
33
|
|
|
33
34
|
exceptions = exceptions.map do |e|
|
|
34
35
|
if e.backtrace && !processed_backtrace_ids.include?(e.backtrace.object_id)
|
|
35
36
|
processed_backtrace_ids << e.backtrace.object_id
|
|
36
|
-
SingleExceptionInterface.build_with_stacktrace(
|
|
37
|
+
SingleExceptionInterface.build_with_stacktrace(
|
|
38
|
+
exception: e,
|
|
39
|
+
stacktrace_builder: stacktrace_builder,
|
|
40
|
+
mechanism: mechanism,
|
|
41
|
+
data_collection: data_collection
|
|
42
|
+
)
|
|
37
43
|
else
|
|
38
44
|
SingleExceptionInterface.new(exception: exception, mechanism: mechanism)
|
|
39
45
|
end
|