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
|
@@ -1,15 +1,11 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
+
require "json"
|
|
4
|
+
|
|
3
5
|
module Sentry
|
|
4
6
|
class RequestInterface < Interface
|
|
5
7
|
REQUEST_ID_HEADERS = %w[action_dispatch.request_id HTTP_X_REQUEST_ID].freeze
|
|
6
8
|
CONTENT_HEADERS = %w[CONTENT_TYPE CONTENT_LENGTH].freeze
|
|
7
|
-
IP_HEADERS = [
|
|
8
|
-
"REMOTE_ADDR",
|
|
9
|
-
"HTTP_CLIENT_IP",
|
|
10
|
-
"HTTP_X_REAL_IP",
|
|
11
|
-
"HTTP_X_FORWARDED_FOR"
|
|
12
|
-
].freeze
|
|
13
9
|
|
|
14
10
|
# Regex to detect lowercase chars — match? is allocation-free (no MatchData/String)
|
|
15
11
|
LOWERCASE_PATTERN = /[a-z]/.freeze
|
|
@@ -27,10 +23,10 @@ module Sentry
|
|
|
27
23
|
# @return [Hash]
|
|
28
24
|
attr_accessor :data
|
|
29
25
|
|
|
30
|
-
# @return [String]
|
|
26
|
+
# @return [String, Hash]
|
|
31
27
|
attr_accessor :query_string
|
|
32
28
|
|
|
33
|
-
# @return [
|
|
29
|
+
# @return [Hash]
|
|
34
30
|
attr_accessor :cookies
|
|
35
31
|
|
|
36
32
|
# @return [Hash]
|
|
@@ -40,33 +36,22 @@ module Sentry
|
|
|
40
36
|
attr_accessor :env
|
|
41
37
|
|
|
42
38
|
# @param env [Hash]
|
|
43
|
-
# @param
|
|
39
|
+
# @param data_collection [DataCollection]
|
|
44
40
|
# @param rack_env_whitelist [Array]
|
|
45
|
-
# @see Configuration#
|
|
41
|
+
# @see Configuration#data_collection
|
|
46
42
|
# @see Configuration#rack_env_whitelist
|
|
47
|
-
def initialize(env:,
|
|
43
|
+
def initialize(env:, data_collection:, rack_env_whitelist:)
|
|
48
44
|
env = env.dup
|
|
49
|
-
|
|
50
|
-
unless send_default_pii
|
|
51
|
-
# need to completely wipe out ip addresses
|
|
52
|
-
RequestInterface::IP_HEADERS.each do |header|
|
|
53
|
-
env.delete(header)
|
|
54
|
-
end
|
|
55
|
-
end
|
|
56
|
-
|
|
57
45
|
request = ::Rack::Request.new(env)
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
self.
|
|
66
|
-
self.
|
|
67
|
-
|
|
68
|
-
self.headers = filter_and_format_headers(env, send_default_pii)
|
|
69
|
-
self.env = filter_and_format_env(env, rack_env_whitelist)
|
|
46
|
+
query = data_collection.url_query_params.filter(request.GET) rescue nil
|
|
47
|
+
|
|
48
|
+
self.method = request.request_method
|
|
49
|
+
self.url = request.scheme && request.url.split("?").first
|
|
50
|
+
self.query_string = query unless query&.empty?
|
|
51
|
+
self.cookies = data_collection.cookies.filter(request.cookies, cookie: true)
|
|
52
|
+
self.data = read_data_from(request) if data_collection.collect_incoming_http_body?
|
|
53
|
+
self.headers = filter_and_format_headers(env, data_collection.http_headers.request)
|
|
54
|
+
self.env = filter_and_format_env(env, data_collection.http_headers.request, rack_env_whitelist)
|
|
70
55
|
end
|
|
71
56
|
|
|
72
57
|
private
|
|
@@ -75,25 +60,31 @@ module Sentry
|
|
|
75
60
|
return "Skipped non-rewindable request body" unless request.body.respond_to?(:rewind)
|
|
76
61
|
|
|
77
62
|
if request.form_data?
|
|
78
|
-
request.POST
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
63
|
+
DataCollection.filter(request.POST)
|
|
64
|
+
else
|
|
65
|
+
body = request.body.read(MAX_BODY_LIMIT)
|
|
66
|
+
body = Utils::EncodingHelper.encode_to_utf_8(body.to_s)
|
|
67
|
+
|
|
68
|
+
if request.media_type == "application/json" || request.media_type&.end_with?("+json")
|
|
69
|
+
parsed_body = JSON.parse(body)
|
|
70
|
+
parsed_body.is_a?(Hash) ? DataCollection.filter(parsed_body) : parsed_body
|
|
71
|
+
else
|
|
72
|
+
body
|
|
73
|
+
end
|
|
84
74
|
end
|
|
85
|
-
rescue IOError => e
|
|
75
|
+
rescue JSON::ParserError, IOError => e
|
|
86
76
|
e.message
|
|
77
|
+
ensure
|
|
78
|
+
request.body.rewind if request.body.respond_to?(:rewind)
|
|
87
79
|
end
|
|
88
80
|
|
|
89
|
-
def filter_and_format_headers(env,
|
|
81
|
+
def filter_and_format_headers(env, collection)
|
|
90
82
|
env.each_with_object({}) do |(key, value), memo|
|
|
91
83
|
begin
|
|
92
84
|
key = key.to_s # rack env can contain symbols
|
|
93
85
|
next memo["X-Request-Id"] ||= Utils::RequestId.read_from(env) if Utils::RequestId::REQUEST_ID_HEADERS.include?(key)
|
|
94
86
|
next if is_server_protocol?(key, value, env["SERVER_PROTOCOL"])
|
|
95
87
|
next if is_skippable_header?(key)
|
|
96
|
-
next if key == "HTTP_AUTHORIZATION" && !send_default_pii
|
|
97
88
|
|
|
98
89
|
# Rack stores headers as HTTP_WHAT_EVER, we need What-Ever
|
|
99
90
|
key = key.delete_prefix("HTTP_")
|
|
@@ -107,12 +98,13 @@ module Sentry
|
|
|
107
98
|
Sentry.sdk_logger.warn(LOGGER_PROGNAME) { "Error raised while formatting headers: #{e.message}" }
|
|
108
99
|
next
|
|
109
100
|
end
|
|
101
|
+
end.then do |e|
|
|
102
|
+
collection.filter(e)
|
|
110
103
|
end
|
|
111
104
|
end
|
|
112
105
|
|
|
113
106
|
def is_skippable_header?(key)
|
|
114
107
|
key.match?(LOWERCASE_PATTERN) || # lower-case envs aren't real http headers
|
|
115
|
-
key == "HTTP_COOKIE" || # Cookies don't go here, they go somewhere else
|
|
116
108
|
!(key.start_with?("HTTP_") || CONTENT_HEADERS.include?(key))
|
|
117
109
|
end
|
|
118
110
|
|
|
@@ -134,11 +126,15 @@ module Sentry
|
|
|
134
126
|
Gem::Version.new(::Rack.release) >= Gem::Version.new("3.0")
|
|
135
127
|
end
|
|
136
128
|
|
|
137
|
-
def filter_and_format_env(env, rack_env_whitelist)
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
129
|
+
def filter_and_format_env(env, collection, rack_env_whitelist)
|
|
130
|
+
if rack_env_whitelist.empty?
|
|
131
|
+
env
|
|
132
|
+
else
|
|
133
|
+
env.select do |k, _v|
|
|
134
|
+
rack_env_whitelist.include? k.to_s
|
|
135
|
+
end
|
|
136
|
+
end.then do |e|
|
|
137
|
+
collection.filter(e)
|
|
142
138
|
end
|
|
143
139
|
end
|
|
144
140
|
end
|
|
@@ -41,7 +41,7 @@ module Sentry
|
|
|
41
41
|
|
|
42
42
|
# patch this method if you want to change an exception's stacktrace frames
|
|
43
43
|
# also see `StacktraceBuilder.build`.
|
|
44
|
-
def self.build_with_stacktrace(exception:, stacktrace_builder:, mechanism:)
|
|
44
|
+
def self.build_with_stacktrace(exception:, stacktrace_builder:, mechanism:, data_collection:)
|
|
45
45
|
stacktrace = stacktrace_builder.build(backtrace: exception.backtrace)
|
|
46
46
|
|
|
47
47
|
if locals = exception.instance_variable_get(:@sentry_locals)
|
|
@@ -60,7 +60,7 @@ module Sentry
|
|
|
60
60
|
end
|
|
61
61
|
end
|
|
62
62
|
|
|
63
|
-
stacktrace.frames.last&.vars = locals
|
|
63
|
+
stacktrace.frames.last&.vars = data_collection.stack_frame_variables.filter(locals)
|
|
64
64
|
end
|
|
65
65
|
|
|
66
66
|
new(exception: exception, stacktrace: stacktrace, mechanism: mechanism)
|
data/lib/sentry/metric_event.rb
CHANGED
|
@@ -6,7 +6,7 @@ module Sentry
|
|
|
6
6
|
class MetricEvent
|
|
7
7
|
include Sentry::Utils::TelemetryAttributes
|
|
8
8
|
|
|
9
|
-
attr_reader :name, :type, :value, :unit, :timestamp, :trace_id, :span_id, :attributes
|
|
9
|
+
attr_reader :name, :type, :value, :unit, :timestamp, :trace_id, :span_id, :attributes, :integration_meta
|
|
10
10
|
attr_writer :trace_id, :span_id, :attributes
|
|
11
11
|
|
|
12
12
|
def initialize(
|
|
@@ -14,13 +14,15 @@ module Sentry
|
|
|
14
14
|
type:,
|
|
15
15
|
value:,
|
|
16
16
|
unit: nil,
|
|
17
|
-
attributes: nil
|
|
17
|
+
attributes: nil,
|
|
18
|
+
integration_meta: nil
|
|
18
19
|
)
|
|
19
20
|
@name = name
|
|
20
21
|
@type = type
|
|
21
22
|
@value = value
|
|
22
23
|
@unit = unit
|
|
23
24
|
@attributes = attributes || {}
|
|
25
|
+
@integration_meta = integration_meta
|
|
24
26
|
|
|
25
27
|
@timestamp = Sentry.utc_now
|
|
26
28
|
@trace_id = nil
|
|
@@ -21,7 +21,7 @@ module Sentry
|
|
|
21
21
|
max_items_before_drop: MAX_METRICS_BEFORE_DROP,
|
|
22
22
|
envelope_type: "trace_metric",
|
|
23
23
|
envelope_content_type: "application/vnd.sentry.items.trace-metric+json",
|
|
24
|
-
before_send:
|
|
24
|
+
before_send: :before_send_metric
|
|
25
25
|
)
|
|
26
26
|
end
|
|
27
27
|
end
|
data/lib/sentry/metrics.rb
CHANGED
|
@@ -9,15 +9,17 @@ module Sentry
|
|
|
9
9
|
# @param name [String] the metric name
|
|
10
10
|
# @param value [Numeric] the value to increment by (default: 1)
|
|
11
11
|
# @param attributes [Hash, nil] additional attributes for the metric (optional)
|
|
12
|
+
# @param integration [String, Symbol, nil] registered integration emitting the metric (optional)
|
|
12
13
|
# @return [void]
|
|
13
|
-
def count(name, value: 1, attributes: nil)
|
|
14
|
+
def count(name, value: 1, attributes: nil, integration: nil)
|
|
14
15
|
return unless Sentry.initialized?
|
|
15
16
|
|
|
16
17
|
Sentry.get_current_hub.capture_metric(
|
|
17
18
|
name: name,
|
|
18
19
|
type: :counter,
|
|
19
20
|
value: value,
|
|
20
|
-
attributes: attributes
|
|
21
|
+
attributes: attributes,
|
|
22
|
+
integration: integration
|
|
21
23
|
)
|
|
22
24
|
end
|
|
23
25
|
|
|
@@ -26,8 +28,9 @@ module Sentry
|
|
|
26
28
|
# @param value [Numeric] the gauge value
|
|
27
29
|
# @param unit [String, nil] the metric unit (optional)
|
|
28
30
|
# @param attributes [Hash, nil] additional attributes for the metric (optional)
|
|
31
|
+
# @param integration [String, Symbol, nil] registered integration emitting the metric (optional)
|
|
29
32
|
# @return [void]
|
|
30
|
-
def gauge(name, value, unit: nil, attributes: nil)
|
|
33
|
+
def gauge(name, value, unit: nil, attributes: nil, integration: nil)
|
|
31
34
|
return unless Sentry.initialized?
|
|
32
35
|
|
|
33
36
|
Sentry.get_current_hub.capture_metric(
|
|
@@ -35,7 +38,8 @@ module Sentry
|
|
|
35
38
|
type: :gauge,
|
|
36
39
|
value: value,
|
|
37
40
|
unit: unit,
|
|
38
|
-
attributes: attributes
|
|
41
|
+
attributes: attributes,
|
|
42
|
+
integration: integration
|
|
39
43
|
)
|
|
40
44
|
end
|
|
41
45
|
|
|
@@ -44,8 +48,9 @@ module Sentry
|
|
|
44
48
|
# @param value [Numeric] the distribution value
|
|
45
49
|
# @param unit [String, nil] the metric unit (optional)
|
|
46
50
|
# @param attributes [Hash, nil] additional attributes for the metric (optional)
|
|
51
|
+
# @param integration [String, Symbol, nil] registered integration emitting the metric (optional)
|
|
47
52
|
# @return [void]
|
|
48
|
-
def distribution(name, value, unit: nil, attributes: nil)
|
|
53
|
+
def distribution(name, value, unit: nil, attributes: nil, integration: nil)
|
|
49
54
|
return unless Sentry.initialized?
|
|
50
55
|
|
|
51
56
|
Sentry.get_current_hub.capture_metric(
|
|
@@ -53,7 +58,8 @@ module Sentry
|
|
|
53
58
|
type: :distribution,
|
|
54
59
|
value: value,
|
|
55
60
|
unit: unit,
|
|
56
|
-
attributes: attributes
|
|
61
|
+
attributes: attributes,
|
|
62
|
+
integration: integration
|
|
57
63
|
)
|
|
58
64
|
end
|
|
59
65
|
end
|
data/lib/sentry/net/http.rb
CHANGED
|
@@ -72,10 +72,9 @@ module Sentry
|
|
|
72
72
|
|
|
73
73
|
result = { method: req.method, url: url }
|
|
74
74
|
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
end
|
|
75
|
+
query = filter_query_params(uri.query)
|
|
76
|
+
result[:query] = query if query
|
|
77
|
+
result[:body] = req.body if Sentry.configuration.data_collection.collect_outgoing_http_body?
|
|
79
78
|
|
|
80
79
|
result
|
|
81
80
|
end
|
data/lib/sentry/redis.rb
CHANGED
|
@@ -62,7 +62,7 @@ module Sentry
|
|
|
62
62
|
command_set = { command: command.to_s.upcase }
|
|
63
63
|
command_set[:key] = key if Utils::EncodingHelper.valid_utf_8?(key)
|
|
64
64
|
|
|
65
|
-
if Sentry.configuration.
|
|
65
|
+
if Sentry.configuration.data_collection.database_query_data
|
|
66
66
|
command_set[:arguments] = arguments
|
|
67
67
|
.select { |a| Utils::EncodingHelper.valid_utf_8?(a) }
|
|
68
68
|
.join(" ")
|
data/lib/sentry/scope.rb
CHANGED
|
@@ -9,6 +9,7 @@ require "etc"
|
|
|
9
9
|
module Sentry
|
|
10
10
|
class Scope
|
|
11
11
|
include ArgumentCheckingHelper
|
|
12
|
+
include CallbackHelper
|
|
12
13
|
|
|
13
14
|
ATTRIBUTES = [
|
|
14
15
|
:transaction_name,
|
|
@@ -71,7 +72,8 @@ module Sentry
|
|
|
71
72
|
|
|
72
73
|
unless all_event_processors.empty?
|
|
73
74
|
all_event_processors.each do |processor_block|
|
|
74
|
-
event = processor_block
|
|
75
|
+
event = safe_dispatch_callback("event_processor", processor_block, [event, hint])
|
|
76
|
+
return unless event
|
|
75
77
|
end
|
|
76
78
|
end
|
|
77
79
|
|
|
@@ -102,8 +104,10 @@ module Sentry
|
|
|
102
104
|
configuration = Sentry.configuration
|
|
103
105
|
return telemetry unless configuration
|
|
104
106
|
|
|
105
|
-
telemetry.
|
|
106
|
-
|
|
107
|
+
sdk_meta = telemetry.respond_to?(:integration_meta) ? telemetry.integration_meta : nil
|
|
108
|
+
sdk_meta ||= Sentry.sdk_meta
|
|
109
|
+
telemetry.attributes["sentry.sdk.name"] ||= sdk_meta[:name] || sdk_meta["name"]
|
|
110
|
+
telemetry.attributes["sentry.sdk.version"] ||= sdk_meta[:version] || sdk_meta["version"]
|
|
107
111
|
telemetry.attributes["sentry.environment"] ||= configuration.environment if configuration.environment
|
|
108
112
|
telemetry.attributes["sentry.release"] ||= configuration.release if configuration.release
|
|
109
113
|
telemetry.attributes["server.address"] ||= configuration.server_name if configuration.server_name
|
|
@@ -35,6 +35,13 @@ module Sentry
|
|
|
35
35
|
|
|
36
36
|
private
|
|
37
37
|
|
|
38
|
+
def reset_if_forked
|
|
39
|
+
return unless super
|
|
40
|
+
|
|
41
|
+
@mutex = Mutex.new
|
|
42
|
+
@pending_aggregates = {}
|
|
43
|
+
end
|
|
44
|
+
|
|
38
45
|
def init_aggregates(aggregation_key)
|
|
39
46
|
aggregates = { started: aggregation_key.iso8601 }
|
|
40
47
|
Session::AGGREGATE_STATUSES.each { |k| aggregates[k] = 0 }
|
|
@@ -4,6 +4,8 @@ module Sentry
|
|
|
4
4
|
# Ruby Logger support Add commentMore actions
|
|
5
5
|
# intercepts any logger instance and send the log to Sentry too.
|
|
6
6
|
module StdLibLogger
|
|
7
|
+
include CallbackHelper
|
|
8
|
+
|
|
7
9
|
SEVERITY_MAP = {
|
|
8
10
|
0 => :debug,
|
|
9
11
|
1 => :info,
|
|
@@ -37,8 +39,12 @@ module Sentry
|
|
|
37
39
|
message = message.to_s.strip
|
|
38
40
|
|
|
39
41
|
if !message.nil? && message != Sentry::Logger::PROGNAME && method = SEVERITY_MAP[severity]
|
|
40
|
-
if
|
|
41
|
-
return result
|
|
42
|
+
if filter = Sentry.configuration.std_lib_logger_filter
|
|
43
|
+
return result unless safe_dispatch_callback(
|
|
44
|
+
"std_lib_logger_filter",
|
|
45
|
+
filter,
|
|
46
|
+
[self, message, method]
|
|
47
|
+
)
|
|
42
48
|
end
|
|
43
49
|
|
|
44
50
|
Sentry.logger.send(method, message, origin: ORIGIN)
|
|
@@ -50,10 +56,6 @@ module Sentry
|
|
|
50
56
|
end
|
|
51
57
|
end
|
|
52
58
|
|
|
53
|
-
Sentry.register_patch(:logger) do
|
|
54
|
-
|
|
55
|
-
::Logger.prepend(Sentry::StdLibLogger)
|
|
56
|
-
else
|
|
57
|
-
config.sdk_logger.warn(":logger patch enabled but `enable_logs` is turned off - skipping applying patch")
|
|
58
|
-
end
|
|
59
|
+
Sentry.register_patch(:logger) do
|
|
60
|
+
::Logger.prepend(Sentry::StdLibLogger)
|
|
59
61
|
end
|
|
@@ -12,7 +12,10 @@ module Sentry
|
|
|
12
12
|
#
|
|
13
13
|
# @!visibility private
|
|
14
14
|
class TelemetryEventBuffer < ThreadedPeriodicWorker
|
|
15
|
+
include CallbackHelper
|
|
16
|
+
|
|
15
17
|
FLUSH_INTERVAL = 5 # seconds
|
|
18
|
+
FLUSH_TIMEOUT = 2 # seconds
|
|
16
19
|
|
|
17
20
|
# @!visibility private
|
|
18
21
|
attr_reader :pending_items, :envelope_type, :data_category, :thread
|
|
@@ -21,6 +24,7 @@ module Sentry
|
|
|
21
24
|
super(configuration.sdk_logger, FLUSH_INTERVAL)
|
|
22
25
|
|
|
23
26
|
@client = client
|
|
27
|
+
@configuration = configuration
|
|
24
28
|
@dsn = configuration.dsn
|
|
25
29
|
@debug = configuration.debug
|
|
26
30
|
@event_class = event_class
|
|
@@ -38,39 +42,44 @@ module Sentry
|
|
|
38
42
|
end
|
|
39
43
|
|
|
40
44
|
def flush
|
|
41
|
-
|
|
42
|
-
return if empty?
|
|
43
|
-
|
|
44
|
-
log_debug("[#{self.class}] flushing #{size} #{@event_class}")
|
|
45
|
+
return unless thread&.alive? && wake
|
|
45
46
|
|
|
46
|
-
|
|
47
|
-
|
|
47
|
+
wait_until_idle(FLUSH_TIMEOUT)
|
|
48
|
+
end
|
|
48
49
|
|
|
49
|
-
|
|
50
|
+
def run
|
|
51
|
+
flush_pending_items
|
|
50
52
|
end
|
|
51
|
-
alias_method :run, :flush
|
|
52
53
|
|
|
53
54
|
def add_item(item)
|
|
55
|
+
# the buffer thread can never add telemetry itself to prevent recursion
|
|
56
|
+
return self if Thread.current == thread
|
|
57
|
+
return unless ensure_thread
|
|
58
|
+
|
|
54
59
|
# Prevent ThreadError from re-entrant locking (e.g. transport instrumentation calling Sentry.metrics.*)
|
|
55
60
|
return self if @mutex.owned?
|
|
56
61
|
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
62
|
+
dropped = false
|
|
63
|
+
size_exceeded = @mutex.synchronize do
|
|
60
64
|
if size >= @max_items_before_drop
|
|
61
|
-
|
|
62
|
-
@client.transport.record_lost_event(
|
|
63
|
-
:queue_overflow,
|
|
64
|
-
@data_category,
|
|
65
|
-
num_bytes: JSON.generate(item.to_h).bytesize
|
|
66
|
-
)
|
|
65
|
+
dropped = true
|
|
67
66
|
else
|
|
68
67
|
@pending_items << item
|
|
69
68
|
end
|
|
70
69
|
|
|
71
|
-
|
|
70
|
+
size >= @max_items
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
if dropped
|
|
74
|
+
log_debug("[#{self.class}] exceeded max capacity, dropping event")
|
|
75
|
+
@client.transport.record_lost_event(
|
|
76
|
+
:queue_overflow,
|
|
77
|
+
@data_category,
|
|
78
|
+
num_bytes: JSON.generate(item.to_h).bytesize
|
|
79
|
+
)
|
|
72
80
|
end
|
|
73
81
|
|
|
82
|
+
wake if size_exceeded
|
|
74
83
|
self
|
|
75
84
|
end
|
|
76
85
|
|
|
@@ -88,21 +97,40 @@ module Sentry
|
|
|
88
97
|
|
|
89
98
|
private
|
|
90
99
|
|
|
91
|
-
def
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
100
|
+
def reset_if_forked
|
|
101
|
+
return unless super
|
|
102
|
+
|
|
103
|
+
# Discard items inherited from the parent to avoid duplicate delivery.
|
|
104
|
+
@mutex = Mutex.new
|
|
105
|
+
@pending_items = []
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
def flush_pending_items
|
|
109
|
+
pending_items = @mutex.synchronize do
|
|
110
|
+
next if @pending_items.empty?
|
|
111
|
+
|
|
112
|
+
items = @pending_items
|
|
113
|
+
@pending_items = []
|
|
114
|
+
items
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
return unless pending_items
|
|
118
|
+
|
|
119
|
+
log_debug("[#{self.class}] flushing #{pending_items.size} #{@event_class}")
|
|
120
|
+
send_items(pending_items)
|
|
121
|
+
self
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
def send_items(pending_items)
|
|
125
|
+
envelope = Envelope.new(sent_at: Sentry.utc_now.iso8601)
|
|
98
126
|
|
|
99
127
|
discarded_count = 0
|
|
100
128
|
discarded_bytes = 0
|
|
101
129
|
envelope_items = []
|
|
102
130
|
|
|
103
|
-
if @before_send
|
|
104
|
-
|
|
105
|
-
processed_item = @before_send.
|
|
131
|
+
if callback = @configuration.send(@before_send)
|
|
132
|
+
pending_items.each do |item|
|
|
133
|
+
processed_item = safe_dispatch_callback(@before_send.to_s, callback, [item])
|
|
106
134
|
|
|
107
135
|
if processed_item
|
|
108
136
|
envelope_items << processed_item.to_h
|
|
@@ -112,7 +140,7 @@ module Sentry
|
|
|
112
140
|
end
|
|
113
141
|
end
|
|
114
142
|
else
|
|
115
|
-
envelope_items =
|
|
143
|
+
envelope_items = pending_items.map(&:to_h)
|
|
116
144
|
end
|
|
117
145
|
|
|
118
146
|
unless discarded_count.zero?
|
|
@@ -133,8 +161,6 @@ module Sentry
|
|
|
133
161
|
@client.send_envelope(envelope)
|
|
134
162
|
rescue => e
|
|
135
163
|
log_error("[#{self.class}] Failed to send #{@event_class}", e, debug: @debug)
|
|
136
|
-
ensure
|
|
137
|
-
clear!
|
|
138
164
|
end
|
|
139
165
|
end
|
|
140
166
|
end
|
data/lib/sentry/test_helper.rb
CHANGED
|
@@ -33,7 +33,7 @@ module Sentry
|
|
|
33
33
|
dummy_config.background_worker_threads = 0
|
|
34
34
|
|
|
35
35
|
# user can overwrite some of the configs, with a few exceptions like:
|
|
36
|
-
# -
|
|
36
|
+
# - data_collection
|
|
37
37
|
# - auto_session_tracking
|
|
38
38
|
block&.call(dummy_config)
|
|
39
39
|
|
|
@@ -90,9 +90,7 @@ module Sentry
|
|
|
90
90
|
transport.clear if transport.respond_to?(:clear)
|
|
91
91
|
end
|
|
92
92
|
|
|
93
|
-
|
|
94
|
-
sentry_logger.clear
|
|
95
|
-
end
|
|
93
|
+
sentry_logger.clear if sentry_logger.respond_to?(:clear)
|
|
96
94
|
end
|
|
97
95
|
|
|
98
96
|
# @return [Sentry::StructuredLogger, Sentry::DebugStructuredLogger]
|
|
@@ -129,6 +127,8 @@ module Sentry
|
|
|
129
127
|
end
|
|
130
128
|
|
|
131
129
|
def sentry_logs
|
|
130
|
+
Sentry.get_current_client&.log_event_buffer&.flush
|
|
131
|
+
|
|
132
132
|
sentry_envelopes
|
|
133
133
|
.flat_map(&:items)
|
|
134
134
|
.select { |item| item.headers[:type] == "log" }
|
|
@@ -136,6 +136,8 @@ module Sentry
|
|
|
136
136
|
end
|
|
137
137
|
|
|
138
138
|
def sentry_metrics
|
|
139
|
+
Sentry.get_current_client&.metric_event_buffer&.flush
|
|
140
|
+
|
|
139
141
|
sentry_envelopes
|
|
140
142
|
.flat_map(&:items)
|
|
141
143
|
.select { |item| item.headers[:type] == "trace_metric" }
|