sentry-ruby 5.23.0 → 5.27.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/.rspec +3 -1
- data/Gemfile +1 -3
- data/README.md +11 -6
- data/Rakefile +7 -11
- data/lib/sentry/background_worker.rb +2 -3
- data/lib/sentry/backpressure_monitor.rb +1 -1
- data/lib/sentry/breadcrumb.rb +5 -4
- data/lib/sentry/check_in_event.rb +2 -1
- data/lib/sentry/client.rb +84 -4
- data/lib/sentry/configuration.rb +63 -2
- data/lib/sentry/debug_structured_logger.rb +94 -0
- data/lib/sentry/dsn.rb +32 -0
- data/lib/sentry/envelope/item.rb +1 -1
- data/lib/sentry/event.rb +2 -1
- data/lib/sentry/hub.rb +13 -1
- data/lib/sentry/interfaces/request.rb +1 -1
- data/lib/sentry/log_event.rb +206 -0
- data/lib/sentry/log_event_buffer.rb +75 -0
- data/lib/sentry/metrics/aggregator.rb +1 -1
- data/lib/sentry/profiler.rb +3 -2
- data/lib/sentry/propagation_context.rb +59 -22
- data/lib/sentry/scope.rb +13 -3
- data/lib/sentry/session_flusher.rb +1 -1
- data/lib/sentry/span.rb +4 -3
- data/lib/sentry/std_lib_logger.rb +50 -0
- data/lib/sentry/structured_logger.rb +138 -0
- data/lib/sentry/test_helper.rb +29 -0
- data/lib/sentry/threaded_periodic_worker.rb +3 -3
- data/lib/sentry/transaction.rb +30 -8
- 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 +17 -8
- data/lib/sentry/utils/logging_helper.rb +10 -3
- data/lib/sentry/utils/sample_rand.rb +97 -0
- data/lib/sentry/utils/uuid.rb +13 -0
- data/lib/sentry/vernier/profiler.rb +3 -2
- data/lib/sentry/version.rb +1 -1
- data/lib/sentry-ruby.rb +67 -4
- metadata +17 -9
@@ -0,0 +1,138 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Sentry
|
4
|
+
# The StructuredLogger class implements Sentry's SDK telemetry logs protocol.
|
5
|
+
# It provides methods for logging messages at different severity levels and
|
6
|
+
# sending them to Sentry with structured data.
|
7
|
+
#
|
8
|
+
# This class follows the Sentry Logs Protocol as defined in:
|
9
|
+
# https://develop.sentry.dev/sdk/telemetry/logs/
|
10
|
+
#
|
11
|
+
# @example Basic usage
|
12
|
+
# Sentry.logger.info("User logged in", user_id: 123)
|
13
|
+
#
|
14
|
+
# @example With structured data
|
15
|
+
# Sentry.logger.warn("API request failed",
|
16
|
+
# status_code: 404,
|
17
|
+
# endpoint: "/api/users",
|
18
|
+
# request_id: "abc-123"
|
19
|
+
# )
|
20
|
+
#
|
21
|
+
# @example With a message template
|
22
|
+
# # Using positional parameters
|
23
|
+
# Sentry.logger.info("User %s logged in", ["Jane Doe"])
|
24
|
+
#
|
25
|
+
# # Using hash parameters
|
26
|
+
# Sentry.logger.info("User %{name} logged in", name: "Jane Doe")
|
27
|
+
#
|
28
|
+
# # Using hash parameters and extra attributes
|
29
|
+
# Sentry.logger.info("User %{name} logged in", name: "Jane Doe", user_id: 312)
|
30
|
+
#
|
31
|
+
# @see https://develop.sentry.dev/sdk/telemetry/logs/ Sentry SDK Telemetry Logs Protocol
|
32
|
+
class StructuredLogger
|
33
|
+
# Severity number mapping for log levels according to the Sentry Logs Protocol
|
34
|
+
# @see https://develop.sentry.dev/sdk/telemetry/logs/#log-severity-number
|
35
|
+
LEVELS = {
|
36
|
+
trace: 1,
|
37
|
+
debug: 5,
|
38
|
+
info: 9,
|
39
|
+
warn: 13,
|
40
|
+
error: 17,
|
41
|
+
fatal: 21
|
42
|
+
}.freeze
|
43
|
+
|
44
|
+
# @return [Configuration] The Sentry configuration
|
45
|
+
# @!visibility private
|
46
|
+
attr_reader :config
|
47
|
+
|
48
|
+
# Initializes a new StructuredLogger instance
|
49
|
+
#
|
50
|
+
# @param config [Configuration] The Sentry configuration
|
51
|
+
def initialize(config)
|
52
|
+
@config = config
|
53
|
+
end
|
54
|
+
|
55
|
+
# Logs a message at TRACE level
|
56
|
+
#
|
57
|
+
# @param message [String] The log message
|
58
|
+
# @param parameters [Array] Array of values to replace template parameters in the message
|
59
|
+
# @param attributes [Hash] Additional attributes to include with the log
|
60
|
+
#
|
61
|
+
# @return [LogEvent, nil] The created log event or nil if logging is disabled
|
62
|
+
def trace(message, parameters = [], **attributes)
|
63
|
+
log(__method__, message, parameters: parameters, **attributes)
|
64
|
+
end
|
65
|
+
|
66
|
+
# Logs a message at DEBUG level
|
67
|
+
#
|
68
|
+
# @param message [String] The log message
|
69
|
+
# @param parameters [Array] Array of values to replace template parameters in the message
|
70
|
+
# @param attributes [Hash] Additional attributes to include with the log
|
71
|
+
#
|
72
|
+
# @return [LogEvent, nil] The created log event or nil if logging is disabled
|
73
|
+
def debug(message, parameters = [], **attributes)
|
74
|
+
log(__method__, message, parameters: parameters, **attributes)
|
75
|
+
end
|
76
|
+
|
77
|
+
# Logs a message at INFO level
|
78
|
+
#
|
79
|
+
# @param message [String] The log message
|
80
|
+
# @param parameters [Array] Array of values to replace template parameters in the message
|
81
|
+
# @param attributes [Hash] Additional attributes to include with the log
|
82
|
+
#
|
83
|
+
# @return [LogEvent, nil] The created log event or nil if logging is disabled
|
84
|
+
def info(message, parameters = [], **attributes)
|
85
|
+
log(__method__, message, parameters: parameters, **attributes)
|
86
|
+
end
|
87
|
+
|
88
|
+
# Logs a message at WARN level
|
89
|
+
#
|
90
|
+
# @param message [String] The log message
|
91
|
+
# @param parameters [Array] Array of values to replace template parameters in the message
|
92
|
+
# @param attributes [Hash] Additional attributes to include with the log
|
93
|
+
#
|
94
|
+
# @return [LogEvent, nil] The created log event or nil if logging is disabled
|
95
|
+
def warn(message, parameters = [], **attributes)
|
96
|
+
log(__method__, message, parameters: parameters, **attributes)
|
97
|
+
end
|
98
|
+
|
99
|
+
# Logs a message at ERROR level
|
100
|
+
#
|
101
|
+
# @param message [String] The log message
|
102
|
+
# @param parameters [Array] Array of values to replace template parameters in the message
|
103
|
+
# @param attributes [Hash] Additional attributes to include with the log
|
104
|
+
#
|
105
|
+
# @return [LogEvent, nil] The created log event or nil if logging is disabled
|
106
|
+
def error(message, parameters = [], **attributes)
|
107
|
+
log(__method__, message, parameters: parameters, **attributes)
|
108
|
+
end
|
109
|
+
|
110
|
+
# Logs a message at FATAL level
|
111
|
+
#
|
112
|
+
# @param message [String] The log message
|
113
|
+
# @param parameters [Array] Array of values to replace template parameters in the message
|
114
|
+
# @param attributes [Hash] Additional attributes to include with the log
|
115
|
+
#
|
116
|
+
# @return [LogEvent, nil] The created log event or nil if logging is disabled
|
117
|
+
def fatal(message, parameters = [], **attributes)
|
118
|
+
log(__method__, message, parameters: parameters, **attributes)
|
119
|
+
end
|
120
|
+
|
121
|
+
# Logs a message at the specified level
|
122
|
+
#
|
123
|
+
# @param level [Symbol] The log level (:trace, :debug, :info, :warn, :error, :fatal)
|
124
|
+
# @param message [String] The log message
|
125
|
+
# @param parameters [Array, Hash] Array or Hash of values to replace template parameters in the message
|
126
|
+
# @param attributes [Hash] Additional attributes to include with the log
|
127
|
+
#
|
128
|
+
# @return [LogEvent, nil] The created log event or nil if logging is disabled
|
129
|
+
def log(level, message, parameters:, **attributes)
|
130
|
+
case parameters
|
131
|
+
when Array then
|
132
|
+
Sentry.capture_log(message, level: level, severity: LEVELS[level], parameters: parameters, **attributes)
|
133
|
+
else
|
134
|
+
Sentry.capture_log(message, level: level, severity: LEVELS[level], **parameters)
|
135
|
+
end
|
136
|
+
end
|
137
|
+
end
|
138
|
+
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.
|
@@ -46,6 +51,8 @@ module Sentry
|
|
46
51
|
def teardown_sentry_test
|
47
52
|
return unless Sentry.initialized?
|
48
53
|
|
54
|
+
clear_sentry_events
|
55
|
+
|
49
56
|
# pop testing layer created by `setup_sentry_test`
|
50
57
|
# but keep the base layer to avoid nil-pointer errors
|
51
58
|
# TODO: find a way to notify users if they somehow popped the test layer before calling this method
|
@@ -55,6 +62,21 @@ module Sentry
|
|
55
62
|
Sentry::Scope.global_event_processors.clear
|
56
63
|
end
|
57
64
|
|
65
|
+
def clear_sentry_events
|
66
|
+
return unless Sentry.initialized?
|
67
|
+
|
68
|
+
sentry_transport.clear if sentry_transport.respond_to?(:clear)
|
69
|
+
|
70
|
+
if Sentry.configuration.enable_logs && sentry_logger.respond_to?(:clear)
|
71
|
+
sentry_logger.clear
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
# @return [Sentry::StructuredLogger, Sentry::DebugStructuredLogger]
|
76
|
+
def sentry_logger
|
77
|
+
Sentry.logger
|
78
|
+
end
|
79
|
+
|
58
80
|
# @return [Transport]
|
59
81
|
def sentry_transport
|
60
82
|
Sentry.get_current_client.transport
|
@@ -72,6 +94,13 @@ module Sentry
|
|
72
94
|
sentry_transport.envelopes
|
73
95
|
end
|
74
96
|
|
97
|
+
def sentry_logs
|
98
|
+
sentry_envelopes
|
99
|
+
.flat_map(&:items)
|
100
|
+
.select { |item| item.headers[:type] == "log" }
|
101
|
+
.flat_map { |item| item.payload[:items] }
|
102
|
+
end
|
103
|
+
|
75
104
|
# Returns the last captured event object.
|
76
105
|
# @return [Event, nil]
|
77
106
|
def last_sentry_event
|
@@ -4,11 +4,11 @@ module Sentry
|
|
4
4
|
class ThreadedPeriodicWorker
|
5
5
|
include LoggingHelper
|
6
6
|
|
7
|
-
def initialize(
|
7
|
+
def initialize(sdk_logger, interval)
|
8
8
|
@thread = nil
|
9
9
|
@exited = false
|
10
|
-
@interval =
|
11
|
-
@
|
10
|
+
@interval = interval
|
11
|
+
@sdk_logger = sdk_logger
|
12
12
|
end
|
13
13
|
|
14
14
|
def ensure_thread
|
data/lib/sentry/transaction.rb
CHANGED
@@ -2,6 +2,7 @@
|
|
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
|
@@ -45,9 +46,6 @@ module Sentry
|
|
45
46
|
# @deprecated Use Sentry.configuration instead.
|
46
47
|
attr_reader :configuration
|
47
48
|
|
48
|
-
# @deprecated Use Sentry.logger instead.
|
49
|
-
attr_reader :logger
|
50
|
-
|
51
49
|
# The effective sample rate at which this transaction was sampled.
|
52
50
|
# @return [Float, nil]
|
53
51
|
attr_reader :effective_sample_rate
|
@@ -60,12 +58,17 @@ module Sentry
|
|
60
58
|
# @return [Profiler]
|
61
59
|
attr_reader :profiler
|
62
60
|
|
61
|
+
# Sample rand value generated from trace_id
|
62
|
+
# @return [String]
|
63
|
+
attr_reader :sample_rand
|
64
|
+
|
63
65
|
def initialize(
|
64
66
|
hub:,
|
65
67
|
name: nil,
|
66
68
|
source: :custom,
|
67
69
|
parent_sampled: nil,
|
68
70
|
baggage: nil,
|
71
|
+
sample_rand: nil,
|
69
72
|
**options
|
70
73
|
)
|
71
74
|
super(transaction: self, **options)
|
@@ -78,19 +81,25 @@ module Sentry
|
|
78
81
|
@tracing_enabled = hub.configuration.tracing_enabled?
|
79
82
|
@traces_sampler = hub.configuration.traces_sampler
|
80
83
|
@traces_sample_rate = hub.configuration.traces_sample_rate
|
81
|
-
@
|
84
|
+
@sdk_logger = hub.configuration.sdk_logger
|
82
85
|
@release = hub.configuration.release
|
83
86
|
@environment = hub.configuration.environment
|
84
87
|
@dsn = hub.configuration.dsn
|
85
88
|
@effective_sample_rate = nil
|
86
89
|
@contexts = {}
|
87
90
|
@measurements = {}
|
91
|
+
@sample_rand = sample_rand
|
88
92
|
|
89
93
|
unless @hub.profiler_running?
|
90
94
|
@profiler = @configuration.profiler_class.new(@configuration)
|
91
95
|
end
|
92
96
|
|
93
97
|
init_span_recorder
|
98
|
+
|
99
|
+
unless @sample_rand
|
100
|
+
generator = Utils::SampleRand.new(trace_id: @trace_id)
|
101
|
+
@sample_rand = generator.generate_from_trace_id
|
102
|
+
end
|
94
103
|
end
|
95
104
|
|
96
105
|
# @deprecated use Sentry.continue_trace instead.
|
@@ -126,12 +135,15 @@ module Sentry
|
|
126
135
|
|
127
136
|
baggage.freeze!
|
128
137
|
|
138
|
+
sample_rand = extract_sample_rand_from_baggage(baggage, trace_id, parent_sampled)
|
139
|
+
|
129
140
|
new(
|
130
141
|
trace_id: trace_id,
|
131
142
|
parent_span_id: parent_span_id,
|
132
143
|
parent_sampled: parent_sampled,
|
133
144
|
hub: hub,
|
134
145
|
baggage: baggage,
|
146
|
+
sample_rand: sample_rand,
|
135
147
|
**options
|
136
148
|
)
|
137
149
|
end
|
@@ -142,6 +154,11 @@ module Sentry
|
|
142
154
|
PropagationContext.extract_sentry_trace(sentry_trace)
|
143
155
|
end
|
144
156
|
|
157
|
+
def self.extract_sample_rand_from_baggage(baggage, trace_id, parent_sampled)
|
158
|
+
PropagationContext.extract_sample_rand_from_baggage(baggage, trace_id) ||
|
159
|
+
PropagationContext.generate_sample_rand(baggage, trace_id, parent_sampled)
|
160
|
+
end
|
161
|
+
|
145
162
|
# @return [Hash]
|
146
163
|
def to_hash
|
147
164
|
hash = super
|
@@ -156,6 +173,13 @@ module Sentry
|
|
156
173
|
hash
|
157
174
|
end
|
158
175
|
|
176
|
+
def parent_sample_rate
|
177
|
+
return unless @baggage&.items
|
178
|
+
|
179
|
+
sample_rate_str = @baggage.items["sample_rate"]
|
180
|
+
sample_rate_str&.to_f
|
181
|
+
end
|
182
|
+
|
159
183
|
# @return [Transaction]
|
160
184
|
def deep_dup
|
161
185
|
copy = super
|
@@ -228,7 +252,7 @@ module Sentry
|
|
228
252
|
@effective_sample_rate /= 2**factor
|
229
253
|
end
|
230
254
|
|
231
|
-
@sampled =
|
255
|
+
@sampled = @sample_rand < @effective_sample_rate
|
232
256
|
end
|
233
257
|
|
234
258
|
if @sampled
|
@@ -334,6 +358,7 @@ module Sentry
|
|
334
358
|
items = {
|
335
359
|
"trace_id" => trace_id,
|
336
360
|
"sample_rate" => effective_sample_rate&.to_s,
|
361
|
+
"sample_rand" => Utils::SampleRand.format(@sample_rand),
|
337
362
|
"sampled" => sampled&.to_s,
|
338
363
|
"environment" => @environment,
|
339
364
|
"release" => @release,
|
@@ -342,9 +367,6 @@ module Sentry
|
|
342
367
|
|
343
368
|
items["transaction"] = name unless source_low_quality?
|
344
369
|
|
345
|
-
user = @hub.current_scope&.user
|
346
|
-
items["user_segment"] = user["segment"] if user && user["segment"]
|
347
|
-
|
348
370
|
items.compact!
|
349
371
|
@baggage = Baggage.new(items, mutable: false)
|
350
372
|
end
|
@@ -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
@@ -26,11 +26,8 @@ module Sentry
|
|
26
26
|
|
27
27
|
attr_reader :rate_limits, :discarded_events, :last_client_report_sent
|
28
28
|
|
29
|
-
# @deprecated Use Sentry.logger to retrieve the current logger instead.
|
30
|
-
attr_reader :logger
|
31
|
-
|
32
29
|
def initialize(configuration)
|
33
|
-
@
|
30
|
+
@sdk_logger = configuration.sdk_logger
|
34
31
|
@transport_configuration = configuration.transport
|
35
32
|
@dsn = configuration.dsn
|
36
33
|
@rate_limits = {}
|
@@ -133,10 +130,21 @@ module Sentry
|
|
133
130
|
|
134
131
|
envelope = Envelope.new(envelope_headers)
|
135
132
|
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
133
|
+
if event.is_a?(LogEvent)
|
134
|
+
envelope.add_item(
|
135
|
+
{
|
136
|
+
type: "log",
|
137
|
+
item_count: 1,
|
138
|
+
content_type: "application/vnd.sentry.items.log+json"
|
139
|
+
},
|
140
|
+
{ items: [event_payload] }
|
141
|
+
)
|
142
|
+
else
|
143
|
+
envelope.add_item(
|
144
|
+
{ type: item_type, content_type: "application/json" },
|
145
|
+
event_payload
|
146
|
+
)
|
147
|
+
end
|
140
148
|
|
141
149
|
if event.is_a?(TransactionEvent) && event.profile
|
142
150
|
envelope.add_item(
|
@@ -215,3 +223,4 @@ end
|
|
215
223
|
require "sentry/transport/dummy_transport"
|
216
224
|
require "sentry/transport/http_transport"
|
217
225
|
require "sentry/transport/spotlight_transport"
|
226
|
+
require "sentry/transport/debug_transport"
|
@@ -1,22 +1,29 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
module Sentry
|
4
|
+
# @private
|
4
5
|
module LoggingHelper
|
6
|
+
# @!visibility private
|
7
|
+
attr_reader :sdk_logger
|
8
|
+
|
9
|
+
# @!visibility private
|
5
10
|
def log_error(message, exception, debug: false)
|
6
11
|
message = "#{message}: #{exception.message}"
|
7
12
|
message += "\n#{exception.backtrace.join("\n")}" if debug
|
8
13
|
|
9
|
-
|
14
|
+
sdk_logger.error(LOGGER_PROGNAME) do
|
10
15
|
message
|
11
16
|
end
|
12
17
|
end
|
13
18
|
|
19
|
+
# @!visibility private
|
14
20
|
def log_debug(message)
|
15
|
-
|
21
|
+
sdk_logger.debug(LOGGER_PROGNAME) { message }
|
16
22
|
end
|
17
23
|
|
24
|
+
# @!visibility private
|
18
25
|
def log_warn(message)
|
19
|
-
|
26
|
+
sdk_logger.warn(LOGGER_PROGNAME) { message }
|
20
27
|
end
|
21
28
|
end
|
22
29
|
end
|
@@ -0,0 +1,97 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Sentry
|
4
|
+
module Utils
|
5
|
+
class SampleRand
|
6
|
+
PRECISION = 1_000_000.0
|
7
|
+
FORMAT_PRECISION = 6
|
8
|
+
|
9
|
+
attr_reader :trace_id
|
10
|
+
|
11
|
+
def self.valid?(value)
|
12
|
+
return false unless value
|
13
|
+
value >= 0.0 && value < 1.0
|
14
|
+
end
|
15
|
+
|
16
|
+
def self.format(value)
|
17
|
+
return unless value
|
18
|
+
|
19
|
+
truncated = (value * PRECISION).floor / PRECISION
|
20
|
+
"%.#{FORMAT_PRECISION}f" % truncated
|
21
|
+
end
|
22
|
+
|
23
|
+
def initialize(trace_id: nil)
|
24
|
+
@trace_id = trace_id
|
25
|
+
end
|
26
|
+
|
27
|
+
def generate_from_trace_id
|
28
|
+
(random_from_trace_id * PRECISION).floor / PRECISION
|
29
|
+
end
|
30
|
+
|
31
|
+
def generate_from_sampling_decision(sampled, sample_rate)
|
32
|
+
if invalid_sample_rate?(sample_rate)
|
33
|
+
fallback_generation
|
34
|
+
else
|
35
|
+
generate_based_on_sampling(sampled, sample_rate)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
def generate_from_value(sample_rand_value)
|
40
|
+
parsed_value = parse_value(sample_rand_value)
|
41
|
+
|
42
|
+
if self.class.valid?(parsed_value)
|
43
|
+
parsed_value
|
44
|
+
else
|
45
|
+
fallback_generation
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
private
|
50
|
+
|
51
|
+
def random_from_trace_id
|
52
|
+
if @trace_id
|
53
|
+
Random.new(@trace_id[0, 16].to_i(16))
|
54
|
+
else
|
55
|
+
Random.new
|
56
|
+
end.rand(1.0)
|
57
|
+
end
|
58
|
+
|
59
|
+
def invalid_sample_rate?(sample_rate)
|
60
|
+
sample_rate.nil? || sample_rate <= 0.0 || sample_rate > 1.0
|
61
|
+
end
|
62
|
+
|
63
|
+
def fallback_generation
|
64
|
+
if @trace_id
|
65
|
+
(random_from_trace_id * PRECISION).floor / PRECISION
|
66
|
+
else
|
67
|
+
format_random(Random.rand(1.0))
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
def generate_based_on_sampling(sampled, sample_rate)
|
72
|
+
random = random_from_trace_id
|
73
|
+
|
74
|
+
result = if sampled
|
75
|
+
random * sample_rate
|
76
|
+
elsif sample_rate == 1.0
|
77
|
+
random
|
78
|
+
else
|
79
|
+
sample_rate + random * (1.0 - sample_rate)
|
80
|
+
end
|
81
|
+
|
82
|
+
format_random(result)
|
83
|
+
end
|
84
|
+
|
85
|
+
def format_random(value)
|
86
|
+
truncated = (value * PRECISION).floor / PRECISION
|
87
|
+
("%.#{FORMAT_PRECISION}f" % truncated).to_f
|
88
|
+
end
|
89
|
+
|
90
|
+
def parse_value(sample_rand_value)
|
91
|
+
Float(sample_rand_value)
|
92
|
+
rescue ArgumentError
|
93
|
+
nil
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|
@@ -3,6 +3,7 @@
|
|
3
3
|
require "securerandom"
|
4
4
|
require_relative "../profiler/helpers"
|
5
5
|
require_relative "output"
|
6
|
+
require "sentry/utils/uuid"
|
6
7
|
|
7
8
|
module Sentry
|
8
9
|
module Vernier
|
@@ -12,7 +13,7 @@ module Sentry
|
|
12
13
|
attr_reader :started, :event_id, :result
|
13
14
|
|
14
15
|
def initialize(configuration)
|
15
|
-
@event_id =
|
16
|
+
@event_id = Utils.uuid
|
16
17
|
|
17
18
|
@started = false
|
18
19
|
@sampled = nil
|
@@ -103,7 +104,7 @@ module Sentry
|
|
103
104
|
private
|
104
105
|
|
105
106
|
def log(message)
|
106
|
-
Sentry.
|
107
|
+
Sentry.sdk_logger.debug(LOGGER_PROGNAME) { "[Profiler::Vernier] #{message}" }
|
107
108
|
end
|
108
109
|
|
109
110
|
def record_lost_event(reason)
|
data/lib/sentry/version.rb
CHANGED