sentry-ruby 5.26.0 → 6.3.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/Gemfile +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/rack/capture_exceptions.rb +5 -1
- data/lib/sentry/rspec.rb +1 -1
- data/lib/sentry/scope.rb +50 -18
- 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 +57 -30
- data/sentry-ruby-core.gemspec +1 -1
- data/sentry-ruby.gemspec +2 -1
- metadata +31 -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
|
@@ -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
|
|
@@ -20,6 +20,7 @@ module Sentry
|
|
|
20
20
|
|
|
21
21
|
@profiling_enabled = defined?(Vernier) && configuration.profiling_enabled?
|
|
22
22
|
@profiles_sample_rate = configuration.profiles_sample_rate
|
|
23
|
+
@profiles_sample_interval = configuration.profiles_sample_interval
|
|
23
24
|
@project_root = configuration.project_root
|
|
24
25
|
@app_dirs_pattern = configuration.app_dirs_pattern
|
|
25
26
|
@in_app_pattern = Regexp.new("^(#{@project_root}/)?#{@app_dirs_pattern}")
|
|
@@ -56,7 +57,7 @@ module Sentry
|
|
|
56
57
|
return unless @sampled
|
|
57
58
|
return if @started
|
|
58
59
|
|
|
59
|
-
@started = ::Vernier.start_profile
|
|
60
|
+
@started = ::Vernier.start_profile(interval: @profiles_sample_interval)
|
|
60
61
|
|
|
61
62
|
log("Started")
|
|
62
63
|
|
|
@@ -90,9 +91,9 @@ module Sentry
|
|
|
90
91
|
Thread.current.object_id
|
|
91
92
|
end
|
|
92
93
|
|
|
93
|
-
def
|
|
94
|
+
def to_h
|
|
94
95
|
unless @sampled
|
|
95
|
-
record_lost_event(:sample_rate)
|
|
96
|
+
record_lost_event(:sample_rate) if @profiling_enabled
|
|
96
97
|
return EMPTY_RESULT
|
|
97
98
|
end
|
|
98
99
|
|
data/lib/sentry/version.rb
CHANGED
data/lib/sentry-ruby.rb
CHANGED
|
@@ -10,8 +10,10 @@ require "sentry/core_ext/object/deep_dup"
|
|
|
10
10
|
require "sentry/utils/argument_checking_helper"
|
|
11
11
|
require "sentry/utils/encoding_helper"
|
|
12
12
|
require "sentry/utils/logging_helper"
|
|
13
|
+
require "sentry/utils/sample_rand"
|
|
13
14
|
require "sentry/configuration"
|
|
14
15
|
require "sentry/structured_logger"
|
|
16
|
+
require "sentry/debug_structured_logger"
|
|
15
17
|
require "sentry/event"
|
|
16
18
|
require "sentry/error_event"
|
|
17
19
|
require "sentry/transaction_event"
|
|
@@ -24,8 +26,8 @@ require "sentry/threaded_periodic_worker"
|
|
|
24
26
|
require "sentry/session_flusher"
|
|
25
27
|
require "sentry/backpressure_monitor"
|
|
26
28
|
require "sentry/cron/monitor_check_ins"
|
|
27
|
-
require "sentry/metrics"
|
|
28
29
|
require "sentry/vernier/profiler"
|
|
30
|
+
require "sentry/metrics"
|
|
29
31
|
|
|
30
32
|
[
|
|
31
33
|
"sentry/rake",
|
|
@@ -57,7 +59,6 @@ module Sentry
|
|
|
57
59
|
logger
|
|
58
60
|
session_flusher
|
|
59
61
|
backpressure_monitor
|
|
60
|
-
metrics_aggregator
|
|
61
62
|
exception_locals_tp
|
|
62
63
|
].freeze
|
|
63
64
|
|
|
@@ -91,10 +92,6 @@ module Sentry
|
|
|
91
92
|
# @return [BackpressureMonitor, nil]
|
|
92
93
|
attr_reader :backpressure_monitor
|
|
93
94
|
|
|
94
|
-
# @!attribute [r] metrics_aggregator
|
|
95
|
-
# @return [Metrics::Aggregator, nil]
|
|
96
|
-
attr_reader :metrics_aggregator
|
|
97
|
-
|
|
98
95
|
##### Patch Registration #####
|
|
99
96
|
|
|
100
97
|
# @!visibility private
|
|
@@ -237,8 +234,7 @@ module Sentry
|
|
|
237
234
|
# @yieldparam config [Configuration]
|
|
238
235
|
# @return [void]
|
|
239
236
|
def init(&block)
|
|
240
|
-
config = Configuration.new
|
|
241
|
-
yield(config) if block_given?
|
|
237
|
+
config = Configuration.new(&block)
|
|
242
238
|
|
|
243
239
|
config.detect_release
|
|
244
240
|
apply_patches(config)
|
|
@@ -251,7 +247,6 @@ module Sentry
|
|
|
251
247
|
@background_worker = Sentry::BackgroundWorker.new(config)
|
|
252
248
|
@session_flusher = config.session_tracking? ? Sentry::SessionFlusher.new(config, client) : nil
|
|
253
249
|
@backpressure_monitor = config.enable_backpressure_handling ? Sentry::BackpressureMonitor.new(config, client) : nil
|
|
254
|
-
@metrics_aggregator = config.metrics.enabled ? Sentry::Metrics::Aggregator.new(config, client) : nil
|
|
255
250
|
exception_locals_tp.enable if config.include_local_variables
|
|
256
251
|
at_exit { close }
|
|
257
252
|
end
|
|
@@ -272,12 +267,6 @@ module Sentry
|
|
|
272
267
|
@backpressure_monitor = nil
|
|
273
268
|
end
|
|
274
269
|
|
|
275
|
-
if @metrics_aggregator
|
|
276
|
-
@metrics_aggregator.flush(force: true)
|
|
277
|
-
@metrics_aggregator.kill
|
|
278
|
-
@metrics_aggregator = nil
|
|
279
|
-
end
|
|
280
|
-
|
|
281
270
|
if client = get_current_client
|
|
282
271
|
client.flush
|
|
283
272
|
|
|
@@ -498,6 +487,7 @@ module Sentry
|
|
|
498
487
|
# @param [Hash] options Extra log event options
|
|
499
488
|
# @option options [Symbol] level The log level (:trace, :debug, :info, :warn, :error, :fatal)
|
|
500
489
|
# @option options [Integer] severity The severity number according to the Sentry Logs Protocol
|
|
490
|
+
# @option options [String] origin The origin of the log event (e.g., "auto.db.rails", "manual")
|
|
501
491
|
# @option options [Hash] Additional attributes to include with the log
|
|
502
492
|
#
|
|
503
493
|
# @example Direct usage (prefer using Sentry.logger instead)
|
|
@@ -633,22 +623,27 @@ module Sentry
|
|
|
633
623
|
#
|
|
634
624
|
# @see https://develop.sentry.dev/sdk/telemetry/logs/ Sentry SDK Telemetry Logs Protocol
|
|
635
625
|
#
|
|
636
|
-
# @return [StructuredLogger
|
|
626
|
+
# @return [StructuredLogger] The structured logger instance or nil if logs are disabled
|
|
637
627
|
def logger
|
|
638
|
-
@logger ||=
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
628
|
+
@logger ||= configuration.structured_logging.logger_class.new(configuration)
|
|
629
|
+
end
|
|
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
|
|
652
647
|
end
|
|
653
648
|
|
|
654
649
|
##### Helpers #####
|
|
@@ -671,6 +666,38 @@ module Sentry
|
|
|
671
666
|
META
|
|
672
667
|
end
|
|
673
668
|
|
|
669
|
+
# Registers a callback function that retrieves the current external propagation context.
|
|
670
|
+
# This is used by OpenTelemetry integration to provide trace_id and span_id from OTel context.
|
|
671
|
+
#
|
|
672
|
+
# @param callback [Proc, nil] A callable that returns [trace_id, span_id] or nil
|
|
673
|
+
# @return [void]
|
|
674
|
+
#
|
|
675
|
+
# @example
|
|
676
|
+
# Sentry.register_external_propagation_context do
|
|
677
|
+
# span_context = OpenTelemetry::Trace.current_span.context
|
|
678
|
+
# return nil unless span_context.valid?
|
|
679
|
+
# [span_context.hex_trace_id, span_context.hex_span_id]
|
|
680
|
+
# end
|
|
681
|
+
def register_external_propagation_context(&callback)
|
|
682
|
+
@external_propagation_context_callback = callback
|
|
683
|
+
end
|
|
684
|
+
|
|
685
|
+
# Returns the external propagation context (trace_id, span_id) if a callback is registered.
|
|
686
|
+
#
|
|
687
|
+
# @return [Array<String>, nil] A tuple of [trace_id, span_id] or nil if no context is available
|
|
688
|
+
def get_external_propagation_context
|
|
689
|
+
return nil unless @external_propagation_context_callback
|
|
690
|
+
|
|
691
|
+
@external_propagation_context_callback.call
|
|
692
|
+
rescue => e
|
|
693
|
+
sdk_logger&.debug(LOGGER_PROGNAME) { "Error getting external propagation context: #{e.message}" } if initialized?
|
|
694
|
+
nil
|
|
695
|
+
end
|
|
696
|
+
|
|
697
|
+
def clear_external_propagation_context
|
|
698
|
+
@external_propagation_context_callback = nil
|
|
699
|
+
end
|
|
700
|
+
|
|
674
701
|
# @!visibility private
|
|
675
702
|
def utc_now
|
|
676
703
|
Time.now.utc
|
data/sentry-ruby-core.gemspec
CHANGED
|
@@ -12,7 +12,7 @@ Gem::Specification.new do |spec|
|
|
|
12
12
|
spec.homepage = "https://github.com/getsentry/sentry-ruby"
|
|
13
13
|
|
|
14
14
|
spec.platform = Gem::Platform::RUBY
|
|
15
|
-
spec.required_ruby_version = '>= 2.
|
|
15
|
+
spec.required_ruby_version = '>= 2.7'
|
|
16
16
|
spec.extra_rdoc_files = ["README.md", "LICENSE.txt"]
|
|
17
17
|
spec.files = `git ls-files | grep -Ev '^(spec|benchmarks|examples|\.rubocop\.yml)'`.split("\n")
|
|
18
18
|
|
data/sentry-ruby.gemspec
CHANGED
|
@@ -11,7 +11,7 @@ Gem::Specification.new do |spec|
|
|
|
11
11
|
spec.license = 'MIT'
|
|
12
12
|
|
|
13
13
|
spec.platform = Gem::Platform::RUBY
|
|
14
|
-
spec.required_ruby_version = '>= 2.
|
|
14
|
+
spec.required_ruby_version = '>= 2.7'
|
|
15
15
|
spec.extra_rdoc_files = ["README.md", "LICENSE.txt"]
|
|
16
16
|
spec.files = `git ls-files | grep -Ev '^(spec|benchmarks|examples|\.rubocop\.yml)'`.split("\n")
|
|
17
17
|
|
|
@@ -30,4 +30,5 @@ Gem::Specification.new do |spec|
|
|
|
30
30
|
|
|
31
31
|
spec.add_dependency "concurrent-ruby", "~> 1.0", ">= 1.0.2"
|
|
32
32
|
spec.add_dependency "bigdecimal"
|
|
33
|
+
spec.add_dependency "logger"
|
|
33
34
|
end
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: sentry-ruby
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version:
|
|
4
|
+
version: 6.3.1
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Sentry Team
|
|
@@ -43,6 +43,20 @@ dependencies:
|
|
|
43
43
|
- - ">="
|
|
44
44
|
- !ruby/object:Gem::Version
|
|
45
45
|
version: '0'
|
|
46
|
+
- !ruby/object:Gem::Dependency
|
|
47
|
+
name: logger
|
|
48
|
+
requirement: !ruby/object:Gem::Requirement
|
|
49
|
+
requirements:
|
|
50
|
+
- - ">="
|
|
51
|
+
- !ruby/object:Gem::Version
|
|
52
|
+
version: '0'
|
|
53
|
+
type: :runtime
|
|
54
|
+
prerelease: false
|
|
55
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
56
|
+
requirements:
|
|
57
|
+
- - ">="
|
|
58
|
+
- !ruby/object:Gem::Version
|
|
59
|
+
version: '0'
|
|
46
60
|
description: A gem that provides a client interface for the Sentry error logger
|
|
47
61
|
email: accounts@sentry.io
|
|
48
62
|
executables: []
|
|
@@ -67,6 +81,7 @@ files:
|
|
|
67
81
|
- lib/sentry/background_worker.rb
|
|
68
82
|
- lib/sentry/backpressure_monitor.rb
|
|
69
83
|
- lib/sentry/backtrace.rb
|
|
84
|
+
- lib/sentry/backtrace/line.rb
|
|
70
85
|
- lib/sentry/baggage.rb
|
|
71
86
|
- lib/sentry/breadcrumb.rb
|
|
72
87
|
- lib/sentry/breadcrumb/sentry_logger.rb
|
|
@@ -80,6 +95,7 @@ files:
|
|
|
80
95
|
- lib/sentry/cron/monitor_check_ins.rb
|
|
81
96
|
- lib/sentry/cron/monitor_config.rb
|
|
82
97
|
- lib/sentry/cron/monitor_schedule.rb
|
|
98
|
+
- lib/sentry/debug_structured_logger.rb
|
|
83
99
|
- lib/sentry/dsn.rb
|
|
84
100
|
- lib/sentry/envelope.rb
|
|
85
101
|
- lib/sentry/envelope/item.rb
|
|
@@ -104,16 +120,9 @@ files:
|
|
|
104
120
|
- lib/sentry/log_event.rb
|
|
105
121
|
- lib/sentry/log_event_buffer.rb
|
|
106
122
|
- lib/sentry/logger.rb
|
|
123
|
+
- lib/sentry/metric_event.rb
|
|
124
|
+
- lib/sentry/metric_event_buffer.rb
|
|
107
125
|
- lib/sentry/metrics.rb
|
|
108
|
-
- lib/sentry/metrics/aggregator.rb
|
|
109
|
-
- lib/sentry/metrics/configuration.rb
|
|
110
|
-
- lib/sentry/metrics/counter_metric.rb
|
|
111
|
-
- lib/sentry/metrics/distribution_metric.rb
|
|
112
|
-
- lib/sentry/metrics/gauge_metric.rb
|
|
113
|
-
- lib/sentry/metrics/local_aggregator.rb
|
|
114
|
-
- lib/sentry/metrics/metric.rb
|
|
115
|
-
- lib/sentry/metrics/set_metric.rb
|
|
116
|
-
- lib/sentry/metrics/timing.rb
|
|
117
126
|
- lib/sentry/net/http.rb
|
|
118
127
|
- lib/sentry/profiler.rb
|
|
119
128
|
- lib/sentry/profiler/helpers.rb
|
|
@@ -126,17 +135,20 @@ files:
|
|
|
126
135
|
- lib/sentry/release_detector.rb
|
|
127
136
|
- lib/sentry/rspec.rb
|
|
128
137
|
- lib/sentry/scope.rb
|
|
138
|
+
- lib/sentry/sequel.rb
|
|
129
139
|
- lib/sentry/session.rb
|
|
130
140
|
- lib/sentry/session_flusher.rb
|
|
131
141
|
- lib/sentry/span.rb
|
|
132
142
|
- lib/sentry/std_lib_logger.rb
|
|
133
143
|
- lib/sentry/structured_logger.rb
|
|
144
|
+
- lib/sentry/telemetry_event_buffer.rb
|
|
134
145
|
- lib/sentry/test_helper.rb
|
|
135
146
|
- lib/sentry/threaded_periodic_worker.rb
|
|
136
147
|
- lib/sentry/transaction.rb
|
|
137
148
|
- lib/sentry/transaction_event.rb
|
|
138
149
|
- lib/sentry/transport.rb
|
|
139
150
|
- lib/sentry/transport/configuration.rb
|
|
151
|
+
- lib/sentry/transport/debug_transport.rb
|
|
140
152
|
- lib/sentry/transport/dummy_transport.rb
|
|
141
153
|
- lib/sentry/transport/http_transport.rb
|
|
142
154
|
- lib/sentry/transport/spotlight_transport.rb
|
|
@@ -149,21 +161,23 @@ files:
|
|
|
149
161
|
- lib/sentry/utils/logging_helper.rb
|
|
150
162
|
- lib/sentry/utils/real_ip.rb
|
|
151
163
|
- lib/sentry/utils/request_id.rb
|
|
164
|
+
- lib/sentry/utils/sample_rand.rb
|
|
165
|
+
- lib/sentry/utils/telemetry_attributes.rb
|
|
152
166
|
- lib/sentry/utils/uuid.rb
|
|
153
167
|
- lib/sentry/vernier/output.rb
|
|
154
168
|
- lib/sentry/vernier/profiler.rb
|
|
155
169
|
- lib/sentry/version.rb
|
|
156
170
|
- sentry-ruby-core.gemspec
|
|
157
171
|
- sentry-ruby.gemspec
|
|
158
|
-
homepage: https://github.com/getsentry/sentry-ruby/tree/
|
|
172
|
+
homepage: https://github.com/getsentry/sentry-ruby/tree/6.3.1/sentry-ruby
|
|
159
173
|
licenses:
|
|
160
174
|
- MIT
|
|
161
175
|
metadata:
|
|
162
|
-
homepage_uri: https://github.com/getsentry/sentry-ruby/tree/
|
|
163
|
-
source_code_uri: https://github.com/getsentry/sentry-ruby/tree/
|
|
164
|
-
changelog_uri: https://github.com/getsentry/sentry-ruby/blob/
|
|
176
|
+
homepage_uri: https://github.com/getsentry/sentry-ruby/tree/6.3.1/sentry-ruby
|
|
177
|
+
source_code_uri: https://github.com/getsentry/sentry-ruby/tree/6.3.1/sentry-ruby
|
|
178
|
+
changelog_uri: https://github.com/getsentry/sentry-ruby/blob/6.3.1/CHANGELOG.md
|
|
165
179
|
bug_tracker_uri: https://github.com/getsentry/sentry-ruby/issues
|
|
166
|
-
documentation_uri: http://www.rubydoc.info/gems/sentry-ruby/
|
|
180
|
+
documentation_uri: http://www.rubydoc.info/gems/sentry-ruby/6.3.1
|
|
167
181
|
rdoc_options: []
|
|
168
182
|
require_paths:
|
|
169
183
|
- lib
|
|
@@ -171,14 +185,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
|
171
185
|
requirements:
|
|
172
186
|
- - ">="
|
|
173
187
|
- !ruby/object:Gem::Version
|
|
174
|
-
version: '2.
|
|
188
|
+
version: '2.7'
|
|
175
189
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
176
190
|
requirements:
|
|
177
191
|
- - ">="
|
|
178
192
|
- !ruby/object:Gem::Version
|
|
179
193
|
version: '0'
|
|
180
194
|
requirements: []
|
|
181
|
-
rubygems_version: 3.6.
|
|
195
|
+
rubygems_version: 3.6.9
|
|
182
196
|
specification_version: 4
|
|
183
197
|
summary: A gem that provides a client interface for the Sentry error logger
|
|
184
198
|
test_files: []
|
|
@@ -1,248 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
module Sentry
|
|
4
|
-
module Metrics
|
|
5
|
-
class Aggregator < ThreadedPeriodicWorker
|
|
6
|
-
FLUSH_INTERVAL = 5
|
|
7
|
-
ROLLUP_IN_SECONDS = 10
|
|
8
|
-
|
|
9
|
-
# this is how far removed from user code in the backtrace we are
|
|
10
|
-
# when we record code locations
|
|
11
|
-
DEFAULT_STACKLEVEL = 4
|
|
12
|
-
|
|
13
|
-
KEY_SANITIZATION_REGEX = /[^a-zA-Z0-9_\-.]+/
|
|
14
|
-
UNIT_SANITIZATION_REGEX = /[^a-zA-Z0-9_]+/
|
|
15
|
-
TAG_KEY_SANITIZATION_REGEX = /[^a-zA-Z0-9_\-.\/]+/
|
|
16
|
-
|
|
17
|
-
TAG_VALUE_SANITIZATION_MAP = {
|
|
18
|
-
"\n" => "\\n",
|
|
19
|
-
"\r" => "\\r",
|
|
20
|
-
"\t" => "\\t",
|
|
21
|
-
"\\" => "\\\\",
|
|
22
|
-
"|" => "\\u{7c}",
|
|
23
|
-
"," => "\\u{2c}"
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
METRIC_TYPES = {
|
|
27
|
-
c: CounterMetric,
|
|
28
|
-
d: DistributionMetric,
|
|
29
|
-
g: GaugeMetric,
|
|
30
|
-
s: SetMetric
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
# exposed only for testing
|
|
34
|
-
attr_reader :client, :thread, :buckets, :flush_shift, :code_locations
|
|
35
|
-
|
|
36
|
-
def initialize(configuration, client)
|
|
37
|
-
super(configuration.sdk_logger, FLUSH_INTERVAL)
|
|
38
|
-
@client = client
|
|
39
|
-
@before_emit = configuration.metrics.before_emit
|
|
40
|
-
@enable_code_locations = configuration.metrics.enable_code_locations
|
|
41
|
-
@stacktrace_builder = configuration.stacktrace_builder
|
|
42
|
-
|
|
43
|
-
@default_tags = {}
|
|
44
|
-
@default_tags["release"] = configuration.release if configuration.release
|
|
45
|
-
@default_tags["environment"] = configuration.environment if configuration.environment
|
|
46
|
-
|
|
47
|
-
@mutex = Mutex.new
|
|
48
|
-
|
|
49
|
-
# a nested hash of timestamp -> bucket keys -> Metric instance
|
|
50
|
-
@buckets = {}
|
|
51
|
-
|
|
52
|
-
# the flush interval needs to be shifted once per startup to create jittering
|
|
53
|
-
@flush_shift = Random.rand * ROLLUP_IN_SECONDS
|
|
54
|
-
|
|
55
|
-
# a nested hash of timestamp (start of day) -> meta keys -> frame
|
|
56
|
-
@code_locations = {}
|
|
57
|
-
end
|
|
58
|
-
|
|
59
|
-
def add(type,
|
|
60
|
-
key,
|
|
61
|
-
value,
|
|
62
|
-
unit: "none",
|
|
63
|
-
tags: {},
|
|
64
|
-
timestamp: nil,
|
|
65
|
-
stacklevel: nil)
|
|
66
|
-
return unless ensure_thread
|
|
67
|
-
return unless METRIC_TYPES.keys.include?(type)
|
|
68
|
-
|
|
69
|
-
updated_tags = get_updated_tags(tags)
|
|
70
|
-
return if @before_emit && !@before_emit.call(key, updated_tags)
|
|
71
|
-
|
|
72
|
-
timestamp ||= Sentry.utc_now
|
|
73
|
-
|
|
74
|
-
# this is integer division and thus takes the floor of the division
|
|
75
|
-
# and buckets into 10 second intervals
|
|
76
|
-
bucket_timestamp = (timestamp.to_i / ROLLUP_IN_SECONDS) * ROLLUP_IN_SECONDS
|
|
77
|
-
|
|
78
|
-
serialized_tags = serialize_tags(updated_tags)
|
|
79
|
-
bucket_key = [type, key, unit, serialized_tags]
|
|
80
|
-
|
|
81
|
-
added = @mutex.synchronize do
|
|
82
|
-
record_code_location(type, key, unit, timestamp, stacklevel: stacklevel) if @enable_code_locations
|
|
83
|
-
process_bucket(bucket_timestamp, bucket_key, type, value)
|
|
84
|
-
end
|
|
85
|
-
|
|
86
|
-
# for sets, we pass on if there was a new entry to the local gauge
|
|
87
|
-
local_value = type == :s ? added : value
|
|
88
|
-
process_span_aggregator(bucket_key, local_value)
|
|
89
|
-
end
|
|
90
|
-
|
|
91
|
-
def flush(force: false)
|
|
92
|
-
flushable_buckets = get_flushable_buckets!(force)
|
|
93
|
-
code_locations = get_code_locations!
|
|
94
|
-
return if flushable_buckets.empty? && code_locations.empty?
|
|
95
|
-
|
|
96
|
-
envelope = Envelope.new
|
|
97
|
-
|
|
98
|
-
unless flushable_buckets.empty?
|
|
99
|
-
payload = serialize_buckets(flushable_buckets)
|
|
100
|
-
envelope.add_item(
|
|
101
|
-
{ type: "statsd", length: payload.bytesize },
|
|
102
|
-
payload
|
|
103
|
-
)
|
|
104
|
-
end
|
|
105
|
-
|
|
106
|
-
unless code_locations.empty?
|
|
107
|
-
code_locations.each do |timestamp, locations|
|
|
108
|
-
payload = serialize_locations(timestamp, locations)
|
|
109
|
-
envelope.add_item(
|
|
110
|
-
{ type: "metric_meta", content_type: "application/json" },
|
|
111
|
-
payload
|
|
112
|
-
)
|
|
113
|
-
end
|
|
114
|
-
end
|
|
115
|
-
|
|
116
|
-
@client.capture_envelope(envelope)
|
|
117
|
-
end
|
|
118
|
-
|
|
119
|
-
alias_method :run, :flush
|
|
120
|
-
|
|
121
|
-
private
|
|
122
|
-
|
|
123
|
-
# important to sort for key consistency
|
|
124
|
-
def serialize_tags(tags)
|
|
125
|
-
tags.flat_map do |k, v|
|
|
126
|
-
if v.is_a?(Array)
|
|
127
|
-
v.map { |x| [k.to_s, x.to_s] }
|
|
128
|
-
else
|
|
129
|
-
[[k.to_s, v.to_s]]
|
|
130
|
-
end
|
|
131
|
-
end.sort
|
|
132
|
-
end
|
|
133
|
-
|
|
134
|
-
def get_flushable_buckets!(force)
|
|
135
|
-
@mutex.synchronize do
|
|
136
|
-
flushable_buckets = {}
|
|
137
|
-
|
|
138
|
-
if force
|
|
139
|
-
flushable_buckets = @buckets
|
|
140
|
-
@buckets = {}
|
|
141
|
-
else
|
|
142
|
-
cutoff = Sentry.utc_now.to_i - ROLLUP_IN_SECONDS - @flush_shift
|
|
143
|
-
flushable_buckets = @buckets.select { |k, _| k <= cutoff }
|
|
144
|
-
@buckets.reject! { |k, _| k <= cutoff }
|
|
145
|
-
end
|
|
146
|
-
|
|
147
|
-
flushable_buckets
|
|
148
|
-
end
|
|
149
|
-
end
|
|
150
|
-
|
|
151
|
-
def get_code_locations!
|
|
152
|
-
@mutex.synchronize do
|
|
153
|
-
code_locations = @code_locations
|
|
154
|
-
@code_locations = {}
|
|
155
|
-
code_locations
|
|
156
|
-
end
|
|
157
|
-
end
|
|
158
|
-
|
|
159
|
-
# serialize buckets to statsd format
|
|
160
|
-
def serialize_buckets(buckets)
|
|
161
|
-
buckets.map do |timestamp, timestamp_buckets|
|
|
162
|
-
timestamp_buckets.map do |metric_key, metric|
|
|
163
|
-
type, key, unit, tags = metric_key
|
|
164
|
-
values = metric.serialize.join(":")
|
|
165
|
-
sanitized_tags = tags.map { |k, v| "#{sanitize_tag_key(k)}:#{sanitize_tag_value(v)}" }.join(",")
|
|
166
|
-
|
|
167
|
-
"#{sanitize_key(key)}@#{sanitize_unit(unit)}:#{values}|#{type}|\##{sanitized_tags}|T#{timestamp}"
|
|
168
|
-
end
|
|
169
|
-
end.flatten.join("\n")
|
|
170
|
-
end
|
|
171
|
-
|
|
172
|
-
def serialize_locations(timestamp, locations)
|
|
173
|
-
mapping = locations.map do |meta_key, location|
|
|
174
|
-
type, key, unit = meta_key
|
|
175
|
-
mri = "#{type}:#{sanitize_key(key)}@#{sanitize_unit(unit)}"
|
|
176
|
-
|
|
177
|
-
# note this needs to be an array but it really doesn't serve a purpose right now
|
|
178
|
-
[mri, [location.merge(type: "location")]]
|
|
179
|
-
end.to_h
|
|
180
|
-
|
|
181
|
-
{ timestamp: timestamp, mapping: mapping }
|
|
182
|
-
end
|
|
183
|
-
|
|
184
|
-
def sanitize_key(key)
|
|
185
|
-
key.gsub(KEY_SANITIZATION_REGEX, "_")
|
|
186
|
-
end
|
|
187
|
-
|
|
188
|
-
def sanitize_unit(unit)
|
|
189
|
-
unit.gsub(UNIT_SANITIZATION_REGEX, "")
|
|
190
|
-
end
|
|
191
|
-
|
|
192
|
-
def sanitize_tag_key(key)
|
|
193
|
-
key.gsub(TAG_KEY_SANITIZATION_REGEX, "")
|
|
194
|
-
end
|
|
195
|
-
|
|
196
|
-
def sanitize_tag_value(value)
|
|
197
|
-
value.chars.map { |c| TAG_VALUE_SANITIZATION_MAP[c] || c }.join
|
|
198
|
-
end
|
|
199
|
-
|
|
200
|
-
def get_transaction_name
|
|
201
|
-
scope = Sentry.get_current_scope
|
|
202
|
-
return nil unless scope && scope.transaction_name
|
|
203
|
-
return nil if scope.transaction_source_low_quality?
|
|
204
|
-
|
|
205
|
-
scope.transaction_name
|
|
206
|
-
end
|
|
207
|
-
|
|
208
|
-
def get_updated_tags(tags)
|
|
209
|
-
updated_tags = @default_tags.merge(tags)
|
|
210
|
-
|
|
211
|
-
transaction_name = get_transaction_name
|
|
212
|
-
updated_tags["transaction"] = transaction_name if transaction_name
|
|
213
|
-
|
|
214
|
-
updated_tags
|
|
215
|
-
end
|
|
216
|
-
|
|
217
|
-
def process_span_aggregator(key, value)
|
|
218
|
-
scope = Sentry.get_current_scope
|
|
219
|
-
return nil unless scope && scope.span
|
|
220
|
-
return nil if scope.transaction_source_low_quality?
|
|
221
|
-
|
|
222
|
-
scope.span.metrics_local_aggregator.add(key, value)
|
|
223
|
-
end
|
|
224
|
-
|
|
225
|
-
def process_bucket(timestamp, key, type, value)
|
|
226
|
-
@buckets[timestamp] ||= {}
|
|
227
|
-
|
|
228
|
-
if (metric = @buckets[timestamp][key])
|
|
229
|
-
old_weight = metric.weight
|
|
230
|
-
metric.add(value)
|
|
231
|
-
metric.weight - old_weight
|
|
232
|
-
else
|
|
233
|
-
metric = METRIC_TYPES[type].new(value)
|
|
234
|
-
@buckets[timestamp][key] = metric
|
|
235
|
-
metric.weight
|
|
236
|
-
end
|
|
237
|
-
end
|
|
238
|
-
|
|
239
|
-
def record_code_location(type, key, unit, timestamp, stacklevel: nil)
|
|
240
|
-
meta_key = [type, key, unit]
|
|
241
|
-
start_of_day = Time.utc(timestamp.year, timestamp.month, timestamp.day).to_i
|
|
242
|
-
|
|
243
|
-
@code_locations[start_of_day] ||= {}
|
|
244
|
-
@code_locations[start_of_day][meta_key] ||= @stacktrace_builder.metrics_code_location(caller[stacklevel || DEFAULT_STACKLEVEL])
|
|
245
|
-
end
|
|
246
|
-
end
|
|
247
|
-
end
|
|
248
|
-
end
|
|
@@ -1,47 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
module Sentry
|
|
4
|
-
module Metrics
|
|
5
|
-
class Configuration
|
|
6
|
-
include ArgumentCheckingHelper
|
|
7
|
-
|
|
8
|
-
# Enable metrics usage.
|
|
9
|
-
# Starts a new {Sentry::Metrics::Aggregator} instance to aggregate metrics
|
|
10
|
-
# and a thread to aggregate flush every 5 seconds.
|
|
11
|
-
# @return [Boolean]
|
|
12
|
-
attr_accessor :enabled
|
|
13
|
-
|
|
14
|
-
# Enable code location reporting.
|
|
15
|
-
# Will be sent once per day.
|
|
16
|
-
# True by default.
|
|
17
|
-
# @return [Boolean]
|
|
18
|
-
attr_accessor :enable_code_locations
|
|
19
|
-
|
|
20
|
-
# Optional Proc, called before emitting a metric to the aggregator.
|
|
21
|
-
# Use it to filter keys (return false/nil) or update tags.
|
|
22
|
-
# Make sure to return true at the end.
|
|
23
|
-
#
|
|
24
|
-
# @example
|
|
25
|
-
# config.metrics.before_emit = lambda do |key, tags|
|
|
26
|
-
# return nil if key == 'foo'
|
|
27
|
-
# tags[:bar] = 42
|
|
28
|
-
# tags.delete(:baz)
|
|
29
|
-
# true
|
|
30
|
-
# end
|
|
31
|
-
#
|
|
32
|
-
# @return [Proc, nil]
|
|
33
|
-
attr_reader :before_emit
|
|
34
|
-
|
|
35
|
-
def initialize
|
|
36
|
-
@enabled = false
|
|
37
|
-
@enable_code_locations = true
|
|
38
|
-
end
|
|
39
|
-
|
|
40
|
-
def before_emit=(value)
|
|
41
|
-
check_callable!("metrics.before_emit", value)
|
|
42
|
-
|
|
43
|
-
@before_emit = value
|
|
44
|
-
end
|
|
45
|
-
end
|
|
46
|
-
end
|
|
47
|
-
end
|