sentry-ruby 5.26.0 → 6.5.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 +27 -5
- data/README.md +3 -3
- data/lib/sentry/background_worker.rb +1 -4
- data/lib/sentry/backtrace/line.rb +99 -0
- data/lib/sentry/backtrace.rb +44 -76
- data/lib/sentry/baggage.rb +2 -2
- 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 +59 -136
- data/lib/sentry/configuration.rb +206 -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 +65 -1
- data/lib/sentry/envelope/item.rb +3 -3
- data/lib/sentry/error_event.rb +3 -3
- data/lib/sentry/event.rb +4 -10
- data/lib/sentry/exceptions.rb +3 -0
- data/lib/sentry/graphql.rb +1 -1
- data/lib/sentry/hub.rb +29 -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 +4 -4
- 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 +33 -138
- data/lib/sentry/log_event_buffer.rb +13 -60
- data/lib/sentry/metric_event.rb +49 -0
- data/lib/sentry/metric_event_buffer.rb +28 -0
- data/lib/sentry/metrics.rb +47 -42
- data/lib/sentry/profiler.rb +4 -5
- data/lib/sentry/propagation_context.rb +101 -24
- data/lib/sentry/rack/capture_exceptions.rb +90 -2
- data/lib/sentry/release_detector.rb +1 -1
- data/lib/sentry/rspec.rb +1 -1
- data/lib/sentry/scope.rb +51 -18
- data/lib/sentry/sequel.rb +35 -0
- data/lib/sentry/span.rb +5 -17
- data/lib/sentry/std_lib_logger.rb +10 -1
- data/lib/sentry/telemetry_event_buffer.rb +130 -0
- data/lib/sentry/test_helper.rb +30 -0
- data/lib/sentry/transaction.rb +73 -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 +16 -16
- data/lib/sentry/transport.rb +10 -7
- data/lib/sentry/utils/encoding_helper.rb +6 -0
- data/lib/sentry/utils/logging_helper.rb +25 -9
- data/lib/sentry/utils/sample_rand.rb +97 -0
- data/lib/sentry/utils/telemetry_attributes.rb +30 -0
- data/lib/sentry/vernier/profiler.rb +4 -3
- data/lib/sentry/version.rb +1 -1
- data/lib/sentry-ruby.rb +57 -30
- data/sentry-ruby-core.gemspec +1 -1
- data/sentry-ruby.gemspec +2 -1
- metadata +31 -17
- 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/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
|
|
@@ -50,6 +49,9 @@ module Sentry
|
|
|
50
49
|
MESSAGING_DESTINATION_NAME = "messaging.destination.name"
|
|
51
50
|
MESSAGING_MESSAGE_RECEIVE_LATENCY = "messaging.message.receive.latency"
|
|
52
51
|
MESSAGING_MESSAGE_RETRY_COUNT = "messaging.message.retry.count"
|
|
52
|
+
|
|
53
|
+
# Time in ms the request spent in the server queue before processing began.
|
|
54
|
+
HTTP_QUEUE_TIME_MS = "http.server.request.time_in_queue"
|
|
53
55
|
end
|
|
54
56
|
|
|
55
57
|
STATUS_MAP = {
|
|
@@ -173,8 +175,8 @@ module Sentry
|
|
|
173
175
|
end
|
|
174
176
|
|
|
175
177
|
# @return [Hash]
|
|
176
|
-
def
|
|
177
|
-
|
|
178
|
+
def to_h
|
|
179
|
+
{
|
|
178
180
|
trace_id: @trace_id,
|
|
179
181
|
span_id: @span_id,
|
|
180
182
|
parent_span_id: @parent_span_id,
|
|
@@ -187,11 +189,6 @@ module Sentry
|
|
|
187
189
|
data: @data,
|
|
188
190
|
origin: @origin
|
|
189
191
|
}
|
|
190
|
-
|
|
191
|
-
summary = metrics_summary
|
|
192
|
-
hash[:_metrics_summary] = summary if summary
|
|
193
|
-
|
|
194
|
-
hash
|
|
195
192
|
end
|
|
196
193
|
|
|
197
194
|
# Returns the span's context that can be used to embed in an Event.
|
|
@@ -307,14 +304,5 @@ module Sentry
|
|
|
307
304
|
def set_origin(origin)
|
|
308
305
|
@origin = origin
|
|
309
306
|
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
307
|
end
|
|
320
308
|
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,11 @@ 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.
|
|
40
|
+
if (filter = Sentry.configuration.std_lib_logger_filter) && !filter.call(self, message, method)
|
|
41
|
+
return result
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
Sentry.logger.send(method, message, origin: ORIGIN)
|
|
36
45
|
end
|
|
37
46
|
end
|
|
38
47
|
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "sentry/threaded_periodic_worker"
|
|
4
|
+
require "sentry/envelope"
|
|
5
|
+
|
|
6
|
+
module Sentry
|
|
7
|
+
# TelemetryEventBuffer is a base class for buffering telemetry events (logs, metrics, etc.)
|
|
8
|
+
# and sending them to Sentry in a single envelope.
|
|
9
|
+
#
|
|
10
|
+
# This is used internally by the `Sentry::Client`.
|
|
11
|
+
#
|
|
12
|
+
# @!visibility private
|
|
13
|
+
class TelemetryEventBuffer < ThreadedPeriodicWorker
|
|
14
|
+
FLUSH_INTERVAL = 5 # seconds
|
|
15
|
+
|
|
16
|
+
# @!visibility private
|
|
17
|
+
attr_reader :pending_items, :envelope_type, :data_category, :thread
|
|
18
|
+
|
|
19
|
+
def initialize(configuration, client, event_class:, max_items:, max_items_before_drop:, envelope_type:, envelope_content_type:, before_send:)
|
|
20
|
+
super(configuration.sdk_logger, FLUSH_INTERVAL)
|
|
21
|
+
|
|
22
|
+
@client = client
|
|
23
|
+
@dsn = configuration.dsn
|
|
24
|
+
@debug = configuration.debug
|
|
25
|
+
@event_class = event_class
|
|
26
|
+
@max_items = max_items
|
|
27
|
+
@max_items_before_drop = max_items_before_drop
|
|
28
|
+
@envelope_type = envelope_type
|
|
29
|
+
@data_category = Sentry::Envelope::Item.data_category(@envelope_type)
|
|
30
|
+
@envelope_content_type = envelope_content_type
|
|
31
|
+
@before_send = before_send
|
|
32
|
+
|
|
33
|
+
@pending_items = []
|
|
34
|
+
@mutex = Mutex.new
|
|
35
|
+
|
|
36
|
+
log_debug("[#{self.class}] Initialized buffer with max_items=#{@max_items}, flush_interval=#{FLUSH_INTERVAL}s")
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def flush
|
|
40
|
+
@mutex.synchronize do
|
|
41
|
+
return if empty?
|
|
42
|
+
|
|
43
|
+
log_debug("[#{self.class}] flushing #{size} #{@event_class}")
|
|
44
|
+
|
|
45
|
+
send_items
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
self
|
|
49
|
+
end
|
|
50
|
+
alias_method :run, :flush
|
|
51
|
+
|
|
52
|
+
def add_item(item)
|
|
53
|
+
@mutex.synchronize do
|
|
54
|
+
return unless ensure_thread
|
|
55
|
+
|
|
56
|
+
if size >= @max_items_before_drop
|
|
57
|
+
log_debug("[#{self.class}] exceeded max capacity, dropping event")
|
|
58
|
+
@client.transport.record_lost_event(:queue_overflow, @data_category)
|
|
59
|
+
else
|
|
60
|
+
@pending_items << item
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
send_items if size >= @max_items
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
self
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
def empty?
|
|
70
|
+
@pending_items.empty?
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
def size
|
|
74
|
+
@pending_items.size
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
def clear!
|
|
78
|
+
@pending_items.clear
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
private
|
|
82
|
+
|
|
83
|
+
def send_items
|
|
84
|
+
envelope = Envelope.new(
|
|
85
|
+
event_id: Sentry::Utils.uuid,
|
|
86
|
+
sent_at: Sentry.utc_now.iso8601,
|
|
87
|
+
dsn: @dsn,
|
|
88
|
+
sdk: Sentry.sdk_meta
|
|
89
|
+
)
|
|
90
|
+
|
|
91
|
+
discarded_count = 0
|
|
92
|
+
envelope_items = []
|
|
93
|
+
|
|
94
|
+
if @before_send
|
|
95
|
+
@pending_items.each do |item|
|
|
96
|
+
processed_item = @before_send.call(item)
|
|
97
|
+
|
|
98
|
+
if processed_item
|
|
99
|
+
envelope_items << processed_item.to_h
|
|
100
|
+
else
|
|
101
|
+
discarded_count += 1
|
|
102
|
+
end
|
|
103
|
+
end
|
|
104
|
+
else
|
|
105
|
+
envelope_items = @pending_items.map(&:to_h)
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
unless discarded_count.zero?
|
|
109
|
+
@client.transport.record_lost_event(:before_send, @data_category, num: discarded_count)
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
return if envelope_items.empty?
|
|
113
|
+
|
|
114
|
+
envelope.add_item(
|
|
115
|
+
{
|
|
116
|
+
type: @envelope_type,
|
|
117
|
+
item_count: envelope_items.size,
|
|
118
|
+
content_type: @envelope_content_type
|
|
119
|
+
},
|
|
120
|
+
{ items: envelope_items }
|
|
121
|
+
)
|
|
122
|
+
|
|
123
|
+
@client.send_envelope(envelope)
|
|
124
|
+
rescue => e
|
|
125
|
+
log_error("[#{self.class}] Failed to send #{@event_class}", e, debug: @debug)
|
|
126
|
+
ensure
|
|
127
|
+
clear!
|
|
128
|
+
end
|
|
129
|
+
end
|
|
130
|
+
end
|
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
|
|
@@ -79,6 +102,13 @@ module Sentry
|
|
|
79
102
|
.flat_map { |item| item.payload[:items] }
|
|
80
103
|
end
|
|
81
104
|
|
|
105
|
+
def sentry_metrics
|
|
106
|
+
sentry_envelopes
|
|
107
|
+
.flat_map(&:items)
|
|
108
|
+
.select { |item| item.headers[:type] == "trace_metric" }
|
|
109
|
+
.flat_map { |item| item.payload[:items] }
|
|
110
|
+
end
|
|
111
|
+
|
|
82
112
|
# Returns the last captured event object.
|
|
83
113
|
# @return [Event, nil]
|
|
84
114
|
def last_sentry_event
|
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,17 @@ 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,
|
|
299
|
+
"org_id" => configuration&.effective_org_id
|
|
338
300
|
}
|
|
339
301
|
|
|
340
302
|
items["transaction"] = name unless source_low_quality?
|
|
@@ -343,6 +305,22 @@ module Sentry
|
|
|
343
305
|
@baggage = Baggage.new(items, mutable: false)
|
|
344
306
|
end
|
|
345
307
|
|
|
308
|
+
def ignore_status_code?
|
|
309
|
+
trace_ignore_status_codes = Sentry.configuration&.trace_ignore_status_codes
|
|
310
|
+
return false unless trace_ignore_status_codes
|
|
311
|
+
|
|
312
|
+
status_code = get_http_status_code
|
|
313
|
+
return false unless status_code
|
|
314
|
+
|
|
315
|
+
trace_ignore_status_codes.any? do |ignored|
|
|
316
|
+
ignored.is_a?(Range) ? ignored.include?(status_code) : status_code == ignored
|
|
317
|
+
end
|
|
318
|
+
end
|
|
319
|
+
|
|
320
|
+
def get_http_status_code
|
|
321
|
+
@data && @data[Span::DataConventions::HTTP_STATUS_CODE]
|
|
322
|
+
end
|
|
323
|
+
|
|
346
324
|
class SpanRecorder
|
|
347
325
|
attr_reader :max_length, :spans
|
|
348
326
|
|
|
@@ -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
|