sentry-ruby 5.26.0 → 6.3.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 +26 -4
- data/README.md +2 -2
- 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/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 +168 -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 +3 -3
- 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 +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 +55 -18
- data/lib/sentry/rspec.rb +1 -1
- data/lib/sentry/scope.rb +32 -5
- data/lib/sentry/sequel.rb +35 -0
- data/lib/sentry/span.rb +2 -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 +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/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 +25 -30
- data/sentry-ruby-core.gemspec +1 -1
- data/sentry-ruby.gemspec +1 -1
- metadata +17 -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/metrics.rb
CHANGED
|
@@ -1,55 +1,60 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
require "sentry/
|
|
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"
|
|
3
|
+
require "sentry/metric_event"
|
|
10
4
|
|
|
11
5
|
module Sentry
|
|
12
6
|
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
7
|
class << self
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
8
|
+
# Increments a counter metric
|
|
9
|
+
# @param name [String] the metric name
|
|
10
|
+
# @param value [Numeric] the value to increment by (default: 1)
|
|
11
|
+
# @param attributes [Hash, nil] additional attributes for the metric (optional)
|
|
12
|
+
# @return [void]
|
|
13
|
+
def count(name, value: 1, attributes: nil)
|
|
14
|
+
return unless Sentry.initialized?
|
|
15
|
+
|
|
16
|
+
Sentry.get_current_hub.capture_metric(
|
|
17
|
+
name: name,
|
|
18
|
+
type: :counter,
|
|
19
|
+
value: value,
|
|
20
|
+
attributes: attributes
|
|
21
|
+
)
|
|
31
22
|
end
|
|
32
23
|
|
|
33
|
-
|
|
34
|
-
|
|
24
|
+
# Records a gauge metric
|
|
25
|
+
# @param name [String] the metric name
|
|
26
|
+
# @param value [Numeric] the gauge value
|
|
27
|
+
# @param unit [String, nil] the metric unit (optional)
|
|
28
|
+
# @param attributes [Hash, nil] additional attributes for the metric (optional)
|
|
29
|
+
# @return [void]
|
|
30
|
+
def gauge(name, value, unit: nil, attributes: nil)
|
|
31
|
+
return unless Sentry.initialized?
|
|
32
|
+
|
|
33
|
+
Sentry.get_current_hub.capture_metric(
|
|
34
|
+
name: name,
|
|
35
|
+
type: :gauge,
|
|
36
|
+
value: value,
|
|
37
|
+
unit: unit,
|
|
38
|
+
attributes: attributes
|
|
39
|
+
)
|
|
35
40
|
end
|
|
36
41
|
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
42
|
+
# Records a distribution metric
|
|
43
|
+
# @param name [String] the metric name
|
|
44
|
+
# @param value [Numeric] the distribution value
|
|
45
|
+
# @param unit [String, nil] the metric unit (optional)
|
|
46
|
+
# @param attributes [Hash, nil] additional attributes for the metric (optional)
|
|
47
|
+
# @return [void]
|
|
48
|
+
def distribution(name, value, unit: nil, attributes: nil)
|
|
49
|
+
return unless Sentry.initialized?
|
|
50
|
+
|
|
51
|
+
Sentry.get_current_hub.capture_metric(
|
|
52
|
+
name: name,
|
|
53
|
+
type: :distribution,
|
|
54
|
+
value: value,
|
|
55
|
+
unit: unit,
|
|
56
|
+
attributes: attributes
|
|
57
|
+
)
|
|
53
58
|
end
|
|
54
59
|
end
|
|
55
60
|
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)
|
data/lib/sentry/scope.rb
CHANGED
|
@@ -46,7 +46,7 @@ module Sentry
|
|
|
46
46
|
# @param hint [Hash] the hint data that'll be passed to event processors.
|
|
47
47
|
# @return [Event]
|
|
48
48
|
def apply_to_event(event, hint = nil)
|
|
49
|
-
unless event.is_a?(CheckInEvent)
|
|
49
|
+
unless event.is_a?(CheckInEvent)
|
|
50
50
|
event.tags = tags.merge(event.tags)
|
|
51
51
|
event.user = user.merge(event.user)
|
|
52
52
|
event.extra = extra.merge(event.extra)
|
|
@@ -60,10 +60,6 @@ module Sentry
|
|
|
60
60
|
event.attachments = attachments
|
|
61
61
|
end
|
|
62
62
|
|
|
63
|
-
if event.is_a?(LogEvent)
|
|
64
|
-
event.user = user.merge(event.user)
|
|
65
|
-
end
|
|
66
|
-
|
|
67
63
|
if span
|
|
68
64
|
event.contexts[:trace] ||= span.get_trace_context
|
|
69
65
|
|
|
@@ -89,6 +85,37 @@ module Sentry
|
|
|
89
85
|
event
|
|
90
86
|
end
|
|
91
87
|
|
|
88
|
+
# A leaner version of apply_to_event that applies to
|
|
89
|
+
# lightweight payloads like Logs and Metrics.
|
|
90
|
+
#
|
|
91
|
+
# Adds trace_id, span_id, user from the scope and default attributes from configuration.
|
|
92
|
+
#
|
|
93
|
+
# @param telemetry [MetricEvent, LogEvent] the telemetry event to apply scope context to
|
|
94
|
+
# @return [MetricEvent, LogEvent] the telemetry event with scope context applied
|
|
95
|
+
def apply_to_telemetry(telemetry)
|
|
96
|
+
# TODO-neel when new scope set_attribute api is added: add them here
|
|
97
|
+
trace_context = span ? span.get_trace_context : propagation_context.get_trace_context
|
|
98
|
+
telemetry.trace_id = trace_context[:trace_id]
|
|
99
|
+
telemetry.span_id = trace_context[:span_id]
|
|
100
|
+
|
|
101
|
+
configuration = Sentry.configuration
|
|
102
|
+
return telemetry unless configuration
|
|
103
|
+
|
|
104
|
+
telemetry.attributes["sentry.sdk.name"] ||= Sentry.sdk_meta["name"]
|
|
105
|
+
telemetry.attributes["sentry.sdk.version"] ||= Sentry.sdk_meta["version"]
|
|
106
|
+
telemetry.attributes["sentry.environment"] ||= configuration.environment if configuration.environment
|
|
107
|
+
telemetry.attributes["sentry.release"] ||= configuration.release if configuration.release
|
|
108
|
+
telemetry.attributes["server.address"] ||= configuration.server_name if configuration.server_name
|
|
109
|
+
|
|
110
|
+
if configuration.send_default_pii && !user.empty?
|
|
111
|
+
telemetry.attributes["user.id"] ||= user[:id] if user[:id]
|
|
112
|
+
telemetry.attributes["user.name"] ||= user[:username] if user[:username]
|
|
113
|
+
telemetry.attributes["user.email"] ||= user[:email] if user[:email]
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
telemetry
|
|
117
|
+
end
|
|
118
|
+
|
|
92
119
|
# Adds the breadcrumb to the scope's breadcrumbs buffer.
|
|
93
120
|
# @param breadcrumb [Breadcrumb]
|
|
94
121
|
# @return [void]
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Sentry
|
|
4
|
+
module Sequel
|
|
5
|
+
OP_NAME = "db.sql.sequel"
|
|
6
|
+
SPAN_ORIGIN = "auto.db.sequel"
|
|
7
|
+
|
|
8
|
+
# Sequel Database extension module that instruments queries
|
|
9
|
+
module DatabaseExtension
|
|
10
|
+
def log_connection_yield(sql, conn, args = nil)
|
|
11
|
+
return super unless Sentry.initialized?
|
|
12
|
+
|
|
13
|
+
Sentry.with_child_span(op: OP_NAME, start_timestamp: Sentry.utc_now.to_f, origin: SPAN_ORIGIN) do |span|
|
|
14
|
+
result = super
|
|
15
|
+
|
|
16
|
+
if span
|
|
17
|
+
span.set_description(sql)
|
|
18
|
+
span.set_data(Span::DataConventions::DB_SYSTEM, database_type.to_s)
|
|
19
|
+
span.set_data(Span::DataConventions::DB_NAME, opts[:database]) if opts[:database]
|
|
20
|
+
span.set_data(Span::DataConventions::SERVER_ADDRESS, opts[:host]) if opts[:host]
|
|
21
|
+
span.set_data(Span::DataConventions::SERVER_PORT, opts[:port]) if opts[:port]
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
result
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
::Sequel::Database.register_extension(:sentry, Sentry::Sequel::DatabaseExtension)
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
Sentry.register_patch(:sequel) do
|
|
34
|
+
::Sequel::Database.extension(:sentry) if defined?(::Sequel::Database)
|
|
35
|
+
end
|
data/lib/sentry/span.rb
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
require "securerandom"
|
|
4
|
-
require "sentry/metrics/local_aggregator"
|
|
5
4
|
require "sentry/utils/uuid"
|
|
6
5
|
|
|
7
6
|
module Sentry
|
|
@@ -173,8 +172,8 @@ module Sentry
|
|
|
173
172
|
end
|
|
174
173
|
|
|
175
174
|
# @return [Hash]
|
|
176
|
-
def
|
|
177
|
-
|
|
175
|
+
def to_h
|
|
176
|
+
{
|
|
178
177
|
trace_id: @trace_id,
|
|
179
178
|
span_id: @span_id,
|
|
180
179
|
parent_span_id: @parent_span_id,
|
|
@@ -187,11 +186,6 @@ module Sentry
|
|
|
187
186
|
data: @data,
|
|
188
187
|
origin: @origin
|
|
189
188
|
}
|
|
190
|
-
|
|
191
|
-
summary = metrics_summary
|
|
192
|
-
hash[:_metrics_summary] = summary if summary
|
|
193
|
-
|
|
194
|
-
hash
|
|
195
189
|
end
|
|
196
190
|
|
|
197
191
|
# Returns the span's context that can be used to embed in an Event.
|
|
@@ -307,14 +301,5 @@ module Sentry
|
|
|
307
301
|
def set_origin(origin)
|
|
308
302
|
@origin = origin
|
|
309
303
|
end
|
|
310
|
-
|
|
311
|
-
# Collects gauge metrics on the span for metric summaries.
|
|
312
|
-
def metrics_local_aggregator
|
|
313
|
-
@metrics_local_aggregator ||= Sentry::Metrics::LocalAggregator.new
|
|
314
|
-
end
|
|
315
|
-
|
|
316
|
-
def metrics_summary
|
|
317
|
-
@metrics_local_aggregator&.to_hash
|
|
318
|
-
end
|
|
319
304
|
end
|
|
320
305
|
end
|
|
@@ -12,11 +12,16 @@ module Sentry
|
|
|
12
12
|
4 => :fatal
|
|
13
13
|
}.freeze
|
|
14
14
|
|
|
15
|
+
ORIGIN = "auto.log.ruby.std_logger"
|
|
16
|
+
|
|
15
17
|
def add(severity, message = nil, progname = nil, &block)
|
|
16
18
|
result = super
|
|
17
19
|
|
|
18
20
|
return unless Sentry.initialized? && Sentry.get_current_hub
|
|
19
21
|
|
|
22
|
+
# Only process logs that meet or exceed the logger's level
|
|
23
|
+
return result if severity < level
|
|
24
|
+
|
|
20
25
|
# exclude sentry SDK logs -- to prevent recursive log action,
|
|
21
26
|
# do not process internal logs again
|
|
22
27
|
if message.nil? && progname != Sentry::Logger::PROGNAME
|
|
@@ -32,7 +37,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
|