sentry-ruby 5.4.2 → 5.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/Gemfile +3 -2
- data/lib/sentry/baggage.rb +81 -0
- data/lib/sentry/client.rb +17 -10
- data/lib/sentry/event.rb +1 -1
- data/lib/sentry/hub.rb +18 -0
- data/lib/sentry/interfaces/request.rb +6 -16
- data/lib/sentry/interfaces/single_exception.rb +9 -1
- data/lib/sentry/net/http.rb +19 -31
- data/lib/sentry/rack/capture_exceptions.rb +6 -4
- data/lib/sentry/rake.rb +1 -1
- data/lib/sentry/redis.rb +14 -16
- data/lib/sentry/scope.rb +29 -3
- data/lib/sentry/session.rb +5 -7
- data/lib/sentry/span.rb +11 -3
- data/lib/sentry/test_helper.rb +2 -0
- data/lib/sentry/transaction.rb +103 -8
- data/lib/sentry/transaction_event.rb +18 -0
- data/lib/sentry/transport.rb +12 -8
- data/lib/sentry/utils/encoding_helper.rb +22 -0
- data/lib/sentry/version.rb +1 -1
- data/lib/sentry-ruby.rb +6 -17
- metadata +4 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 292dc37d7e5703be7d5d2e33a84f02b492759f0c8ef656e3ae660a7196301fe4
|
4
|
+
data.tar.gz: 1b76f778b06e4928eb32cabe31fc208a4aa92bf8b62f447abf9dd204c97e26cb
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: ae4dbbb4e60e1dbad0e0e0e5a1fedad72e383b0709453ab4f9f6dc8cc367625e3663848885b893f28f47a3758aed7aec68fc3aa32de3974fdc9e807aa0f5d4bf
|
7
|
+
data.tar.gz: 2194f3cb7e4d604d8ddeaf3761406133a049a5e5870ac44e54e8d7ca90d4a41e489b39681b53c912d97c4710bd1813716863364f9de6f233e345c353b31d133f
|
data/Gemfile
CHANGED
@@ -3,12 +3,13 @@ git_source(:github) { |name| "https://github.com/#{name}.git" }
|
|
3
3
|
|
4
4
|
gem "sentry-ruby", path: "./"
|
5
5
|
|
6
|
-
|
6
|
+
rack_version = ENV["RACK_VERSION"]
|
7
|
+
rack_version = "3.0.0" if rack_version.nil?
|
8
|
+
gem "rack", "~> #{Gem::Version.new(rack_version)}" unless rack_version == "0"
|
7
9
|
|
8
10
|
gem "rake", "~> 12.0"
|
9
11
|
gem "rspec", "~> 3.0"
|
10
12
|
gem "rspec-retry"
|
11
|
-
gem "webmock"
|
12
13
|
gem "fakeredis"
|
13
14
|
gem "timecop"
|
14
15
|
gem 'simplecov'
|
@@ -0,0 +1,81 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'cgi'
|
4
|
+
|
5
|
+
module Sentry
|
6
|
+
# A {https://www.w3.org/TR/baggage W3C Baggage Header} implementation.
|
7
|
+
class Baggage
|
8
|
+
SENTRY_PREFIX = 'sentry-'
|
9
|
+
SENTRY_PREFIX_REGEX = /^sentry-/.freeze
|
10
|
+
|
11
|
+
DSC_KEYS = %w(
|
12
|
+
trace_id
|
13
|
+
public_key
|
14
|
+
sample_rate
|
15
|
+
release
|
16
|
+
environment
|
17
|
+
transaction
|
18
|
+
user_id
|
19
|
+
user_segment
|
20
|
+
).freeze
|
21
|
+
|
22
|
+
# @return [Hash]
|
23
|
+
attr_reader :items
|
24
|
+
|
25
|
+
# @return [Boolean]
|
26
|
+
attr_reader :mutable
|
27
|
+
|
28
|
+
def initialize(items, mutable: true)
|
29
|
+
@items = items
|
30
|
+
@mutable = mutable
|
31
|
+
end
|
32
|
+
|
33
|
+
# Creates a Baggage object from an incoming W3C Baggage header string.
|
34
|
+
#
|
35
|
+
# Sentry items are identified with the 'sentry-' prefix and stored in a hash.
|
36
|
+
# The presence of a Sentry item makes the baggage object immutable.
|
37
|
+
#
|
38
|
+
# @param header [String] The incoming Baggage header string.
|
39
|
+
# @return [Baggage, nil]
|
40
|
+
def self.from_incoming_header(header)
|
41
|
+
items = {}
|
42
|
+
mutable = true
|
43
|
+
|
44
|
+
header.split(',').each do |item|
|
45
|
+
item = item.strip
|
46
|
+
key, val = item.split('=')
|
47
|
+
|
48
|
+
next unless key && val
|
49
|
+
next unless key =~ SENTRY_PREFIX_REGEX
|
50
|
+
|
51
|
+
baggage_key = key.split('-')[1]
|
52
|
+
next unless baggage_key
|
53
|
+
|
54
|
+
items[CGI.unescape(baggage_key)] = CGI.unescape(val)
|
55
|
+
mutable = false
|
56
|
+
end
|
57
|
+
|
58
|
+
new(items, mutable: mutable)
|
59
|
+
end
|
60
|
+
|
61
|
+
# Make the Baggage immutable.
|
62
|
+
# @return [void]
|
63
|
+
def freeze!
|
64
|
+
@mutable = false
|
65
|
+
end
|
66
|
+
|
67
|
+
# A {https://develop.sentry.dev/sdk/performance/dynamic-sampling-context/#envelope-header Dynamic Sampling Context}
|
68
|
+
# hash to be used in the trace envelope header.
|
69
|
+
# @return [Hash]
|
70
|
+
def dynamic_sampling_context
|
71
|
+
@items.select { |k, _v| DSC_KEYS.include?(k) }
|
72
|
+
end
|
73
|
+
|
74
|
+
# Serialize the Baggage object back to a string.
|
75
|
+
# @return [String]
|
76
|
+
def serialize
|
77
|
+
items = @items.map { |k, v| "#{SENTRY_PREFIX}#{CGI.escape(k)}=#{CGI.escape(v)}" }
|
78
|
+
items.join(',')
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
data/lib/sentry/client.rb
CHANGED
@@ -105,16 +105,7 @@ module Sentry
|
|
105
105
|
# @param transaction [Transaction] the transaction to be recorded.
|
106
106
|
# @return [TransactionEvent]
|
107
107
|
def event_from_transaction(transaction)
|
108
|
-
TransactionEvent.new(configuration: configuration
|
109
|
-
event.transaction = transaction.name
|
110
|
-
event.contexts.merge!(trace: transaction.get_trace_context)
|
111
|
-
event.timestamp = transaction.timestamp
|
112
|
-
event.start_timestamp = transaction.start_timestamp
|
113
|
-
event.tags = transaction.tags
|
114
|
-
|
115
|
-
finished_spans = transaction.span_recorder.spans.select { |span| span.timestamp && span != transaction }
|
116
|
-
event.spans = finished_spans.map(&:to_hash)
|
117
|
-
end
|
108
|
+
TransactionEvent.new(configuration: configuration, transaction: transaction)
|
118
109
|
end
|
119
110
|
|
120
111
|
# @!macro send_event
|
@@ -156,6 +147,22 @@ module Sentry
|
|
156
147
|
trace
|
157
148
|
end
|
158
149
|
|
150
|
+
# Generates a W3C Baggage header for distribted tracing from the given Span.
|
151
|
+
# Returns `nil` if `config.propagate_traces` is `false`.
|
152
|
+
# @param span [Span] the span to generate trace from.
|
153
|
+
# @return [String, nil]
|
154
|
+
def generate_baggage(span)
|
155
|
+
return unless configuration.propagate_traces
|
156
|
+
|
157
|
+
baggage = span.to_baggage
|
158
|
+
|
159
|
+
if baggage && !baggage.empty?
|
160
|
+
log_debug("[Tracing] Adding #{BAGGAGE_HEADER_NAME} header to outgoing request: #{baggage}")
|
161
|
+
end
|
162
|
+
|
163
|
+
baggage
|
164
|
+
end
|
165
|
+
|
159
166
|
private
|
160
167
|
|
161
168
|
def dispatch_background_event(event, hint)
|
data/lib/sentry/event.rb
CHANGED
data/lib/sentry/hub.rb
CHANGED
@@ -92,6 +92,24 @@ module Sentry
|
|
92
92
|
transaction
|
93
93
|
end
|
94
94
|
|
95
|
+
def with_child_span(**attributes, &block)
|
96
|
+
current_span = current_scope.get_span
|
97
|
+
return yield(nil) unless current_span
|
98
|
+
|
99
|
+
result = nil
|
100
|
+
|
101
|
+
begin
|
102
|
+
current_span.with_child_span(**attributes) do |child_span|
|
103
|
+
current_scope.set_span(child_span)
|
104
|
+
result = yield(child_span)
|
105
|
+
end
|
106
|
+
ensure
|
107
|
+
current_scope.set_span(current_span)
|
108
|
+
end
|
109
|
+
|
110
|
+
result
|
111
|
+
end
|
112
|
+
|
95
113
|
def capture_exception(exception, **options, &block)
|
96
114
|
check_argument_type!(exception, ::Exception)
|
97
115
|
|
@@ -73,7 +73,7 @@ module Sentry
|
|
73
73
|
request.POST
|
74
74
|
elsif request.body # JSON requests, etc
|
75
75
|
data = request.body.read(MAX_BODY_LIMIT)
|
76
|
-
data = encode_to_utf_8(data.to_s)
|
76
|
+
data = Utils::EncodingHelper.encode_to_utf_8(data.to_s)
|
77
77
|
request.body.rewind
|
78
78
|
data
|
79
79
|
end
|
@@ -94,7 +94,7 @@ module Sentry
|
|
94
94
|
key = key.sub(/^HTTP_/, "")
|
95
95
|
key = key.split('_').map(&:capitalize).join('-')
|
96
96
|
|
97
|
-
memo[key] = encode_to_utf_8(value.to_s)
|
97
|
+
memo[key] = Utils::EncodingHelper.encode_to_utf_8(value.to_s)
|
98
98
|
rescue StandardError => e
|
99
99
|
# Rails adds objects to the Rack env that can sometimes raise exceptions
|
100
100
|
# when `to_s` is called.
|
@@ -105,31 +105,21 @@ module Sentry
|
|
105
105
|
end
|
106
106
|
end
|
107
107
|
|
108
|
-
def encode_to_utf_8(value)
|
109
|
-
if value.encoding != Encoding::UTF_8 && value.respond_to?(:force_encoding)
|
110
|
-
value = value.dup.force_encoding(Encoding::UTF_8)
|
111
|
-
end
|
112
|
-
|
113
|
-
if !value.valid_encoding?
|
114
|
-
value = value.scrub
|
115
|
-
end
|
116
|
-
|
117
|
-
value
|
118
|
-
end
|
119
|
-
|
120
108
|
def is_skippable_header?(key)
|
121
109
|
key.upcase != key || # lower-case envs aren't real http headers
|
122
110
|
key == "HTTP_COOKIE" || # Cookies don't go here, they go somewhere else
|
123
111
|
!(key.start_with?('HTTP_') || CONTENT_HEADERS.include?(key))
|
124
112
|
end
|
125
113
|
|
126
|
-
# Rack adds in an incorrect HTTP_VERSION key, which causes downstream
|
114
|
+
# In versions < 3, Rack adds in an incorrect HTTP_VERSION key, which causes downstream
|
127
115
|
# to think this is a Version header. Instead, this is mapped to
|
128
116
|
# env['SERVER_PROTOCOL']. But we don't want to ignore a valid header
|
129
117
|
# if the request has legitimately sent a Version header themselves.
|
130
118
|
# See: https://github.com/rack/rack/blob/028438f/lib/rack/handler/cgi.rb#L29
|
131
|
-
# NOTE: This will be removed in version 3.0+
|
132
119
|
def is_server_protocol?(key, value, protocol_version)
|
120
|
+
rack_version = Gem::Version.new(::Rack.release)
|
121
|
+
return false if rack_version >= Gem::Version.new("3.0")
|
122
|
+
|
133
123
|
key == 'HTTP_VERSION' && value == protocol_version
|
134
124
|
end
|
135
125
|
|
@@ -15,7 +15,15 @@ module Sentry
|
|
15
15
|
|
16
16
|
def initialize(exception:, stacktrace: nil)
|
17
17
|
@type = exception.class.to_s
|
18
|
-
|
18
|
+
exception_message =
|
19
|
+
if exception.respond_to?(:detailed_message)
|
20
|
+
exception.detailed_message(highlight: false)
|
21
|
+
else
|
22
|
+
exception.message || ""
|
23
|
+
end
|
24
|
+
|
25
|
+
@value = exception_message.byteslice(0..Event::MAX_MESSAGE_SIZE_IN_BYTES)
|
26
|
+
|
19
27
|
@module = exception.class.to_s.split('::')[0...-1].join('::')
|
20
28
|
@thread_id = Thread.current.object_id
|
21
29
|
@stacktrace = stacktrace
|
data/lib/sentry/net/http.rb
CHANGED
@@ -26,14 +26,21 @@ module Sentry
|
|
26
26
|
#
|
27
27
|
# So we're only instrumenting request when `Net::HTTP` is already started
|
28
28
|
def request(req, body = nil, &block)
|
29
|
-
return super unless started?
|
29
|
+
return super unless started? && Sentry.initialized?
|
30
|
+
return super if from_sentry_sdk?
|
30
31
|
|
31
|
-
|
32
|
-
|
32
|
+
Sentry.with_child_span(op: OP_NAME, start_timestamp: Sentry.utc_now.to_f) do |sentry_span|
|
33
|
+
set_sentry_trace_header(req, sentry_span)
|
33
34
|
|
34
|
-
|
35
|
-
|
36
|
-
|
35
|
+
super.tap do |res|
|
36
|
+
record_sentry_breadcrumb(req, res)
|
37
|
+
|
38
|
+
if sentry_span
|
39
|
+
request_info = extract_request_info(req)
|
40
|
+
sentry_span.set_description("#{request_info[:method]} #{request_info[:url]}")
|
41
|
+
sentry_span.set_data(:status, res.code.to_i)
|
42
|
+
end
|
43
|
+
end
|
37
44
|
end
|
38
45
|
end
|
39
46
|
|
@@ -42,13 +49,17 @@ module Sentry
|
|
42
49
|
def set_sentry_trace_header(req, sentry_span)
|
43
50
|
return unless sentry_span
|
44
51
|
|
45
|
-
|
52
|
+
client = Sentry.get_current_client
|
53
|
+
|
54
|
+
trace = client.generate_sentry_trace(sentry_span)
|
46
55
|
req[SENTRY_TRACE_HEADER_NAME] = trace if trace
|
56
|
+
|
57
|
+
baggage = client.generate_baggage(sentry_span)
|
58
|
+
req[BAGGAGE_HEADER_NAME] = baggage if baggage && !baggage.empty?
|
47
59
|
end
|
48
60
|
|
49
61
|
def record_sentry_breadcrumb(req, res)
|
50
62
|
return unless Sentry.initialized? && Sentry.configuration.breadcrumbs_logger.include?(:http_logger)
|
51
|
-
return if from_sentry_sdk?
|
52
63
|
|
53
64
|
request_info = extract_request_info(req)
|
54
65
|
|
@@ -64,29 +75,6 @@ module Sentry
|
|
64
75
|
Sentry.add_breadcrumb(crumb)
|
65
76
|
end
|
66
77
|
|
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)
|
74
|
-
end
|
75
|
-
|
76
|
-
def start_sentry_span
|
77
|
-
return unless Sentry.initialized? && span = Sentry.get_current_scope.get_span
|
78
|
-
return if from_sentry_sdk?
|
79
|
-
return if span.sampled == false
|
80
|
-
|
81
|
-
span.start_child(op: OP_NAME, start_timestamp: Sentry.utc_now.to_f)
|
82
|
-
end
|
83
|
-
|
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)
|
88
|
-
end
|
89
|
-
|
90
78
|
def from_sentry_sdk?
|
91
79
|
dsn = Sentry.configuration.dsn
|
92
80
|
dsn && dsn.host == self.address
|
@@ -18,7 +18,7 @@ module Sentry
|
|
18
18
|
Sentry.with_scope do |scope|
|
19
19
|
Sentry.with_session_tracking do
|
20
20
|
scope.clear_breadcrumbs
|
21
|
-
scope.set_transaction_name(env["PATH_INFO"]) if env["PATH_INFO"]
|
21
|
+
scope.set_transaction_name(env["PATH_INFO"], source: :url) if env["PATH_INFO"]
|
22
22
|
scope.set_rack_env(env)
|
23
23
|
|
24
24
|
transaction = start_transaction(env, scope)
|
@@ -52,7 +52,7 @@ module Sentry
|
|
52
52
|
end
|
53
53
|
|
54
54
|
def transaction_op
|
55
|
-
"
|
55
|
+
"http.server".freeze
|
56
56
|
end
|
57
57
|
|
58
58
|
def capture_exception(exception, env)
|
@@ -63,8 +63,10 @@ module Sentry
|
|
63
63
|
|
64
64
|
def start_transaction(env, scope)
|
65
65
|
sentry_trace = env["HTTP_SENTRY_TRACE"]
|
66
|
-
|
67
|
-
|
66
|
+
baggage = env["HTTP_BAGGAGE"]
|
67
|
+
|
68
|
+
options = { name: scope.transaction_name, source: scope.transaction_source, op: transaction_op }
|
69
|
+
transaction = Sentry::Transaction.from_sentry_trace(sentry_trace, baggage: baggage, **options) if sentry_trace
|
68
70
|
Sentry.start_transaction(transaction: transaction, custom_sampling_context: { env: env }, **options)
|
69
71
|
end
|
70
72
|
|
data/lib/sentry/rake.rb
CHANGED
@@ -10,7 +10,7 @@ module Sentry
|
|
10
10
|
def display_error_message(ex)
|
11
11
|
Sentry.capture_exception(ex) do |scope|
|
12
12
|
task_name = top_level_tasks.join(' ')
|
13
|
-
scope.set_transaction_name(task_name)
|
13
|
+
scope.set_transaction_name(task_name, source: :task)
|
14
14
|
scope.set_tag("rake_task", task_name)
|
15
15
|
end if Sentry.initialized? && !Sentry.configuration.skip_rake_integration
|
16
16
|
|
data/lib/sentry/redis.rb
CHANGED
@@ -3,7 +3,7 @@
|
|
3
3
|
module Sentry
|
4
4
|
# @api private
|
5
5
|
class Redis
|
6
|
-
OP_NAME = "db.redis
|
6
|
+
OP_NAME = "db.redis"
|
7
7
|
LOGGER_NAME = :redis_logger
|
8
8
|
|
9
9
|
def initialize(commands, host, port, db)
|
@@ -13,9 +13,14 @@ module Sentry
|
|
13
13
|
def instrument
|
14
14
|
return yield unless Sentry.initialized?
|
15
15
|
|
16
|
-
|
16
|
+
Sentry.with_child_span(op: OP_NAME, start_timestamp: Sentry.utc_now.to_f) do |span|
|
17
17
|
yield.tap do
|
18
18
|
record_breadcrumb
|
19
|
+
|
20
|
+
if span
|
21
|
+
span.set_description(commands_description)
|
22
|
+
span.set_data(:server, server_description)
|
23
|
+
end
|
19
24
|
end
|
20
25
|
end
|
21
26
|
end
|
@@ -24,18 +29,6 @@ module Sentry
|
|
24
29
|
|
25
30
|
attr_reader :commands, :host, :port, :db
|
26
31
|
|
27
|
-
def record_span
|
28
|
-
return yield unless (transaction = Sentry.get_current_scope.get_transaction) && transaction.sampled
|
29
|
-
|
30
|
-
sentry_span = transaction.start_child(op: OP_NAME, start_timestamp: Sentry.utc_now.to_f)
|
31
|
-
|
32
|
-
yield.tap do
|
33
|
-
sentry_span.set_description(commands_description)
|
34
|
-
sentry_span.set_data(:server, server_description)
|
35
|
-
sentry_span.set_timestamp(Sentry.utc_now.to_f)
|
36
|
-
end
|
37
|
-
end
|
38
|
-
|
39
32
|
def record_breadcrumb
|
40
33
|
return unless Sentry.configuration.breadcrumbs_logger.include?(LOGGER_NAME)
|
41
34
|
|
@@ -61,10 +54,15 @@ module Sentry
|
|
61
54
|
def parsed_commands
|
62
55
|
commands.map do |statement|
|
63
56
|
command, key, *arguments = statement
|
57
|
+
command_set = { command: command.to_s.upcase, key: key }
|
64
58
|
|
65
|
-
|
66
|
-
command_set[:arguments] = arguments
|
59
|
+
if Sentry.configuration.send_default_pii
|
60
|
+
command_set[:arguments] = arguments
|
61
|
+
.select { |a| Utils::EncodingHelper.valid_utf_8?(a) }
|
62
|
+
.join(" ")
|
67
63
|
end
|
64
|
+
|
65
|
+
command_set
|
68
66
|
end
|
69
67
|
end
|
70
68
|
|
data/lib/sentry/scope.rb
CHANGED
@@ -7,7 +7,21 @@ module Sentry
|
|
7
7
|
class Scope
|
8
8
|
include ArgumentCheckingHelper
|
9
9
|
|
10
|
-
ATTRIBUTES = [
|
10
|
+
ATTRIBUTES = [
|
11
|
+
:transaction_names,
|
12
|
+
:transaction_sources,
|
13
|
+
:contexts,
|
14
|
+
:extra,
|
15
|
+
:tags,
|
16
|
+
:user,
|
17
|
+
:level,
|
18
|
+
:breadcrumbs,
|
19
|
+
:fingerprint,
|
20
|
+
:event_processors,
|
21
|
+
:rack_env,
|
22
|
+
:span,
|
23
|
+
:session
|
24
|
+
]
|
11
25
|
|
12
26
|
attr_reader(*ATTRIBUTES)
|
13
27
|
|
@@ -33,6 +47,7 @@ module Sentry
|
|
33
47
|
event.extra = extra.merge(event.extra)
|
34
48
|
event.contexts = contexts.merge(event.contexts)
|
35
49
|
event.transaction = transaction_name if transaction_name
|
50
|
+
event.transaction_info = { source: transaction_source } if transaction_source
|
36
51
|
|
37
52
|
if span
|
38
53
|
event.contexts[:trace] = span.get_trace_context
|
@@ -73,7 +88,8 @@ module Sentry
|
|
73
88
|
copy.extra = extra.deep_dup
|
74
89
|
copy.tags = tags.deep_dup
|
75
90
|
copy.user = user.deep_dup
|
76
|
-
copy.transaction_names = transaction_names.
|
91
|
+
copy.transaction_names = transaction_names.dup
|
92
|
+
copy.transaction_sources = transaction_sources.dup
|
77
93
|
copy.fingerprint = fingerprint.deep_dup
|
78
94
|
copy.span = span.deep_dup
|
79
95
|
copy.session = session.deep_dup
|
@@ -90,6 +106,7 @@ module Sentry
|
|
90
106
|
self.tags = scope.tags
|
91
107
|
self.user = scope.user
|
92
108
|
self.transaction_names = scope.transaction_names
|
109
|
+
self.transaction_sources = scope.transaction_sources
|
93
110
|
self.fingerprint = scope.fingerprint
|
94
111
|
self.span = scope.span
|
95
112
|
end
|
@@ -195,8 +212,9 @@ module Sentry
|
|
195
212
|
# The "transaction" here does not refer to `Transaction` objects.
|
196
213
|
# @param transaction_name [String]
|
197
214
|
# @return [void]
|
198
|
-
def set_transaction_name(transaction_name)
|
215
|
+
def set_transaction_name(transaction_name, source: :custom)
|
199
216
|
@transaction_names << transaction_name
|
217
|
+
@transaction_sources << source
|
200
218
|
end
|
201
219
|
|
202
220
|
# Sets the currently active session on the scope.
|
@@ -213,6 +231,13 @@ module Sentry
|
|
213
231
|
@transaction_names.last
|
214
232
|
end
|
215
233
|
|
234
|
+
# Returns current transaction source.
|
235
|
+
# The "transaction" here does not refer to `Transaction` objects.
|
236
|
+
# @return [String, nil]
|
237
|
+
def transaction_source
|
238
|
+
@transaction_sources.last
|
239
|
+
end
|
240
|
+
|
216
241
|
# Returns the associated Transaction object.
|
217
242
|
# @return [Transaction, nil]
|
218
243
|
def get_transaction
|
@@ -256,6 +281,7 @@ module Sentry
|
|
256
281
|
@level = :error
|
257
282
|
@fingerprint = []
|
258
283
|
@transaction_names = []
|
284
|
+
@transaction_sources = []
|
259
285
|
@event_processors = []
|
260
286
|
@rack_env = {}
|
261
287
|
@span = nil
|
data/lib/sentry/session.rb
CHANGED
@@ -2,7 +2,7 @@
|
|
2
2
|
|
3
3
|
module Sentry
|
4
4
|
class Session
|
5
|
-
attr_reader :started, :status
|
5
|
+
attr_reader :started, :status, :aggregation_key
|
6
6
|
|
7
7
|
# TODO-neel add :crashed after adding handled mechanism
|
8
8
|
STATUSES = %i(ok errored exited)
|
@@ -11,6 +11,10 @@ module Sentry
|
|
11
11
|
def initialize
|
12
12
|
@started = Sentry.utc_now
|
13
13
|
@status = :ok
|
14
|
+
|
15
|
+
# truncate seconds from the timestamp since we only care about
|
16
|
+
# minute level granularity for aggregation
|
17
|
+
@aggregation_key = Time.utc(@started.year, @started.month, @started.day, @started.hour, @started.min)
|
14
18
|
end
|
15
19
|
|
16
20
|
# TODO-neel add :crashed after adding handled mechanism
|
@@ -22,12 +26,6 @@ module Sentry
|
|
22
26
|
@status = :exited if @status == :ok
|
23
27
|
end
|
24
28
|
|
25
|
-
# truncate seconds from the timestamp since we only care about
|
26
|
-
# minute level granularity for aggregation
|
27
|
-
def aggregation_key
|
28
|
-
Time.utc(started.year, started.month, started.day, started.hour, started.min)
|
29
|
-
end
|
30
|
-
|
31
29
|
def deep_dup
|
32
30
|
dup
|
33
31
|
end
|
data/lib/sentry/span.rb
CHANGED
@@ -60,9 +60,10 @@ module Sentry
|
|
60
60
|
# The Transaction object the Span belongs to.
|
61
61
|
# Every span needs to be attached to a Transaction and their child spans will also inherit the same transaction.
|
62
62
|
# @return [Transaction]
|
63
|
-
|
63
|
+
attr_reader :transaction
|
64
64
|
|
65
65
|
def initialize(
|
66
|
+
transaction:,
|
66
67
|
description: nil,
|
67
68
|
op: nil,
|
68
69
|
status: nil,
|
@@ -79,6 +80,7 @@ module Sentry
|
|
79
80
|
@start_timestamp = start_timestamp || Sentry.utc_now.to_f
|
80
81
|
@timestamp = timestamp
|
81
82
|
@description = description
|
83
|
+
@transaction = transaction
|
82
84
|
@op = op
|
83
85
|
@status = status
|
84
86
|
@data = {}
|
@@ -104,6 +106,13 @@ module Sentry
|
|
104
106
|
"#{@trace_id}-#{@span_id}-#{sampled_flag}"
|
105
107
|
end
|
106
108
|
|
109
|
+
# Generates a W3C Baggage header string for distributed tracing
|
110
|
+
# from the incoming baggage stored on the transaction.
|
111
|
+
# @return [String, nil]
|
112
|
+
def to_baggage
|
113
|
+
transaction.get_baggage&.serialize
|
114
|
+
end
|
115
|
+
|
107
116
|
# @return [Hash]
|
108
117
|
def to_hash
|
109
118
|
{
|
@@ -136,9 +145,8 @@ module Sentry
|
|
136
145
|
# Starts a child span with given attributes.
|
137
146
|
# @param attributes [Hash] the attributes for the child span.
|
138
147
|
def start_child(**attributes)
|
139
|
-
attributes = attributes.dup.merge(trace_id: @trace_id, parent_span_id: @span_id, sampled: @sampled)
|
148
|
+
attributes = attributes.dup.merge(transaction: @transaction, trace_id: @trace_id, parent_span_id: @span_id, sampled: @sampled)
|
140
149
|
new_span = Span.new(**attributes)
|
141
|
-
new_span.transaction = transaction
|
142
150
|
new_span.span_recorder = span_recorder
|
143
151
|
|
144
152
|
if span_recorder
|
data/lib/sentry/test_helper.rb
CHANGED
@@ -31,6 +31,7 @@ module Sentry
|
|
31
31
|
|
32
32
|
test_client = Sentry::Client.new(copied_config)
|
33
33
|
Sentry.get_current_hub.bind_client(test_client)
|
34
|
+
Sentry.get_current_scope.clear
|
34
35
|
end
|
35
36
|
|
36
37
|
# Clears all stored events and envelopes.
|
@@ -41,6 +42,7 @@ module Sentry
|
|
41
42
|
|
42
43
|
sentry_transport.events = []
|
43
44
|
sentry_transport.envelopes = []
|
45
|
+
Sentry.get_current_scope.clear
|
44
46
|
end
|
45
47
|
|
46
48
|
# @return [Transport]
|
data/lib/sentry/transaction.rb
CHANGED
@@ -1,5 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require "sentry/baggage"
|
4
|
+
|
3
5
|
module Sentry
|
4
6
|
class Transaction < Span
|
5
7
|
SENTRY_TRACE_REGEXP = Regexp.new(
|
@@ -12,16 +14,29 @@ module Sentry
|
|
12
14
|
UNLABELD_NAME = "<unlabeled transaction>".freeze
|
13
15
|
MESSAGE_PREFIX = "[Tracing]"
|
14
16
|
|
17
|
+
# https://develop.sentry.dev/sdk/event-payloads/transaction/#transaction-annotations
|
18
|
+
SOURCES = %i(custom url route view component task)
|
19
|
+
|
15
20
|
include LoggingHelper
|
16
21
|
|
17
22
|
# The name of the transaction.
|
18
23
|
# @return [String]
|
19
24
|
attr_reader :name
|
20
25
|
|
26
|
+
# The source of the transaction name.
|
27
|
+
# @return [Symbol]
|
28
|
+
attr_reader :source
|
29
|
+
|
21
30
|
# The sampling decision of the parent transaction, which will be considered when making the current transaction's sampling decision.
|
22
31
|
# @return [String]
|
23
32
|
attr_reader :parent_sampled
|
24
33
|
|
34
|
+
# The parsed incoming W3C baggage header.
|
35
|
+
# This is only for accessing the current baggage variable.
|
36
|
+
# Please use the #get_baggage method for interfacing outside this class.
|
37
|
+
# @return [Baggage, nil]
|
38
|
+
attr_reader :baggage
|
39
|
+
|
25
40
|
# @deprecated Use Sentry.get_current_hub instead.
|
26
41
|
attr_reader :hub
|
27
42
|
|
@@ -31,18 +46,34 @@ module Sentry
|
|
31
46
|
# @deprecated Use Sentry.logger instead.
|
32
47
|
attr_reader :logger
|
33
48
|
|
34
|
-
|
35
|
-
|
49
|
+
# The effective sample rate at which this transaction was sampled.
|
50
|
+
# @return [Float, nil]
|
51
|
+
attr_reader :effective_sample_rate
|
52
|
+
|
53
|
+
def initialize(
|
54
|
+
hub:,
|
55
|
+
name: nil,
|
56
|
+
source: :custom,
|
57
|
+
parent_sampled: nil,
|
58
|
+
baggage: nil,
|
59
|
+
**options
|
60
|
+
)
|
61
|
+
super(transaction: self, **options)
|
36
62
|
|
37
63
|
@name = name
|
64
|
+
@source = SOURCES.include?(source) ? source.to_sym : :custom
|
38
65
|
@parent_sampled = parent_sampled
|
39
|
-
@transaction = self
|
40
66
|
@hub = hub
|
67
|
+
@baggage = baggage
|
41
68
|
@configuration = hub.configuration # to be removed
|
42
69
|
@tracing_enabled = hub.configuration.tracing_enabled?
|
43
70
|
@traces_sampler = hub.configuration.traces_sampler
|
44
71
|
@traces_sample_rate = hub.configuration.traces_sample_rate
|
45
72
|
@logger = hub.configuration.logger
|
73
|
+
@release = hub.configuration.release
|
74
|
+
@environment = hub.configuration.environment
|
75
|
+
@dsn = hub.configuration.dsn
|
76
|
+
@effective_sample_rate = nil
|
46
77
|
init_span_recorder
|
47
78
|
end
|
48
79
|
|
@@ -52,10 +83,11 @@ module Sentry
|
|
52
83
|
#
|
53
84
|
# The child transaction will also store the parent's sampling decision in its `parent_sampled` attribute.
|
54
85
|
# @param sentry_trace [String] the trace string from the previous transaction.
|
86
|
+
# @param baggage [String, nil] the incoming baggage header string.
|
55
87
|
# @param hub [Hub] the hub that'll be responsible for sending this transaction when it's finished.
|
56
88
|
# @param options [Hash] the options you want to use to initialize a Transaction instance.
|
57
89
|
# @return [Transaction, nil]
|
58
|
-
def self.from_sentry_trace(sentry_trace, hub: Sentry.get_current_hub, **options)
|
90
|
+
def self.from_sentry_trace(sentry_trace, baggage: nil, hub: Sentry.get_current_hub, **options)
|
59
91
|
return unless hub.configuration.tracing_enabled?
|
60
92
|
return unless sentry_trace
|
61
93
|
|
@@ -70,13 +102,38 @@ module Sentry
|
|
70
102
|
sampled_flag != "0"
|
71
103
|
end
|
72
104
|
|
73
|
-
|
105
|
+
baggage = if baggage && !baggage.empty?
|
106
|
+
Baggage.from_incoming_header(baggage)
|
107
|
+
else
|
108
|
+
# If there's an incoming sentry-trace but no incoming baggage header,
|
109
|
+
# for instance in traces coming from older SDKs,
|
110
|
+
# baggage will be empty and frozen and won't be populated as head SDK.
|
111
|
+
Baggage.new({})
|
112
|
+
end
|
113
|
+
|
114
|
+
baggage.freeze!
|
115
|
+
|
116
|
+
new(
|
117
|
+
trace_id: trace_id,
|
118
|
+
parent_span_id: parent_span_id,
|
119
|
+
parent_sampled: parent_sampled,
|
120
|
+
hub: hub,
|
121
|
+
baggage: baggage,
|
122
|
+
**options
|
123
|
+
)
|
74
124
|
end
|
75
125
|
|
76
126
|
# @return [Hash]
|
77
127
|
def to_hash
|
78
128
|
hash = super
|
79
|
-
|
129
|
+
|
130
|
+
hash.merge!(
|
131
|
+
name: @name,
|
132
|
+
source: @source,
|
133
|
+
sampled: @sampled,
|
134
|
+
parent_sampled: @parent_sampled
|
135
|
+
)
|
136
|
+
|
80
137
|
hash
|
81
138
|
end
|
82
139
|
|
@@ -103,7 +160,10 @@ module Sentry
|
|
103
160
|
return
|
104
161
|
end
|
105
162
|
|
106
|
-
|
163
|
+
unless @sampled.nil?
|
164
|
+
@effective_sample_rate = @sampled ? 1.0 : 0.0
|
165
|
+
return
|
166
|
+
end
|
107
167
|
|
108
168
|
sample_rate =
|
109
169
|
if @traces_sampler.is_a?(Proc)
|
@@ -116,7 +176,11 @@ module Sentry
|
|
116
176
|
|
117
177
|
transaction_description = generate_transaction_description
|
118
178
|
|
119
|
-
|
179
|
+
if [true, false].include?(sample_rate)
|
180
|
+
@effective_sample_rate = sample_rate ? 1.0 : 0.0
|
181
|
+
elsif sample_rate.is_a?(Numeric) && sample_rate >= 0.0 && sample_rate <= 1.0
|
182
|
+
@effective_sample_rate = sample_rate.to_f
|
183
|
+
else
|
120
184
|
@sampled = false
|
121
185
|
log_warn("#{MESSAGE_PREFIX} Discarding #{transaction_description} because of invalid sample_rate: #{sample_rate}")
|
122
186
|
return
|
@@ -172,6 +236,14 @@ module Sentry
|
|
172
236
|
end
|
173
237
|
end
|
174
238
|
|
239
|
+
# Get the existing frozen incoming baggage
|
240
|
+
# or populate one with sentry- items as the head SDK.
|
241
|
+
# @return [Baggage]
|
242
|
+
def get_baggage
|
243
|
+
populate_head_baggage if @baggage.nil? || @baggage.mutable
|
244
|
+
@baggage
|
245
|
+
end
|
246
|
+
|
175
247
|
protected
|
176
248
|
|
177
249
|
def init_span_recorder(limit = 1000)
|
@@ -188,6 +260,29 @@ module Sentry
|
|
188
260
|
result
|
189
261
|
end
|
190
262
|
|
263
|
+
def populate_head_baggage
|
264
|
+
items = {
|
265
|
+
"trace_id" => trace_id,
|
266
|
+
"sample_rate" => effective_sample_rate&.to_s,
|
267
|
+
"environment" => @environment,
|
268
|
+
"release" => @release,
|
269
|
+
"public_key" => @dsn&.public_key
|
270
|
+
}
|
271
|
+
|
272
|
+
items["transaction"] = name unless source_low_quality?
|
273
|
+
|
274
|
+
user = @hub.current_scope&.user
|
275
|
+
items["user_segment"] = user["segment"] if user && user["segment"]
|
276
|
+
|
277
|
+
items.compact!
|
278
|
+
@baggage = Baggage.new(items, mutable: false)
|
279
|
+
end
|
280
|
+
|
281
|
+
# These are high cardinality and thus bad
|
282
|
+
def source_low_quality?
|
283
|
+
source == :url
|
284
|
+
end
|
285
|
+
|
191
286
|
class SpanRecorder
|
192
287
|
attr_reader :max_length, :spans
|
193
288
|
|
@@ -8,9 +8,27 @@ module Sentry
|
|
8
8
|
# @return [<Array[Span]>]
|
9
9
|
attr_accessor :spans
|
10
10
|
|
11
|
+
# @return [Hash, nil]
|
12
|
+
attr_accessor :dynamic_sampling_context
|
13
|
+
|
11
14
|
# @return [Float, nil]
|
12
15
|
attr_reader :start_timestamp
|
13
16
|
|
17
|
+
def initialize(transaction:, **options)
|
18
|
+
super(**options)
|
19
|
+
|
20
|
+
self.transaction = transaction.name
|
21
|
+
self.transaction_info = { source: transaction.source }
|
22
|
+
self.contexts.merge!(trace: transaction.get_trace_context)
|
23
|
+
self.timestamp = transaction.timestamp
|
24
|
+
self.start_timestamp = transaction.start_timestamp
|
25
|
+
self.tags = transaction.tags
|
26
|
+
self.dynamic_sampling_context = transaction.get_baggage.dynamic_sampling_context
|
27
|
+
|
28
|
+
finished_spans = transaction.span_recorder.spans.select { |span| span.timestamp && span != transaction }
|
29
|
+
self.spans = finished_spans.map(&:to_hash)
|
30
|
+
end
|
31
|
+
|
14
32
|
# Sets the event's start_timestamp.
|
15
33
|
# @param time [Time, Float]
|
16
34
|
# @return [void]
|
data/lib/sentry/transport.rb
CHANGED
@@ -136,14 +136,18 @@ module Sentry
|
|
136
136
|
event_id = event_payload[:event_id] || event_payload["event_id"]
|
137
137
|
item_type = event_payload[:type] || event_payload["type"]
|
138
138
|
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
)
|
139
|
+
envelope_headers = {
|
140
|
+
event_id: event_id,
|
141
|
+
dsn: @dsn.to_s,
|
142
|
+
sdk: Sentry.sdk_meta,
|
143
|
+
sent_at: Sentry.utc_now.iso8601
|
144
|
+
}
|
145
|
+
|
146
|
+
if event.is_a?(TransactionEvent) && event.dynamic_sampling_context
|
147
|
+
envelope_headers[:trace] = event.dynamic_sampling_context
|
148
|
+
end
|
149
|
+
|
150
|
+
envelope = Envelope.new(envelope_headers)
|
147
151
|
|
148
152
|
envelope.add_item(
|
149
153
|
{ type: item_type, content_type: 'application/json' },
|
@@ -0,0 +1,22 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Sentry
|
4
|
+
module Utils
|
5
|
+
module EncodingHelper
|
6
|
+
def self.encode_to_utf_8(value)
|
7
|
+
if value.encoding != Encoding::UTF_8 && value.respond_to?(:force_encoding)
|
8
|
+
value = value.dup.force_encoding(Encoding::UTF_8)
|
9
|
+
end
|
10
|
+
|
11
|
+
value = value.scrub unless value.valid_encoding?
|
12
|
+
value
|
13
|
+
end
|
14
|
+
|
15
|
+
def self.valid_utf_8?(value)
|
16
|
+
return true unless value.respond_to?(:force_encoding)
|
17
|
+
|
18
|
+
value.dup.force_encoding(Encoding::UTF_8).valid_encoding?
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
data/lib/sentry/version.rb
CHANGED
data/lib/sentry-ruby.rb
CHANGED
@@ -8,6 +8,7 @@ require "sentry/version"
|
|
8
8
|
require "sentry/exceptions"
|
9
9
|
require "sentry/core_ext/object/deep_dup"
|
10
10
|
require "sentry/utils/argument_checking_helper"
|
11
|
+
require "sentry/utils/encoding_helper"
|
11
12
|
require "sentry/utils/logging_helper"
|
12
13
|
require "sentry/configuration"
|
13
14
|
require "sentry/logger"
|
@@ -39,6 +40,8 @@ module Sentry
|
|
39
40
|
|
40
41
|
SENTRY_TRACE_HEADER_NAME = "sentry-trace".freeze
|
41
42
|
|
43
|
+
BAGGAGE_HEADER_NAME = "baggage".freeze
|
44
|
+
|
42
45
|
THREAD_LOCAL = :sentry_hub
|
43
46
|
|
44
47
|
class << self
|
@@ -348,7 +351,7 @@ module Sentry
|
|
348
351
|
# @yieldparam scope [Scope]
|
349
352
|
# @return [void]
|
350
353
|
def with_scope(&block)
|
351
|
-
return unless initialized?
|
354
|
+
return yield unless initialized?
|
352
355
|
get_current_hub.with_scope(&block)
|
353
356
|
end
|
354
357
|
|
@@ -439,22 +442,8 @@ module Sentry
|
|
439
442
|
# end
|
440
443
|
#
|
441
444
|
def with_child_span(**attributes, &block)
|
442
|
-
|
443
|
-
|
444
|
-
|
445
|
-
begin
|
446
|
-
current_span.with_child_span(**attributes) do |child_span|
|
447
|
-
get_current_scope.set_span(child_span)
|
448
|
-
result = yield(child_span)
|
449
|
-
end
|
450
|
-
ensure
|
451
|
-
get_current_scope.set_span(current_span)
|
452
|
-
end
|
453
|
-
|
454
|
-
result
|
455
|
-
else
|
456
|
-
yield(nil)
|
457
|
-
end
|
445
|
+
return yield(nil) unless Sentry.initialized?
|
446
|
+
get_current_hub.with_child_span(**attributes, &block)
|
458
447
|
end
|
459
448
|
|
460
449
|
# Returns the id of the lastly reported Sentry::Event.
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: sentry-ruby
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 5.
|
4
|
+
version: 5.6.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Sentry Team
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2022-08
|
11
|
+
date: 2022-11-08 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: concurrent-ruby
|
@@ -53,6 +53,7 @@ files:
|
|
53
53
|
- lib/sentry-ruby.rb
|
54
54
|
- lib/sentry/background_worker.rb
|
55
55
|
- lib/sentry/backtrace.rb
|
56
|
+
- lib/sentry/baggage.rb
|
56
57
|
- lib/sentry/breadcrumb.rb
|
57
58
|
- lib/sentry/breadcrumb/sentry_logger.rb
|
58
59
|
- lib/sentry/breadcrumb_buffer.rb
|
@@ -95,6 +96,7 @@ files:
|
|
95
96
|
- lib/sentry/transport/http_transport.rb
|
96
97
|
- lib/sentry/utils/argument_checking_helper.rb
|
97
98
|
- lib/sentry/utils/custom_inspection.rb
|
99
|
+
- lib/sentry/utils/encoding_helper.rb
|
98
100
|
- lib/sentry/utils/exception_cause_chain.rb
|
99
101
|
- lib/sentry/utils/logging_helper.rb
|
100
102
|
- lib/sentry/utils/real_ip.rb
|