sentry-ruby 5.3.0 → 5.8.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/.gitignore +11 -0
- data/.rspec +2 -0
- data/.yardopts +2 -0
- data/CHANGELOG.md +313 -0
- data/CODE_OF_CONDUCT.md +74 -0
- data/Gemfile +31 -0
- data/Makefile +4 -0
- data/README.md +10 -6
- data/Rakefile +13 -0
- data/bin/console +18 -0
- data/bin/setup +8 -0
- data/lib/sentry/background_worker.rb +72 -0
- data/lib/sentry/backtrace.rb +124 -0
- data/lib/sentry/baggage.rb +81 -0
- data/lib/sentry/breadcrumb/sentry_logger.rb +90 -0
- data/lib/sentry/breadcrumb.rb +70 -0
- data/lib/sentry/breadcrumb_buffer.rb +64 -0
- data/lib/sentry/client.rb +207 -0
- data/lib/sentry/configuration.rb +543 -0
- data/lib/sentry/core_ext/object/deep_dup.rb +61 -0
- data/lib/sentry/core_ext/object/duplicable.rb +155 -0
- data/lib/sentry/dsn.rb +53 -0
- data/lib/sentry/envelope.rb +96 -0
- data/lib/sentry/error_event.rb +38 -0
- data/lib/sentry/event.rb +178 -0
- data/lib/sentry/exceptions.rb +9 -0
- data/lib/sentry/hub.rb +241 -0
- data/lib/sentry/integrable.rb +26 -0
- data/lib/sentry/interface.rb +16 -0
- data/lib/sentry/interfaces/exception.rb +43 -0
- data/lib/sentry/interfaces/request.rb +134 -0
- data/lib/sentry/interfaces/single_exception.rb +65 -0
- data/lib/sentry/interfaces/stacktrace.rb +87 -0
- data/lib/sentry/interfaces/stacktrace_builder.rb +79 -0
- data/lib/sentry/interfaces/threads.rb +42 -0
- data/lib/sentry/linecache.rb +47 -0
- data/lib/sentry/logger.rb +20 -0
- data/lib/sentry/net/http.rb +103 -0
- data/lib/sentry/rack/capture_exceptions.rb +82 -0
- data/lib/sentry/rack.rb +5 -0
- data/lib/sentry/rake.rb +41 -0
- data/lib/sentry/redis.rb +107 -0
- data/lib/sentry/release_detector.rb +39 -0
- data/lib/sentry/scope.rb +339 -0
- data/lib/sentry/session.rb +33 -0
- data/lib/sentry/session_flusher.rb +90 -0
- data/lib/sentry/span.rb +236 -0
- data/lib/sentry/test_helper.rb +78 -0
- data/lib/sentry/transaction.rb +345 -0
- data/lib/sentry/transaction_event.rb +53 -0
- data/lib/sentry/transport/configuration.rb +25 -0
- data/lib/sentry/transport/dummy_transport.rb +21 -0
- data/lib/sentry/transport/http_transport.rb +175 -0
- data/lib/sentry/transport.rb +214 -0
- data/lib/sentry/utils/argument_checking_helper.rb +13 -0
- data/lib/sentry/utils/custom_inspection.rb +14 -0
- data/lib/sentry/utils/encoding_helper.rb +22 -0
- data/lib/sentry/utils/exception_cause_chain.rb +20 -0
- data/lib/sentry/utils/logging_helper.rb +26 -0
- data/lib/sentry/utils/real_ip.rb +84 -0
- data/lib/sentry/utils/request_id.rb +18 -0
- data/lib/sentry/version.rb +5 -0
- data/lib/sentry-ruby.rb +511 -0
- data/sentry-ruby-core.gemspec +23 -0
- data/sentry-ruby.gemspec +24 -0
- metadata +66 -16
data/lib/sentry/scope.rb
ADDED
@@ -0,0 +1,339 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "sentry/breadcrumb_buffer"
|
4
|
+
require "etc"
|
5
|
+
|
6
|
+
module Sentry
|
7
|
+
class Scope
|
8
|
+
include ArgumentCheckingHelper
|
9
|
+
|
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
|
+
]
|
25
|
+
|
26
|
+
attr_reader(*ATTRIBUTES)
|
27
|
+
|
28
|
+
# @param max_breadcrumbs [Integer] the maximum number of breadcrumbs to be stored in the scope.
|
29
|
+
def initialize(max_breadcrumbs: nil)
|
30
|
+
@max_breadcrumbs = max_breadcrumbs
|
31
|
+
set_default_value
|
32
|
+
end
|
33
|
+
|
34
|
+
# Resets the scope's attributes to defaults.
|
35
|
+
# @return [void]
|
36
|
+
def clear
|
37
|
+
set_default_value
|
38
|
+
end
|
39
|
+
|
40
|
+
# Applies stored attributes and event processors to the given event.
|
41
|
+
# @param event [Event]
|
42
|
+
# @param hint [Hash] the hint data that'll be passed to event processors.
|
43
|
+
# @return [Event]
|
44
|
+
def apply_to_event(event, hint = nil)
|
45
|
+
event.tags = tags.merge(event.tags)
|
46
|
+
event.user = user.merge(event.user)
|
47
|
+
event.extra = extra.merge(event.extra)
|
48
|
+
event.contexts = contexts.merge(event.contexts)
|
49
|
+
event.transaction = transaction_name if transaction_name
|
50
|
+
event.transaction_info = { source: transaction_source } if transaction_source
|
51
|
+
|
52
|
+
if span
|
53
|
+
event.contexts[:trace] = span.get_trace_context
|
54
|
+
end
|
55
|
+
|
56
|
+
event.fingerprint = fingerprint
|
57
|
+
event.level = level
|
58
|
+
event.breadcrumbs = breadcrumbs
|
59
|
+
event.rack_env = rack_env if rack_env
|
60
|
+
|
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|
|
65
|
+
event = processor_block.call(event, hint)
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
event
|
70
|
+
end
|
71
|
+
|
72
|
+
# Adds the breadcrumb to the scope's breadcrumbs buffer.
|
73
|
+
# @param breadcrumb [Breadcrumb]
|
74
|
+
# @return [void]
|
75
|
+
def add_breadcrumb(breadcrumb)
|
76
|
+
breadcrumbs.record(breadcrumb)
|
77
|
+
end
|
78
|
+
|
79
|
+
# Clears the scope's breadcrumbs buffer
|
80
|
+
# @return [void]
|
81
|
+
def clear_breadcrumbs
|
82
|
+
set_new_breadcrumb_buffer
|
83
|
+
end
|
84
|
+
|
85
|
+
# @return [Scope]
|
86
|
+
def dup
|
87
|
+
copy = super
|
88
|
+
copy.breadcrumbs = breadcrumbs.dup
|
89
|
+
copy.contexts = contexts.deep_dup
|
90
|
+
copy.extra = extra.deep_dup
|
91
|
+
copy.tags = tags.deep_dup
|
92
|
+
copy.user = user.deep_dup
|
93
|
+
copy.transaction_names = transaction_names.dup
|
94
|
+
copy.transaction_sources = transaction_sources.dup
|
95
|
+
copy.fingerprint = fingerprint.deep_dup
|
96
|
+
copy.span = span.deep_dup
|
97
|
+
copy.session = session.deep_dup
|
98
|
+
copy
|
99
|
+
end
|
100
|
+
|
101
|
+
# Updates the scope's data from a given scope.
|
102
|
+
# @param scope [Scope]
|
103
|
+
# @return [void]
|
104
|
+
def update_from_scope(scope)
|
105
|
+
self.breadcrumbs = scope.breadcrumbs
|
106
|
+
self.contexts = scope.contexts
|
107
|
+
self.extra = scope.extra
|
108
|
+
self.tags = scope.tags
|
109
|
+
self.user = scope.user
|
110
|
+
self.transaction_names = scope.transaction_names
|
111
|
+
self.transaction_sources = scope.transaction_sources
|
112
|
+
self.fingerprint = scope.fingerprint
|
113
|
+
self.span = scope.span
|
114
|
+
end
|
115
|
+
|
116
|
+
# Updates the scope's data from the given options.
|
117
|
+
# @param contexts [Hash]
|
118
|
+
# @param extras [Hash]
|
119
|
+
# @param tags [Hash]
|
120
|
+
# @param user [Hash]
|
121
|
+
# @param level [String, Symbol]
|
122
|
+
# @param fingerprint [Array]
|
123
|
+
# @return [void]
|
124
|
+
def update_from_options(
|
125
|
+
contexts: nil,
|
126
|
+
extra: nil,
|
127
|
+
tags: nil,
|
128
|
+
user: nil,
|
129
|
+
level: nil,
|
130
|
+
fingerprint: nil
|
131
|
+
)
|
132
|
+
self.contexts.merge!(contexts) if contexts
|
133
|
+
self.extra.merge!(extra) if extra
|
134
|
+
self.tags.merge!(tags) if tags
|
135
|
+
self.user = user if user
|
136
|
+
self.level = level if level
|
137
|
+
self.fingerprint = fingerprint if fingerprint
|
138
|
+
end
|
139
|
+
|
140
|
+
# Sets the scope's rack_env attribute.
|
141
|
+
# @param env [Hash]
|
142
|
+
# @return [Hash]
|
143
|
+
def set_rack_env(env)
|
144
|
+
env = env || {}
|
145
|
+
@rack_env = env
|
146
|
+
end
|
147
|
+
|
148
|
+
# Sets the scope's span attribute.
|
149
|
+
# @param span [Span]
|
150
|
+
# @return [Span]
|
151
|
+
def set_span(span)
|
152
|
+
check_argument_type!(span, Span)
|
153
|
+
@span = span
|
154
|
+
end
|
155
|
+
|
156
|
+
# @!macro set_user
|
157
|
+
def set_user(user_hash)
|
158
|
+
check_argument_type!(user_hash, Hash)
|
159
|
+
@user = user_hash
|
160
|
+
end
|
161
|
+
|
162
|
+
# @!macro set_extras
|
163
|
+
def set_extras(extras_hash)
|
164
|
+
check_argument_type!(extras_hash, Hash)
|
165
|
+
@extra.merge!(extras_hash)
|
166
|
+
end
|
167
|
+
|
168
|
+
# Adds a new key-value pair to current extras.
|
169
|
+
# @param key [String, Symbol]
|
170
|
+
# @param value [Object]
|
171
|
+
# @return [Hash]
|
172
|
+
def set_extra(key, value)
|
173
|
+
set_extras(key => value)
|
174
|
+
end
|
175
|
+
|
176
|
+
# @!macro set_tags
|
177
|
+
def set_tags(tags_hash)
|
178
|
+
check_argument_type!(tags_hash, Hash)
|
179
|
+
@tags.merge!(tags_hash)
|
180
|
+
end
|
181
|
+
|
182
|
+
# Adds a new key-value pair to current tags.
|
183
|
+
# @param key [String, Symbol]
|
184
|
+
# @param value [Object]
|
185
|
+
# @return [Hash]
|
186
|
+
def set_tag(key, value)
|
187
|
+
set_tags(key => value)
|
188
|
+
end
|
189
|
+
|
190
|
+
# Updates the scope's contexts attribute by merging with the old value.
|
191
|
+
# @param contexts [Hash]
|
192
|
+
# @return [Hash]
|
193
|
+
def set_contexts(contexts_hash)
|
194
|
+
check_argument_type!(contexts_hash, Hash)
|
195
|
+
@contexts.merge!(contexts_hash) do |key, old, new|
|
196
|
+
old.merge(new)
|
197
|
+
end
|
198
|
+
end
|
199
|
+
|
200
|
+
# @!macro set_context
|
201
|
+
def set_context(key, value)
|
202
|
+
check_argument_type!(value, Hash)
|
203
|
+
set_contexts(key => value)
|
204
|
+
end
|
205
|
+
|
206
|
+
# Sets the scope's level attribute.
|
207
|
+
# @param level [String, Symbol]
|
208
|
+
# @return [void]
|
209
|
+
def set_level(level)
|
210
|
+
@level = level
|
211
|
+
end
|
212
|
+
|
213
|
+
# Appends a new transaction name to the scope.
|
214
|
+
# The "transaction" here does not refer to `Transaction` objects.
|
215
|
+
# @param transaction_name [String]
|
216
|
+
# @return [void]
|
217
|
+
def set_transaction_name(transaction_name, source: :custom)
|
218
|
+
@transaction_names << transaction_name
|
219
|
+
@transaction_sources << source
|
220
|
+
end
|
221
|
+
|
222
|
+
# Sets the currently active session on the scope.
|
223
|
+
# @param session [Session, nil]
|
224
|
+
# @return [void]
|
225
|
+
def set_session(session)
|
226
|
+
@session = session
|
227
|
+
end
|
228
|
+
|
229
|
+
# Returns current transaction name.
|
230
|
+
# The "transaction" here does not refer to `Transaction` objects.
|
231
|
+
# @return [String, nil]
|
232
|
+
def transaction_name
|
233
|
+
@transaction_names.last
|
234
|
+
end
|
235
|
+
|
236
|
+
# Returns current transaction source.
|
237
|
+
# The "transaction" here does not refer to `Transaction` objects.
|
238
|
+
# @return [String, nil]
|
239
|
+
def transaction_source
|
240
|
+
@transaction_sources.last
|
241
|
+
end
|
242
|
+
|
243
|
+
# Returns the associated Transaction object.
|
244
|
+
# @return [Transaction, nil]
|
245
|
+
def get_transaction
|
246
|
+
span.transaction if span
|
247
|
+
end
|
248
|
+
|
249
|
+
# Returns the associated Span object.
|
250
|
+
# @return [Span, nil]
|
251
|
+
def get_span
|
252
|
+
span
|
253
|
+
end
|
254
|
+
|
255
|
+
# Sets the scope's fingerprint attribute.
|
256
|
+
# @param fingerprint [Array]
|
257
|
+
# @return [Array]
|
258
|
+
def set_fingerprint(fingerprint)
|
259
|
+
check_argument_type!(fingerprint, Array)
|
260
|
+
|
261
|
+
@fingerprint = fingerprint
|
262
|
+
end
|
263
|
+
|
264
|
+
# Adds a new event processor [Proc] to the scope.
|
265
|
+
# @param block [Proc]
|
266
|
+
# @return [void]
|
267
|
+
def add_event_processor(&block)
|
268
|
+
@event_processors << block
|
269
|
+
end
|
270
|
+
|
271
|
+
protected
|
272
|
+
|
273
|
+
# for duplicating scopes internally
|
274
|
+
attr_writer(*ATTRIBUTES)
|
275
|
+
|
276
|
+
private
|
277
|
+
|
278
|
+
def set_default_value
|
279
|
+
@contexts = { :os => self.class.os_context, :runtime => self.class.runtime_context }
|
280
|
+
@extra = {}
|
281
|
+
@tags = {}
|
282
|
+
@user = {}
|
283
|
+
@level = :error
|
284
|
+
@fingerprint = []
|
285
|
+
@transaction_names = []
|
286
|
+
@transaction_sources = []
|
287
|
+
@event_processors = []
|
288
|
+
@rack_env = {}
|
289
|
+
@span = nil
|
290
|
+
@session = nil
|
291
|
+
set_new_breadcrumb_buffer
|
292
|
+
end
|
293
|
+
|
294
|
+
def set_new_breadcrumb_buffer
|
295
|
+
@breadcrumbs = BreadcrumbBuffer.new(@max_breadcrumbs)
|
296
|
+
end
|
297
|
+
|
298
|
+
class << self
|
299
|
+
# @return [Hash]
|
300
|
+
def os_context
|
301
|
+
@os_context ||=
|
302
|
+
begin
|
303
|
+
uname = Etc.uname
|
304
|
+
{
|
305
|
+
name: uname[:sysname] || RbConfig::CONFIG["host_os"],
|
306
|
+
version: uname[:version],
|
307
|
+
build: uname[:release],
|
308
|
+
kernel_version: uname[:version]
|
309
|
+
}
|
310
|
+
end
|
311
|
+
end
|
312
|
+
|
313
|
+
# @return [Hash]
|
314
|
+
def runtime_context
|
315
|
+
@runtime_context ||= {
|
316
|
+
name: RbConfig::CONFIG["ruby_install_name"],
|
317
|
+
version: RUBY_DESCRIPTION || Sentry.sys_command("ruby -v")
|
318
|
+
}
|
319
|
+
end
|
320
|
+
|
321
|
+
# Returns the global event processors array.
|
322
|
+
# @return [Array<Proc>]
|
323
|
+
def global_event_processors
|
324
|
+
@global_event_processors ||= []
|
325
|
+
end
|
326
|
+
|
327
|
+
# Adds a new global event processor [Proc].
|
328
|
+
# Sometimes we need a global event processor without needing to configure scope.
|
329
|
+
# These run before scope event processors.
|
330
|
+
#
|
331
|
+
# @param block [Proc]
|
332
|
+
# @return [void]
|
333
|
+
def add_global_event_processor(&block)
|
334
|
+
global_event_processors << block
|
335
|
+
end
|
336
|
+
end
|
337
|
+
|
338
|
+
end
|
339
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Sentry
|
4
|
+
class Session
|
5
|
+
attr_reader :started, :status, :aggregation_key
|
6
|
+
|
7
|
+
# TODO-neel add :crashed after adding handled mechanism
|
8
|
+
STATUSES = %i(ok errored exited)
|
9
|
+
AGGREGATE_STATUSES = %i(errored exited)
|
10
|
+
|
11
|
+
def initialize
|
12
|
+
@started = Sentry.utc_now
|
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)
|
18
|
+
end
|
19
|
+
|
20
|
+
# TODO-neel add :crashed after adding handled mechanism
|
21
|
+
def update_from_exception(_exception = nil)
|
22
|
+
@status = :errored
|
23
|
+
end
|
24
|
+
|
25
|
+
def close
|
26
|
+
@status = :exited if @status == :ok
|
27
|
+
end
|
28
|
+
|
29
|
+
def deep_dup
|
30
|
+
dup
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,90 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Sentry
|
4
|
+
class SessionFlusher
|
5
|
+
include LoggingHelper
|
6
|
+
|
7
|
+
FLUSH_INTERVAL = 60
|
8
|
+
|
9
|
+
def initialize(configuration, client)
|
10
|
+
@thread = nil
|
11
|
+
@exited = false
|
12
|
+
@client = client
|
13
|
+
@pending_aggregates = {}
|
14
|
+
@release = configuration.release
|
15
|
+
@environment = configuration.environment
|
16
|
+
@logger = configuration.logger
|
17
|
+
|
18
|
+
log_debug("[Sessions] Sessions won't be captured without a valid release") unless @release
|
19
|
+
end
|
20
|
+
|
21
|
+
def flush
|
22
|
+
return if @pending_aggregates.empty?
|
23
|
+
envelope = pending_envelope
|
24
|
+
|
25
|
+
Sentry.background_worker.perform do
|
26
|
+
@client.transport.send_envelope(envelope)
|
27
|
+
end
|
28
|
+
|
29
|
+
@pending_aggregates = {}
|
30
|
+
end
|
31
|
+
|
32
|
+
def add_session(session)
|
33
|
+
return if @exited
|
34
|
+
return unless @release
|
35
|
+
|
36
|
+
begin
|
37
|
+
ensure_thread
|
38
|
+
rescue ThreadError
|
39
|
+
log_debug("Session flusher thread creation failed")
|
40
|
+
@exited = true
|
41
|
+
return
|
42
|
+
end
|
43
|
+
|
44
|
+
return unless Session::AGGREGATE_STATUSES.include?(session.status)
|
45
|
+
@pending_aggregates[session.aggregation_key] ||= init_aggregates(session.aggregation_key)
|
46
|
+
@pending_aggregates[session.aggregation_key][session.status] += 1
|
47
|
+
end
|
48
|
+
|
49
|
+
def kill
|
50
|
+
log_debug("Killing session flusher")
|
51
|
+
|
52
|
+
@exited = true
|
53
|
+
@thread&.kill
|
54
|
+
end
|
55
|
+
|
56
|
+
private
|
57
|
+
|
58
|
+
def init_aggregates(aggregation_key)
|
59
|
+
aggregates = { started: aggregation_key.iso8601 }
|
60
|
+
Session::AGGREGATE_STATUSES.each { |k| aggregates[k] = 0 }
|
61
|
+
aggregates
|
62
|
+
end
|
63
|
+
|
64
|
+
def pending_envelope
|
65
|
+
envelope = Envelope.new
|
66
|
+
|
67
|
+
header = { type: 'sessions' }
|
68
|
+
payload = { attrs: attrs, aggregates: @pending_aggregates.values }
|
69
|
+
|
70
|
+
envelope.add_item(header, payload)
|
71
|
+
envelope
|
72
|
+
end
|
73
|
+
|
74
|
+
def attrs
|
75
|
+
{ release: @release, environment: @environment }
|
76
|
+
end
|
77
|
+
|
78
|
+
def ensure_thread
|
79
|
+
return if @thread&.alive?
|
80
|
+
|
81
|
+
@thread = Thread.new do
|
82
|
+
loop do
|
83
|
+
sleep(FLUSH_INTERVAL)
|
84
|
+
flush
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
end
|
90
|
+
end
|
data/lib/sentry/span.rb
ADDED
@@ -0,0 +1,236 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "securerandom"
|
4
|
+
|
5
|
+
module Sentry
|
6
|
+
class Span
|
7
|
+
STATUS_MAP = {
|
8
|
+
400 => "invalid_argument",
|
9
|
+
401 => "unauthenticated",
|
10
|
+
403 => "permission_denied",
|
11
|
+
404 => "not_found",
|
12
|
+
409 => "already_exists",
|
13
|
+
429 => "resource_exhausted",
|
14
|
+
499 => "cancelled",
|
15
|
+
500 => "internal_error",
|
16
|
+
501 => "unimplemented",
|
17
|
+
503 => "unavailable",
|
18
|
+
504 => "deadline_exceeded"
|
19
|
+
}
|
20
|
+
|
21
|
+
# An uuid that can be used to identify a trace.
|
22
|
+
# @return [String]
|
23
|
+
attr_reader :trace_id
|
24
|
+
# An uuid that can be used to identify the span.
|
25
|
+
# @return [String]
|
26
|
+
attr_reader :span_id
|
27
|
+
# Span parent's span_id.
|
28
|
+
# @return [String]
|
29
|
+
attr_reader :parent_span_id
|
30
|
+
# Sampling result of the span.
|
31
|
+
# @return [Boolean, nil]
|
32
|
+
attr_reader :sampled
|
33
|
+
# Starting timestamp of the span.
|
34
|
+
# @return [Float]
|
35
|
+
attr_reader :start_timestamp
|
36
|
+
# Finishing timestamp of the span.
|
37
|
+
# @return [Float]
|
38
|
+
attr_reader :timestamp
|
39
|
+
# Span description
|
40
|
+
# @return [String]
|
41
|
+
attr_reader :description
|
42
|
+
# Span operation
|
43
|
+
# @return [String]
|
44
|
+
attr_reader :op
|
45
|
+
# Span status
|
46
|
+
# @return [String]
|
47
|
+
attr_reader :status
|
48
|
+
# Span tags
|
49
|
+
# @return [Hash]
|
50
|
+
attr_reader :tags
|
51
|
+
# Span data
|
52
|
+
# @return [Hash]
|
53
|
+
attr_reader :data
|
54
|
+
|
55
|
+
# The SpanRecorder the current span belongs to.
|
56
|
+
# SpanRecorder holds all spans under the same Transaction object (including the Transaction itself).
|
57
|
+
# @return [SpanRecorder]
|
58
|
+
attr_accessor :span_recorder
|
59
|
+
|
60
|
+
# The Transaction object the Span belongs to.
|
61
|
+
# Every span needs to be attached to a Transaction and their child spans will also inherit the same transaction.
|
62
|
+
# @return [Transaction]
|
63
|
+
attr_reader :transaction
|
64
|
+
|
65
|
+
def initialize(
|
66
|
+
transaction:,
|
67
|
+
description: nil,
|
68
|
+
op: nil,
|
69
|
+
status: nil,
|
70
|
+
trace_id: nil,
|
71
|
+
span_id: nil,
|
72
|
+
parent_span_id: nil,
|
73
|
+
sampled: nil,
|
74
|
+
start_timestamp: nil,
|
75
|
+
timestamp: nil
|
76
|
+
)
|
77
|
+
@trace_id = trace_id || SecureRandom.uuid.delete("-")
|
78
|
+
@span_id = span_id || SecureRandom.hex(8)
|
79
|
+
@parent_span_id = parent_span_id
|
80
|
+
@sampled = sampled
|
81
|
+
@start_timestamp = start_timestamp || Sentry.utc_now.to_f
|
82
|
+
@timestamp = timestamp
|
83
|
+
@description = description
|
84
|
+
@transaction = transaction
|
85
|
+
@op = op
|
86
|
+
@status = status
|
87
|
+
@data = {}
|
88
|
+
@tags = {}
|
89
|
+
end
|
90
|
+
|
91
|
+
# Finishes the span by adding a timestamp.
|
92
|
+
# @return [self]
|
93
|
+
def finish(end_timestamp: nil)
|
94
|
+
@timestamp = end_timestamp || @timestamp || Sentry.utc_now.to_f
|
95
|
+
self
|
96
|
+
end
|
97
|
+
|
98
|
+
# Generates a trace string that can be used to connect other transactions.
|
99
|
+
# @return [String]
|
100
|
+
def to_sentry_trace
|
101
|
+
sampled_flag = ""
|
102
|
+
sampled_flag = @sampled ? 1 : 0 unless @sampled.nil?
|
103
|
+
|
104
|
+
"#{@trace_id}-#{@span_id}-#{sampled_flag}"
|
105
|
+
end
|
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
|
+
|
114
|
+
# @return [Hash]
|
115
|
+
def to_hash
|
116
|
+
{
|
117
|
+
trace_id: @trace_id,
|
118
|
+
span_id: @span_id,
|
119
|
+
parent_span_id: @parent_span_id,
|
120
|
+
start_timestamp: @start_timestamp,
|
121
|
+
timestamp: @timestamp,
|
122
|
+
description: @description,
|
123
|
+
op: @op,
|
124
|
+
status: @status,
|
125
|
+
tags: @tags,
|
126
|
+
data: @data
|
127
|
+
}
|
128
|
+
end
|
129
|
+
|
130
|
+
# Returns the span's context that can be used to embed in an Event.
|
131
|
+
# @return [Hash]
|
132
|
+
def get_trace_context
|
133
|
+
{
|
134
|
+
trace_id: @trace_id,
|
135
|
+
span_id: @span_id,
|
136
|
+
parent_span_id: @parent_span_id,
|
137
|
+
description: @description,
|
138
|
+
op: @op,
|
139
|
+
status: @status
|
140
|
+
}
|
141
|
+
end
|
142
|
+
|
143
|
+
# Starts a child span with given attributes.
|
144
|
+
# @param attributes [Hash] the attributes for the child span.
|
145
|
+
def start_child(**attributes)
|
146
|
+
attributes = attributes.dup.merge(transaction: @transaction, trace_id: @trace_id, parent_span_id: @span_id, sampled: @sampled)
|
147
|
+
new_span = Span.new(**attributes)
|
148
|
+
new_span.span_recorder = span_recorder
|
149
|
+
|
150
|
+
if span_recorder
|
151
|
+
span_recorder.add(new_span)
|
152
|
+
end
|
153
|
+
|
154
|
+
new_span
|
155
|
+
end
|
156
|
+
|
157
|
+
# Starts a child span, yield it to the given block, and then finish the span after the block is executed.
|
158
|
+
# @example
|
159
|
+
# span.with_child_span do |child_span|
|
160
|
+
# # things happen here will be recorded in a child span
|
161
|
+
# end
|
162
|
+
#
|
163
|
+
# @param attributes [Hash] the attributes for the child span.
|
164
|
+
# @param block [Proc] the action to be recorded in the child span.
|
165
|
+
# @yieldparam child_span [Span]
|
166
|
+
def with_child_span(**attributes, &block)
|
167
|
+
child_span = start_child(**attributes)
|
168
|
+
|
169
|
+
yield(child_span)
|
170
|
+
|
171
|
+
child_span.finish
|
172
|
+
rescue
|
173
|
+
child_span.set_http_status(500)
|
174
|
+
child_span.finish
|
175
|
+
raise
|
176
|
+
end
|
177
|
+
|
178
|
+
def deep_dup
|
179
|
+
dup
|
180
|
+
end
|
181
|
+
|
182
|
+
# Sets the span's operation.
|
183
|
+
# @param op [String] operation of the span.
|
184
|
+
def set_op(op)
|
185
|
+
@op = op
|
186
|
+
end
|
187
|
+
|
188
|
+
# Sets the span's description.
|
189
|
+
# @param description [String] description of the span.
|
190
|
+
def set_description(description)
|
191
|
+
@description = description
|
192
|
+
end
|
193
|
+
|
194
|
+
|
195
|
+
# Sets the span's status.
|
196
|
+
# @param satus [String] status of the span.
|
197
|
+
def set_status(status)
|
198
|
+
@status = status
|
199
|
+
end
|
200
|
+
|
201
|
+
# Sets the span's finish timestamp.
|
202
|
+
# @param timestamp [Float] finished time in float format (most precise).
|
203
|
+
def set_timestamp(timestamp)
|
204
|
+
@timestamp = timestamp
|
205
|
+
end
|
206
|
+
|
207
|
+
# Sets the span's status with given http status code.
|
208
|
+
# @param status_code [String] example: "500".
|
209
|
+
def set_http_status(status_code)
|
210
|
+
status_code = status_code.to_i
|
211
|
+
set_data("status_code", status_code)
|
212
|
+
|
213
|
+
status =
|
214
|
+
if status_code >= 200 && status_code < 299
|
215
|
+
"ok"
|
216
|
+
else
|
217
|
+
STATUS_MAP[status_code]
|
218
|
+
end
|
219
|
+
set_status(status)
|
220
|
+
end
|
221
|
+
|
222
|
+
# Inserts a key-value pair to the span's data payload.
|
223
|
+
# @param key [String, Symbol]
|
224
|
+
# @param value [Object]
|
225
|
+
def set_data(key, value)
|
226
|
+
@data[key] = value
|
227
|
+
end
|
228
|
+
|
229
|
+
# Sets a tag to the span.
|
230
|
+
# @param key [String, Symbol]
|
231
|
+
# @param value [String]
|
232
|
+
def set_tag(key, value)
|
233
|
+
@tags[key] = value
|
234
|
+
end
|
235
|
+
end
|
236
|
+
end
|