sentry-ruby 5.16.1 → 5.20.1
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 +3 -0
- data/README.md +20 -10
- data/Rakefile +1 -1
- data/bin/console +1 -0
- data/lib/sentry/attachment.rb +40 -0
- data/lib/sentry/background_worker.rb +1 -1
- data/lib/sentry/backpressure_monitor.rb +2 -32
- data/lib/sentry/backtrace.rb +8 -6
- data/lib/sentry/baggage.rb +6 -6
- data/lib/sentry/breadcrumb/sentry_logger.rb +6 -6
- data/lib/sentry/check_in_event.rb +5 -5
- data/lib/sentry/client.rb +61 -11
- data/lib/sentry/configuration.rb +53 -25
- data/lib/sentry/core_ext/object/deep_dup.rb +1 -1
- data/lib/sentry/cron/monitor_check_ins.rb +1 -1
- data/lib/sentry/cron/monitor_config.rb +1 -1
- data/lib/sentry/cron/monitor_schedule.rb +1 -1
- data/lib/sentry/dsn.rb +4 -4
- data/lib/sentry/envelope.rb +19 -2
- data/lib/sentry/error_event.rb +2 -2
- data/lib/sentry/event.rb +20 -18
- data/lib/sentry/faraday.rb +77 -0
- data/lib/sentry/graphql.rb +9 -0
- data/lib/sentry/hub.rb +23 -3
- data/lib/sentry/integrable.rb +4 -0
- data/lib/sentry/interface.rb +1 -0
- data/lib/sentry/interfaces/exception.rb +5 -3
- data/lib/sentry/interfaces/mechanism.rb +20 -0
- data/lib/sentry/interfaces/request.rb +7 -7
- data/lib/sentry/interfaces/single_exception.rb +7 -5
- data/lib/sentry/interfaces/stacktrace.rb +3 -1
- data/lib/sentry/interfaces/stacktrace_builder.rb +23 -2
- data/lib/sentry/logger.rb +1 -1
- data/lib/sentry/metrics/aggregator.rb +248 -0
- data/lib/sentry/metrics/configuration.rb +47 -0
- data/lib/sentry/metrics/counter_metric.rb +25 -0
- data/lib/sentry/metrics/distribution_metric.rb +25 -0
- data/lib/sentry/metrics/gauge_metric.rb +35 -0
- data/lib/sentry/metrics/local_aggregator.rb +53 -0
- data/lib/sentry/metrics/metric.rb +19 -0
- data/lib/sentry/metrics/set_metric.rb +28 -0
- data/lib/sentry/metrics/timing.rb +43 -0
- data/lib/sentry/metrics.rb +56 -0
- data/lib/sentry/net/http.rb +18 -39
- data/lib/sentry/profiler.rb +19 -20
- data/lib/sentry/propagation_context.rb +10 -9
- data/lib/sentry/puma.rb +1 -1
- data/lib/sentry/rack/capture_exceptions.rb +15 -3
- data/lib/sentry/rack.rb +2 -2
- data/lib/sentry/rake.rb +4 -2
- data/lib/sentry/redis.rb +2 -1
- data/lib/sentry/release_detector.rb +4 -4
- data/lib/sentry/scope.rb +36 -26
- data/lib/sentry/session.rb +2 -2
- data/lib/sentry/session_flusher.rb +7 -39
- data/lib/sentry/span.rb +46 -5
- data/lib/sentry/test_helper.rb +3 -2
- data/lib/sentry/threaded_periodic_worker.rb +39 -0
- data/lib/sentry/transaction.rb +17 -15
- data/lib/sentry/transaction_event.rb +6 -1
- data/lib/sentry/transport/configuration.rb +0 -1
- data/lib/sentry/transport/http_transport.rb +12 -12
- data/lib/sentry/transport.rb +18 -26
- data/lib/sentry/utils/argument_checking_helper.rb +6 -0
- data/lib/sentry/utils/env_helper.rb +21 -0
- data/lib/sentry/utils/http_tracing.rb +41 -0
- data/lib/sentry/utils/logging_helper.rb +0 -4
- data/lib/sentry/utils/real_ip.rb +2 -2
- data/lib/sentry/utils/request_id.rb +1 -1
- data/lib/sentry/version.rb +1 -1
- data/lib/sentry-ruby.rb +34 -3
- data/sentry-ruby-core.gemspec +1 -1
- data/sentry-ruby.gemspec +13 -6
- metadata +40 -7
@@ -0,0 +1,56 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "sentry/metrics/metric"
|
4
|
+
require "sentry/metrics/counter_metric"
|
5
|
+
require "sentry/metrics/distribution_metric"
|
6
|
+
require "sentry/metrics/gauge_metric"
|
7
|
+
require "sentry/metrics/set_metric"
|
8
|
+
require "sentry/metrics/timing"
|
9
|
+
require "sentry/metrics/aggregator"
|
10
|
+
|
11
|
+
module Sentry
|
12
|
+
module Metrics
|
13
|
+
DURATION_UNITS = %w[nanosecond microsecond millisecond second minute hour day week]
|
14
|
+
INFORMATION_UNITS = %w[bit byte kilobyte kibibyte megabyte mebibyte gigabyte gibibyte terabyte tebibyte petabyte pebibyte exabyte exbibyte]
|
15
|
+
FRACTIONAL_UNITS = %w[ratio percent]
|
16
|
+
|
17
|
+
OP_NAME = "metric.timing"
|
18
|
+
SPAN_ORIGIN = "auto.metric.timing"
|
19
|
+
|
20
|
+
class << self
|
21
|
+
def increment(key, value = 1.0, unit: "none", tags: {}, timestamp: nil)
|
22
|
+
Sentry.metrics_aggregator&.add(:c, key, value, unit: unit, tags: tags, timestamp: timestamp)
|
23
|
+
end
|
24
|
+
|
25
|
+
def distribution(key, value, unit: "none", tags: {}, timestamp: nil)
|
26
|
+
Sentry.metrics_aggregator&.add(:d, key, value, unit: unit, tags: tags, timestamp: timestamp)
|
27
|
+
end
|
28
|
+
|
29
|
+
def set(key, value, unit: "none", tags: {}, timestamp: nil)
|
30
|
+
Sentry.metrics_aggregator&.add(:s, key, value, unit: unit, tags: tags, timestamp: timestamp)
|
31
|
+
end
|
32
|
+
|
33
|
+
def gauge(key, value, unit: "none", tags: {}, timestamp: nil)
|
34
|
+
Sentry.metrics_aggregator&.add(:g, key, value, unit: unit, tags: tags, timestamp: timestamp)
|
35
|
+
end
|
36
|
+
|
37
|
+
def timing(key, unit: "second", tags: {}, timestamp: nil, &block)
|
38
|
+
return unless block_given?
|
39
|
+
return yield unless DURATION_UNITS.include?(unit)
|
40
|
+
|
41
|
+
result, value = Sentry.with_child_span(op: OP_NAME, description: key, origin: SPAN_ORIGIN) do |span|
|
42
|
+
tags.each { |k, v| span.set_tag(k, v.is_a?(Array) ? v.join(", ") : v.to_s) } if span
|
43
|
+
|
44
|
+
start = Timing.send(unit.to_sym)
|
45
|
+
result = yield
|
46
|
+
value = Timing.send(unit.to_sym) - start
|
47
|
+
|
48
|
+
[result, value]
|
49
|
+
end
|
50
|
+
|
51
|
+
Sentry.metrics_aggregator&.add(:d, key, value, unit: unit, tags: tags, timestamp: timestamp)
|
52
|
+
result
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
data/lib/sentry/net/http.rb
CHANGED
@@ -2,12 +2,16 @@
|
|
2
2
|
|
3
3
|
require "net/http"
|
4
4
|
require "resolv"
|
5
|
+
require "sentry/utils/http_tracing"
|
5
6
|
|
6
7
|
module Sentry
|
7
8
|
# @api private
|
8
9
|
module Net
|
9
10
|
module HTTP
|
11
|
+
include Utils::HttpTracing
|
12
|
+
|
10
13
|
OP_NAME = "http.client"
|
14
|
+
SPAN_ORIGIN = "auto.http.net_http"
|
11
15
|
BREADCRUMB_CATEGORY = "net.http"
|
12
16
|
|
13
17
|
# To explain how the entire thing works, we need to know how the original Net::HTTP#request works
|
@@ -20,8 +24,7 @@ module Sentry
|
|
20
24
|
# req['connection'] ||= 'close'
|
21
25
|
# return request(req, body, &block) # <- request will be called for the second time from the first call
|
22
26
|
# }
|
23
|
-
# end
|
24
|
-
# # .....
|
27
|
+
# end # .....
|
25
28
|
# end
|
26
29
|
# ```
|
27
30
|
#
|
@@ -30,47 +33,29 @@ module Sentry
|
|
30
33
|
return super unless started? && Sentry.initialized?
|
31
34
|
return super if from_sentry_sdk?
|
32
35
|
|
33
|
-
Sentry.with_child_span(op: OP_NAME, start_timestamp: Sentry.utc_now.to_f) do |sentry_span|
|
36
|
+
Sentry.with_child_span(op: OP_NAME, start_timestamp: Sentry.utc_now.to_f, origin: SPAN_ORIGIN) do |sentry_span|
|
34
37
|
request_info = extract_request_info(req)
|
35
38
|
|
36
|
-
if propagate_trace?(request_info[:url]
|
39
|
+
if propagate_trace?(request_info[:url])
|
37
40
|
set_propagation_headers(req)
|
38
41
|
end
|
39
42
|
|
40
|
-
|
41
|
-
|
43
|
+
res = super
|
44
|
+
response_status = res.code.to_i
|
42
45
|
|
43
|
-
|
44
|
-
|
45
|
-
sentry_span.set_data(Span::DataConventions::URL, request_info[:url])
|
46
|
-
sentry_span.set_data(Span::DataConventions::HTTP_METHOD, request_info[:method])
|
47
|
-
sentry_span.set_data(Span::DataConventions::HTTP_QUERY, request_info[:query]) if request_info[:query]
|
48
|
-
sentry_span.set_data(Span::DataConventions::HTTP_STATUS_CODE, res.code.to_i)
|
49
|
-
end
|
46
|
+
if record_sentry_breadcrumb?
|
47
|
+
record_sentry_breadcrumb(request_info, response_status)
|
50
48
|
end
|
51
|
-
end
|
52
|
-
end
|
53
49
|
|
54
|
-
|
50
|
+
if sentry_span
|
51
|
+
set_span_info(sentry_span, request_info, response_status)
|
52
|
+
end
|
55
53
|
|
56
|
-
|
57
|
-
|
54
|
+
res
|
55
|
+
end
|
58
56
|
end
|
59
57
|
|
60
|
-
|
61
|
-
return unless Sentry.initialized? && Sentry.configuration.breadcrumbs_logger.include?(:http_logger)
|
62
|
-
|
63
|
-
crumb = Sentry::Breadcrumb.new(
|
64
|
-
level: :info,
|
65
|
-
category: BREADCRUMB_CATEGORY,
|
66
|
-
type: :info,
|
67
|
-
data: {
|
68
|
-
status: res.code.to_i,
|
69
|
-
**request_info
|
70
|
-
}
|
71
|
-
)
|
72
|
-
Sentry.add_breadcrumb(crumb)
|
73
|
-
end
|
58
|
+
private
|
74
59
|
|
75
60
|
def from_sentry_sdk?
|
76
61
|
dsn = Sentry.configuration.dsn
|
@@ -81,7 +66,7 @@ module Sentry
|
|
81
66
|
# IPv6 url could look like '::1/path', and that won't parse without
|
82
67
|
# wrapping it in square brackets.
|
83
68
|
hostname = address =~ Resolv::IPv6::Regex ? "[#{address}]" : address
|
84
|
-
uri = req.uri || URI.parse("#{use_ssl? ? 'https' : 'http'}://#{hostname}#{req.path}")
|
69
|
+
uri = req.uri || URI.parse(URI::DEFAULT_PARSER.escape("#{use_ssl? ? 'https' : 'http'}://#{hostname}#{req.path}"))
|
85
70
|
url = "#{uri.scheme}://#{uri.host}#{uri.path}" rescue uri.to_s
|
86
71
|
|
87
72
|
result = { method: req.method, url: url }
|
@@ -93,12 +78,6 @@ module Sentry
|
|
93
78
|
|
94
79
|
result
|
95
80
|
end
|
96
|
-
|
97
|
-
def propagate_trace?(url, configuration)
|
98
|
-
url &&
|
99
|
-
configuration.propagate_traces &&
|
100
|
-
configuration.trace_propagation_targets.any? { |target| url.match?(target) }
|
101
|
-
end
|
102
81
|
end
|
103
82
|
end
|
104
83
|
end
|
data/lib/sentry/profiler.rb
CHANGED
@@ -1,11 +1,11 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require
|
3
|
+
require "securerandom"
|
4
4
|
|
5
5
|
module Sentry
|
6
6
|
class Profiler
|
7
|
-
VERSION =
|
8
|
-
PLATFORM =
|
7
|
+
VERSION = "1"
|
8
|
+
PLATFORM = "ruby"
|
9
9
|
# 101 Hz in microseconds
|
10
10
|
DEFAULT_INTERVAL = 1e6 / 101
|
11
11
|
MICRO_TO_NANO_SECONDS = 1e3
|
@@ -14,14 +14,14 @@ module Sentry
|
|
14
14
|
attr_reader :sampled, :started, :event_id
|
15
15
|
|
16
16
|
def initialize(configuration)
|
17
|
-
@event_id = SecureRandom.uuid.delete(
|
17
|
+
@event_id = SecureRandom.uuid.delete("-")
|
18
18
|
@started = false
|
19
19
|
@sampled = nil
|
20
20
|
|
21
21
|
@profiling_enabled = defined?(StackProf) && configuration.profiling_enabled?
|
22
22
|
@profiles_sample_rate = configuration.profiles_sample_rate
|
23
23
|
@project_root = configuration.project_root
|
24
|
-
@app_dirs_pattern = configuration.app_dirs_pattern
|
24
|
+
@app_dirs_pattern = configuration.app_dirs_pattern
|
25
25
|
@in_app_pattern = Regexp.new("^(#{@project_root}/)?#{@app_dirs_pattern}")
|
26
26
|
end
|
27
27
|
|
@@ -33,7 +33,7 @@ module Sentry
|
|
33
33
|
raw: true,
|
34
34
|
aggregate: false)
|
35
35
|
|
36
|
-
@started ? log(
|
36
|
+
@started ? log("Started") : log("Not started since running elsewhere")
|
37
37
|
end
|
38
38
|
|
39
39
|
def stop
|
@@ -41,7 +41,7 @@ module Sentry
|
|
41
41
|
return unless @started
|
42
42
|
|
43
43
|
StackProf.stop
|
44
|
-
log(
|
44
|
+
log("Stopped")
|
45
45
|
end
|
46
46
|
|
47
47
|
# Sets initial sampling decision of the profile.
|
@@ -54,14 +54,14 @@ module Sentry
|
|
54
54
|
|
55
55
|
unless transaction_sampled
|
56
56
|
@sampled = false
|
57
|
-
log(
|
57
|
+
log("Discarding profile because transaction not sampled")
|
58
58
|
return
|
59
59
|
end
|
60
60
|
|
61
61
|
case @profiles_sample_rate
|
62
62
|
when 0.0
|
63
63
|
@sampled = false
|
64
|
-
log(
|
64
|
+
log("Discarding profile because sample_rate is 0")
|
65
65
|
return
|
66
66
|
when 1.0
|
67
67
|
@sampled = true
|
@@ -70,7 +70,7 @@ module Sentry
|
|
70
70
|
@sampled = Random.rand < @profiles_sample_rate
|
71
71
|
end
|
72
72
|
|
73
|
-
log(
|
73
|
+
log("Discarding profile due to sampling decision") unless @sampled
|
74
74
|
end
|
75
75
|
|
76
76
|
def to_hash
|
@@ -90,13 +90,12 @@ module Sentry
|
|
90
90
|
|
91
91
|
frame_map = {}
|
92
92
|
|
93
|
-
frames = results[:frames].
|
94
|
-
frame_id, frame_data = frame
|
95
|
-
|
93
|
+
frames = results[:frames].map.with_index do |(frame_id, frame_data), idx|
|
96
94
|
# need to map over stackprof frame ids to ours
|
97
95
|
frame_map[frame_id] = idx
|
98
96
|
|
99
97
|
file_path = frame_data[:file]
|
98
|
+
lineno = frame_data[:line]
|
100
99
|
in_app = in_app?(file_path)
|
101
100
|
filename = compute_filename(file_path, in_app)
|
102
101
|
function, mod = split_module(frame_data[:name])
|
@@ -109,7 +108,7 @@ module Sentry
|
|
109
108
|
}
|
110
109
|
|
111
110
|
frame_hash[:module] = mod if mod
|
112
|
-
frame_hash[:lineno] =
|
111
|
+
frame_hash[:lineno] = lineno if lineno && lineno >= 0
|
113
112
|
|
114
113
|
frame_hash
|
115
114
|
end
|
@@ -130,7 +129,7 @@ module Sentry
|
|
130
129
|
num_seen << results[:raw][idx + len]
|
131
130
|
idx += len + 1
|
132
131
|
|
133
|
-
log(
|
132
|
+
log("Unknown frame in stack") if stack.size != len
|
134
133
|
end
|
135
134
|
|
136
135
|
idx = 0
|
@@ -155,16 +154,16 @@ module Sentry
|
|
155
154
|
# Till then, on multi-threaded servers like puma, we will get frames from other active threads when the one
|
156
155
|
# we're profiling is idle/sleeping/waiting for IO etc.
|
157
156
|
# https://bugs.ruby-lang.org/issues/10602
|
158
|
-
thread_id:
|
157
|
+
thread_id: "0",
|
159
158
|
elapsed_since_start_ns: elapsed_since_start_ns.to_s
|
160
159
|
}
|
161
160
|
end
|
162
161
|
end
|
163
162
|
|
164
|
-
log(
|
163
|
+
log("Some samples thrown away") if samples.size != results[:samples]
|
165
164
|
|
166
165
|
if samples.size <= MIN_SAMPLES_REQUIRED
|
167
|
-
log(
|
166
|
+
log("Not enough samples, discarding profiler")
|
168
167
|
record_lost_event(:insufficient_data)
|
169
168
|
return {}
|
170
169
|
end
|
@@ -219,7 +218,7 @@ module Sentry
|
|
219
218
|
|
220
219
|
def split_module(name)
|
221
220
|
# last module plus class/instance method
|
222
|
-
i = name.rindex(
|
221
|
+
i = name.rindex("::")
|
223
222
|
function = i ? name[(i + 2)..-1] : name
|
224
223
|
mod = i ? name[0...i] : nil
|
225
224
|
|
@@ -227,7 +226,7 @@ module Sentry
|
|
227
226
|
end
|
228
227
|
|
229
228
|
def record_lost_event(reason)
|
230
|
-
Sentry.get_current_client&.transport&.record_lost_event(reason,
|
229
|
+
Sentry.get_current_client&.transport&.record_lost_event(reason, "profile")
|
231
230
|
end
|
232
231
|
end
|
233
232
|
end
|
@@ -50,14 +50,15 @@ module Sentry
|
|
50
50
|
if sentry_trace_data
|
51
51
|
@trace_id, @parent_span_id, @parent_sampled = sentry_trace_data
|
52
52
|
|
53
|
-
@baggage =
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
53
|
+
@baggage =
|
54
|
+
if baggage_header && !baggage_header.empty?
|
55
|
+
Baggage.from_incoming_header(baggage_header)
|
56
|
+
else
|
57
|
+
# If there's an incoming sentry-trace but no incoming baggage header,
|
58
|
+
# for instance in traces coming from older SDKs,
|
59
|
+
# baggage will be empty and frozen and won't be populated as head SDK.
|
60
|
+
Baggage.new({})
|
61
|
+
end
|
61
62
|
|
62
63
|
@baggage.freeze!
|
63
64
|
@incoming_trace = true
|
@@ -107,7 +108,7 @@ module Sentry
|
|
107
108
|
end
|
108
109
|
|
109
110
|
# Returns the Dynamic Sampling Context from the baggage.
|
110
|
-
# @return [
|
111
|
+
# @return [Hash, nil]
|
111
112
|
def get_dynamic_sampling_context
|
112
113
|
get_baggage&.dynamic_sampling_context
|
113
114
|
end
|
data/lib/sentry/puma.rb
CHANGED
@@ -4,6 +4,8 @@ module Sentry
|
|
4
4
|
module Rack
|
5
5
|
class CaptureExceptions
|
6
6
|
ERROR_EVENT_ID_KEY = "sentry.error_event_id"
|
7
|
+
MECHANISM_TYPE = "rack"
|
8
|
+
SPAN_ORIGIN = "auto.http.rack"
|
7
9
|
|
8
10
|
def initialize(app)
|
9
11
|
@app = app
|
@@ -48,7 +50,7 @@ module Sentry
|
|
48
50
|
private
|
49
51
|
|
50
52
|
def collect_exception(env)
|
51
|
-
env[
|
53
|
+
env["rack.exception"] || env["sinatra.error"]
|
52
54
|
end
|
53
55
|
|
54
56
|
def transaction_op
|
@@ -56,13 +58,19 @@ module Sentry
|
|
56
58
|
end
|
57
59
|
|
58
60
|
def capture_exception(exception, env)
|
59
|
-
Sentry.capture_exception(exception).tap do |event|
|
61
|
+
Sentry.capture_exception(exception, hint: { mechanism: mechanism }).tap do |event|
|
60
62
|
env[ERROR_EVENT_ID_KEY] = event.event_id if event
|
61
63
|
end
|
62
64
|
end
|
63
65
|
|
64
66
|
def start_transaction(env, scope)
|
65
|
-
options = {
|
67
|
+
options = {
|
68
|
+
name: scope.transaction_name,
|
69
|
+
source: scope.transaction_source,
|
70
|
+
op: transaction_op,
|
71
|
+
origin: SPAN_ORIGIN
|
72
|
+
}
|
73
|
+
|
66
74
|
transaction = Sentry.continue_trace(env, **options)
|
67
75
|
Sentry.start_transaction(transaction: transaction, custom_sampling_context: { env: env }, **options)
|
68
76
|
end
|
@@ -74,6 +82,10 @@ module Sentry
|
|
74
82
|
transaction.set_http_status(status_code)
|
75
83
|
transaction.finish
|
76
84
|
end
|
85
|
+
|
86
|
+
def mechanism
|
87
|
+
Sentry::Mechanism.new(type: MECHANISM_TYPE, handled: false)
|
88
|
+
end
|
77
89
|
end
|
78
90
|
end
|
79
91
|
end
|
data/lib/sentry/rack.rb
CHANGED
data/lib/sentry/rake.rb
CHANGED
@@ -8,8 +8,10 @@ module Sentry
|
|
8
8
|
module Application
|
9
9
|
# @api private
|
10
10
|
def display_error_message(ex)
|
11
|
-
Sentry.
|
12
|
-
|
11
|
+
mechanism = Sentry::Mechanism.new(type: "rake", handled: false)
|
12
|
+
|
13
|
+
Sentry.capture_exception(ex, hint: { mechanism: mechanism }) do |scope|
|
14
|
+
task_name = top_level_tasks.join(" ")
|
13
15
|
scope.set_transaction_name(task_name, source: :task)
|
14
16
|
scope.set_tag("rake_task", task_name)
|
15
17
|
end if Sentry.initialized? && !Sentry.configuration.skip_rake_integration
|
data/lib/sentry/redis.rb
CHANGED
@@ -4,6 +4,7 @@ module Sentry
|
|
4
4
|
# @api private
|
5
5
|
class Redis
|
6
6
|
OP_NAME = "db.redis"
|
7
|
+
SPAN_ORIGIN = "auto.db.redis"
|
7
8
|
LOGGER_NAME = :redis_logger
|
8
9
|
|
9
10
|
def initialize(commands, host, port, db)
|
@@ -13,7 +14,7 @@ module Sentry
|
|
13
14
|
def instrument
|
14
15
|
return yield unless Sentry.initialized?
|
15
16
|
|
16
|
-
Sentry.with_child_span(op: OP_NAME, start_timestamp: Sentry.utc_now.to_f) do |span|
|
17
|
+
Sentry.with_child_span(op: OP_NAME, start_timestamp: Sentry.utc_now.to_f, origin: SPAN_ORIGIN) do |span|
|
17
18
|
yield.tap do
|
18
19
|
record_breadcrumb
|
19
20
|
|
@@ -13,12 +13,12 @@ module Sentry
|
|
13
13
|
|
14
14
|
def detect_release_from_heroku(running_on_heroku)
|
15
15
|
return unless running_on_heroku
|
16
|
-
ENV[
|
16
|
+
ENV["HEROKU_SLUG_COMMIT"]
|
17
17
|
end
|
18
18
|
|
19
19
|
def detect_release_from_capistrano(project_root)
|
20
|
-
revision_file = File.join(project_root,
|
21
|
-
revision_log = File.join(project_root,
|
20
|
+
revision_file = File.join(project_root, "REVISION")
|
21
|
+
revision_log = File.join(project_root, "..", "revisions.log")
|
22
22
|
|
23
23
|
if File.exist?(revision_file)
|
24
24
|
File.read(revision_file).strip
|
@@ -32,7 +32,7 @@ module Sentry
|
|
32
32
|
end
|
33
33
|
|
34
34
|
def detect_release_from_env
|
35
|
-
ENV[
|
35
|
+
ENV["SENTRY_RELEASE"]
|
36
36
|
end
|
37
37
|
end
|
38
38
|
end
|
data/lib/sentry/scope.rb
CHANGED
@@ -2,6 +2,7 @@
|
|
2
2
|
|
3
3
|
require "sentry/breadcrumb_buffer"
|
4
4
|
require "sentry/propagation_context"
|
5
|
+
require "sentry/attachment"
|
5
6
|
require "etc"
|
6
7
|
|
7
8
|
module Sentry
|
@@ -9,8 +10,8 @@ module Sentry
|
|
9
10
|
include ArgumentCheckingHelper
|
10
11
|
|
11
12
|
ATTRIBUTES = [
|
12
|
-
:
|
13
|
-
:
|
13
|
+
:transaction_name,
|
14
|
+
:transaction_source,
|
14
15
|
:contexts,
|
15
16
|
:extra,
|
16
17
|
:tags,
|
@@ -22,6 +23,7 @@ module Sentry
|
|
22
23
|
:rack_env,
|
23
24
|
:span,
|
24
25
|
:session,
|
26
|
+
:attachments,
|
25
27
|
:propagation_context
|
26
28
|
]
|
27
29
|
|
@@ -55,10 +57,12 @@ module Sentry
|
|
55
57
|
event.level = level
|
56
58
|
event.breadcrumbs = breadcrumbs
|
57
59
|
event.rack_env = rack_env if rack_env
|
60
|
+
event.attachments = attachments
|
58
61
|
end
|
59
62
|
|
60
63
|
if span
|
61
64
|
event.contexts[:trace] ||= span.get_trace_context
|
65
|
+
event.dynamic_sampling_context ||= span.get_dynamic_sampling_context
|
62
66
|
else
|
63
67
|
event.contexts[:trace] ||= propagation_context.get_trace_context
|
64
68
|
event.dynamic_sampling_context ||= propagation_context.get_dynamic_sampling_context
|
@@ -96,12 +100,13 @@ module Sentry
|
|
96
100
|
copy.extra = extra.deep_dup
|
97
101
|
copy.tags = tags.deep_dup
|
98
102
|
copy.user = user.deep_dup
|
99
|
-
copy.
|
100
|
-
copy.
|
103
|
+
copy.transaction_name = transaction_name.dup
|
104
|
+
copy.transaction_source = transaction_source.dup
|
101
105
|
copy.fingerprint = fingerprint.deep_dup
|
102
106
|
copy.span = span.deep_dup
|
103
107
|
copy.session = session.deep_dup
|
104
108
|
copy.propagation_context = propagation_context.deep_dup
|
109
|
+
copy.attachments = attachments.dup
|
105
110
|
copy
|
106
111
|
end
|
107
112
|
|
@@ -114,11 +119,12 @@ module Sentry
|
|
114
119
|
self.extra = scope.extra
|
115
120
|
self.tags = scope.tags
|
116
121
|
self.user = scope.user
|
117
|
-
self.
|
118
|
-
self.
|
122
|
+
self.transaction_name = scope.transaction_name
|
123
|
+
self.transaction_source = scope.transaction_source
|
119
124
|
self.fingerprint = scope.fingerprint
|
120
125
|
self.span = scope.span
|
121
126
|
self.propagation_context = scope.propagation_context
|
127
|
+
self.attachments = scope.attachments
|
122
128
|
end
|
123
129
|
|
124
130
|
# Updates the scope's data from the given options.
|
@@ -128,14 +134,17 @@ module Sentry
|
|
128
134
|
# @param user [Hash]
|
129
135
|
# @param level [String, Symbol]
|
130
136
|
# @param fingerprint [Array]
|
131
|
-
# @
|
137
|
+
# @param attachments [Array<Attachment>]
|
138
|
+
# @return [Array]
|
132
139
|
def update_from_options(
|
133
140
|
contexts: nil,
|
134
141
|
extra: nil,
|
135
142
|
tags: nil,
|
136
143
|
user: nil,
|
137
144
|
level: nil,
|
138
|
-
fingerprint: nil
|
145
|
+
fingerprint: nil,
|
146
|
+
attachments: nil,
|
147
|
+
**options
|
139
148
|
)
|
140
149
|
self.contexts.merge!(contexts) if contexts
|
141
150
|
self.extra.merge!(extra) if extra
|
@@ -143,6 +152,9 @@ module Sentry
|
|
143
152
|
self.user = user if user
|
144
153
|
self.level = level if level
|
145
154
|
self.fingerprint = fingerprint if fingerprint
|
155
|
+
|
156
|
+
# Returns unsupported option keys so we can notify users.
|
157
|
+
options.keys
|
146
158
|
end
|
147
159
|
|
148
160
|
# Sets the scope's rack_env attribute.
|
@@ -227,8 +239,8 @@ module Sentry
|
|
227
239
|
# @param transaction_name [String]
|
228
240
|
# @return [void]
|
229
241
|
def set_transaction_name(transaction_name, source: :custom)
|
230
|
-
@
|
231
|
-
@
|
242
|
+
@transaction_name = transaction_name
|
243
|
+
@transaction_source = source
|
232
244
|
end
|
233
245
|
|
234
246
|
# Sets the currently active session on the scope.
|
@@ -238,18 +250,10 @@ module Sentry
|
|
238
250
|
@session = session
|
239
251
|
end
|
240
252
|
|
241
|
-
#
|
242
|
-
#
|
243
|
-
|
244
|
-
|
245
|
-
@transaction_names.last
|
246
|
-
end
|
247
|
-
|
248
|
-
# Returns current transaction source.
|
249
|
-
# The "transaction" here does not refer to `Transaction` objects.
|
250
|
-
# @return [String, nil]
|
251
|
-
def transaction_source
|
252
|
-
@transaction_sources.last
|
253
|
+
# These are high cardinality and thus bad.
|
254
|
+
# @return [Boolean]
|
255
|
+
def transaction_source_low_quality?
|
256
|
+
transaction_source == :url
|
253
257
|
end
|
254
258
|
|
255
259
|
# Returns the associated Transaction object.
|
@@ -287,6 +291,12 @@ module Sentry
|
|
287
291
|
@propagation_context = PropagationContext.new(self, env)
|
288
292
|
end
|
289
293
|
|
294
|
+
# Add a new attachment to the scope.
|
295
|
+
def add_attachment(**opts)
|
296
|
+
attachments << (attachment = Attachment.new(**opts))
|
297
|
+
attachment
|
298
|
+
end
|
299
|
+
|
290
300
|
protected
|
291
301
|
|
292
302
|
# for duplicating scopes internally
|
@@ -295,18 +305,19 @@ module Sentry
|
|
295
305
|
private
|
296
306
|
|
297
307
|
def set_default_value
|
298
|
-
@contexts = { :
|
308
|
+
@contexts = { os: self.class.os_context, runtime: self.class.runtime_context }
|
299
309
|
@extra = {}
|
300
310
|
@tags = {}
|
301
311
|
@user = {}
|
302
312
|
@level = :error
|
303
313
|
@fingerprint = []
|
304
|
-
@
|
305
|
-
@
|
314
|
+
@transaction_name = nil
|
315
|
+
@transaction_source = nil
|
306
316
|
@event_processors = []
|
307
317
|
@rack_env = {}
|
308
318
|
@span = nil
|
309
319
|
@session = nil
|
320
|
+
@attachments = []
|
310
321
|
generate_propagation_context
|
311
322
|
set_new_breadcrumb_buffer
|
312
323
|
end
|
@@ -355,6 +366,5 @@ module Sentry
|
|
355
366
|
global_event_processors << block
|
356
367
|
end
|
357
368
|
end
|
358
|
-
|
359
369
|
end
|
360
370
|
end
|
data/lib/sentry/session.rb
CHANGED
@@ -5,8 +5,8 @@ module Sentry
|
|
5
5
|
attr_reader :started, :status, :aggregation_key
|
6
6
|
|
7
7
|
# TODO-neel add :crashed after adding handled mechanism
|
8
|
-
STATUSES = %i
|
9
|
-
AGGREGATE_STATUSES = %i
|
8
|
+
STATUSES = %i[ok errored exited]
|
9
|
+
AGGREGATE_STATUSES = %i[errored exited]
|
10
10
|
|
11
11
|
def initialize
|
12
12
|
@started = Sentry.utc_now
|