sentry-ruby 5.4.2 → 5.7.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/README.md +3 -0
- data/lib/sentry/baggage.rb +81 -0
- data/lib/sentry/client.rb +17 -10
- data/lib/sentry/configuration.rb +11 -0
- data/lib/sentry/event.rb +1 -1
- data/lib/sentry/hub.rb +22 -1
- 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 +15 -9
- data/lib/sentry/test_helper.rb +2 -0
- data/lib/sentry/transaction.rb +144 -19
- data/lib/sentry/transaction_event.rb +19 -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: a60b754ccac4d1fd8cf47cd94d8896c46dc8232f87d06132681a19ed223ac14c
|
4
|
+
data.tar.gz: 923f906cd698d18f2fcb419bfeb2d1262c4165e6a8ffe2484de7027a90e69644
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 7d46f719bd4e0abb6f9fcd254b6e6321c9796c2278bc31fa5f86dc541cc0ce54a4f6cc10078d6d0277c89ff0cd4896e2a0ec0aa04e3052214b01b85cb2aa115c
|
7
|
+
data.tar.gz: 14b28401b32d9ce9ee7ac0351de736d0d9f11221010e61091e4976b71a58b1fef61ca4e282ec31a714e040148197c5fab46f0a0e39e475ea1f6dd0196b5f104d
|
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'
|
data/README.md
CHANGED
@@ -20,6 +20,7 @@ Sentry SDK for Ruby
|
|
20
20
|
| [](https://rubygems.org/gems/sentry-sidekiq) | [](https://github.com/getsentry/sentry-ruby/actions/workflows/sentry_sidekiq_test.yml) | [](https://codecov.io/gh/getsentry/sentry-ruby/branch/master) | [](https://rubygems.org/gems/sentry-sidekiq/) |
|
21
21
|
| [](https://rubygems.org/gems/sentry-delayed_job) | [](https://github.com/getsentry/sentry-ruby/actions/workflows/sentry_delayed_job_test.yml) | [](https://codecov.io/gh/getsentry/sentry-ruby/branch/master) | [](https://rubygems.org/gems/sentry-delayed_job/) |
|
22
22
|
| [](https://rubygems.org/gems/sentry-resque) | [](https://github.com/getsentry/sentry-ruby/actions/workflows/sentry_resque_test.yml) | [](https://codecov.io/gh/getsentry/sentry-ruby/branch/master) | [](https://rubygems.org/gems/sentry-resque/) |
|
23
|
+
| [](https://rubygems.org/gems/sentry-opentelemetry) | [](https://github.com/getsentry/sentry-ruby/actions/workflows/sentry_opentelemetry_test.yml) | [](https://codecov.io/gh/getsentry/sentry-ruby/branch/master) | [](https://rubygems.org/gems/sentry-opentelemetry/) |
|
23
24
|
|
24
25
|
|
25
26
|
|
@@ -51,6 +52,7 @@ gem "sentry-rails"
|
|
51
52
|
gem "sentry-sidekiq"
|
52
53
|
gem "sentry-delayed_job"
|
53
54
|
gem "sentry-resque"
|
55
|
+
gem "sentry-opentelemetry"
|
54
56
|
```
|
55
57
|
|
56
58
|
### Configuration
|
@@ -88,6 +90,7 @@ To learn more about sampling transactions, please visit the [official documentat
|
|
88
90
|
- [Sidekiq](https://docs.sentry.io/platforms/ruby/guides/sidekiq/)
|
89
91
|
- [DelayedJob](https://docs.sentry.io/platforms/ruby/guides/delayed_job/)
|
90
92
|
- [Resque](https://docs.sentry.io/platforms/ruby/guides/resque/)
|
93
|
+
- [OpenTemeletry](https://docs.sentry.io/platforms/ruby/performance/instrumentation/opentelemetry/)
|
91
94
|
|
92
95
|
### Enriching Events
|
93
96
|
|
@@ -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/configuration.rb
CHANGED
@@ -211,6 +211,10 @@ module Sentry
|
|
211
211
|
# @return [Boolean]
|
212
212
|
attr_accessor :auto_session_tracking
|
213
213
|
|
214
|
+
# The instrumenter to use, :sentry or :otel
|
215
|
+
# @return [Symbol]
|
216
|
+
attr_reader :instrumenter
|
217
|
+
|
214
218
|
# these are not config options
|
215
219
|
# @!visibility private
|
216
220
|
attr_reader :errors, :gem_specs
|
@@ -237,6 +241,8 @@ module Sentry
|
|
237
241
|
MODULE_SEPARATOR = "::".freeze
|
238
242
|
SKIP_INSPECTION_ATTRIBUTES = [:@linecache, :@stacktrace_builder]
|
239
243
|
|
244
|
+
INSTRUMENTERS = [:sentry, :otel]
|
245
|
+
|
240
246
|
# Post initialization callbacks are called at the end of initialization process
|
241
247
|
# allowing extending the configuration of sentry-ruby by multiple extensions
|
242
248
|
@@post_initialization_callbacks = []
|
@@ -269,6 +275,7 @@ module Sentry
|
|
269
275
|
self.trusted_proxies = []
|
270
276
|
self.dsn = ENV['SENTRY_DSN']
|
271
277
|
self.server_name = server_name_from_env
|
278
|
+
self.instrumenter = :sentry
|
272
279
|
|
273
280
|
self.before_send = nil
|
274
281
|
self.rack_env_whitelist = RACK_ENV_WHITELIST_DEFAULT
|
@@ -332,6 +339,10 @@ module Sentry
|
|
332
339
|
@environment = environment.to_s
|
333
340
|
end
|
334
341
|
|
342
|
+
def instrumenter=(instrumenter)
|
343
|
+
@instrumenter = INSTRUMENTERS.include?(instrumenter) ? instrumenter : :sentry
|
344
|
+
end
|
345
|
+
|
335
346
|
def sending_allowed?
|
336
347
|
@errors = []
|
337
348
|
|
data/lib/sentry/event.rb
CHANGED
data/lib/sentry/hub.rb
CHANGED
@@ -76,8 +76,9 @@ module Sentry
|
|
76
76
|
@stack.pop
|
77
77
|
end
|
78
78
|
|
79
|
-
def start_transaction(transaction: nil, custom_sampling_context: {}, **options)
|
79
|
+
def start_transaction(transaction: nil, custom_sampling_context: {}, instrumenter: :sentry, **options)
|
80
80
|
return unless configuration.tracing_enabled?
|
81
|
+
return unless instrumenter == configuration.instrumenter
|
81
82
|
|
82
83
|
transaction ||= Transaction.new(**options.merge(hub: self))
|
83
84
|
|
@@ -92,6 +93,26 @@ module Sentry
|
|
92
93
|
transaction
|
93
94
|
end
|
94
95
|
|
96
|
+
def with_child_span(instrumenter: :sentry, **attributes, &block)
|
97
|
+
return yield(nil) unless instrumenter == configuration.instrumenter
|
98
|
+
|
99
|
+
current_span = current_scope.get_span
|
100
|
+
return yield(nil) unless current_span
|
101
|
+
|
102
|
+
result = nil
|
103
|
+
|
104
|
+
begin
|
105
|
+
current_span.with_child_span(**attributes) do |child_span|
|
106
|
+
current_scope.set_span(child_span)
|
107
|
+
result = yield(child_span)
|
108
|
+
end
|
109
|
+
ensure
|
110
|
+
current_scope.set_span(current_span)
|
111
|
+
end
|
112
|
+
|
113
|
+
result
|
114
|
+
end
|
115
|
+
|
95
116
|
def capture_exception(exception, **options, &block)
|
96
117
|
check_argument_type!(exception, ::Exception)
|
97
118
|
|
@@ -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,25 +60,28 @@ 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,
|
69
70
|
trace_id: nil,
|
71
|
+
span_id: nil,
|
70
72
|
parent_span_id: nil,
|
71
73
|
sampled: nil,
|
72
74
|
start_timestamp: nil,
|
73
75
|
timestamp: nil
|
74
76
|
)
|
75
77
|
@trace_id = trace_id || SecureRandom.uuid.delete("-")
|
76
|
-
@span_id = SecureRandom.hex(8)
|
78
|
+
@span_id = span_id || SecureRandom.hex(8)
|
77
79
|
@parent_span_id = parent_span_id
|
78
80
|
@sampled = sampled
|
79
81
|
@start_timestamp = start_timestamp || Sentry.utc_now.to_f
|
80
82
|
@timestamp = timestamp
|
81
83
|
@description = description
|
84
|
+
@transaction = transaction
|
82
85
|
@op = op
|
83
86
|
@status = status
|
84
87
|
@data = {}
|
@@ -87,11 +90,8 @@ module Sentry
|
|
87
90
|
|
88
91
|
# Finishes the span by adding a timestamp.
|
89
92
|
# @return [self]
|
90
|
-
def finish
|
91
|
-
|
92
|
-
return if @timestamp
|
93
|
-
|
94
|
-
@timestamp = Sentry.utc_now.to_f
|
93
|
+
def finish(end_timestamp: nil)
|
94
|
+
@timestamp = end_timestamp || @timestamp || Sentry.utc_now.to_f
|
95
95
|
self
|
96
96
|
end
|
97
97
|
|
@@ -104,6 +104,13 @@ module Sentry
|
|
104
104
|
"#{@trace_id}-#{@span_id}-#{sampled_flag}"
|
105
105
|
end
|
106
106
|
|
107
|
+
# Generates a W3C Baggage header string for distributed tracing
|
108
|
+
# from the incoming baggage stored on the transaction.
|
109
|
+
# @return [String, nil]
|
110
|
+
def to_baggage
|
111
|
+
transaction.get_baggage&.serialize
|
112
|
+
end
|
113
|
+
|
107
114
|
# @return [Hash]
|
108
115
|
def to_hash
|
109
116
|
{
|
@@ -136,9 +143,8 @@ module Sentry
|
|
136
143
|
# Starts a child span with given attributes.
|
137
144
|
# @param attributes [Hash] the attributes for the child span.
|
138
145
|
def start_child(**attributes)
|
139
|
-
attributes = attributes.dup.merge(trace_id: @trace_id, parent_span_id: @span_id, sampled: @sampled)
|
146
|
+
attributes = attributes.dup.merge(transaction: @transaction, trace_id: @trace_id, parent_span_id: @span_id, sampled: @sampled)
|
140
147
|
new_span = Span.new(**attributes)
|
141
|
-
new_span.transaction = transaction
|
142
148
|
new_span.span_recorder = span_recorder
|
143
149
|
|
144
150
|
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,38 @@ 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
|
36
52
|
|
37
|
-
|
53
|
+
# Additional contexts stored directly on the transaction object.
|
54
|
+
# @return [Hash]
|
55
|
+
attr_reader :contexts
|
56
|
+
|
57
|
+
def initialize(
|
58
|
+
hub:,
|
59
|
+
name: nil,
|
60
|
+
source: :custom,
|
61
|
+
parent_sampled: nil,
|
62
|
+
baggage: nil,
|
63
|
+
**options
|
64
|
+
)
|
65
|
+
super(transaction: self, **options)
|
66
|
+
|
67
|
+
set_name(name, source: source)
|
38
68
|
@parent_sampled = parent_sampled
|
39
|
-
@transaction = self
|
40
69
|
@hub = hub
|
70
|
+
@baggage = baggage
|
41
71
|
@configuration = hub.configuration # to be removed
|
42
72
|
@tracing_enabled = hub.configuration.tracing_enabled?
|
43
73
|
@traces_sampler = hub.configuration.traces_sampler
|
44
74
|
@traces_sample_rate = hub.configuration.traces_sample_rate
|
45
75
|
@logger = hub.configuration.logger
|
76
|
+
@release = hub.configuration.release
|
77
|
+
@environment = hub.configuration.environment
|
78
|
+
@dsn = hub.configuration.dsn
|
79
|
+
@effective_sample_rate = nil
|
80
|
+
@contexts = {}
|
46
81
|
init_span_recorder
|
47
82
|
end
|
48
83
|
|
@@ -52,31 +87,65 @@ module Sentry
|
|
52
87
|
#
|
53
88
|
# The child transaction will also store the parent's sampling decision in its `parent_sampled` attribute.
|
54
89
|
# @param sentry_trace [String] the trace string from the previous transaction.
|
90
|
+
# @param baggage [String, nil] the incoming baggage header string.
|
55
91
|
# @param hub [Hub] the hub that'll be responsible for sending this transaction when it's finished.
|
56
92
|
# @param options [Hash] the options you want to use to initialize a Transaction instance.
|
57
93
|
# @return [Transaction, nil]
|
58
|
-
def self.from_sentry_trace(sentry_trace, hub: Sentry.get_current_hub, **options)
|
94
|
+
def self.from_sentry_trace(sentry_trace, baggage: nil, hub: Sentry.get_current_hub, **options)
|
59
95
|
return unless hub.configuration.tracing_enabled?
|
60
96
|
return unless sentry_trace
|
61
97
|
|
98
|
+
sentry_trace_data = extract_sentry_trace(sentry_trace)
|
99
|
+
return unless sentry_trace_data
|
100
|
+
|
101
|
+
trace_id, parent_span_id, parent_sampled = sentry_trace_data
|
102
|
+
|
103
|
+
baggage = if baggage && !baggage.empty?
|
104
|
+
Baggage.from_incoming_header(baggage)
|
105
|
+
else
|
106
|
+
# If there's an incoming sentry-trace but no incoming baggage header,
|
107
|
+
# for instance in traces coming from older SDKs,
|
108
|
+
# baggage will be empty and frozen and won't be populated as head SDK.
|
109
|
+
Baggage.new({})
|
110
|
+
end
|
111
|
+
|
112
|
+
baggage.freeze!
|
113
|
+
|
114
|
+
new(
|
115
|
+
trace_id: trace_id,
|
116
|
+
parent_span_id: parent_span_id,
|
117
|
+
parent_sampled: parent_sampled,
|
118
|
+
hub: hub,
|
119
|
+
baggage: baggage,
|
120
|
+
**options
|
121
|
+
)
|
122
|
+
end
|
123
|
+
|
124
|
+
# Extract the trace_id, parent_span_id and parent_sampled values from a sentry-trace header.
|
125
|
+
#
|
126
|
+
# @param sentry_trace [String] the sentry-trace header value from the previous transaction.
|
127
|
+
# @return [Array, nil]
|
128
|
+
def self.extract_sentry_trace(sentry_trace)
|
62
129
|
match = SENTRY_TRACE_REGEXP.match(sentry_trace)
|
63
|
-
return if match.nil?
|
64
|
-
trace_id, parent_span_id, sampled_flag = match[1..3]
|
130
|
+
return nil if match.nil?
|
65
131
|
|
66
|
-
|
67
|
-
|
68
|
-
nil
|
69
|
-
else
|
70
|
-
sampled_flag != "0"
|
71
|
-
end
|
132
|
+
trace_id, parent_span_id, sampled_flag = match[1..3]
|
133
|
+
parent_sampled = sampled_flag.nil? ? nil : sampled_flag != "0"
|
72
134
|
|
73
|
-
|
135
|
+
[trace_id, parent_span_id, parent_sampled]
|
74
136
|
end
|
75
137
|
|
76
138
|
# @return [Hash]
|
77
139
|
def to_hash
|
78
140
|
hash = super
|
79
|
-
|
141
|
+
|
142
|
+
hash.merge!(
|
143
|
+
name: @name,
|
144
|
+
source: @source,
|
145
|
+
sampled: @sampled,
|
146
|
+
parent_sampled: @parent_sampled
|
147
|
+
)
|
148
|
+
|
80
149
|
hash
|
81
150
|
end
|
82
151
|
|
@@ -103,7 +172,10 @@ module Sentry
|
|
103
172
|
return
|
104
173
|
end
|
105
174
|
|
106
|
-
|
175
|
+
unless @sampled.nil?
|
176
|
+
@effective_sample_rate = @sampled ? 1.0 : 0.0
|
177
|
+
return
|
178
|
+
end
|
107
179
|
|
108
180
|
sample_rate =
|
109
181
|
if @traces_sampler.is_a?(Proc)
|
@@ -116,7 +188,11 @@ module Sentry
|
|
116
188
|
|
117
189
|
transaction_description = generate_transaction_description
|
118
190
|
|
119
|
-
|
191
|
+
if [true, false].include?(sample_rate)
|
192
|
+
@effective_sample_rate = sample_rate ? 1.0 : 0.0
|
193
|
+
elsif sample_rate.is_a?(Numeric) && sample_rate >= 0.0 && sample_rate <= 1.0
|
194
|
+
@effective_sample_rate = sample_rate.to_f
|
195
|
+
else
|
120
196
|
@sampled = false
|
121
197
|
log_warn("#{MESSAGE_PREFIX} Discarding #{transaction_description} because of invalid sample_rate: #{sample_rate}")
|
122
198
|
return
|
@@ -146,7 +222,7 @@ module Sentry
|
|
146
222
|
# Finishes the transaction's recording and send it to Sentry.
|
147
223
|
# @param hub [Hub] the hub that'll send this transaction. (Deprecated)
|
148
224
|
# @return [TransactionEvent]
|
149
|
-
def finish(hub: nil)
|
225
|
+
def finish(hub: nil, end_timestamp: nil)
|
150
226
|
if hub
|
151
227
|
log_warn(
|
152
228
|
<<~MSG
|
@@ -158,7 +234,7 @@ module Sentry
|
|
158
234
|
|
159
235
|
hub ||= @hub
|
160
236
|
|
161
|
-
super()
|
237
|
+
super(end_timestamp: end_timestamp)
|
162
238
|
|
163
239
|
if @name.nil?
|
164
240
|
@name = UNLABELD_NAME
|
@@ -172,6 +248,32 @@ module Sentry
|
|
172
248
|
end
|
173
249
|
end
|
174
250
|
|
251
|
+
# Get the existing frozen incoming baggage
|
252
|
+
# or populate one with sentry- items as the head SDK.
|
253
|
+
# @return [Baggage]
|
254
|
+
def get_baggage
|
255
|
+
populate_head_baggage if @baggage.nil? || @baggage.mutable
|
256
|
+
@baggage
|
257
|
+
end
|
258
|
+
|
259
|
+
# Set the transaction name directly.
|
260
|
+
# Considered internal api since it bypasses the usual scope logic.
|
261
|
+
# @param name [String]
|
262
|
+
# @param source [Symbol]
|
263
|
+
# @return [void]
|
264
|
+
def set_name(name, source: :custom)
|
265
|
+
@name = name
|
266
|
+
@source = SOURCES.include?(source) ? source.to_sym : :custom
|
267
|
+
end
|
268
|
+
|
269
|
+
# Set contexts directly on the transaction.
|
270
|
+
# @param key [String, Symbol]
|
271
|
+
# @param value [Object]
|
272
|
+
# @return [void]
|
273
|
+
def set_context(key, value)
|
274
|
+
@contexts[key] = value
|
275
|
+
end
|
276
|
+
|
175
277
|
protected
|
176
278
|
|
177
279
|
def init_span_recorder(limit = 1000)
|
@@ -188,6 +290,29 @@ module Sentry
|
|
188
290
|
result
|
189
291
|
end
|
190
292
|
|
293
|
+
def populate_head_baggage
|
294
|
+
items = {
|
295
|
+
"trace_id" => trace_id,
|
296
|
+
"sample_rate" => effective_sample_rate&.to_s,
|
297
|
+
"environment" => @environment,
|
298
|
+
"release" => @release,
|
299
|
+
"public_key" => @dsn&.public_key
|
300
|
+
}
|
301
|
+
|
302
|
+
items["transaction"] = name unless source_low_quality?
|
303
|
+
|
304
|
+
user = @hub.current_scope&.user
|
305
|
+
items["user_segment"] = user["segment"] if user && user["segment"]
|
306
|
+
|
307
|
+
items.compact!
|
308
|
+
@baggage = Baggage.new(items, mutable: false)
|
309
|
+
end
|
310
|
+
|
311
|
+
# These are high cardinality and thus bad
|
312
|
+
def source_low_quality?
|
313
|
+
source == :url
|
314
|
+
end
|
315
|
+
|
191
316
|
class SpanRecorder
|
192
317
|
attr_reader :max_length, :spans
|
193
318
|
|
@@ -8,9 +8,28 @@ 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!(transaction.contexts)
|
23
|
+
self.contexts.merge!(trace: transaction.get_trace_context)
|
24
|
+
self.timestamp = transaction.timestamp
|
25
|
+
self.start_timestamp = transaction.start_timestamp
|
26
|
+
self.tags = transaction.tags
|
27
|
+
self.dynamic_sampling_context = transaction.get_baggage.dynamic_sampling_context
|
28
|
+
|
29
|
+
finished_spans = transaction.span_recorder.spans.select { |span| span.timestamp && span != transaction }
|
30
|
+
self.spans = finished_spans.map(&:to_hash)
|
31
|
+
end
|
32
|
+
|
14
33
|
# Sets the event's start_timestamp.
|
15
34
|
# @param time [Time, Float]
|
16
35
|
# @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.7.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-
|
11
|
+
date: 2022-12-01 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
|