sentry-ruby 5.4.2 → 5.10.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/.rspec +0 -1
- data/Gemfile +17 -7
- data/README.md +3 -0
- data/Rakefile +8 -1
- data/lib/sentry/backtrace.rb +1 -1
- data/lib/sentry/baggage.rb +70 -0
- data/lib/sentry/client.rb +31 -11
- data/lib/sentry/configuration.rb +128 -26
- data/lib/sentry/envelope.rb +1 -4
- data/lib/sentry/event.rb +1 -1
- data/lib/sentry/hub.rb +31 -3
- data/lib/sentry/interfaces/request.rb +6 -16
- data/lib/sentry/interfaces/single_exception.rb +9 -1
- data/lib/sentry/net/http.rb +26 -38
- data/lib/sentry/profiler.rb +222 -0
- data/lib/sentry/puma.rb +25 -0
- data/lib/sentry/rack/capture_exceptions.rb +6 -4
- data/lib/sentry/rake.rb +1 -1
- data/lib/sentry/redis.rb +36 -23
- data/lib/sentry/scope.rb +55 -6
- data/lib/sentry/session.rb +5 -7
- data/lib/sentry/span.rb +19 -9
- data/lib/sentry/test_helper.rb +3 -1
- data/lib/sentry/transaction.rb +173 -19
- data/lib/sentry/transaction_event.rb +54 -0
- data/lib/sentry/transport.rb +19 -8
- data/lib/sentry/utils/argument_checking_helper.rb +3 -3
- data/lib/sentry/utils/encoding_helper.rb +22 -0
- data/lib/sentry/version.rb +1 -1
- data/lib/sentry-ruby.rb +38 -21
- metadata +6 -3
- data/CODE_OF_CONDUCT.md +0 -74
@@ -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,24 @@ 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?
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
29
|
+
return super unless started? && Sentry.initialized?
|
30
|
+
return super if from_sentry_sdk?
|
31
|
+
|
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)
|
34
|
+
|
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('url', request_info[:url])
|
42
|
+
sentry_span.set_data('http.method', request_info[:method])
|
43
|
+
sentry_span.set_data('http.query', request_info[:query]) if request_info[:query]
|
44
|
+
sentry_span.set_data('status', res.code.to_i)
|
45
|
+
end
|
46
|
+
end
|
37
47
|
end
|
38
48
|
end
|
39
49
|
|
@@ -42,13 +52,17 @@ module Sentry
|
|
42
52
|
def set_sentry_trace_header(req, sentry_span)
|
43
53
|
return unless sentry_span
|
44
54
|
|
45
|
-
|
55
|
+
client = Sentry.get_current_client
|
56
|
+
|
57
|
+
trace = client.generate_sentry_trace(sentry_span)
|
46
58
|
req[SENTRY_TRACE_HEADER_NAME] = trace if trace
|
59
|
+
|
60
|
+
baggage = client.generate_baggage(sentry_span)
|
61
|
+
req[BAGGAGE_HEADER_NAME] = baggage if baggage && !baggage.empty?
|
47
62
|
end
|
48
63
|
|
49
64
|
def record_sentry_breadcrumb(req, res)
|
50
65
|
return unless Sentry.initialized? && Sentry.configuration.breadcrumbs_logger.include?(:http_logger)
|
51
|
-
return if from_sentry_sdk?
|
52
66
|
|
53
67
|
request_info = extract_request_info(req)
|
54
68
|
|
@@ -64,29 +78,6 @@ module Sentry
|
|
64
78
|
Sentry.add_breadcrumb(crumb)
|
65
79
|
end
|
66
80
|
|
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
81
|
def from_sentry_sdk?
|
91
82
|
dsn = Sentry.configuration.dsn
|
92
83
|
dsn && dsn.host == self.address
|
@@ -99,7 +90,7 @@ module Sentry
|
|
99
90
|
result = { method: req.method, url: url }
|
100
91
|
|
101
92
|
if Sentry.configuration.send_default_pii
|
102
|
-
result[:
|
93
|
+
result[:query] = uri.query
|
103
94
|
result[:body] = req.body
|
104
95
|
end
|
105
96
|
|
@@ -109,7 +100,4 @@ module Sentry
|
|
109
100
|
end
|
110
101
|
end
|
111
102
|
|
112
|
-
Sentry.register_patch
|
113
|
-
patch = Sentry::Net::HTTP
|
114
|
-
Net::HTTP.send(:prepend, patch) unless Net::HTTP.ancestors.include?(patch)
|
115
|
-
end
|
103
|
+
Sentry.register_patch(Sentry::Net::HTTP, Net::HTTP)
|
@@ -0,0 +1,222 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'securerandom'
|
4
|
+
|
5
|
+
module Sentry
|
6
|
+
class Profiler
|
7
|
+
VERSION = '1'
|
8
|
+
PLATFORM = 'ruby'
|
9
|
+
# 101 Hz in microseconds
|
10
|
+
DEFAULT_INTERVAL = 1e6 / 101
|
11
|
+
MICRO_TO_NANO_SECONDS = 1e3
|
12
|
+
|
13
|
+
attr_reader :sampled, :started, :event_id
|
14
|
+
|
15
|
+
def initialize(configuration)
|
16
|
+
@event_id = SecureRandom.uuid.delete('-')
|
17
|
+
@started = false
|
18
|
+
@sampled = nil
|
19
|
+
|
20
|
+
@profiling_enabled = defined?(StackProf) && configuration.profiling_enabled?
|
21
|
+
@profiles_sample_rate = configuration.profiles_sample_rate
|
22
|
+
@project_root = configuration.project_root
|
23
|
+
@app_dirs_pattern = configuration.app_dirs_pattern || Backtrace::APP_DIRS_PATTERN
|
24
|
+
@in_app_pattern = Regexp.new("^(#{@project_root}/)?#{@app_dirs_pattern}")
|
25
|
+
end
|
26
|
+
|
27
|
+
def start
|
28
|
+
return unless @sampled
|
29
|
+
|
30
|
+
@started = StackProf.start(interval: DEFAULT_INTERVAL,
|
31
|
+
mode: :wall,
|
32
|
+
raw: true,
|
33
|
+
aggregate: false)
|
34
|
+
|
35
|
+
@started ? log('Started') : log('Not started since running elsewhere')
|
36
|
+
end
|
37
|
+
|
38
|
+
def stop
|
39
|
+
return unless @sampled
|
40
|
+
return unless @started
|
41
|
+
|
42
|
+
StackProf.stop
|
43
|
+
log('Stopped')
|
44
|
+
end
|
45
|
+
|
46
|
+
# Sets initial sampling decision of the profile.
|
47
|
+
# @return [void]
|
48
|
+
def set_initial_sample_decision(transaction_sampled)
|
49
|
+
unless @profiling_enabled
|
50
|
+
@sampled = false
|
51
|
+
return
|
52
|
+
end
|
53
|
+
|
54
|
+
unless transaction_sampled
|
55
|
+
@sampled = false
|
56
|
+
log('Discarding profile because transaction not sampled')
|
57
|
+
return
|
58
|
+
end
|
59
|
+
|
60
|
+
case @profiles_sample_rate
|
61
|
+
when 0.0
|
62
|
+
@sampled = false
|
63
|
+
log('Discarding profile because sample_rate is 0')
|
64
|
+
return
|
65
|
+
when 1.0
|
66
|
+
@sampled = true
|
67
|
+
return
|
68
|
+
else
|
69
|
+
@sampled = Random.rand < @profiles_sample_rate
|
70
|
+
end
|
71
|
+
|
72
|
+
log('Discarding profile due to sampling decision') unless @sampled
|
73
|
+
end
|
74
|
+
|
75
|
+
def to_hash
|
76
|
+
return {} unless @sampled
|
77
|
+
return {} unless @started
|
78
|
+
|
79
|
+
results = StackProf.results
|
80
|
+
return {} unless results
|
81
|
+
return {} if results.empty?
|
82
|
+
return {} if results[:samples] == 0
|
83
|
+
return {} unless results[:raw]
|
84
|
+
|
85
|
+
frame_map = {}
|
86
|
+
|
87
|
+
frames = results[:frames].to_enum.with_index.map do |frame, idx|
|
88
|
+
frame_id, frame_data = frame
|
89
|
+
|
90
|
+
# need to map over stackprof frame ids to ours
|
91
|
+
frame_map[frame_id] = idx
|
92
|
+
|
93
|
+
file_path = frame_data[:file]
|
94
|
+
in_app = in_app?(file_path)
|
95
|
+
filename = compute_filename(file_path, in_app)
|
96
|
+
function, mod = split_module(frame_data[:name])
|
97
|
+
|
98
|
+
frame_hash = {
|
99
|
+
abs_path: file_path,
|
100
|
+
function: function,
|
101
|
+
filename: filename,
|
102
|
+
in_app: in_app
|
103
|
+
}
|
104
|
+
|
105
|
+
frame_hash[:module] = mod if mod
|
106
|
+
frame_hash[:lineno] = frame_data[:line] if frame_data[:line]
|
107
|
+
|
108
|
+
frame_hash
|
109
|
+
end
|
110
|
+
|
111
|
+
idx = 0
|
112
|
+
stacks = []
|
113
|
+
num_seen = []
|
114
|
+
|
115
|
+
# extract stacks from raw
|
116
|
+
# raw is a single array of [.., len_stack, *stack_frames(len_stack), num_stack_seen , ..]
|
117
|
+
while (len = results[:raw][idx])
|
118
|
+
idx += 1
|
119
|
+
|
120
|
+
# our call graph is reversed
|
121
|
+
stack = results[:raw].slice(idx, len).map { |id| frame_map[id] }.compact.reverse
|
122
|
+
stacks << stack
|
123
|
+
|
124
|
+
num_seen << results[:raw][idx + len]
|
125
|
+
idx += len + 1
|
126
|
+
|
127
|
+
log('Unknown frame in stack') if stack.size != len
|
128
|
+
end
|
129
|
+
|
130
|
+
idx = 0
|
131
|
+
elapsed_since_start_ns = 0
|
132
|
+
samples = []
|
133
|
+
|
134
|
+
num_seen.each_with_index do |n, i|
|
135
|
+
n.times do
|
136
|
+
# stackprof deltas are in microseconds
|
137
|
+
delta = results[:raw_timestamp_deltas][idx]
|
138
|
+
elapsed_since_start_ns += (delta * MICRO_TO_NANO_SECONDS).to_i
|
139
|
+
idx += 1
|
140
|
+
|
141
|
+
# Not sure why but some deltas are very small like 0/1 values,
|
142
|
+
# they pollute our flamegraph so just ignore them for now.
|
143
|
+
# Open issue at https://github.com/tmm1/stackprof/issues/201
|
144
|
+
next if delta < 10
|
145
|
+
|
146
|
+
samples << {
|
147
|
+
stack_id: i,
|
148
|
+
# TODO-neel-profiler we need to patch rb_profile_frames and write our own C extension to enable threading info.
|
149
|
+
# Till then, on multi-threaded servers like puma, we will get frames from other active threads when the one
|
150
|
+
# we're profiling is idle/sleeping/waiting for IO etc.
|
151
|
+
# https://bugs.ruby-lang.org/issues/10602
|
152
|
+
thread_id: '0',
|
153
|
+
elapsed_since_start_ns: elapsed_since_start_ns.to_s
|
154
|
+
}
|
155
|
+
end
|
156
|
+
end
|
157
|
+
|
158
|
+
log('Some samples thrown away') if samples.size != results[:samples]
|
159
|
+
|
160
|
+
if samples.size <= 2
|
161
|
+
log('Not enough samples, discarding profiler')
|
162
|
+
return {}
|
163
|
+
end
|
164
|
+
|
165
|
+
profile = {
|
166
|
+
frames: frames,
|
167
|
+
stacks: stacks,
|
168
|
+
samples: samples
|
169
|
+
}
|
170
|
+
|
171
|
+
{
|
172
|
+
event_id: @event_id,
|
173
|
+
platform: PLATFORM,
|
174
|
+
version: VERSION,
|
175
|
+
profile: profile
|
176
|
+
}
|
177
|
+
end
|
178
|
+
|
179
|
+
private
|
180
|
+
|
181
|
+
def log(message)
|
182
|
+
Sentry.logger.debug(LOGGER_PROGNAME) { "[Profiler] #{message}" }
|
183
|
+
end
|
184
|
+
|
185
|
+
def in_app?(abs_path)
|
186
|
+
abs_path.match?(@in_app_pattern)
|
187
|
+
end
|
188
|
+
|
189
|
+
# copied from stacktrace.rb since I don't want to touch existing code
|
190
|
+
# TODO-neel-profiler try to fetch this from stackprof once we patch
|
191
|
+
# the native extension
|
192
|
+
def compute_filename(abs_path, in_app)
|
193
|
+
return nil if abs_path.nil?
|
194
|
+
|
195
|
+
under_project_root = @project_root && abs_path.start_with?(@project_root)
|
196
|
+
|
197
|
+
prefix =
|
198
|
+
if under_project_root && in_app
|
199
|
+
@project_root
|
200
|
+
else
|
201
|
+
longest_load_path = $LOAD_PATH.select { |path| abs_path.start_with?(path.to_s) }.max_by(&:size)
|
202
|
+
|
203
|
+
if under_project_root
|
204
|
+
longest_load_path || @project_root
|
205
|
+
else
|
206
|
+
longest_load_path
|
207
|
+
end
|
208
|
+
end
|
209
|
+
|
210
|
+
prefix ? abs_path[prefix.to_s.chomp(File::SEPARATOR).length + 1..-1] : abs_path
|
211
|
+
end
|
212
|
+
|
213
|
+
def split_module(name)
|
214
|
+
# last module plus class/instance method
|
215
|
+
i = name.rindex('::')
|
216
|
+
function = i ? name[(i + 2)..-1] : name
|
217
|
+
mod = i ? name[0...i] : nil
|
218
|
+
|
219
|
+
[function, mod]
|
220
|
+
end
|
221
|
+
end
|
222
|
+
end
|
data/lib/sentry/puma.rb
ADDED
@@ -0,0 +1,25 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Sentry
|
4
|
+
module Puma
|
5
|
+
module Server
|
6
|
+
def lowlevel_error(e, env, status=500)
|
7
|
+
result = super
|
8
|
+
|
9
|
+
begin
|
10
|
+
Sentry.capture_exception(e) do |scope|
|
11
|
+
scope.set_rack_env(env)
|
12
|
+
end
|
13
|
+
rescue
|
14
|
+
# if anything happens, we don't want to break the app
|
15
|
+
end
|
16
|
+
|
17
|
+
result
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
if defined?(Puma::Server)
|
24
|
+
Sentry.register_patch(Sentry::Puma::Server, Puma::Server)
|
25
|
+
end
|
@@ -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,19 +29,8 @@ 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
|
33
|
+
return unless Sentry.initialized?
|
40
34
|
return unless Sentry.configuration.breadcrumbs_logger.include?(LOGGER_NAME)
|
41
35
|
|
42
36
|
Sentry.add_breadcrumb(
|
@@ -61,10 +55,16 @@ module Sentry
|
|
61
55
|
def parsed_commands
|
62
56
|
commands.map do |statement|
|
63
57
|
command, key, *arguments = statement
|
58
|
+
command_set = { command: command.to_s.upcase }
|
59
|
+
command_set[:key] = key if Utils::EncodingHelper.valid_utf_8?(key)
|
64
60
|
|
65
|
-
|
66
|
-
command_set[:arguments] = arguments
|
61
|
+
if Sentry.configuration.send_default_pii
|
62
|
+
command_set[:arguments] = arguments
|
63
|
+
.select { |a| Utils::EncodingHelper.valid_utf_8?(a) }
|
64
|
+
.join(" ")
|
67
65
|
end
|
66
|
+
|
67
|
+
command_set
|
68
68
|
end
|
69
69
|
end
|
70
70
|
|
@@ -72,19 +72,32 @@ module Sentry
|
|
72
72
|
"#{host}:#{port}/#{db}"
|
73
73
|
end
|
74
74
|
|
75
|
-
module
|
75
|
+
module OldClientPatch
|
76
76
|
def logging(commands, &block)
|
77
|
-
Sentry::Redis.new(commands, host, port, db).instrument
|
78
|
-
|
79
|
-
|
77
|
+
Sentry::Redis.new(commands, host, port, db).instrument { super }
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
module GlobalRedisInstrumentation
|
82
|
+
def call(command, redis_config)
|
83
|
+
Sentry::Redis
|
84
|
+
.new([command], redis_config.host, redis_config.port, redis_config.db)
|
85
|
+
.instrument { super }
|
86
|
+
end
|
87
|
+
|
88
|
+
def call_pipelined(commands, redis_config)
|
89
|
+
Sentry::Redis
|
90
|
+
.new(commands, redis_config.host, redis_config.port, redis_config.db)
|
91
|
+
.instrument { super }
|
80
92
|
end
|
81
93
|
end
|
82
94
|
end
|
83
95
|
end
|
84
96
|
|
85
97
|
if defined?(::Redis::Client)
|
86
|
-
|
87
|
-
|
88
|
-
|
98
|
+
if Gem::Version.new(::Redis::VERSION) < Gem::Version.new("5.0")
|
99
|
+
Sentry.register_patch(Sentry::Redis::OldClientPatch, ::Redis::Client)
|
100
|
+
elsif defined?(RedisClient)
|
101
|
+
RedisClient.register(Sentry::Redis::GlobalRedisInstrumentation)
|
89
102
|
end
|
90
103
|
end
|
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
|
@@ -43,8 +58,10 @@ module Sentry
|
|
43
58
|
event.breadcrumbs = breadcrumbs
|
44
59
|
event.rack_env = rack_env if rack_env
|
45
60
|
|
46
|
-
|
47
|
-
|
61
|
+
all_event_processors = self.class.global_event_processors + @event_processors
|
62
|
+
|
63
|
+
unless all_event_processors.empty?
|
64
|
+
all_event_processors.each do |processor_block|
|
48
65
|
event = processor_block.call(event, hint)
|
49
66
|
end
|
50
67
|
end
|
@@ -73,7 +90,8 @@ module Sentry
|
|
73
90
|
copy.extra = extra.deep_dup
|
74
91
|
copy.tags = tags.deep_dup
|
75
92
|
copy.user = user.deep_dup
|
76
|
-
copy.transaction_names = transaction_names.
|
93
|
+
copy.transaction_names = transaction_names.dup
|
94
|
+
copy.transaction_sources = transaction_sources.dup
|
77
95
|
copy.fingerprint = fingerprint.deep_dup
|
78
96
|
copy.span = span.deep_dup
|
79
97
|
copy.session = session.deep_dup
|
@@ -90,6 +108,7 @@ module Sentry
|
|
90
108
|
self.tags = scope.tags
|
91
109
|
self.user = scope.user
|
92
110
|
self.transaction_names = scope.transaction_names
|
111
|
+
self.transaction_sources = scope.transaction_sources
|
93
112
|
self.fingerprint = scope.fingerprint
|
94
113
|
self.span = scope.span
|
95
114
|
end
|
@@ -173,6 +192,10 @@ module Sentry
|
|
173
192
|
# @return [Hash]
|
174
193
|
def set_contexts(contexts_hash)
|
175
194
|
check_argument_type!(contexts_hash, Hash)
|
195
|
+
contexts_hash.values.each do |val|
|
196
|
+
check_argument_type!(val, Hash)
|
197
|
+
end
|
198
|
+
|
176
199
|
@contexts.merge!(contexts_hash) do |key, old, new|
|
177
200
|
old.merge(new)
|
178
201
|
end
|
@@ -195,8 +218,9 @@ module Sentry
|
|
195
218
|
# The "transaction" here does not refer to `Transaction` objects.
|
196
219
|
# @param transaction_name [String]
|
197
220
|
# @return [void]
|
198
|
-
def set_transaction_name(transaction_name)
|
221
|
+
def set_transaction_name(transaction_name, source: :custom)
|
199
222
|
@transaction_names << transaction_name
|
223
|
+
@transaction_sources << source
|
200
224
|
end
|
201
225
|
|
202
226
|
# Sets the currently active session on the scope.
|
@@ -213,6 +237,13 @@ module Sentry
|
|
213
237
|
@transaction_names.last
|
214
238
|
end
|
215
239
|
|
240
|
+
# Returns current transaction source.
|
241
|
+
# The "transaction" here does not refer to `Transaction` objects.
|
242
|
+
# @return [String, nil]
|
243
|
+
def transaction_source
|
244
|
+
@transaction_sources.last
|
245
|
+
end
|
246
|
+
|
216
247
|
# Returns the associated Transaction object.
|
217
248
|
# @return [Transaction, nil]
|
218
249
|
def get_transaction
|
@@ -256,6 +287,7 @@ module Sentry
|
|
256
287
|
@level = :error
|
257
288
|
@fingerprint = []
|
258
289
|
@transaction_names = []
|
290
|
+
@transaction_sources = []
|
259
291
|
@event_processors = []
|
260
292
|
@rack_env = {}
|
261
293
|
@span = nil
|
@@ -277,7 +309,8 @@ module Sentry
|
|
277
309
|
name: uname[:sysname] || RbConfig::CONFIG["host_os"],
|
278
310
|
version: uname[:version],
|
279
311
|
build: uname[:release],
|
280
|
-
kernel_version: uname[:version]
|
312
|
+
kernel_version: uname[:version],
|
313
|
+
machine: uname[:machine]
|
281
314
|
}
|
282
315
|
end
|
283
316
|
end
|
@@ -289,6 +322,22 @@ module Sentry
|
|
289
322
|
version: RUBY_DESCRIPTION || Sentry.sys_command("ruby -v")
|
290
323
|
}
|
291
324
|
end
|
325
|
+
|
326
|
+
# Returns the global event processors array.
|
327
|
+
# @return [Array<Proc>]
|
328
|
+
def global_event_processors
|
329
|
+
@global_event_processors ||= []
|
330
|
+
end
|
331
|
+
|
332
|
+
# Adds a new global event processor [Proc].
|
333
|
+
# Sometimes we need a global event processor without needing to configure scope.
|
334
|
+
# These run before scope event processors.
|
335
|
+
#
|
336
|
+
# @param block [Proc]
|
337
|
+
# @return [void]
|
338
|
+
def add_global_event_processor(&block)
|
339
|
+
global_event_processors << block
|
340
|
+
end
|
292
341
|
end
|
293
342
|
|
294
343
|
end
|
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
|