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
|
@@ -14,12 +14,12 @@ module Sentry
|
|
|
14
14
|
:in_progress,
|
|
15
15
|
monitor_config: monitor_config)
|
|
16
16
|
|
|
17
|
-
start =
|
|
17
|
+
start = Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
|
18
18
|
|
|
19
19
|
begin
|
|
20
20
|
# need to do this on ruby <= 2.6 sadly
|
|
21
21
|
ret = method(:perform).super_method.arity == 0 ? super() : super
|
|
22
|
-
duration =
|
|
22
|
+
duration = Process.clock_gettime(Process::CLOCK_MONOTONIC) - start
|
|
23
23
|
|
|
24
24
|
Sentry.capture_check_in(slug,
|
|
25
25
|
:ok,
|
|
@@ -29,7 +29,7 @@ module Sentry
|
|
|
29
29
|
|
|
30
30
|
ret
|
|
31
31
|
rescue Exception
|
|
32
|
-
duration =
|
|
32
|
+
duration = Process.clock_gettime(Process::CLOCK_MONOTONIC) - start
|
|
33
33
|
|
|
34
34
|
Sentry.capture_check_in(slug,
|
|
35
35
|
:error,
|
|
@@ -40,9 +40,9 @@ module Sentry
|
|
|
40
40
|
new(MonitorSchedule::Interval.new(num, unit), **options)
|
|
41
41
|
end
|
|
42
42
|
|
|
43
|
-
def
|
|
43
|
+
def to_h
|
|
44
44
|
{
|
|
45
|
-
schedule: schedule.
|
|
45
|
+
schedule: schedule.to_h,
|
|
46
46
|
checkin_margin: checkin_margin,
|
|
47
47
|
max_runtime: max_runtime,
|
|
48
48
|
timezone: timezone
|
|
@@ -12,7 +12,7 @@ module Sentry
|
|
|
12
12
|
@value = value
|
|
13
13
|
end
|
|
14
14
|
|
|
15
|
-
def
|
|
15
|
+
def to_h
|
|
16
16
|
{ type: :crontab, value: value }
|
|
17
17
|
end
|
|
18
18
|
end
|
|
@@ -33,7 +33,7 @@ module Sentry
|
|
|
33
33
|
@unit = unit
|
|
34
34
|
end
|
|
35
35
|
|
|
36
|
-
def
|
|
36
|
+
def to_h
|
|
37
37
|
{ type: :interval, value: value, unit: unit }
|
|
38
38
|
end
|
|
39
39
|
end
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "json"
|
|
4
|
+
require "fileutils"
|
|
5
|
+
require "pathname"
|
|
6
|
+
require "delegate"
|
|
7
|
+
|
|
8
|
+
module Sentry
|
|
9
|
+
# DebugStructuredLogger is a logger that captures structured log events to a file for debugging purposes.
|
|
10
|
+
#
|
|
11
|
+
# It can optionally also send log events to Sentry via the normal structured logger if logging
|
|
12
|
+
# is enabled.
|
|
13
|
+
class DebugStructuredLogger < SimpleDelegator
|
|
14
|
+
DEFAULT_LOG_FILE_PATH = File.join("log", "sentry_debug_logs.log")
|
|
15
|
+
|
|
16
|
+
attr_reader :log_file, :backend
|
|
17
|
+
|
|
18
|
+
def initialize(configuration)
|
|
19
|
+
@log_file = initialize_log_file(
|
|
20
|
+
configuration.structured_logging.file_path || DEFAULT_LOG_FILE_PATH
|
|
21
|
+
)
|
|
22
|
+
@backend = initialize_backend(configuration)
|
|
23
|
+
|
|
24
|
+
super(@backend)
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
# Override all log level methods to capture events
|
|
28
|
+
%i[trace debug info warn error fatal].each do |level|
|
|
29
|
+
define_method(level) do |message, parameters = [], **attributes|
|
|
30
|
+
log_event = capture_log_event(level, message, parameters, **attributes)
|
|
31
|
+
backend.public_send(level, message, parameters, **attributes)
|
|
32
|
+
log_event
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def log(level, message, parameters:, **attributes)
|
|
37
|
+
log_event = capture_log_event(level, message, parameters, **attributes)
|
|
38
|
+
backend.log(level, message, parameters: parameters, **attributes)
|
|
39
|
+
log_event
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def capture_log_event(level, message, parameters, **attributes)
|
|
43
|
+
log_event_json = {
|
|
44
|
+
timestamp: Time.now.utc.iso8601,
|
|
45
|
+
level: level.to_s,
|
|
46
|
+
message: message,
|
|
47
|
+
parameters: parameters,
|
|
48
|
+
attributes: attributes
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
File.open(log_file, "a") { |file| file << JSON.dump(log_event_json) << "\n" }
|
|
52
|
+
log_event_json
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
def logged_events
|
|
56
|
+
File.readlines(log_file).map do |line|
|
|
57
|
+
JSON.parse(line)
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
def clear
|
|
62
|
+
File.write(log_file, "")
|
|
63
|
+
if backend.respond_to?(:config)
|
|
64
|
+
backend.config.sdk_logger.debug("DebugStructuredLogger: Cleared events from #{log_file}")
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
private
|
|
69
|
+
|
|
70
|
+
def initialize_backend(configuration)
|
|
71
|
+
if configuration.enable_logs
|
|
72
|
+
StructuredLogger.new(configuration)
|
|
73
|
+
else
|
|
74
|
+
# Create a no-op logger if logging is disabled
|
|
75
|
+
NoOpLogger.new
|
|
76
|
+
end
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
def initialize_log_file(log_file_path)
|
|
80
|
+
log_file = Pathname(log_file_path)
|
|
81
|
+
|
|
82
|
+
FileUtils.mkdir_p(log_file.dirname) unless log_file.dirname.exist?
|
|
83
|
+
|
|
84
|
+
log_file
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
# No-op logger for when structured logging is disabled
|
|
88
|
+
class NoOpLogger
|
|
89
|
+
%i[trace debug info warn error fatal log].each do |method|
|
|
90
|
+
define_method(method) { |*args, **kwargs| nil }
|
|
91
|
+
end
|
|
92
|
+
end
|
|
93
|
+
end
|
|
94
|
+
end
|
data/lib/sentry/dsn.rb
CHANGED
|
@@ -1,11 +1,15 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
require "uri"
|
|
4
|
+
require "ipaddr"
|
|
5
|
+
require "resolv"
|
|
4
6
|
|
|
5
7
|
module Sentry
|
|
6
8
|
class DSN
|
|
7
9
|
PORT_MAP = { "http" => 80, "https" => 443 }.freeze
|
|
8
10
|
REQUIRED_ATTRIBUTES = %w[host path public_key project_id].freeze
|
|
11
|
+
LOCALHOST_NAMES = %w[localhost 127.0.0.1 ::1 [::1]].freeze
|
|
12
|
+
LOCALHOST_PATTERN = /\.local(host|domain)?$/i
|
|
9
13
|
|
|
10
14
|
attr_reader :scheme, :secret_key, :port, *REQUIRED_ATTRIBUTES
|
|
11
15
|
|
|
@@ -49,5 +53,33 @@ module Sentry
|
|
|
49
53
|
def envelope_endpoint
|
|
50
54
|
"#{path}/api/#{project_id}/envelope/"
|
|
51
55
|
end
|
|
56
|
+
|
|
57
|
+
def local?
|
|
58
|
+
@local ||= (localhost? || private_ip? || resolved_ips_private?)
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
def localhost?
|
|
62
|
+
LOCALHOST_NAMES.include?(host.downcase) || LOCALHOST_PATTERN.match?(host)
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
def private_ip?
|
|
66
|
+
@private_ip ||= begin
|
|
67
|
+
begin
|
|
68
|
+
IPAddr.new(host).private?
|
|
69
|
+
rescue IPAddr::InvalidAddressError
|
|
70
|
+
false
|
|
71
|
+
end
|
|
72
|
+
end
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
def resolved_ips_private?
|
|
76
|
+
@resolved_ips_private ||= begin
|
|
77
|
+
begin
|
|
78
|
+
Resolv.getaddresses(host).any? { |ip| IPAddr.new(ip).private? }
|
|
79
|
+
rescue Resolv::ResolvError, IPAddr::InvalidAddressError
|
|
80
|
+
false
|
|
81
|
+
end
|
|
82
|
+
end
|
|
83
|
+
end
|
|
52
84
|
end
|
|
53
85
|
end
|
data/lib/sentry/envelope/item.rb
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
module Sentry
|
|
4
4
|
# @api private
|
|
5
5
|
class Envelope::Item
|
|
6
|
-
STACKTRACE_FRAME_LIMIT_ON_OVERSIZED_PAYLOAD =
|
|
6
|
+
STACKTRACE_FRAME_LIMIT_ON_OVERSIZED_PAYLOAD = 1000
|
|
7
7
|
MAX_SERIALIZED_PAYLOAD_SIZE = 1024 * 1000
|
|
8
8
|
|
|
9
9
|
SIZE_LIMITS = Hash.new(MAX_SERIALIZED_PAYLOAD_SIZE).update(
|
|
@@ -18,7 +18,6 @@ module Sentry
|
|
|
18
18
|
when "session", "attachment", "transaction", "profile", "span", "log" then type
|
|
19
19
|
when "sessions" then "session"
|
|
20
20
|
when "check_in" then "monitor"
|
|
21
|
-
when "statsd", "metric_meta" then "metric_bucket"
|
|
22
21
|
when "event" then "error"
|
|
23
22
|
when "client_report" then "internal"
|
|
24
23
|
else "default"
|
data/lib/sentry/error_event.rb
CHANGED
|
@@ -10,10 +10,10 @@ module Sentry
|
|
|
10
10
|
attr_reader :threads
|
|
11
11
|
|
|
12
12
|
# @return [Hash]
|
|
13
|
-
def
|
|
13
|
+
def to_h
|
|
14
14
|
data = super
|
|
15
|
-
data[:threads] = threads.
|
|
16
|
-
data[:exception] = exception.
|
|
15
|
+
data[:threads] = threads.to_h if threads
|
|
16
|
+
data[:exception] = exception.to_h if exception
|
|
17
17
|
data
|
|
18
18
|
end
|
|
19
19
|
|
data/lib/sentry/event.rb
CHANGED
|
@@ -81,12 +81,6 @@ module Sentry
|
|
|
81
81
|
@message = (message || "").byteslice(0..MAX_MESSAGE_SIZE_IN_BYTES)
|
|
82
82
|
end
|
|
83
83
|
|
|
84
|
-
# @deprecated This method will be removed in v5.0.0. Please just use Sentry.configuration
|
|
85
|
-
# @return [Configuration]
|
|
86
|
-
def configuration
|
|
87
|
-
Sentry.configuration
|
|
88
|
-
end
|
|
89
|
-
|
|
90
84
|
# Sets the event's timestamp.
|
|
91
85
|
# @param time [Time, Float]
|
|
92
86
|
# @return [void]
|
|
@@ -118,16 +112,16 @@ module Sentry
|
|
|
118
112
|
end
|
|
119
113
|
|
|
120
114
|
# @return [Hash]
|
|
121
|
-
def
|
|
115
|
+
def to_h
|
|
122
116
|
data = serialize_attributes
|
|
123
|
-
data[:breadcrumbs] = breadcrumbs.
|
|
124
|
-
data[:request] = request.
|
|
117
|
+
data[:breadcrumbs] = breadcrumbs.to_h if breadcrumbs
|
|
118
|
+
data[:request] = request.to_h if request
|
|
125
119
|
data
|
|
126
120
|
end
|
|
127
121
|
|
|
128
122
|
# @return [Hash]
|
|
129
123
|
def to_json_compatible
|
|
130
|
-
JSON.parse(JSON.generate(
|
|
124
|
+
JSON.parse(JSON.generate(to_h))
|
|
131
125
|
end
|
|
132
126
|
|
|
133
127
|
private
|
data/lib/sentry/graphql.rb
CHANGED
|
@@ -4,6 +4,6 @@ Sentry.register_patch(:graphql) do |config|
|
|
|
4
4
|
if defined?(::GraphQL::Schema) && defined?(::GraphQL::Tracing::SentryTrace) && ::GraphQL::Schema.respond_to?(:trace_with)
|
|
5
5
|
::GraphQL::Schema.trace_with(::GraphQL::Tracing::SentryTrace, set_transaction_name: true)
|
|
6
6
|
else
|
|
7
|
-
config.
|
|
7
|
+
config.sdk_logger.warn(Sentry::LOGGER_PROGNAME) { "You tried to enable the GraphQL integration but no GraphQL gem was detected. Make sure you have the `graphql` gem (>= 2.2.6) in your Gemfile." }
|
|
8
8
|
end
|
|
9
9
|
end
|
data/lib/sentry/hub.rb
CHANGED
|
@@ -116,11 +116,12 @@ module Sentry
|
|
|
116
116
|
return unless configuration.tracing_enabled?
|
|
117
117
|
return unless instrumenter == configuration.instrumenter
|
|
118
118
|
|
|
119
|
-
transaction ||= Transaction.new(**options
|
|
119
|
+
transaction ||= Transaction.new(**options)
|
|
120
120
|
|
|
121
121
|
sampling_context = {
|
|
122
|
-
transaction_context: transaction.
|
|
123
|
-
parent_sampled: transaction.parent_sampled
|
|
122
|
+
transaction_context: transaction.to_h,
|
|
123
|
+
parent_sampled: transaction.parent_sampled,
|
|
124
|
+
parent_sample_rate: transaction.parent_sample_rate
|
|
124
125
|
}
|
|
125
126
|
|
|
126
127
|
sampling_context.merge!(custom_sampling_context)
|
|
@@ -217,7 +218,7 @@ module Sentry
|
|
|
217
218
|
end
|
|
218
219
|
|
|
219
220
|
def capture_log_event(message, **options)
|
|
220
|
-
return unless current_client
|
|
221
|
+
return unless current_client && current_client.configuration.enable_logs
|
|
221
222
|
|
|
222
223
|
event = current_client.event_from_log(message, **options)
|
|
223
224
|
|
|
@@ -352,11 +353,11 @@ module Sentry
|
|
|
352
353
|
return nil unless propagation_context.incoming_trace
|
|
353
354
|
|
|
354
355
|
Transaction.new(
|
|
355
|
-
hub: self,
|
|
356
356
|
trace_id: propagation_context.trace_id,
|
|
357
357
|
parent_span_id: propagation_context.parent_span_id,
|
|
358
358
|
parent_sampled: propagation_context.parent_sampled,
|
|
359
359
|
baggage: propagation_context.baggage,
|
|
360
|
+
sample_rand: propagation_context.sample_rand,
|
|
360
361
|
**options
|
|
361
362
|
)
|
|
362
363
|
end
|
data/lib/sentry/interface.rb
CHANGED
|
@@ -32,10 +32,10 @@ module Sentry
|
|
|
32
32
|
@mechanism = mechanism
|
|
33
33
|
end
|
|
34
34
|
|
|
35
|
-
def
|
|
35
|
+
def to_h
|
|
36
36
|
data = super
|
|
37
|
-
data[:stacktrace] = data[:stacktrace].
|
|
38
|
-
data[:mechanism] = data[:mechanism].
|
|
37
|
+
data[:stacktrace] = data[:stacktrace].to_h if data[:stacktrace]
|
|
38
|
+
data[:mechanism] = data[:mechanism].to_h
|
|
39
39
|
data
|
|
40
40
|
end
|
|
41
41
|
|
|
@@ -11,8 +11,8 @@ module Sentry
|
|
|
11
11
|
end
|
|
12
12
|
|
|
13
13
|
# @return [Hash]
|
|
14
|
-
def
|
|
15
|
-
{ frames: @frames.map(&:
|
|
14
|
+
def to_h
|
|
15
|
+
{ frames: @frames.map(&:to_h) }
|
|
16
16
|
end
|
|
17
17
|
|
|
18
18
|
# @return [String]
|
|
@@ -66,7 +66,7 @@ module Sentry
|
|
|
66
66
|
linecache.get_file_context(abs_path, lineno, context_lines)
|
|
67
67
|
end
|
|
68
68
|
|
|
69
|
-
def
|
|
69
|
+
def to_h(*args)
|
|
70
70
|
data = super(*args)
|
|
71
71
|
data.delete(:vars) unless vars && !vars.empty?
|
|
72
72
|
data.delete(:pre_context) unless pre_context && !pre_context.empty?
|
|
@@ -75,14 +75,6 @@ module Sentry
|
|
|
75
75
|
StacktraceInterface.new(frames: frames)
|
|
76
76
|
end
|
|
77
77
|
|
|
78
|
-
# Get the code location hash for a single line for where metrics where added.
|
|
79
|
-
# @return [Hash]
|
|
80
|
-
def metrics_code_location(unparsed_line)
|
|
81
|
-
parsed_line = Backtrace::Line.parse(unparsed_line)
|
|
82
|
-
frame = convert_parsed_line_into_frame(parsed_line)
|
|
83
|
-
frame.to_hash.reject { |k, _| %i[project_root in_app].include?(k) }
|
|
84
|
-
end
|
|
85
|
-
|
|
86
78
|
private
|
|
87
79
|
|
|
88
80
|
def convert_parsed_line_into_frame(line)
|
|
@@ -13,7 +13,7 @@ module Sentry
|
|
|
13
13
|
end
|
|
14
14
|
|
|
15
15
|
# @return [Hash]
|
|
16
|
-
def
|
|
16
|
+
def to_h
|
|
17
17
|
{
|
|
18
18
|
values: [
|
|
19
19
|
{
|
|
@@ -21,7 +21,7 @@ module Sentry
|
|
|
21
21
|
name: @name,
|
|
22
22
|
crashed: @crashed,
|
|
23
23
|
current: @current,
|
|
24
|
-
stacktrace: @stacktrace&.
|
|
24
|
+
stacktrace: @stacktrace&.to_h
|
|
25
25
|
}
|
|
26
26
|
]
|
|
27
27
|
}
|
data/lib/sentry/log_event.rb
CHANGED
|
@@ -29,9 +29,12 @@ module Sentry
|
|
|
29
29
|
"sentry.address" => :server_name,
|
|
30
30
|
"sentry.sdk.name" => :sdk_name,
|
|
31
31
|
"sentry.sdk.version" => :sdk_version,
|
|
32
|
-
"sentry.message.template" => :template
|
|
32
|
+
"sentry.message.template" => :template,
|
|
33
|
+
"sentry.origin" => :origin
|
|
33
34
|
}
|
|
34
35
|
|
|
36
|
+
PARAMETER_PREFIX = "sentry.message.parameter"
|
|
37
|
+
|
|
35
38
|
USER_ATTRIBUTES = {
|
|
36
39
|
"user.id" => :user_id,
|
|
37
40
|
"user.name" => :user_username,
|
|
@@ -40,9 +43,9 @@ module Sentry
|
|
|
40
43
|
|
|
41
44
|
LEVELS = %i[trace debug info warn error fatal].freeze
|
|
42
45
|
|
|
43
|
-
attr_accessor :level, :body, :template, :attributes, :user
|
|
46
|
+
attr_accessor :level, :body, :template, :attributes, :user, :origin
|
|
44
47
|
|
|
45
|
-
attr_reader :configuration, *SERIALIZEABLE_ATTRIBUTES
|
|
48
|
+
attr_reader :configuration, *(SERIALIZEABLE_ATTRIBUTES - %i[level body attributes])
|
|
46
49
|
|
|
47
50
|
SERIALIZERS = %i[
|
|
48
51
|
attributes
|
|
@@ -51,6 +54,7 @@ module Sentry
|
|
|
51
54
|
parent_span_id
|
|
52
55
|
sdk_name
|
|
53
56
|
sdk_version
|
|
57
|
+
template
|
|
54
58
|
timestamp
|
|
55
59
|
trace_id
|
|
56
60
|
user_id
|
|
@@ -79,10 +83,11 @@ module Sentry
|
|
|
79
83
|
@template = @body if is_template?
|
|
80
84
|
@attributes = options[:attributes] || DEFAULT_ATTRIBUTES
|
|
81
85
|
@user = options[:user] || {}
|
|
86
|
+
@origin = options[:origin]
|
|
82
87
|
@contexts = {}
|
|
83
88
|
end
|
|
84
89
|
|
|
85
|
-
def
|
|
90
|
+
def to_h
|
|
86
91
|
SERIALIZEABLE_ATTRIBUTES.each_with_object({}) do |name, memo|
|
|
87
92
|
memo[name] = serialize(name)
|
|
88
93
|
end
|
|
@@ -146,6 +151,10 @@ module Sentry
|
|
|
146
151
|
user[:email]
|
|
147
152
|
end
|
|
148
153
|
|
|
154
|
+
def serialize_template
|
|
155
|
+
template if has_parameters?
|
|
156
|
+
end
|
|
157
|
+
|
|
149
158
|
def serialize_attributes
|
|
150
159
|
hash = {}
|
|
151
160
|
|
|
@@ -185,11 +194,11 @@ module Sentry
|
|
|
185
194
|
|
|
186
195
|
if parameters.is_a?(Hash)
|
|
187
196
|
parameters.each do |key, value|
|
|
188
|
-
attributes["
|
|
197
|
+
attributes["#{PARAMETER_PREFIX}.#{key}"] = value
|
|
189
198
|
end
|
|
190
199
|
else
|
|
191
200
|
parameters.each_with_index do |param, index|
|
|
192
|
-
attributes["
|
|
201
|
+
attributes["#{PARAMETER_PREFIX}.#{index}"] = param
|
|
193
202
|
end
|
|
194
203
|
end
|
|
195
204
|
end
|
|
@@ -202,5 +211,9 @@ module Sentry
|
|
|
202
211
|
def is_template?
|
|
203
212
|
body.include?("%s") || TOKEN_REGEXP.match?(body)
|
|
204
213
|
end
|
|
214
|
+
|
|
215
|
+
def has_parameters?
|
|
216
|
+
attributes.keys.any? { |key| key.start_with?(PARAMETER_PREFIX) }
|
|
217
|
+
end
|
|
205
218
|
end
|
|
206
219
|
end
|
data/lib/sentry/profiler.rb
CHANGED
|
@@ -10,8 +10,6 @@ module Sentry
|
|
|
10
10
|
|
|
11
11
|
VERSION = "1"
|
|
12
12
|
PLATFORM = "ruby"
|
|
13
|
-
# 101 Hz in microseconds
|
|
14
|
-
DEFAULT_INTERVAL = 1e6 / 101
|
|
15
13
|
MICRO_TO_NANO_SECONDS = 1e3
|
|
16
14
|
MIN_SAMPLES_REQUIRED = 2
|
|
17
15
|
|
|
@@ -24,6 +22,7 @@ module Sentry
|
|
|
24
22
|
|
|
25
23
|
@profiling_enabled = defined?(StackProf) && configuration.profiling_enabled?
|
|
26
24
|
@profiles_sample_rate = configuration.profiles_sample_rate
|
|
25
|
+
@profiles_sample_interval = configuration.profiles_sample_interval
|
|
27
26
|
@project_root = configuration.project_root
|
|
28
27
|
@app_dirs_pattern = configuration.app_dirs_pattern
|
|
29
28
|
@in_app_pattern = Regexp.new("^(#{@project_root}/)?#{@app_dirs_pattern}")
|
|
@@ -32,7 +31,7 @@ module Sentry
|
|
|
32
31
|
def start
|
|
33
32
|
return unless @sampled
|
|
34
33
|
|
|
35
|
-
@started = StackProf.start(interval:
|
|
34
|
+
@started = StackProf.start(interval: @profiles_sample_interval,
|
|
36
35
|
mode: :wall,
|
|
37
36
|
raw: true,
|
|
38
37
|
aggregate: false)
|
|
@@ -81,9 +80,9 @@ module Sentry
|
|
|
81
80
|
log("Discarding profile due to sampling decision") unless @sampled
|
|
82
81
|
end
|
|
83
82
|
|
|
84
|
-
def
|
|
83
|
+
def to_h
|
|
85
84
|
unless @sampled
|
|
86
|
-
record_lost_event(:sample_rate)
|
|
85
|
+
record_lost_event(:sample_rate) if @profiling_enabled
|
|
87
86
|
return {}
|
|
88
87
|
end
|
|
89
88
|
|
|
@@ -3,15 +3,14 @@
|
|
|
3
3
|
require "securerandom"
|
|
4
4
|
require "sentry/baggage"
|
|
5
5
|
require "sentry/utils/uuid"
|
|
6
|
+
require "sentry/utils/sample_rand"
|
|
6
7
|
|
|
7
8
|
module Sentry
|
|
8
9
|
class PropagationContext
|
|
9
10
|
SENTRY_TRACE_REGEXP = Regexp.new(
|
|
10
|
-
"
|
|
11
|
-
"([0-9a-f]{32})?" + # trace_id
|
|
11
|
+
"\\A([0-9a-f]{32})?" + # trace_id
|
|
12
12
|
"-?([0-9a-f]{16})?" + # span_id
|
|
13
|
-
"-?([01])
|
|
14
|
-
"[ \t]*$" # whitespace
|
|
13
|
+
"-?([01])?\\z" # sampled
|
|
15
14
|
)
|
|
16
15
|
|
|
17
16
|
# An uuid that can be used to identify a trace.
|
|
@@ -33,6 +32,53 @@ module Sentry
|
|
|
33
32
|
# Please use the #get_baggage method for interfacing outside this class.
|
|
34
33
|
# @return [Baggage, nil]
|
|
35
34
|
attr_reader :baggage
|
|
35
|
+
# The propagated random value used for sampling decisions.
|
|
36
|
+
# @return [Float, nil]
|
|
37
|
+
attr_reader :sample_rand
|
|
38
|
+
|
|
39
|
+
# Extract the trace_id, parent_span_id and parent_sampled values from a sentry-trace header.
|
|
40
|
+
#
|
|
41
|
+
# @param sentry_trace [String] the sentry-trace header value from the previous transaction.
|
|
42
|
+
# @return [Array, nil]
|
|
43
|
+
def self.extract_sentry_trace(sentry_trace)
|
|
44
|
+
value = sentry_trace.to_s.strip
|
|
45
|
+
return if value.empty?
|
|
46
|
+
|
|
47
|
+
match = SENTRY_TRACE_REGEXP.match(value)
|
|
48
|
+
return if match.nil?
|
|
49
|
+
|
|
50
|
+
trace_id, parent_span_id, sampled_flag = match[1..3]
|
|
51
|
+
parent_sampled = sampled_flag.nil? ? nil : sampled_flag != "0"
|
|
52
|
+
|
|
53
|
+
[trace_id, parent_span_id, parent_sampled]
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
def self.extract_sample_rand_from_baggage(baggage, trace_id = nil)
|
|
57
|
+
return unless baggage&.items
|
|
58
|
+
|
|
59
|
+
sample_rand_str = baggage.items["sample_rand"]
|
|
60
|
+
return unless sample_rand_str
|
|
61
|
+
|
|
62
|
+
generator = Utils::SampleRand.new(trace_id: trace_id)
|
|
63
|
+
generator.generate_from_value(sample_rand_str)
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
def self.generate_sample_rand(baggage, trace_id, parent_sampled)
|
|
67
|
+
generator = Utils::SampleRand.new(trace_id: trace_id)
|
|
68
|
+
|
|
69
|
+
if baggage&.items && !parent_sampled.nil?
|
|
70
|
+
sample_rate_str = baggage.items["sample_rate"]
|
|
71
|
+
sample_rate = sample_rate_str&.to_f
|
|
72
|
+
|
|
73
|
+
if sample_rate && !parent_sampled.nil?
|
|
74
|
+
generator.generate_from_sampling_decision(parent_sampled, sample_rate)
|
|
75
|
+
else
|
|
76
|
+
generator.generate_from_trace_id
|
|
77
|
+
end
|
|
78
|
+
else
|
|
79
|
+
generator.generate_from_trace_id
|
|
80
|
+
end
|
|
81
|
+
end
|
|
36
82
|
|
|
37
83
|
def initialize(scope, env = nil)
|
|
38
84
|
@scope = scope
|
|
@@ -40,6 +86,7 @@ module Sentry
|
|
|
40
86
|
@parent_sampled = nil
|
|
41
87
|
@baggage = nil
|
|
42
88
|
@incoming_trace = false
|
|
89
|
+
@sample_rand = nil
|
|
43
90
|
|
|
44
91
|
if env
|
|
45
92
|
sentry_trace_header = env["HTTP_SENTRY_TRACE"] || env[SENTRY_TRACE_HEADER_NAME]
|
|
@@ -61,6 +108,8 @@ module Sentry
|
|
|
61
108
|
Baggage.new({})
|
|
62
109
|
end
|
|
63
110
|
|
|
111
|
+
@sample_rand = self.class.extract_sample_rand_from_baggage(@baggage, @trace_id)
|
|
112
|
+
|
|
64
113
|
@baggage.freeze!
|
|
65
114
|
@incoming_trace = true
|
|
66
115
|
end
|
|
@@ -69,20 +118,7 @@ module Sentry
|
|
|
69
118
|
|
|
70
119
|
@trace_id ||= Utils.uuid
|
|
71
120
|
@span_id = Utils.uuid.slice(0, 16)
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
# Extract the trace_id, parent_span_id and parent_sampled values from a sentry-trace header.
|
|
75
|
-
#
|
|
76
|
-
# @param sentry_trace [String] the sentry-trace header value from the previous transaction.
|
|
77
|
-
# @return [Array, nil]
|
|
78
|
-
def self.extract_sentry_trace(sentry_trace)
|
|
79
|
-
match = SENTRY_TRACE_REGEXP.match(sentry_trace)
|
|
80
|
-
return nil if match.nil?
|
|
81
|
-
|
|
82
|
-
trace_id, parent_span_id, sampled_flag = match[1..3]
|
|
83
|
-
parent_sampled = sampled_flag.nil? ? nil : sampled_flag != "0"
|
|
84
|
-
|
|
85
|
-
[trace_id, parent_span_id, parent_sampled]
|
|
121
|
+
@sample_rand ||= self.class.generate_sample_rand(@baggage, @trace_id, @parent_sampled)
|
|
86
122
|
end
|
|
87
123
|
|
|
88
124
|
# Returns the trace context that can be used to embed in an Event.
|
|
@@ -123,6 +159,7 @@ module Sentry
|
|
|
123
159
|
|
|
124
160
|
items = {
|
|
125
161
|
"trace_id" => trace_id,
|
|
162
|
+
"sample_rand" => Utils::SampleRand.format(@sample_rand),
|
|
126
163
|
"environment" => configuration.environment,
|
|
127
164
|
"release" => configuration.release,
|
|
128
165
|
"public_key" => configuration.dsn&.public_key
|
data/lib/sentry/rspec.rb
CHANGED
|
@@ -70,7 +70,7 @@ RSpec::Matchers.define :include_sentry_event do |event_message = "", **opts|
|
|
|
70
70
|
end
|
|
71
71
|
|
|
72
72
|
def dump_events(sentry_events)
|
|
73
|
-
sentry_events.map(
|
|
73
|
+
sentry_events.map(&:to_h).map do |hash|
|
|
74
74
|
hash.select { |k, _| [:message, :contexts, :tags, :exception].include?(k) }
|
|
75
75
|
end.map do |hash|
|
|
76
76
|
JSON.pretty_generate(hash)
|