sentry-ruby 5.1.0 → 5.4.2
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 +3 -0
- data/.yardopts +2 -0
- data/CHANGELOG.md +313 -0
- data/CODE_OF_CONDUCT.md +74 -0
- data/Gemfile +27 -0
- data/Makefile +4 -0
- data/README.md +8 -7
- 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/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 +190 -0
- data/lib/sentry/configuration.rb +502 -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 +220 -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 +144 -0
- data/lib/sentry/interfaces/single_exception.rb +57 -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 +115 -0
- data/lib/sentry/rack/capture_exceptions.rb +80 -0
- data/lib/sentry/rack.rb +5 -0
- data/lib/sentry/rake.rb +41 -0
- data/lib/sentry/redis.rb +90 -0
- data/lib/sentry/release_detector.rb +39 -0
- data/lib/sentry/scope.rb +295 -0
- data/lib/sentry/session.rb +35 -0
- data/lib/sentry/session_flusher.rb +90 -0
- data/lib/sentry/span.rb +226 -0
- data/lib/sentry/test_helper.rb +76 -0
- data/lib/sentry/transaction.rb +206 -0
- data/lib/sentry/transaction_event.rb +29 -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 +210 -0
- data/lib/sentry/utils/argument_checking_helper.rb +13 -0
- data/lib/sentry/utils/custom_inspection.rb +14 -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 +505 -0
- data/sentry-ruby-core.gemspec +23 -0
- data/sentry-ruby.gemspec +24 -0
- metadata +64 -16
data/lib/sentry/span.rb
ADDED
@@ -0,0 +1,226 @@
|
|
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_accessor :transaction
|
64
|
+
|
65
|
+
def initialize(
|
66
|
+
description: nil,
|
67
|
+
op: nil,
|
68
|
+
status: nil,
|
69
|
+
trace_id: nil,
|
70
|
+
parent_span_id: nil,
|
71
|
+
sampled: nil,
|
72
|
+
start_timestamp: nil,
|
73
|
+
timestamp: nil
|
74
|
+
)
|
75
|
+
@trace_id = trace_id || SecureRandom.uuid.delete("-")
|
76
|
+
@span_id = SecureRandom.hex(8)
|
77
|
+
@parent_span_id = parent_span_id
|
78
|
+
@sampled = sampled
|
79
|
+
@start_timestamp = start_timestamp || Sentry.utc_now.to_f
|
80
|
+
@timestamp = timestamp
|
81
|
+
@description = description
|
82
|
+
@op = op
|
83
|
+
@status = status
|
84
|
+
@data = {}
|
85
|
+
@tags = {}
|
86
|
+
end
|
87
|
+
|
88
|
+
# Finishes the span by adding a timestamp.
|
89
|
+
# @return [self]
|
90
|
+
def finish
|
91
|
+
# already finished
|
92
|
+
return if @timestamp
|
93
|
+
|
94
|
+
@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
|
+
# @return [Hash]
|
108
|
+
def to_hash
|
109
|
+
{
|
110
|
+
trace_id: @trace_id,
|
111
|
+
span_id: @span_id,
|
112
|
+
parent_span_id: @parent_span_id,
|
113
|
+
start_timestamp: @start_timestamp,
|
114
|
+
timestamp: @timestamp,
|
115
|
+
description: @description,
|
116
|
+
op: @op,
|
117
|
+
status: @status,
|
118
|
+
tags: @tags,
|
119
|
+
data: @data
|
120
|
+
}
|
121
|
+
end
|
122
|
+
|
123
|
+
# Returns the span's context that can be used to embed in an Event.
|
124
|
+
# @return [Hash]
|
125
|
+
def get_trace_context
|
126
|
+
{
|
127
|
+
trace_id: @trace_id,
|
128
|
+
span_id: @span_id,
|
129
|
+
parent_span_id: @parent_span_id,
|
130
|
+
description: @description,
|
131
|
+
op: @op,
|
132
|
+
status: @status
|
133
|
+
}
|
134
|
+
end
|
135
|
+
|
136
|
+
# Starts a child span with given attributes.
|
137
|
+
# @param attributes [Hash] the attributes for the child span.
|
138
|
+
def start_child(**attributes)
|
139
|
+
attributes = attributes.dup.merge(trace_id: @trace_id, parent_span_id: @span_id, sampled: @sampled)
|
140
|
+
new_span = Span.new(**attributes)
|
141
|
+
new_span.transaction = transaction
|
142
|
+
new_span.span_recorder = span_recorder
|
143
|
+
|
144
|
+
if span_recorder
|
145
|
+
span_recorder.add(new_span)
|
146
|
+
end
|
147
|
+
|
148
|
+
new_span
|
149
|
+
end
|
150
|
+
|
151
|
+
# Starts a child span, yield it to the given block, and then finish the span after the block is executed.
|
152
|
+
# @example
|
153
|
+
# span.with_child_span do |child_span|
|
154
|
+
# # things happen here will be recorded in a child span
|
155
|
+
# end
|
156
|
+
#
|
157
|
+
# @param attributes [Hash] the attributes for the child span.
|
158
|
+
# @param block [Proc] the action to be recorded in the child span.
|
159
|
+
# @yieldparam child_span [Span]
|
160
|
+
def with_child_span(**attributes, &block)
|
161
|
+
child_span = start_child(**attributes)
|
162
|
+
|
163
|
+
yield(child_span)
|
164
|
+
|
165
|
+
child_span.finish
|
166
|
+
end
|
167
|
+
|
168
|
+
def deep_dup
|
169
|
+
dup
|
170
|
+
end
|
171
|
+
|
172
|
+
# Sets the span's operation.
|
173
|
+
# @param op [String] operation of the span.
|
174
|
+
def set_op(op)
|
175
|
+
@op = op
|
176
|
+
end
|
177
|
+
|
178
|
+
# Sets the span's description.
|
179
|
+
# @param description [String] description of the span.
|
180
|
+
def set_description(description)
|
181
|
+
@description = description
|
182
|
+
end
|
183
|
+
|
184
|
+
|
185
|
+
# Sets the span's status.
|
186
|
+
# @param satus [String] status of the span.
|
187
|
+
def set_status(status)
|
188
|
+
@status = status
|
189
|
+
end
|
190
|
+
|
191
|
+
# Sets the span's finish timestamp.
|
192
|
+
# @param timestamp [Float] finished time in float format (most precise).
|
193
|
+
def set_timestamp(timestamp)
|
194
|
+
@timestamp = timestamp
|
195
|
+
end
|
196
|
+
|
197
|
+
# Sets the span's status with given http status code.
|
198
|
+
# @param status_code [String] example: "500".
|
199
|
+
def set_http_status(status_code)
|
200
|
+
status_code = status_code.to_i
|
201
|
+
set_data("status_code", status_code)
|
202
|
+
|
203
|
+
status =
|
204
|
+
if status_code >= 200 && status_code < 299
|
205
|
+
"ok"
|
206
|
+
else
|
207
|
+
STATUS_MAP[status_code]
|
208
|
+
end
|
209
|
+
set_status(status)
|
210
|
+
end
|
211
|
+
|
212
|
+
# Inserts a key-value pair to the span's data payload.
|
213
|
+
# @param key [String, Symbol]
|
214
|
+
# @param value [Object]
|
215
|
+
def set_data(key, value)
|
216
|
+
@data[key] = value
|
217
|
+
end
|
218
|
+
|
219
|
+
# Sets a tag to the span.
|
220
|
+
# @param key [String, Symbol]
|
221
|
+
# @param value [String]
|
222
|
+
def set_tag(key, value)
|
223
|
+
@tags[key] = value
|
224
|
+
end
|
225
|
+
end
|
226
|
+
end
|
@@ -0,0 +1,76 @@
|
|
1
|
+
module Sentry
|
2
|
+
module TestHelper
|
3
|
+
DUMMY_DSN = 'http://12345:67890@sentry.localdomain/sentry/42'
|
4
|
+
|
5
|
+
# Alters the existing SDK configuration with test-suitable options. Mainly:
|
6
|
+
# - Sets a dummy DSN instead of `nil` or an actual DSN.
|
7
|
+
# - Sets the transport to DummyTransport, which allows easy access to the captured events.
|
8
|
+
# - Disables background worker.
|
9
|
+
# - Makes sure the SDK is enabled under the current environment ("test" in most cases).
|
10
|
+
#
|
11
|
+
# It should be called **before** every test case.
|
12
|
+
#
|
13
|
+
# @yieldparam config [Configuration]
|
14
|
+
# @return [void]
|
15
|
+
def setup_sentry_test(&block)
|
16
|
+
raise "please make sure the SDK is initialized for testing" unless Sentry.initialized?
|
17
|
+
copied_config = Sentry.configuration.dup
|
18
|
+
# configure dummy DSN, so the events will not be sent to the actual service
|
19
|
+
copied_config.dsn = DUMMY_DSN
|
20
|
+
# set transport to DummyTransport, so we can easily intercept the captured events
|
21
|
+
copied_config.transport.transport_class = Sentry::DummyTransport
|
22
|
+
# make sure SDK allows sending under the current environment
|
23
|
+
copied_config.enabled_environments << copied_config.environment unless copied_config.enabled_environments.include?(copied_config.environment)
|
24
|
+
# disble async event sending
|
25
|
+
copied_config.background_worker_threads = 0
|
26
|
+
|
27
|
+
# user can overwrite some of the configs, with a few exceptions like:
|
28
|
+
# - capture_exception_frame_locals
|
29
|
+
# - auto_session_tracking
|
30
|
+
block&.call(copied_config)
|
31
|
+
|
32
|
+
test_client = Sentry::Client.new(copied_config)
|
33
|
+
Sentry.get_current_hub.bind_client(test_client)
|
34
|
+
end
|
35
|
+
|
36
|
+
# Clears all stored events and envelopes.
|
37
|
+
# It should be called **after** every test case.
|
38
|
+
# @return [void]
|
39
|
+
def teardown_sentry_test
|
40
|
+
return unless Sentry.initialized?
|
41
|
+
|
42
|
+
sentry_transport.events = []
|
43
|
+
sentry_transport.envelopes = []
|
44
|
+
end
|
45
|
+
|
46
|
+
# @return [Transport]
|
47
|
+
def sentry_transport
|
48
|
+
Sentry.get_current_client.transport
|
49
|
+
end
|
50
|
+
|
51
|
+
# Returns the captured event objects.
|
52
|
+
# @return [Array<Event>]
|
53
|
+
def sentry_events
|
54
|
+
sentry_transport.events
|
55
|
+
end
|
56
|
+
|
57
|
+
# Returns the captured envelope objects.
|
58
|
+
# @return [Array<Envelope>]
|
59
|
+
def sentry_envelopes
|
60
|
+
sentry_transport.envelopes
|
61
|
+
end
|
62
|
+
|
63
|
+
# Returns the last captured event object.
|
64
|
+
# @return [Event, nil]
|
65
|
+
def last_sentry_event
|
66
|
+
sentry_events.last
|
67
|
+
end
|
68
|
+
|
69
|
+
# Extracts SDK's internal exception container (not actual exception objects) from an given event.
|
70
|
+
# @return [Array<Sentry::SingleExceptionInterface>]
|
71
|
+
def extract_sentry_exceptions(event)
|
72
|
+
event&.exception&.values || []
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
@@ -0,0 +1,206 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Sentry
|
4
|
+
class Transaction < Span
|
5
|
+
SENTRY_TRACE_REGEXP = Regexp.new(
|
6
|
+
"^[ \t]*" + # whitespace
|
7
|
+
"([0-9a-f]{32})?" + # trace_id
|
8
|
+
"-?([0-9a-f]{16})?" + # span_id
|
9
|
+
"-?([01])?" + # sampled
|
10
|
+
"[ \t]*$" # whitespace
|
11
|
+
)
|
12
|
+
UNLABELD_NAME = "<unlabeled transaction>".freeze
|
13
|
+
MESSAGE_PREFIX = "[Tracing]"
|
14
|
+
|
15
|
+
include LoggingHelper
|
16
|
+
|
17
|
+
# The name of the transaction.
|
18
|
+
# @return [String]
|
19
|
+
attr_reader :name
|
20
|
+
|
21
|
+
# The sampling decision of the parent transaction, which will be considered when making the current transaction's sampling decision.
|
22
|
+
# @return [String]
|
23
|
+
attr_reader :parent_sampled
|
24
|
+
|
25
|
+
# @deprecated Use Sentry.get_current_hub instead.
|
26
|
+
attr_reader :hub
|
27
|
+
|
28
|
+
# @deprecated Use Sentry.configuration instead.
|
29
|
+
attr_reader :configuration
|
30
|
+
|
31
|
+
# @deprecated Use Sentry.logger instead.
|
32
|
+
attr_reader :logger
|
33
|
+
|
34
|
+
def initialize(name: nil, parent_sampled: nil, hub:, **options)
|
35
|
+
super(**options)
|
36
|
+
|
37
|
+
@name = name
|
38
|
+
@parent_sampled = parent_sampled
|
39
|
+
@transaction = self
|
40
|
+
@hub = hub
|
41
|
+
@configuration = hub.configuration # to be removed
|
42
|
+
@tracing_enabled = hub.configuration.tracing_enabled?
|
43
|
+
@traces_sampler = hub.configuration.traces_sampler
|
44
|
+
@traces_sample_rate = hub.configuration.traces_sample_rate
|
45
|
+
@logger = hub.configuration.logger
|
46
|
+
init_span_recorder
|
47
|
+
end
|
48
|
+
|
49
|
+
# Initalizes a Transaction instance with a Sentry trace string from another transaction (usually from an external request).
|
50
|
+
#
|
51
|
+
# The original transaction will become the parent of the new Transaction instance. And they will share the same `trace_id`.
|
52
|
+
#
|
53
|
+
# The child transaction will also store the parent's sampling decision in its `parent_sampled` attribute.
|
54
|
+
# @param sentry_trace [String] the trace string from the previous transaction.
|
55
|
+
# @param hub [Hub] the hub that'll be responsible for sending this transaction when it's finished.
|
56
|
+
# @param options [Hash] the options you want to use to initialize a Transaction instance.
|
57
|
+
# @return [Transaction, nil]
|
58
|
+
def self.from_sentry_trace(sentry_trace, hub: Sentry.get_current_hub, **options)
|
59
|
+
return unless hub.configuration.tracing_enabled?
|
60
|
+
return unless sentry_trace
|
61
|
+
|
62
|
+
match = SENTRY_TRACE_REGEXP.match(sentry_trace)
|
63
|
+
return if match.nil?
|
64
|
+
trace_id, parent_span_id, sampled_flag = match[1..3]
|
65
|
+
|
66
|
+
parent_sampled =
|
67
|
+
if sampled_flag.nil?
|
68
|
+
nil
|
69
|
+
else
|
70
|
+
sampled_flag != "0"
|
71
|
+
end
|
72
|
+
|
73
|
+
new(trace_id: trace_id, parent_span_id: parent_span_id, parent_sampled: parent_sampled, hub: hub, **options)
|
74
|
+
end
|
75
|
+
|
76
|
+
# @return [Hash]
|
77
|
+
def to_hash
|
78
|
+
hash = super
|
79
|
+
hash.merge!(name: @name, sampled: @sampled, parent_sampled: @parent_sampled)
|
80
|
+
hash
|
81
|
+
end
|
82
|
+
|
83
|
+
# @return [Transaction]
|
84
|
+
def deep_dup
|
85
|
+
copy = super
|
86
|
+
copy.init_span_recorder(@span_recorder.max_length)
|
87
|
+
|
88
|
+
@span_recorder.spans.each do |span|
|
89
|
+
# span_recorder's first span is the current span, which should not be added to the copy's spans
|
90
|
+
next if span == self
|
91
|
+
copy.span_recorder.add(span.dup)
|
92
|
+
end
|
93
|
+
|
94
|
+
copy
|
95
|
+
end
|
96
|
+
|
97
|
+
# Sets initial sampling decision of the transaction.
|
98
|
+
# @param sampling_context [Hash] a context Hash that'll be passed to `traces_sampler` (if provided).
|
99
|
+
# @return [void]
|
100
|
+
def set_initial_sample_decision(sampling_context:)
|
101
|
+
unless @tracing_enabled
|
102
|
+
@sampled = false
|
103
|
+
return
|
104
|
+
end
|
105
|
+
|
106
|
+
return unless @sampled.nil?
|
107
|
+
|
108
|
+
sample_rate =
|
109
|
+
if @traces_sampler.is_a?(Proc)
|
110
|
+
@traces_sampler.call(sampling_context)
|
111
|
+
elsif !sampling_context[:parent_sampled].nil?
|
112
|
+
sampling_context[:parent_sampled]
|
113
|
+
else
|
114
|
+
@traces_sample_rate
|
115
|
+
end
|
116
|
+
|
117
|
+
transaction_description = generate_transaction_description
|
118
|
+
|
119
|
+
unless [true, false].include?(sample_rate) || (sample_rate.is_a?(Numeric) && sample_rate >= 0.0 && sample_rate <= 1.0)
|
120
|
+
@sampled = false
|
121
|
+
log_warn("#{MESSAGE_PREFIX} Discarding #{transaction_description} because of invalid sample_rate: #{sample_rate}")
|
122
|
+
return
|
123
|
+
end
|
124
|
+
|
125
|
+
if sample_rate == 0.0 || sample_rate == false
|
126
|
+
@sampled = false
|
127
|
+
log_debug("#{MESSAGE_PREFIX} Discarding #{transaction_description} because traces_sampler returned 0 or false")
|
128
|
+
return
|
129
|
+
end
|
130
|
+
|
131
|
+
if sample_rate == true
|
132
|
+
@sampled = true
|
133
|
+
else
|
134
|
+
@sampled = Random.rand < sample_rate
|
135
|
+
end
|
136
|
+
|
137
|
+
if @sampled
|
138
|
+
log_debug("#{MESSAGE_PREFIX} Starting #{transaction_description}")
|
139
|
+
else
|
140
|
+
log_debug(
|
141
|
+
"#{MESSAGE_PREFIX} Discarding #{transaction_description} because it's not included in the random sample (sampling rate = #{sample_rate})"
|
142
|
+
)
|
143
|
+
end
|
144
|
+
end
|
145
|
+
|
146
|
+
# Finishes the transaction's recording and send it to Sentry.
|
147
|
+
# @param hub [Hub] the hub that'll send this transaction. (Deprecated)
|
148
|
+
# @return [TransactionEvent]
|
149
|
+
def finish(hub: nil)
|
150
|
+
if hub
|
151
|
+
log_warn(
|
152
|
+
<<~MSG
|
153
|
+
Specifying a different hub in `Transaction#finish` will be deprecated in version 5.0.
|
154
|
+
Please use `Hub#start_transaction` with the designated hub.
|
155
|
+
MSG
|
156
|
+
)
|
157
|
+
end
|
158
|
+
|
159
|
+
hub ||= @hub
|
160
|
+
|
161
|
+
super() # Span#finish doesn't take arguments
|
162
|
+
|
163
|
+
if @name.nil?
|
164
|
+
@name = UNLABELD_NAME
|
165
|
+
end
|
166
|
+
|
167
|
+
if @sampled
|
168
|
+
event = hub.current_client.event_from_transaction(self)
|
169
|
+
hub.capture_event(event)
|
170
|
+
else
|
171
|
+
hub.current_client.transport.record_lost_event(:sample_rate, 'transaction')
|
172
|
+
end
|
173
|
+
end
|
174
|
+
|
175
|
+
protected
|
176
|
+
|
177
|
+
def init_span_recorder(limit = 1000)
|
178
|
+
@span_recorder = SpanRecorder.new(limit)
|
179
|
+
@span_recorder.add(self)
|
180
|
+
end
|
181
|
+
|
182
|
+
private
|
183
|
+
|
184
|
+
def generate_transaction_description
|
185
|
+
result = op.nil? ? "" : "<#{@op}> "
|
186
|
+
result += "transaction"
|
187
|
+
result += " <#{@name}>" if @name
|
188
|
+
result
|
189
|
+
end
|
190
|
+
|
191
|
+
class SpanRecorder
|
192
|
+
attr_reader :max_length, :spans
|
193
|
+
|
194
|
+
def initialize(max_length)
|
195
|
+
@max_length = max_length
|
196
|
+
@spans = []
|
197
|
+
end
|
198
|
+
|
199
|
+
def add(span)
|
200
|
+
if @spans.count < @max_length
|
201
|
+
@spans << span
|
202
|
+
end
|
203
|
+
end
|
204
|
+
end
|
205
|
+
end
|
206
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Sentry
|
4
|
+
# TransactionEvent represents events that carry transaction data (type: "transaction").
|
5
|
+
class TransactionEvent < Event
|
6
|
+
TYPE = "transaction"
|
7
|
+
|
8
|
+
# @return [<Array[Span]>]
|
9
|
+
attr_accessor :spans
|
10
|
+
|
11
|
+
# @return [Float, nil]
|
12
|
+
attr_reader :start_timestamp
|
13
|
+
|
14
|
+
# Sets the event's start_timestamp.
|
15
|
+
# @param time [Time, Float]
|
16
|
+
# @return [void]
|
17
|
+
def start_timestamp=(time)
|
18
|
+
@start_timestamp = time.is_a?(Time) ? time.to_f : time
|
19
|
+
end
|
20
|
+
|
21
|
+
# @return [Hash]
|
22
|
+
def to_hash
|
23
|
+
data = super
|
24
|
+
data[:spans] = @spans.map(&:to_hash) if @spans
|
25
|
+
data[:start_timestamp] = @start_timestamp
|
26
|
+
data
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Sentry
|
4
|
+
class Transport
|
5
|
+
class Configuration
|
6
|
+
attr_accessor :timeout, :open_timeout, :proxy, :ssl, :ssl_ca_file, :ssl_verification, :encoding
|
7
|
+
attr_reader :transport_class
|
8
|
+
|
9
|
+
def initialize
|
10
|
+
@ssl_verification = true
|
11
|
+
@open_timeout = 1
|
12
|
+
@timeout = 2
|
13
|
+
@encoding = HTTPTransport::GZIP_ENCODING
|
14
|
+
end
|
15
|
+
|
16
|
+
def transport_class=(klass)
|
17
|
+
unless klass.is_a?(Class)
|
18
|
+
raise Sentry::Error.new("config.transport.transport_class must a class. got: #{klass.class}")
|
19
|
+
end
|
20
|
+
|
21
|
+
@transport_class = klass
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Sentry
|
4
|
+
class DummyTransport < Transport
|
5
|
+
attr_accessor :events, :envelopes
|
6
|
+
|
7
|
+
def initialize(*)
|
8
|
+
super
|
9
|
+
@events = []
|
10
|
+
@envelopes = []
|
11
|
+
end
|
12
|
+
|
13
|
+
def send_event(event)
|
14
|
+
@events << event
|
15
|
+
end
|
16
|
+
|
17
|
+
def send_envelope(envelope)
|
18
|
+
@envelopes << envelope
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|