sentry-ruby-core 6.2.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 +24 -0
- data/README.md +1 -1
- data/lib/sentry/client.rb +22 -49
- data/lib/sentry/configuration.rb +43 -0
- data/lib/sentry/envelope/item.rb +2 -1
- data/lib/sentry/hub.rb +23 -0
- data/lib/sentry/interfaces/single_exception.rb +1 -1
- data/lib/sentry/log_event.rb +23 -141
- data/lib/sentry/log_event_buffer.rb +13 -67
- data/lib/sentry/metric_event.rb +49 -0
- data/lib/sentry/metric_event_buffer.rb +28 -0
- data/lib/sentry/metrics.rb +61 -0
- data/lib/sentry/scope.rb +32 -5
- data/lib/sentry/sequel.rb +35 -0
- data/lib/sentry/std_lib_logger.rb +4 -0
- data/lib/sentry/telemetry_event_buffer.rb +130 -0
- data/lib/sentry/test_helper.rb +7 -0
- data/lib/sentry/utils/logging_helper.rb +18 -4
- data/lib/sentry/utils/telemetry_attributes.rb +30 -0
- data/lib/sentry/version.rb +1 -1
- data/lib/sentry-ruby.rb +19 -0
- metadata +9 -3
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 6dcf2768081f524baa62e2df5f33a2b1bc927f6ed818e17481376e63e83153b2
|
|
4
|
+
data.tar.gz: 71c6db91301a24a1b05afa798a249de12b31a5d68c45d16f138f73c0999b3183
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 32e0f2534e8785d3e145f574ef8cbc66abdd6300ed4b0695d69a5a8f4788870d1d0c89da5257ce5de9114505b90b0902981afae0365e028d7e4e8c8caec00d23
|
|
7
|
+
data.tar.gz: 5072c6873787d7e070050df7aeeb3e9040ca3add4c04f1c4e21c63d12fb241ee6615eced7f71fac5edeee4381d60272069bdc38ce967033ead0e2e76894b2573
|
data/Gemfile
CHANGED
|
@@ -7,8 +7,11 @@ eval_gemfile "../Gemfile.dev"
|
|
|
7
7
|
|
|
8
8
|
gem "sentry-ruby", path: "./"
|
|
9
9
|
|
|
10
|
+
ruby_version = Gem::Version.new(RUBY_VERSION)
|
|
11
|
+
|
|
10
12
|
rack_version = ENV["RACK_VERSION"]
|
|
11
13
|
rack_version = "3.0.0" if rack_version.nil?
|
|
14
|
+
|
|
12
15
|
gem "rack", "~> #{Gem::Version.new(rack_version)}" unless rack_version == "0"
|
|
13
16
|
|
|
14
17
|
redis_rb_version = ENV.fetch("REDIS_RB_VERSION", "5.0")
|
|
@@ -32,3 +35,24 @@ gem "webrick"
|
|
|
32
35
|
gem "faraday"
|
|
33
36
|
gem "excon"
|
|
34
37
|
gem "webmock"
|
|
38
|
+
|
|
39
|
+
group :sequel do
|
|
40
|
+
gem "sequel"
|
|
41
|
+
|
|
42
|
+
sqlite_version = if ruby_version >= Gem::Version.new("3.2")
|
|
43
|
+
"2.1.0"
|
|
44
|
+
elsif ruby_version >= Gem::Version.new("3.0")
|
|
45
|
+
"1.4.0"
|
|
46
|
+
else
|
|
47
|
+
"1.3.0"
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
platform :ruby do
|
|
51
|
+
gem "sqlite3", "~> #{sqlite_version}"
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
platform :jruby do
|
|
55
|
+
gem "activerecord-jdbcmysql-adapter"
|
|
56
|
+
gem "jdbc-sqlite3"
|
|
57
|
+
end
|
|
58
|
+
end
|
data/README.md
CHANGED
|
@@ -105,7 +105,7 @@ To learn more about sampling transactions, please visit the [official documentat
|
|
|
105
105
|
|
|
106
106
|
* [](https://docs.sentry.io/platforms/ruby/)
|
|
107
107
|
* [](https://forum.sentry.io/c/sdks)
|
|
108
|
-
* [](https://discord.gg/
|
|
108
|
+
* [](https://discord.gg/sentry)
|
|
109
109
|
* [](https://stackoverflow.com/questions/tagged/sentry)
|
|
110
110
|
* [](https://x.com/intent/follow?screen_name=sentry)
|
|
111
111
|
|
data/lib/sentry/client.rb
CHANGED
|
@@ -3,6 +3,8 @@
|
|
|
3
3
|
require "sentry/transport"
|
|
4
4
|
require "sentry/log_event"
|
|
5
5
|
require "sentry/log_event_buffer"
|
|
6
|
+
require "sentry/metric_event"
|
|
7
|
+
require "sentry/metric_event_buffer"
|
|
6
8
|
require "sentry/utils/uuid"
|
|
7
9
|
require "sentry/utils/encoding_helper"
|
|
8
10
|
|
|
@@ -21,6 +23,9 @@ module Sentry
|
|
|
21
23
|
# @!visibility private
|
|
22
24
|
attr_reader :log_event_buffer
|
|
23
25
|
|
|
26
|
+
# @!visibility private
|
|
27
|
+
attr_reader :metric_event_buffer
|
|
28
|
+
|
|
24
29
|
# @!macro configuration
|
|
25
30
|
attr_reader :configuration
|
|
26
31
|
|
|
@@ -44,7 +49,11 @@ module Sentry
|
|
|
44
49
|
@spotlight_transport = SpotlightTransport.new(configuration) if configuration.spotlight
|
|
45
50
|
|
|
46
51
|
if configuration.enable_logs
|
|
47
|
-
@log_event_buffer = LogEventBuffer.new(configuration, self)
|
|
52
|
+
@log_event_buffer = LogEventBuffer.new(configuration, self)
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
if configuration.enable_metrics
|
|
56
|
+
@metric_event_buffer = MetricEventBuffer.new(configuration, self)
|
|
48
57
|
end
|
|
49
58
|
end
|
|
50
59
|
|
|
@@ -98,7 +107,17 @@ module Sentry
|
|
|
98
107
|
# @return [LogEvent]
|
|
99
108
|
def buffer_log_event(event, scope)
|
|
100
109
|
return unless event.is_a?(LogEvent)
|
|
101
|
-
@log_event_buffer.
|
|
110
|
+
@log_event_buffer.add_item(scope.apply_to_telemetry(event))
|
|
111
|
+
event
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
# Buffer a metric event to be sent later with other metrics in a single envelope
|
|
115
|
+
# @param event [MetricEvent] the metric event to be buffered
|
|
116
|
+
# @return [MetricEvent]
|
|
117
|
+
def buffer_metric_event(event, scope)
|
|
118
|
+
return unless event.is_a?(MetricEvent)
|
|
119
|
+
event = scope.apply_to_telemetry(event)
|
|
120
|
+
@metric_event_buffer.add_item(event)
|
|
102
121
|
event
|
|
103
122
|
end
|
|
104
123
|
|
|
@@ -115,6 +134,7 @@ module Sentry
|
|
|
115
134
|
transport.flush if configuration.sending_to_dsn_allowed?
|
|
116
135
|
spotlight_transport.flush if spotlight_transport
|
|
117
136
|
@log_event_buffer&.flush
|
|
137
|
+
@metric_event_buffer&.flush
|
|
118
138
|
end
|
|
119
139
|
|
|
120
140
|
# Initializes an Event object with the given exception. Returns `nil` if the exception's class is excluded from reporting.
|
|
@@ -275,53 +295,6 @@ module Sentry
|
|
|
275
295
|
raise
|
|
276
296
|
end
|
|
277
297
|
|
|
278
|
-
# Send an envelope with batched logs
|
|
279
|
-
# @param log_events [Array<LogEvent>] the log events to be sent
|
|
280
|
-
# @api private
|
|
281
|
-
# @return [void]
|
|
282
|
-
def send_logs(log_events)
|
|
283
|
-
envelope = Envelope.new(
|
|
284
|
-
event_id: Sentry::Utils.uuid,
|
|
285
|
-
sent_at: Sentry.utc_now.iso8601,
|
|
286
|
-
dsn: configuration.dsn,
|
|
287
|
-
sdk: Sentry.sdk_meta
|
|
288
|
-
)
|
|
289
|
-
|
|
290
|
-
discarded_count = 0
|
|
291
|
-
envelope_items = []
|
|
292
|
-
|
|
293
|
-
if configuration.before_send_log
|
|
294
|
-
log_events.each do |log_event|
|
|
295
|
-
processed_log_event = configuration.before_send_log.call(log_event)
|
|
296
|
-
|
|
297
|
-
if processed_log_event
|
|
298
|
-
envelope_items << processed_log_event.to_h
|
|
299
|
-
else
|
|
300
|
-
discarded_count += 1
|
|
301
|
-
end
|
|
302
|
-
end
|
|
303
|
-
|
|
304
|
-
envelope_items
|
|
305
|
-
else
|
|
306
|
-
envelope_items = log_events.map(&:to_h)
|
|
307
|
-
end
|
|
308
|
-
|
|
309
|
-
envelope.add_item(
|
|
310
|
-
{
|
|
311
|
-
type: "log",
|
|
312
|
-
item_count: envelope_items.size,
|
|
313
|
-
content_type: "application/vnd.sentry.items.log+json"
|
|
314
|
-
},
|
|
315
|
-
{ items: envelope_items }
|
|
316
|
-
)
|
|
317
|
-
|
|
318
|
-
send_envelope(envelope)
|
|
319
|
-
|
|
320
|
-
unless discarded_count.zero?
|
|
321
|
-
transport.record_lost_event(:before_send, "log_item", num: discarded_count)
|
|
322
|
-
end
|
|
323
|
-
end
|
|
324
|
-
|
|
325
298
|
# Send an envelope directly to Sentry.
|
|
326
299
|
# @param envelope [Envelope] the envelope to be sent.
|
|
327
300
|
# @return [void]
|
data/lib/sentry/configuration.rb
CHANGED
|
@@ -14,6 +14,7 @@ require "sentry/interfaces/stacktrace_builder"
|
|
|
14
14
|
require "sentry/logger"
|
|
15
15
|
require "sentry/structured_logger"
|
|
16
16
|
require "sentry/log_event_buffer"
|
|
17
|
+
require "sentry/metric_event_buffer"
|
|
17
18
|
|
|
18
19
|
module Sentry
|
|
19
20
|
class Configuration
|
|
@@ -337,6 +338,32 @@ module Sentry
|
|
|
337
338
|
# @return [Integer]
|
|
338
339
|
attr_accessor :max_log_events
|
|
339
340
|
|
|
341
|
+
# Enable metrics collection, defaults to true
|
|
342
|
+
# @return [Boolean]
|
|
343
|
+
attr_accessor :enable_metrics
|
|
344
|
+
|
|
345
|
+
# Maximum number of metric events to buffer before sending
|
|
346
|
+
# @return [Integer]
|
|
347
|
+
attr_accessor :max_metric_events
|
|
348
|
+
|
|
349
|
+
# Optional Proc, called before sending a metric
|
|
350
|
+
# @example
|
|
351
|
+
# config.before_send_metric = lambda do |metric|
|
|
352
|
+
# # return nil to drop the metric
|
|
353
|
+
# metric
|
|
354
|
+
# end
|
|
355
|
+
# @return [Proc, nil]
|
|
356
|
+
attr_reader :before_send_metric
|
|
357
|
+
|
|
358
|
+
# Optional Proc, called to filter log messages before sending to Sentry
|
|
359
|
+
# @example
|
|
360
|
+
# config.std_lib_logger_filter = lambda do |logger, message, level|
|
|
361
|
+
# # Only send error and fatal logs to Sentry
|
|
362
|
+
# [:error, :fatal].include?(level)
|
|
363
|
+
# end
|
|
364
|
+
# @return [Proc, nil]
|
|
365
|
+
attr_reader :std_lib_logger_filter
|
|
366
|
+
|
|
340
367
|
# these are not config options
|
|
341
368
|
# @!visibility private
|
|
342
369
|
attr_reader :errors, :gem_specs
|
|
@@ -499,9 +526,12 @@ module Sentry
|
|
|
499
526
|
self.before_send_transaction = nil
|
|
500
527
|
self.before_send_check_in = nil
|
|
501
528
|
self.before_send_log = nil
|
|
529
|
+
self.before_send_metric = nil
|
|
530
|
+
self.std_lib_logger_filter = nil
|
|
502
531
|
self.rack_env_whitelist = RACK_ENV_WHITELIST_DEFAULT
|
|
503
532
|
self.traces_sampler = nil
|
|
504
533
|
self.enable_logs = false
|
|
534
|
+
self.enable_metrics = true
|
|
505
535
|
|
|
506
536
|
self.profiler_class = Sentry::Profiler
|
|
507
537
|
self.profiles_sample_interval = DEFAULT_PROFILES_SAMPLE_INTERVAL
|
|
@@ -512,6 +542,7 @@ module Sentry
|
|
|
512
542
|
@gem_specs = Hash[Gem::Specification.map { |spec| [spec.name, spec.version.to_s] }] if Gem::Specification.respond_to?(:map)
|
|
513
543
|
|
|
514
544
|
self.max_log_events = LogEventBuffer::DEFAULT_MAX_EVENTS
|
|
545
|
+
self.max_metric_events = MetricEventBuffer::DEFAULT_MAX_METRICS
|
|
515
546
|
|
|
516
547
|
run_callbacks(:after, :initialize)
|
|
517
548
|
|
|
@@ -581,12 +612,24 @@ module Sentry
|
|
|
581
612
|
@before_send_check_in = value
|
|
582
613
|
end
|
|
583
614
|
|
|
615
|
+
def before_send_metric=(value)
|
|
616
|
+
check_callable!("before_send_metric", value)
|
|
617
|
+
|
|
618
|
+
@before_send_metric = value
|
|
619
|
+
end
|
|
620
|
+
|
|
584
621
|
def before_breadcrumb=(value)
|
|
585
622
|
check_callable!("before_breadcrumb", value)
|
|
586
623
|
|
|
587
624
|
@before_breadcrumb = value
|
|
588
625
|
end
|
|
589
626
|
|
|
627
|
+
def std_lib_logger_filter=(value)
|
|
628
|
+
check_callable!("std_lib_logger_filter", value)
|
|
629
|
+
|
|
630
|
+
@std_lib_logger_filter = value
|
|
631
|
+
end
|
|
632
|
+
|
|
590
633
|
def environment=(environment)
|
|
591
634
|
@environment = environment.to_s
|
|
592
635
|
end
|
data/lib/sentry/envelope/item.rb
CHANGED
|
@@ -15,7 +15,8 @@ module Sentry
|
|
|
15
15
|
# rate limits and client reports use the data_category rather than envelope item type
|
|
16
16
|
def self.data_category(type)
|
|
17
17
|
case type
|
|
18
|
-
when "session", "attachment", "transaction", "profile", "span", "
|
|
18
|
+
when "session", "attachment", "transaction", "profile", "span", "trace_metric" then type
|
|
19
|
+
when "log" then "log_item"
|
|
19
20
|
when "sessions" then "session"
|
|
20
21
|
when "check_in" then "monitor"
|
|
21
22
|
when "event" then "error"
|
data/lib/sentry/hub.rb
CHANGED
|
@@ -227,6 +227,29 @@ module Sentry
|
|
|
227
227
|
current_client.buffer_log_event(event, current_scope)
|
|
228
228
|
end
|
|
229
229
|
|
|
230
|
+
# Captures a metric and sends it to Sentry
|
|
231
|
+
#
|
|
232
|
+
# @param name [String] the metric name
|
|
233
|
+
# @param type [Symbol] the metric type (:counter, :gauge, :distribution)
|
|
234
|
+
# @param value [Numeric] the metric value
|
|
235
|
+
# @param unit [String, nil] (optional) the metric unit
|
|
236
|
+
# @param attributes [Hash, nil] (optional) additional attributes for the metric
|
|
237
|
+
# @return [void]
|
|
238
|
+
def capture_metric(name:, type:, value:, unit: nil, attributes: nil)
|
|
239
|
+
return unless current_client&.configuration.enable_metrics
|
|
240
|
+
|
|
241
|
+
metric = MetricEvent.new(
|
|
242
|
+
name: name,
|
|
243
|
+
value: value,
|
|
244
|
+
type: type,
|
|
245
|
+
unit: unit,
|
|
246
|
+
attributes: attributes,
|
|
247
|
+
)
|
|
248
|
+
|
|
249
|
+
current_client.buffer_metric_event(metric, current_scope)
|
|
250
|
+
end
|
|
251
|
+
|
|
252
|
+
|
|
230
253
|
def capture_event(event, **options, &block)
|
|
231
254
|
check_argument_type!(event, Sentry::Event)
|
|
232
255
|
|
data/lib/sentry/log_event.rb
CHANGED
|
@@ -1,134 +1,52 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
+
require "sentry/utils/telemetry_attributes"
|
|
4
|
+
|
|
3
5
|
module Sentry
|
|
4
6
|
# Event type that represents a log entry with its attributes
|
|
5
7
|
#
|
|
6
8
|
# @see https://develop.sentry.dev/sdk/telemetry/logs/#log-envelope-item-payload
|
|
7
9
|
class LogEvent
|
|
10
|
+
include Sentry::Utils::TelemetryAttributes
|
|
11
|
+
|
|
8
12
|
TYPE = "log"
|
|
9
13
|
|
|
10
14
|
DEFAULT_PARAMETERS = [].freeze
|
|
11
|
-
DEFAULT_ATTRIBUTES = {}.freeze
|
|
12
|
-
|
|
13
|
-
SERIALIZEABLE_ATTRIBUTES = %i[
|
|
14
|
-
level
|
|
15
|
-
body
|
|
16
|
-
timestamp
|
|
17
|
-
environment
|
|
18
|
-
release
|
|
19
|
-
server_name
|
|
20
|
-
trace_id
|
|
21
|
-
attributes
|
|
22
|
-
contexts
|
|
23
|
-
]
|
|
24
|
-
|
|
25
|
-
SENTRY_ATTRIBUTES = {
|
|
26
|
-
"sentry.trace.parent_span_id" => :parent_span_id,
|
|
27
|
-
"sentry.environment" => :environment,
|
|
28
|
-
"sentry.release" => :release,
|
|
29
|
-
"sentry.address" => :server_name,
|
|
30
|
-
"sentry.sdk.name" => :sdk_name,
|
|
31
|
-
"sentry.sdk.version" => :sdk_version,
|
|
32
|
-
"sentry.message.template" => :template,
|
|
33
|
-
"sentry.origin" => :origin
|
|
34
|
-
}
|
|
35
15
|
|
|
36
16
|
PARAMETER_PREFIX = "sentry.message.parameter"
|
|
37
17
|
|
|
38
|
-
USER_ATTRIBUTES = {
|
|
39
|
-
"user.id" => :user_id,
|
|
40
|
-
"user.name" => :user_username,
|
|
41
|
-
"user.email" => :user_email
|
|
42
|
-
}
|
|
43
|
-
|
|
44
18
|
LEVELS = %i[trace debug info warn error fatal].freeze
|
|
45
19
|
|
|
46
|
-
attr_accessor :level, :body, :template, :attributes, :
|
|
47
|
-
|
|
48
|
-
attr_reader :configuration, *(SERIALIZEABLE_ATTRIBUTES - %i[level body attributes])
|
|
49
|
-
|
|
50
|
-
SERIALIZERS = %i[
|
|
51
|
-
attributes
|
|
52
|
-
body
|
|
53
|
-
level
|
|
54
|
-
parent_span_id
|
|
55
|
-
sdk_name
|
|
56
|
-
sdk_version
|
|
57
|
-
template
|
|
58
|
-
timestamp
|
|
59
|
-
trace_id
|
|
60
|
-
user_id
|
|
61
|
-
user_username
|
|
62
|
-
user_email
|
|
63
|
-
].map { |name| [name, :"serialize_#{name}"] }.to_h
|
|
64
|
-
|
|
65
|
-
VALUE_TYPES = Hash.new("string").merge!({
|
|
66
|
-
TrueClass => "boolean",
|
|
67
|
-
FalseClass => "boolean",
|
|
68
|
-
Integer => "integer",
|
|
69
|
-
Float => "double"
|
|
70
|
-
}).freeze
|
|
20
|
+
attr_accessor :level, :body, :template, :attributes, :origin, :trace_id, :span_id
|
|
21
|
+
attr_reader :timestamp
|
|
71
22
|
|
|
72
23
|
TOKEN_REGEXP = /%\{(\w+)\}/
|
|
73
24
|
|
|
74
|
-
def initialize(
|
|
75
|
-
@configuration = configuration
|
|
25
|
+
def initialize(**options)
|
|
76
26
|
@type = TYPE
|
|
77
|
-
@server_name = configuration.server_name
|
|
78
|
-
@environment = configuration.environment
|
|
79
|
-
@release = configuration.release
|
|
80
27
|
@timestamp = Sentry.utc_now
|
|
81
28
|
@level = options.fetch(:level)
|
|
82
29
|
@body = options[:body]
|
|
83
30
|
@template = @body if is_template?
|
|
84
|
-
@attributes = options[:attributes] ||
|
|
85
|
-
@user = options[:user] || {}
|
|
31
|
+
@attributes = options[:attributes] || {}
|
|
86
32
|
@origin = options[:origin]
|
|
87
|
-
@
|
|
33
|
+
@trace_id = nil
|
|
34
|
+
@span_id = nil
|
|
88
35
|
end
|
|
89
36
|
|
|
90
37
|
def to_h
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
38
|
+
{
|
|
39
|
+
level: level.to_s,
|
|
40
|
+
timestamp: timestamp.to_f,
|
|
41
|
+
trace_id: @trace_id,
|
|
42
|
+
span_id: @span_id,
|
|
43
|
+
body: serialize_body,
|
|
44
|
+
attributes: serialize_attributes
|
|
45
|
+
}.compact
|
|
94
46
|
end
|
|
95
47
|
|
|
96
48
|
private
|
|
97
49
|
|
|
98
|
-
def serialize(name)
|
|
99
|
-
serializer = SERIALIZERS[name]
|
|
100
|
-
|
|
101
|
-
if serializer
|
|
102
|
-
__send__(serializer)
|
|
103
|
-
else
|
|
104
|
-
public_send(name)
|
|
105
|
-
end
|
|
106
|
-
end
|
|
107
|
-
|
|
108
|
-
def serialize_level
|
|
109
|
-
level.to_s
|
|
110
|
-
end
|
|
111
|
-
|
|
112
|
-
def serialize_sdk_name
|
|
113
|
-
Sentry.sdk_meta["name"]
|
|
114
|
-
end
|
|
115
|
-
|
|
116
|
-
def serialize_sdk_version
|
|
117
|
-
Sentry.sdk_meta["version"]
|
|
118
|
-
end
|
|
119
|
-
|
|
120
|
-
def serialize_timestamp
|
|
121
|
-
timestamp.to_f
|
|
122
|
-
end
|
|
123
|
-
|
|
124
|
-
def serialize_trace_id
|
|
125
|
-
contexts.dig(:trace, :trace_id)
|
|
126
|
-
end
|
|
127
|
-
|
|
128
|
-
def serialize_parent_span_id
|
|
129
|
-
contexts.dig(:trace, :parent_span_id)
|
|
130
|
-
end
|
|
131
|
-
|
|
132
50
|
def serialize_body
|
|
133
51
|
if parameters.empty?
|
|
134
52
|
body
|
|
@@ -139,50 +57,14 @@ module Sentry
|
|
|
139
57
|
end
|
|
140
58
|
end
|
|
141
59
|
|
|
142
|
-
def serialize_user_id
|
|
143
|
-
user[:id]
|
|
144
|
-
end
|
|
145
|
-
|
|
146
|
-
def serialize_user_username
|
|
147
|
-
user[:username]
|
|
148
|
-
end
|
|
149
|
-
|
|
150
|
-
def serialize_user_email
|
|
151
|
-
user[:email]
|
|
152
|
-
end
|
|
153
|
-
|
|
154
|
-
def serialize_template
|
|
155
|
-
template if has_parameters?
|
|
156
|
-
end
|
|
157
|
-
|
|
158
60
|
def serialize_attributes
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
attributes.each do |key, value|
|
|
162
|
-
hash[key] = attribute_hash(value)
|
|
163
|
-
end
|
|
164
|
-
|
|
165
|
-
SENTRY_ATTRIBUTES.each do |key, name|
|
|
166
|
-
if (value = serialize(name))
|
|
167
|
-
hash[key] = attribute_hash(value)
|
|
168
|
-
end
|
|
169
|
-
end
|
|
170
|
-
|
|
171
|
-
USER_ATTRIBUTES.each do |key, name|
|
|
172
|
-
if (value = serialize(name))
|
|
173
|
-
hash[key] = value
|
|
174
|
-
end
|
|
175
|
-
end
|
|
176
|
-
|
|
177
|
-
hash
|
|
178
|
-
end
|
|
179
|
-
|
|
180
|
-
def attribute_hash(value)
|
|
181
|
-
{ value: value, type: value_type(value) }
|
|
61
|
+
populate_sentry_attributes!
|
|
62
|
+
@attributes.transform_values! { |v| attribute_hash(v) }
|
|
182
63
|
end
|
|
183
64
|
|
|
184
|
-
def
|
|
185
|
-
|
|
65
|
+
def populate_sentry_attributes!
|
|
66
|
+
@attributes["sentry.origin"] ||= @origin if @origin
|
|
67
|
+
@attributes["sentry.message.template"] ||= template if has_parameters?
|
|
186
68
|
end
|
|
187
69
|
|
|
188
70
|
def parameters
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
require "sentry/
|
|
3
|
+
require "sentry/telemetry_event_buffer"
|
|
4
4
|
|
|
5
5
|
module Sentry
|
|
6
6
|
# LogEventBuffer buffers log events and sends them to Sentry in a single envelope.
|
|
@@ -8,75 +8,21 @@ module Sentry
|
|
|
8
8
|
# This is used internally by the `Sentry::Client`.
|
|
9
9
|
#
|
|
10
10
|
# @!visibility private
|
|
11
|
-
class LogEventBuffer <
|
|
12
|
-
FLUSH_INTERVAL = 5 # seconds
|
|
11
|
+
class LogEventBuffer < TelemetryEventBuffer
|
|
13
12
|
DEFAULT_MAX_EVENTS = 100
|
|
14
|
-
|
|
15
|
-
# @!visibility private
|
|
16
|
-
attr_reader :pending_events
|
|
13
|
+
MAX_EVENTS_BEFORE_DROP = 1000
|
|
17
14
|
|
|
18
15
|
def initialize(configuration, client)
|
|
19
|
-
super(
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
def start
|
|
30
|
-
ensure_thread
|
|
31
|
-
self
|
|
32
|
-
end
|
|
33
|
-
|
|
34
|
-
def flush
|
|
35
|
-
@mutex.synchronize do
|
|
36
|
-
return if empty?
|
|
37
|
-
|
|
38
|
-
log_debug("[LogEventBuffer] flushing #{size} log events")
|
|
39
|
-
|
|
40
|
-
send_events
|
|
41
|
-
end
|
|
42
|
-
|
|
43
|
-
log_debug("[LogEventBuffer] flushed #{size} log events")
|
|
44
|
-
|
|
45
|
-
self
|
|
46
|
-
end
|
|
47
|
-
alias_method :run, :flush
|
|
48
|
-
|
|
49
|
-
def add_event(event)
|
|
50
|
-
raise ArgumentError, "expected a LogEvent, got #{event.class}" unless event.is_a?(LogEvent)
|
|
51
|
-
|
|
52
|
-
@mutex.synchronize do
|
|
53
|
-
@pending_events << event
|
|
54
|
-
send_events if size >= @max_events
|
|
55
|
-
end
|
|
56
|
-
|
|
57
|
-
self
|
|
58
|
-
end
|
|
59
|
-
|
|
60
|
-
def empty?
|
|
61
|
-
@pending_events.empty?
|
|
62
|
-
end
|
|
63
|
-
|
|
64
|
-
def size
|
|
65
|
-
@pending_events.size
|
|
66
|
-
end
|
|
67
|
-
|
|
68
|
-
def clear!
|
|
69
|
-
@pending_events.clear
|
|
70
|
-
end
|
|
71
|
-
|
|
72
|
-
private
|
|
73
|
-
|
|
74
|
-
def send_events
|
|
75
|
-
@client.send_logs(@pending_events)
|
|
76
|
-
rescue => e
|
|
77
|
-
log_debug("[LogEventBuffer] Failed to send logs: #{e.message}")
|
|
78
|
-
ensure
|
|
79
|
-
clear!
|
|
16
|
+
super(
|
|
17
|
+
configuration,
|
|
18
|
+
client,
|
|
19
|
+
event_class: LogEvent,
|
|
20
|
+
max_items: configuration.max_log_events || DEFAULT_MAX_EVENTS,
|
|
21
|
+
max_items_before_drop: MAX_EVENTS_BEFORE_DROP,
|
|
22
|
+
envelope_type: "log",
|
|
23
|
+
envelope_content_type: "application/vnd.sentry.items.log+json",
|
|
24
|
+
before_send: configuration.before_send_log
|
|
25
|
+
)
|
|
80
26
|
end
|
|
81
27
|
end
|
|
82
28
|
end
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "sentry/utils/telemetry_attributes"
|
|
4
|
+
|
|
5
|
+
module Sentry
|
|
6
|
+
class MetricEvent
|
|
7
|
+
include Sentry::Utils::TelemetryAttributes
|
|
8
|
+
|
|
9
|
+
attr_reader :name, :type, :value, :unit, :timestamp, :trace_id, :span_id, :attributes
|
|
10
|
+
attr_writer :trace_id, :span_id, :attributes
|
|
11
|
+
|
|
12
|
+
def initialize(
|
|
13
|
+
name:,
|
|
14
|
+
type:,
|
|
15
|
+
value:,
|
|
16
|
+
unit: nil,
|
|
17
|
+
attributes: nil
|
|
18
|
+
)
|
|
19
|
+
@name = name
|
|
20
|
+
@type = type
|
|
21
|
+
@value = value
|
|
22
|
+
@unit = unit
|
|
23
|
+
@attributes = attributes || {}
|
|
24
|
+
|
|
25
|
+
@timestamp = Sentry.utc_now
|
|
26
|
+
@trace_id = nil
|
|
27
|
+
@span_id = nil
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def to_h
|
|
31
|
+
{
|
|
32
|
+
name: @name,
|
|
33
|
+
type: @type,
|
|
34
|
+
value: @value,
|
|
35
|
+
unit: @unit,
|
|
36
|
+
timestamp: @timestamp,
|
|
37
|
+
trace_id: @trace_id,
|
|
38
|
+
span_id: @span_id,
|
|
39
|
+
attributes: serialize_attributes
|
|
40
|
+
}.compact
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
private
|
|
44
|
+
|
|
45
|
+
def serialize_attributes
|
|
46
|
+
@attributes.transform_values! { |v| attribute_hash(v) }
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
end
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "sentry/telemetry_event_buffer"
|
|
4
|
+
|
|
5
|
+
module Sentry
|
|
6
|
+
# MetricEventBuffer buffers metric events and sends them to Sentry in a single envelope.
|
|
7
|
+
#
|
|
8
|
+
# This is used internally by the `Sentry::Client`.
|
|
9
|
+
#
|
|
10
|
+
# @!visibility private
|
|
11
|
+
class MetricEventBuffer < TelemetryEventBuffer
|
|
12
|
+
DEFAULT_MAX_METRICS = 1000
|
|
13
|
+
MAX_METRICS_BEFORE_DROP = 10_000
|
|
14
|
+
|
|
15
|
+
def initialize(configuration, client)
|
|
16
|
+
super(
|
|
17
|
+
configuration,
|
|
18
|
+
client,
|
|
19
|
+
event_class: MetricEvent,
|
|
20
|
+
max_items: configuration.max_metric_events || DEFAULT_MAX_METRICS,
|
|
21
|
+
max_items_before_drop: MAX_METRICS_BEFORE_DROP,
|
|
22
|
+
envelope_type: "trace_metric",
|
|
23
|
+
envelope_content_type: "application/vnd.sentry.items.trace-metric+json",
|
|
24
|
+
before_send: configuration.before_send_metric
|
|
25
|
+
)
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
end
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "sentry/metric_event"
|
|
4
|
+
|
|
5
|
+
module Sentry
|
|
6
|
+
module Metrics
|
|
7
|
+
class << self
|
|
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
|
+
)
|
|
22
|
+
end
|
|
23
|
+
|
|
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
|
+
)
|
|
40
|
+
end
|
|
41
|
+
|
|
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
|
+
)
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
end
|
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
|
|
@@ -37,6 +37,10 @@ module Sentry
|
|
|
37
37
|
message = message.to_s.strip
|
|
38
38
|
|
|
39
39
|
if !message.nil? && message != Sentry::Logger::PROGNAME && method = SEVERITY_MAP[severity]
|
|
40
|
+
if (filter = Sentry.configuration.std_lib_logger_filter) && !filter.call(self, message, method)
|
|
41
|
+
return result
|
|
42
|
+
end
|
|
43
|
+
|
|
40
44
|
Sentry.logger.send(method, message, origin: ORIGIN)
|
|
41
45
|
end
|
|
42
46
|
end
|
|
@@ -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
|
@@ -102,6 +102,13 @@ module Sentry
|
|
|
102
102
|
.flat_map { |item| item.payload[:items] }
|
|
103
103
|
end
|
|
104
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
|
+
|
|
105
112
|
# Returns the last captured event object.
|
|
106
113
|
# @return [Event, nil]
|
|
107
114
|
def last_sentry_event
|
|
@@ -6,26 +6,40 @@ module Sentry
|
|
|
6
6
|
# @!visibility private
|
|
7
7
|
def log_error(message, exception, debug: false)
|
|
8
8
|
message = "#{message}: #{exception.message}"
|
|
9
|
-
message += "\n#{exception.backtrace.join("\n")}" if debug
|
|
9
|
+
message += "\n#{exception.backtrace.join("\n")}" if debug && exception.backtrace
|
|
10
10
|
|
|
11
|
-
sdk_logger&.error(LOGGER_PROGNAME)
|
|
12
|
-
|
|
13
|
-
|
|
11
|
+
sdk_logger&.error(LOGGER_PROGNAME) { message }
|
|
12
|
+
rescue StandardError => e
|
|
13
|
+
log_to_stderr(e, message)
|
|
14
14
|
end
|
|
15
15
|
|
|
16
16
|
# @!visibility private
|
|
17
17
|
def log_debug(message)
|
|
18
18
|
sdk_logger&.debug(LOGGER_PROGNAME) { message }
|
|
19
|
+
rescue StandardError => e
|
|
20
|
+
log_to_stderr(e, message)
|
|
19
21
|
end
|
|
20
22
|
|
|
21
23
|
# @!visibility private
|
|
22
24
|
def log_warn(message)
|
|
23
25
|
sdk_logger&.warn(LOGGER_PROGNAME) { message }
|
|
26
|
+
rescue StandardError => e
|
|
27
|
+
log_to_stderr(e, message)
|
|
24
28
|
end
|
|
25
29
|
|
|
26
30
|
# @!visibility private
|
|
27
31
|
def sdk_logger
|
|
28
32
|
@sdk_logger ||= Sentry.sdk_logger
|
|
29
33
|
end
|
|
34
|
+
|
|
35
|
+
# @!visibility private
|
|
36
|
+
def log_to_stderr(error, message)
|
|
37
|
+
error_msg = "Sentry SDK logging failed (#{error.class}: #{error.message}): #{message}".scrub(%q(<?>))
|
|
38
|
+
error_msg += "\n#{error.backtrace.map { |line| line.scrub(%q(<?>)) }.join("\n")}" if error.backtrace
|
|
39
|
+
|
|
40
|
+
$stderr.puts(error_msg)
|
|
41
|
+
rescue StandardError
|
|
42
|
+
# swallow everything – logging must never crash the app
|
|
43
|
+
end
|
|
30
44
|
end
|
|
31
45
|
end
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "json"
|
|
4
|
+
|
|
5
|
+
module Sentry
|
|
6
|
+
module Utils
|
|
7
|
+
module TelemetryAttributes
|
|
8
|
+
private
|
|
9
|
+
|
|
10
|
+
def attribute_hash(value)
|
|
11
|
+
case value
|
|
12
|
+
when String
|
|
13
|
+
{ value: value, type: "string" }
|
|
14
|
+
when TrueClass, FalseClass
|
|
15
|
+
{ value: value, type: "boolean" }
|
|
16
|
+
when Integer
|
|
17
|
+
{ value: value, type: "integer" }
|
|
18
|
+
when Float
|
|
19
|
+
{ value: value, type: "double" }
|
|
20
|
+
else
|
|
21
|
+
begin
|
|
22
|
+
{ value: JSON.generate(value), type: "string" }
|
|
23
|
+
rescue
|
|
24
|
+
{ value: value, type: "string" }
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|
data/lib/sentry/version.rb
CHANGED
data/lib/sentry-ruby.rb
CHANGED
|
@@ -27,6 +27,7 @@ require "sentry/session_flusher"
|
|
|
27
27
|
require "sentry/backpressure_monitor"
|
|
28
28
|
require "sentry/cron/monitor_check_ins"
|
|
29
29
|
require "sentry/vernier/profiler"
|
|
30
|
+
require "sentry/metrics"
|
|
30
31
|
|
|
31
32
|
[
|
|
32
33
|
"sentry/rake",
|
|
@@ -627,6 +628,24 @@ module Sentry
|
|
|
627
628
|
@logger ||= configuration.structured_logging.logger_class.new(configuration)
|
|
628
629
|
end
|
|
629
630
|
|
|
631
|
+
# Returns the metrics API for capturing custom metrics.
|
|
632
|
+
#
|
|
633
|
+
# @example Enable metrics
|
|
634
|
+
# Sentry.init do |config|
|
|
635
|
+
# config.dsn = "YOUR_DSN"
|
|
636
|
+
# config.enable_metrics = true
|
|
637
|
+
# end
|
|
638
|
+
#
|
|
639
|
+
# @example Usage
|
|
640
|
+
# Sentry.metrics.count("button.click", 1, attributes: { button_id: "submit" })
|
|
641
|
+
# Sentry.metrics.distribution("response.time", 120.5, unit: "millisecond")
|
|
642
|
+
# Sentry.metrics.gauge("cpu.usage", 75.2, unit: "percent")
|
|
643
|
+
#
|
|
644
|
+
# @return [Metrics] The metrics API
|
|
645
|
+
def metrics
|
|
646
|
+
Metrics
|
|
647
|
+
end
|
|
648
|
+
|
|
630
649
|
##### Helpers #####
|
|
631
650
|
|
|
632
651
|
# @!visibility private
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: sentry-ruby-core
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 6.
|
|
4
|
+
version: 6.3.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Sentry Team
|
|
@@ -15,14 +15,14 @@ dependencies:
|
|
|
15
15
|
requirements:
|
|
16
16
|
- - '='
|
|
17
17
|
- !ruby/object:Gem::Version
|
|
18
|
-
version: 6.
|
|
18
|
+
version: 6.3.0
|
|
19
19
|
type: :runtime
|
|
20
20
|
prerelease: false
|
|
21
21
|
version_requirements: !ruby/object:Gem::Requirement
|
|
22
22
|
requirements:
|
|
23
23
|
- - '='
|
|
24
24
|
- !ruby/object:Gem::Version
|
|
25
|
-
version: 6.
|
|
25
|
+
version: 6.3.0
|
|
26
26
|
- !ruby/object:Gem::Dependency
|
|
27
27
|
name: concurrent-ruby
|
|
28
28
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -100,6 +100,9 @@ files:
|
|
|
100
100
|
- lib/sentry/log_event.rb
|
|
101
101
|
- lib/sentry/log_event_buffer.rb
|
|
102
102
|
- lib/sentry/logger.rb
|
|
103
|
+
- lib/sentry/metric_event.rb
|
|
104
|
+
- lib/sentry/metric_event_buffer.rb
|
|
105
|
+
- lib/sentry/metrics.rb
|
|
103
106
|
- lib/sentry/net/http.rb
|
|
104
107
|
- lib/sentry/profiler.rb
|
|
105
108
|
- lib/sentry/profiler/helpers.rb
|
|
@@ -112,11 +115,13 @@ files:
|
|
|
112
115
|
- lib/sentry/release_detector.rb
|
|
113
116
|
- lib/sentry/rspec.rb
|
|
114
117
|
- lib/sentry/scope.rb
|
|
118
|
+
- lib/sentry/sequel.rb
|
|
115
119
|
- lib/sentry/session.rb
|
|
116
120
|
- lib/sentry/session_flusher.rb
|
|
117
121
|
- lib/sentry/span.rb
|
|
118
122
|
- lib/sentry/std_lib_logger.rb
|
|
119
123
|
- lib/sentry/structured_logger.rb
|
|
124
|
+
- lib/sentry/telemetry_event_buffer.rb
|
|
120
125
|
- lib/sentry/test_helper.rb
|
|
121
126
|
- lib/sentry/threaded_periodic_worker.rb
|
|
122
127
|
- lib/sentry/transaction.rb
|
|
@@ -137,6 +142,7 @@ files:
|
|
|
137
142
|
- lib/sentry/utils/real_ip.rb
|
|
138
143
|
- lib/sentry/utils/request_id.rb
|
|
139
144
|
- lib/sentry/utils/sample_rand.rb
|
|
145
|
+
- lib/sentry/utils/telemetry_attributes.rb
|
|
140
146
|
- lib/sentry/utils/uuid.rb
|
|
141
147
|
- lib/sentry/vernier/output.rb
|
|
142
148
|
- lib/sentry/vernier/profiler.rb
|