sentry-ruby 5.26.0 → 6.1.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 +2 -4
- data/lib/sentry/background_worker.rb +1 -4
- data/lib/sentry/breadcrumb.rb +1 -1
- data/lib/sentry/breadcrumb_buffer.rb +2 -2
- data/lib/sentry/check_in_event.rb +2 -2
- data/lib/sentry/client.rb +29 -89
- data/lib/sentry/configuration.rb +125 -78
- data/lib/sentry/cron/monitor_check_ins.rb +3 -3
- data/lib/sentry/cron/monitor_config.rb +2 -2
- data/lib/sentry/cron/monitor_schedule.rb +2 -2
- data/lib/sentry/debug_structured_logger.rb +94 -0
- data/lib/sentry/dsn.rb +32 -0
- data/lib/sentry/envelope/item.rb +1 -2
- data/lib/sentry/error_event.rb +3 -3
- data/lib/sentry/event.rb +4 -10
- data/lib/sentry/graphql.rb +1 -1
- data/lib/sentry/hub.rb +6 -5
- data/lib/sentry/interface.rb +1 -1
- data/lib/sentry/interfaces/exception.rb +2 -2
- data/lib/sentry/interfaces/request.rb +2 -0
- data/lib/sentry/interfaces/single_exception.rb +3 -3
- data/lib/sentry/interfaces/stacktrace.rb +3 -3
- data/lib/sentry/interfaces/stacktrace_builder.rb +0 -8
- data/lib/sentry/interfaces/threads.rb +2 -2
- data/lib/sentry/log_event.rb +19 -6
- data/lib/sentry/profiler.rb +4 -5
- data/lib/sentry/propagation_context.rb +55 -18
- data/lib/sentry/rspec.rb +1 -1
- data/lib/sentry/span.rb +2 -17
- data/lib/sentry/std_lib_logger.rb +6 -1
- data/lib/sentry/test_helper.rb +23 -0
- data/lib/sentry/transaction.rb +72 -95
- data/lib/sentry/transaction_event.rb +4 -9
- data/lib/sentry/transport/debug_transport.rb +70 -0
- data/lib/sentry/transport/dummy_transport.rb +1 -0
- data/lib/sentry/transport/http_transport.rb +9 -5
- data/lib/sentry/transport.rb +3 -5
- data/lib/sentry/utils/logging_helper.rb +8 -6
- data/lib/sentry/utils/sample_rand.rb +97 -0
- data/lib/sentry/vernier/profiler.rb +4 -3
- data/lib/sentry/version.rb +1 -1
- data/lib/sentry-ruby.rb +6 -30
- data/sentry-ruby-core.gemspec +1 -1
- data/sentry-ruby.gemspec +1 -1
- metadata +11 -18
- data/lib/sentry/metrics/aggregator.rb +0 -248
- data/lib/sentry/metrics/configuration.rb +0 -47
- data/lib/sentry/metrics/counter_metric.rb +0 -25
- data/lib/sentry/metrics/distribution_metric.rb +0 -25
- data/lib/sentry/metrics/gauge_metric.rb +0 -35
- data/lib/sentry/metrics/local_aggregator.rb +0 -53
- data/lib/sentry/metrics/metric.rb +0 -19
- data/lib/sentry/metrics/set_metric.rb +0 -28
- data/lib/sentry/metrics/timing.rb +0 -51
- data/lib/sentry/metrics.rb +0 -56
data/lib/sentry/span.rb
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
require "securerandom"
|
|
4
|
-
require "sentry/metrics/local_aggregator"
|
|
5
4
|
require "sentry/utils/uuid"
|
|
6
5
|
|
|
7
6
|
module Sentry
|
|
@@ -173,8 +172,8 @@ module Sentry
|
|
|
173
172
|
end
|
|
174
173
|
|
|
175
174
|
# @return [Hash]
|
|
176
|
-
def
|
|
177
|
-
|
|
175
|
+
def to_h
|
|
176
|
+
{
|
|
178
177
|
trace_id: @trace_id,
|
|
179
178
|
span_id: @span_id,
|
|
180
179
|
parent_span_id: @parent_span_id,
|
|
@@ -187,11 +186,6 @@ module Sentry
|
|
|
187
186
|
data: @data,
|
|
188
187
|
origin: @origin
|
|
189
188
|
}
|
|
190
|
-
|
|
191
|
-
summary = metrics_summary
|
|
192
|
-
hash[:_metrics_summary] = summary if summary
|
|
193
|
-
|
|
194
|
-
hash
|
|
195
189
|
end
|
|
196
190
|
|
|
197
191
|
# Returns the span's context that can be used to embed in an Event.
|
|
@@ -307,14 +301,5 @@ module Sentry
|
|
|
307
301
|
def set_origin(origin)
|
|
308
302
|
@origin = origin
|
|
309
303
|
end
|
|
310
|
-
|
|
311
|
-
# Collects gauge metrics on the span for metric summaries.
|
|
312
|
-
def metrics_local_aggregator
|
|
313
|
-
@metrics_local_aggregator ||= Sentry::Metrics::LocalAggregator.new
|
|
314
|
-
end
|
|
315
|
-
|
|
316
|
-
def metrics_summary
|
|
317
|
-
@metrics_local_aggregator&.to_hash
|
|
318
|
-
end
|
|
319
304
|
end
|
|
320
305
|
end
|
|
@@ -12,11 +12,16 @@ module Sentry
|
|
|
12
12
|
4 => :fatal
|
|
13
13
|
}.freeze
|
|
14
14
|
|
|
15
|
+
ORIGIN = "auto.log.ruby.std_logger"
|
|
16
|
+
|
|
15
17
|
def add(severity, message = nil, progname = nil, &block)
|
|
16
18
|
result = super
|
|
17
19
|
|
|
18
20
|
return unless Sentry.initialized? && Sentry.get_current_hub
|
|
19
21
|
|
|
22
|
+
# Only process logs that meet or exceed the logger's level
|
|
23
|
+
return result if severity < level
|
|
24
|
+
|
|
20
25
|
# exclude sentry SDK logs -- to prevent recursive log action,
|
|
21
26
|
# do not process internal logs again
|
|
22
27
|
if message.nil? && progname != Sentry::Logger::PROGNAME
|
|
@@ -32,7 +37,7 @@ module Sentry
|
|
|
32
37
|
message = message.to_s.strip
|
|
33
38
|
|
|
34
39
|
if !message.nil? && message != Sentry::Logger::PROGNAME && method = SEVERITY_MAP[severity]
|
|
35
|
-
Sentry.logger.send(method, message)
|
|
40
|
+
Sentry.logger.send(method, message, origin: ORIGIN)
|
|
36
41
|
end
|
|
37
42
|
end
|
|
38
43
|
|
data/lib/sentry/test_helper.rb
CHANGED
|
@@ -2,8 +2,13 @@
|
|
|
2
2
|
|
|
3
3
|
module Sentry
|
|
4
4
|
module TestHelper
|
|
5
|
+
module_function
|
|
6
|
+
|
|
5
7
|
DUMMY_DSN = "http://12345:67890@sentry.localdomain/sentry/42"
|
|
6
8
|
|
|
9
|
+
# Not really real, but it will be resolved as a non-local for testing needs
|
|
10
|
+
REAL_DSN = "https://user:pass@getsentry.io/project/42"
|
|
11
|
+
|
|
7
12
|
# Alters the existing SDK configuration with test-suitable options. Mainly:
|
|
8
13
|
# - Sets a dummy DSN instead of `nil` or an actual DSN.
|
|
9
14
|
# - Sets the transport to DummyTransport, which allows easy access to the captured events.
|
|
@@ -22,6 +27,7 @@ module Sentry
|
|
|
22
27
|
# set transport to DummyTransport, so we can easily intercept the captured events
|
|
23
28
|
dummy_config.transport.transport_class = Sentry::DummyTransport
|
|
24
29
|
# make sure SDK allows sending under the current environment
|
|
30
|
+
dummy_config.enabled_environments ||= []
|
|
25
31
|
dummy_config.enabled_environments += [dummy_config.environment] unless dummy_config.enabled_environments.include?(dummy_config.environment)
|
|
26
32
|
# disble async event sending
|
|
27
33
|
dummy_config.background_worker_threads = 0
|
|
@@ -46,6 +52,8 @@ module Sentry
|
|
|
46
52
|
def teardown_sentry_test
|
|
47
53
|
return unless Sentry.initialized?
|
|
48
54
|
|
|
55
|
+
clear_sentry_events
|
|
56
|
+
|
|
49
57
|
# pop testing layer created by `setup_sentry_test`
|
|
50
58
|
# but keep the base layer to avoid nil-pointer errors
|
|
51
59
|
# TODO: find a way to notify users if they somehow popped the test layer before calling this method
|
|
@@ -55,6 +63,21 @@ module Sentry
|
|
|
55
63
|
Sentry::Scope.global_event_processors.clear
|
|
56
64
|
end
|
|
57
65
|
|
|
66
|
+
def clear_sentry_events
|
|
67
|
+
return unless Sentry.initialized?
|
|
68
|
+
|
|
69
|
+
sentry_transport.clear if sentry_transport.respond_to?(:clear)
|
|
70
|
+
|
|
71
|
+
if Sentry.configuration.enable_logs && sentry_logger.respond_to?(:clear)
|
|
72
|
+
sentry_logger.clear
|
|
73
|
+
end
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
# @return [Sentry::StructuredLogger, Sentry::DebugStructuredLogger]
|
|
77
|
+
def sentry_logger
|
|
78
|
+
Sentry.logger
|
|
79
|
+
end
|
|
80
|
+
|
|
58
81
|
# @return [Transport]
|
|
59
82
|
def sentry_transport
|
|
60
83
|
Sentry.get_current_client.transport
|
data/lib/sentry/transaction.rb
CHANGED
|
@@ -2,13 +2,11 @@
|
|
|
2
2
|
|
|
3
3
|
require "sentry/baggage"
|
|
4
4
|
require "sentry/profiler"
|
|
5
|
+
require "sentry/utils/sample_rand"
|
|
5
6
|
require "sentry/propagation_context"
|
|
6
7
|
|
|
7
8
|
module Sentry
|
|
8
9
|
class Transaction < Span
|
|
9
|
-
# @deprecated Use Sentry::PropagationContext::SENTRY_TRACE_REGEXP instead.
|
|
10
|
-
SENTRY_TRACE_REGEXP = PropagationContext::SENTRY_TRACE_REGEXP
|
|
11
|
-
|
|
12
10
|
UNLABELD_NAME = "<unlabeled transaction>"
|
|
13
11
|
MESSAGE_PREFIX = "[Tracing]"
|
|
14
12
|
|
|
@@ -39,12 +37,6 @@ module Sentry
|
|
|
39
37
|
# @return [Hash]
|
|
40
38
|
attr_reader :measurements
|
|
41
39
|
|
|
42
|
-
# @deprecated Use Sentry.get_current_hub instead.
|
|
43
|
-
attr_reader :hub
|
|
44
|
-
|
|
45
|
-
# @deprecated Use Sentry.configuration instead.
|
|
46
|
-
attr_reader :configuration
|
|
47
|
-
|
|
48
40
|
# The effective sample rate at which this transaction was sampled.
|
|
49
41
|
# @return [Float, nil]
|
|
50
42
|
attr_reader :effective_sample_rate
|
|
@@ -57,90 +49,39 @@ module Sentry
|
|
|
57
49
|
# @return [Profiler]
|
|
58
50
|
attr_reader :profiler
|
|
59
51
|
|
|
52
|
+
# Sample rand value generated from trace_id
|
|
53
|
+
# @return [String]
|
|
54
|
+
attr_reader :sample_rand
|
|
55
|
+
|
|
60
56
|
def initialize(
|
|
61
|
-
hub:,
|
|
62
57
|
name: nil,
|
|
63
58
|
source: :custom,
|
|
64
59
|
parent_sampled: nil,
|
|
65
60
|
baggage: nil,
|
|
61
|
+
sample_rand: nil,
|
|
66
62
|
**options
|
|
67
63
|
)
|
|
68
64
|
super(transaction: self, **options)
|
|
69
65
|
|
|
70
66
|
set_name(name, source: source)
|
|
71
67
|
@parent_sampled = parent_sampled
|
|
72
|
-
@hub = hub
|
|
73
68
|
@baggage = baggage
|
|
74
|
-
@configuration = hub.configuration # to be removed
|
|
75
|
-
@tracing_enabled = hub.configuration.tracing_enabled?
|
|
76
|
-
@traces_sampler = hub.configuration.traces_sampler
|
|
77
|
-
@traces_sample_rate = hub.configuration.traces_sample_rate
|
|
78
|
-
@sdk_logger = hub.configuration.sdk_logger
|
|
79
|
-
@release = hub.configuration.release
|
|
80
|
-
@environment = hub.configuration.environment
|
|
81
|
-
@dsn = hub.configuration.dsn
|
|
82
69
|
@effective_sample_rate = nil
|
|
83
70
|
@contexts = {}
|
|
84
71
|
@measurements = {}
|
|
85
|
-
|
|
86
|
-
unless @hub.profiler_running?
|
|
87
|
-
@profiler = @configuration.profiler_class.new(@configuration)
|
|
88
|
-
end
|
|
72
|
+
@sample_rand = sample_rand
|
|
89
73
|
|
|
90
74
|
init_span_recorder
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
# @deprecated use Sentry.continue_trace instead.
|
|
94
|
-
#
|
|
95
|
-
# Initalizes a Transaction instance with a Sentry trace string from another transaction (usually from an external request).
|
|
96
|
-
#
|
|
97
|
-
# The original transaction will become the parent of the new Transaction instance. And they will share the same `trace_id`.
|
|
98
|
-
#
|
|
99
|
-
# The child transaction will also store the parent's sampling decision in its `parent_sampled` attribute.
|
|
100
|
-
# @param sentry_trace [String] the trace string from the previous transaction.
|
|
101
|
-
# @param baggage [String, nil] the incoming baggage header string.
|
|
102
|
-
# @param hub [Hub] the hub that'll be responsible for sending this transaction when it's finished.
|
|
103
|
-
# @param options [Hash] the options you want to use to initialize a Transaction instance.
|
|
104
|
-
# @return [Transaction, nil]
|
|
105
|
-
def self.from_sentry_trace(sentry_trace, baggage: nil, hub: Sentry.get_current_hub, **options)
|
|
106
|
-
return unless hub.configuration.tracing_enabled?
|
|
107
|
-
return unless sentry_trace
|
|
108
|
-
|
|
109
|
-
sentry_trace_data = extract_sentry_trace(sentry_trace)
|
|
110
|
-
return unless sentry_trace_data
|
|
111
|
-
|
|
112
|
-
trace_id, parent_span_id, parent_sampled = sentry_trace_data
|
|
113
|
-
|
|
114
|
-
baggage =
|
|
115
|
-
if baggage && !baggage.empty?
|
|
116
|
-
Baggage.from_incoming_header(baggage)
|
|
117
|
-
else
|
|
118
|
-
# If there's an incoming sentry-trace but no incoming baggage header,
|
|
119
|
-
# for instance in traces coming from older SDKs,
|
|
120
|
-
# baggage will be empty and frozen and won't be populated as head SDK.
|
|
121
|
-
Baggage.new({})
|
|
122
|
-
end
|
|
75
|
+
init_profiler
|
|
123
76
|
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
parent_span_id: parent_span_id,
|
|
129
|
-
parent_sampled: parent_sampled,
|
|
130
|
-
hub: hub,
|
|
131
|
-
baggage: baggage,
|
|
132
|
-
**options
|
|
133
|
-
)
|
|
134
|
-
end
|
|
135
|
-
|
|
136
|
-
# @deprecated Use Sentry::PropagationContext.extract_sentry_trace instead.
|
|
137
|
-
# @return [Array, nil]
|
|
138
|
-
def self.extract_sentry_trace(sentry_trace)
|
|
139
|
-
PropagationContext.extract_sentry_trace(sentry_trace)
|
|
77
|
+
unless @sample_rand
|
|
78
|
+
generator = Utils::SampleRand.new(trace_id: @trace_id)
|
|
79
|
+
@sample_rand = generator.generate_from_trace_id
|
|
80
|
+
end
|
|
140
81
|
end
|
|
141
82
|
|
|
142
83
|
# @return [Hash]
|
|
143
|
-
def
|
|
84
|
+
def to_h
|
|
144
85
|
hash = super
|
|
145
86
|
|
|
146
87
|
hash.merge!(
|
|
@@ -153,6 +94,13 @@ module Sentry
|
|
|
153
94
|
hash
|
|
154
95
|
end
|
|
155
96
|
|
|
97
|
+
def parent_sample_rate
|
|
98
|
+
return unless @baggage&.items
|
|
99
|
+
|
|
100
|
+
sample_rate_str = @baggage.items["sample_rate"]
|
|
101
|
+
sample_rate_str&.to_f
|
|
102
|
+
end
|
|
103
|
+
|
|
156
104
|
# @return [Transaction]
|
|
157
105
|
def deep_dup
|
|
158
106
|
copy = super
|
|
@@ -180,7 +128,9 @@ module Sentry
|
|
|
180
128
|
# @param sampling_context [Hash] a context Hash that'll be passed to `traces_sampler` (if provided).
|
|
181
129
|
# @return [void]
|
|
182
130
|
def set_initial_sample_decision(sampling_context:)
|
|
183
|
-
|
|
131
|
+
configuration = Sentry.configuration
|
|
132
|
+
|
|
133
|
+
unless configuration && configuration.tracing_enabled?
|
|
184
134
|
@sampled = false
|
|
185
135
|
return
|
|
186
136
|
end
|
|
@@ -191,12 +141,12 @@ module Sentry
|
|
|
191
141
|
end
|
|
192
142
|
|
|
193
143
|
sample_rate =
|
|
194
|
-
if
|
|
195
|
-
|
|
144
|
+
if configuration.traces_sampler.is_a?(Proc)
|
|
145
|
+
configuration.traces_sampler.call(sampling_context)
|
|
196
146
|
elsif !sampling_context[:parent_sampled].nil?
|
|
197
147
|
sampling_context[:parent_sampled]
|
|
198
148
|
else
|
|
199
|
-
|
|
149
|
+
configuration.traces_sample_rate
|
|
200
150
|
end
|
|
201
151
|
|
|
202
152
|
transaction_description = generate_transaction_description
|
|
@@ -225,7 +175,7 @@ module Sentry
|
|
|
225
175
|
@effective_sample_rate /= 2**factor
|
|
226
176
|
end
|
|
227
177
|
|
|
228
|
-
@sampled =
|
|
178
|
+
@sampled = @sample_rand < @effective_sample_rate
|
|
229
179
|
end
|
|
230
180
|
|
|
231
181
|
if @sampled
|
|
@@ -238,29 +188,28 @@ module Sentry
|
|
|
238
188
|
end
|
|
239
189
|
|
|
240
190
|
# Finishes the transaction's recording and send it to Sentry.
|
|
241
|
-
# @param hub [Hub] the hub that'll send this transaction. (Deprecated)
|
|
242
191
|
# @return [TransactionEvent]
|
|
243
|
-
def finish(
|
|
244
|
-
if hub
|
|
245
|
-
log_warn(
|
|
246
|
-
<<~MSG
|
|
247
|
-
Specifying a different hub in `Transaction#finish` will be deprecated in version 5.0.
|
|
248
|
-
Please use `Hub#start_transaction` with the designated hub.
|
|
249
|
-
MSG
|
|
250
|
-
)
|
|
251
|
-
end
|
|
252
|
-
|
|
253
|
-
hub ||= @hub
|
|
254
|
-
|
|
192
|
+
def finish(end_timestamp: nil)
|
|
255
193
|
super(end_timestamp: end_timestamp)
|
|
256
194
|
|
|
257
195
|
if @name.nil?
|
|
258
196
|
@name = UNLABELD_NAME
|
|
259
197
|
end
|
|
260
198
|
|
|
261
|
-
|
|
199
|
+
hub = Sentry.get_current_hub
|
|
200
|
+
return unless hub
|
|
262
201
|
|
|
263
|
-
|
|
202
|
+
hub.stop_profiler!(self)
|
|
203
|
+
|
|
204
|
+
if @sampled && ignore_status_code?
|
|
205
|
+
@sampled = false
|
|
206
|
+
|
|
207
|
+
status_code = get_http_status_code
|
|
208
|
+
log_debug("#{MESSAGE_PREFIX} Discarding #{generate_transaction_description} due to ignored HTTP status code: #{status_code}")
|
|
209
|
+
|
|
210
|
+
hub.current_client.transport.record_lost_event(:event_processor, "transaction")
|
|
211
|
+
hub.current_client.transport.record_lost_event(:event_processor, "span")
|
|
212
|
+
elsif @sampled
|
|
264
213
|
event = hub.current_client.event_from_transaction(self)
|
|
265
214
|
hub.capture_event(event)
|
|
266
215
|
else
|
|
@@ -318,6 +267,15 @@ module Sentry
|
|
|
318
267
|
@span_recorder.add(self)
|
|
319
268
|
end
|
|
320
269
|
|
|
270
|
+
def init_profiler
|
|
271
|
+
hub = Sentry.get_current_hub
|
|
272
|
+
return unless hub
|
|
273
|
+
|
|
274
|
+
unless hub.profiler_running?
|
|
275
|
+
@profiler = hub.configuration.profiler_class.new(hub.configuration)
|
|
276
|
+
end
|
|
277
|
+
end
|
|
278
|
+
|
|
321
279
|
private
|
|
322
280
|
|
|
323
281
|
def generate_transaction_description
|
|
@@ -328,13 +286,16 @@ module Sentry
|
|
|
328
286
|
end
|
|
329
287
|
|
|
330
288
|
def populate_head_baggage
|
|
289
|
+
configuration = Sentry.configuration
|
|
290
|
+
|
|
331
291
|
items = {
|
|
332
292
|
"trace_id" => trace_id,
|
|
333
293
|
"sample_rate" => effective_sample_rate&.to_s,
|
|
294
|
+
"sample_rand" => Utils::SampleRand.format(@sample_rand),
|
|
334
295
|
"sampled" => sampled&.to_s,
|
|
335
|
-
"environment" =>
|
|
336
|
-
"release" =>
|
|
337
|
-
"public_key" =>
|
|
296
|
+
"environment" => configuration&.environment,
|
|
297
|
+
"release" => configuration&.release,
|
|
298
|
+
"public_key" => configuration&.dsn&.public_key
|
|
338
299
|
}
|
|
339
300
|
|
|
340
301
|
items["transaction"] = name unless source_low_quality?
|
|
@@ -343,6 +304,22 @@ module Sentry
|
|
|
343
304
|
@baggage = Baggage.new(items, mutable: false)
|
|
344
305
|
end
|
|
345
306
|
|
|
307
|
+
def ignore_status_code?
|
|
308
|
+
trace_ignore_status_codes = Sentry.configuration&.trace_ignore_status_codes
|
|
309
|
+
return false unless trace_ignore_status_codes
|
|
310
|
+
|
|
311
|
+
status_code = get_http_status_code
|
|
312
|
+
return false unless status_code
|
|
313
|
+
|
|
314
|
+
trace_ignore_status_codes.any? do |ignored|
|
|
315
|
+
ignored.is_a?(Range) ? ignored.include?(status_code) : status_code == ignored
|
|
316
|
+
end
|
|
317
|
+
end
|
|
318
|
+
|
|
319
|
+
def get_http_status_code
|
|
320
|
+
@data && @data[Span::DataConventions::HTTP_STATUS_CODE]
|
|
321
|
+
end
|
|
322
|
+
|
|
346
323
|
class SpanRecorder
|
|
347
324
|
attr_reader :max_length, :spans
|
|
348
325
|
|
|
@@ -17,9 +17,6 @@ module Sentry
|
|
|
17
17
|
# @return [Hash, nil]
|
|
18
18
|
attr_accessor :profile
|
|
19
19
|
|
|
20
|
-
# @return [Hash, nil]
|
|
21
|
-
attr_accessor :metrics_summary
|
|
22
|
-
|
|
23
20
|
def initialize(transaction:, **options)
|
|
24
21
|
super(**options)
|
|
25
22
|
|
|
@@ -32,10 +29,9 @@ module Sentry
|
|
|
32
29
|
self.tags = transaction.tags
|
|
33
30
|
self.dynamic_sampling_context = transaction.get_baggage.dynamic_sampling_context
|
|
34
31
|
self.measurements = transaction.measurements
|
|
35
|
-
self.metrics_summary = transaction.metrics_summary
|
|
36
32
|
|
|
37
33
|
finished_spans = transaction.span_recorder.spans.select { |span| span.timestamp && span != transaction }
|
|
38
|
-
self.spans = finished_spans.map(&:
|
|
34
|
+
self.spans = finished_spans.map(&:to_h)
|
|
39
35
|
|
|
40
36
|
populate_profile(transaction)
|
|
41
37
|
end
|
|
@@ -48,12 +44,11 @@ module Sentry
|
|
|
48
44
|
end
|
|
49
45
|
|
|
50
46
|
# @return [Hash]
|
|
51
|
-
def
|
|
47
|
+
def to_h
|
|
52
48
|
data = super
|
|
53
|
-
data[:spans] = @spans.map(&:
|
|
49
|
+
data[:spans] = @spans.map(&:to_h) if @spans
|
|
54
50
|
data[:start_timestamp] = @start_timestamp
|
|
55
51
|
data[:measurements] = @measurements
|
|
56
|
-
data[:_metrics_summary] = @metrics_summary if @metrics_summary
|
|
57
52
|
data
|
|
58
53
|
end
|
|
59
54
|
|
|
@@ -62,7 +57,7 @@ module Sentry
|
|
|
62
57
|
EMPTY_PROFILE = {}.freeze
|
|
63
58
|
|
|
64
59
|
def populate_profile(transaction)
|
|
65
|
-
profile_hash = transaction.profiler&.
|
|
60
|
+
profile_hash = transaction.profiler&.to_h || EMPTY_PROFILE
|
|
66
61
|
|
|
67
62
|
return if profile_hash.empty?
|
|
68
63
|
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "json"
|
|
4
|
+
require "fileutils"
|
|
5
|
+
require "pathname"
|
|
6
|
+
require "delegate"
|
|
7
|
+
|
|
8
|
+
module Sentry
|
|
9
|
+
# DebugTransport is a transport that logs events to a file for debugging purposes.
|
|
10
|
+
#
|
|
11
|
+
# It can optionally also send events to Sentry via HTTP transport if a real DSN
|
|
12
|
+
# is provided.
|
|
13
|
+
class DebugTransport < SimpleDelegator
|
|
14
|
+
DEFAULT_LOG_FILE_PATH = File.join("log", "sentry_debug_events.log")
|
|
15
|
+
|
|
16
|
+
attr_reader :log_file, :backend
|
|
17
|
+
|
|
18
|
+
def initialize(configuration)
|
|
19
|
+
@log_file = initialize_log_file(configuration)
|
|
20
|
+
@backend = initialize_backend(configuration)
|
|
21
|
+
|
|
22
|
+
super(@backend)
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def send_event(event)
|
|
26
|
+
log_envelope(envelope_from_event(event))
|
|
27
|
+
backend.send_event(event)
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def log_envelope(envelope)
|
|
31
|
+
envelope_json = {
|
|
32
|
+
timestamp: Time.now.utc.iso8601,
|
|
33
|
+
envelope_headers: envelope.headers,
|
|
34
|
+
items: envelope.items.map do |item|
|
|
35
|
+
{ headers: item.headers, payload: item.payload }
|
|
36
|
+
end
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
File.open(log_file, "a") { |file| file << JSON.dump(envelope_json) << "\n" }
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def logged_envelopes
|
|
43
|
+
return [] unless File.exist?(log_file)
|
|
44
|
+
|
|
45
|
+
File.readlines(log_file).map do |line|
|
|
46
|
+
JSON.parse(line)
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def clear
|
|
51
|
+
File.write(log_file, "")
|
|
52
|
+
log_debug("DebugTransport: Cleared events from #{log_file}")
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
private
|
|
56
|
+
|
|
57
|
+
def initialize_backend(configuration)
|
|
58
|
+
backend = configuration.dsn.local? ? DummyTransport : HTTPTransport
|
|
59
|
+
backend.new(configuration)
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
def initialize_log_file(configuration)
|
|
63
|
+
log_file = Pathname(configuration.sdk_debug_transport_log_file || DEFAULT_LOG_FILE_PATH)
|
|
64
|
+
|
|
65
|
+
FileUtils.mkdir_p(log_file.dirname) unless log_file.dirname.exist?
|
|
66
|
+
|
|
67
|
+
log_file
|
|
68
|
+
end
|
|
69
|
+
end
|
|
70
|
+
end
|
|
@@ -45,11 +45,7 @@ module Sentry
|
|
|
45
45
|
auth_header = generate_auth_header
|
|
46
46
|
headers["X-Sentry-Auth"] = auth_header if auth_header
|
|
47
47
|
|
|
48
|
-
response =
|
|
49
|
-
request = ::Net::HTTP::Post.new(endpoint, headers)
|
|
50
|
-
request.body = data
|
|
51
|
-
http.request(request)
|
|
52
|
-
end
|
|
48
|
+
response = do_request(endpoint, headers, data)
|
|
53
49
|
|
|
54
50
|
if response.code.match?(/\A2\d{2}/)
|
|
55
51
|
handle_rate_limited_response(response) if has_rate_limited_header?(response)
|
|
@@ -111,6 +107,14 @@ module Sentry
|
|
|
111
107
|
connection
|
|
112
108
|
end
|
|
113
109
|
|
|
110
|
+
def do_request(endpoint, headers, body)
|
|
111
|
+
conn.start do |http|
|
|
112
|
+
request = ::Net::HTTP::Post.new(endpoint, headers)
|
|
113
|
+
request.body = body
|
|
114
|
+
http.request(request)
|
|
115
|
+
end
|
|
116
|
+
end
|
|
117
|
+
|
|
114
118
|
private
|
|
115
119
|
|
|
116
120
|
def has_rate_limited_header?(headers)
|
data/lib/sentry/transport.rb
CHANGED
|
@@ -113,7 +113,7 @@ module Sentry
|
|
|
113
113
|
|
|
114
114
|
def envelope_from_event(event)
|
|
115
115
|
# Convert to hash
|
|
116
|
-
event_payload = event.
|
|
116
|
+
event_payload = event.to_h
|
|
117
117
|
event_id = event_payload[:event_id] || event_payload["event_id"]
|
|
118
118
|
item_type = event_payload[:type] || event_payload["type"]
|
|
119
119
|
|
|
@@ -124,10 +124,7 @@ module Sentry
|
|
|
124
124
|
sent_at: Sentry.utc_now.iso8601
|
|
125
125
|
}
|
|
126
126
|
|
|
127
|
-
|
|
128
|
-
envelope_headers[:trace] = event.dynamic_sampling_context
|
|
129
|
-
end
|
|
130
|
-
|
|
127
|
+
envelope_headers[:trace] = event.dynamic_sampling_context if event.dynamic_sampling_context
|
|
131
128
|
envelope = Envelope.new(envelope_headers)
|
|
132
129
|
|
|
133
130
|
if event.is_a?(LogEvent)
|
|
@@ -223,3 +220,4 @@ end
|
|
|
223
220
|
require "sentry/transport/dummy_transport"
|
|
224
221
|
require "sentry/transport/http_transport"
|
|
225
222
|
require "sentry/transport/spotlight_transport"
|
|
223
|
+
require "sentry/transport/debug_transport"
|
|
@@ -3,27 +3,29 @@
|
|
|
3
3
|
module Sentry
|
|
4
4
|
# @private
|
|
5
5
|
module LoggingHelper
|
|
6
|
-
# @!visibility private
|
|
7
|
-
attr_reader :sdk_logger
|
|
8
|
-
|
|
9
6
|
# @!visibility private
|
|
10
7
|
def log_error(message, exception, debug: false)
|
|
11
8
|
message = "#{message}: #{exception.message}"
|
|
12
9
|
message += "\n#{exception.backtrace.join("\n")}" if debug
|
|
13
10
|
|
|
14
|
-
sdk_logger
|
|
11
|
+
sdk_logger&.error(LOGGER_PROGNAME) do
|
|
15
12
|
message
|
|
16
13
|
end
|
|
17
14
|
end
|
|
18
15
|
|
|
19
16
|
# @!visibility private
|
|
20
17
|
def log_debug(message)
|
|
21
|
-
sdk_logger
|
|
18
|
+
sdk_logger&.debug(LOGGER_PROGNAME) { message }
|
|
22
19
|
end
|
|
23
20
|
|
|
24
21
|
# @!visibility private
|
|
25
22
|
def log_warn(message)
|
|
26
|
-
sdk_logger
|
|
23
|
+
sdk_logger&.warn(LOGGER_PROGNAME) { message }
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
# @!visibility private
|
|
27
|
+
def sdk_logger
|
|
28
|
+
@sdk_logger ||= Sentry.sdk_logger
|
|
27
29
|
end
|
|
28
30
|
end
|
|
29
31
|
end
|