sentry-ruby 5.28.1 → 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 +25 -1
- 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 +57 -135
- data/lib/sentry/configuration.rb +117 -75
- 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/envelope/item.rb +3 -3
- data/lib/sentry/error_event.rb +3 -3
- data/lib/sentry/event.rb +4 -10
- data/lib/sentry/hub.rb +26 -4
- 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 +24 -142
- 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 -54
- data/lib/sentry/profiler.rb +4 -5
- 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 +4 -0
- data/lib/sentry/telemetry_event_buffer.rb +130 -0
- data/lib/sentry/test_helper.rb +8 -0
- data/lib/sentry/transaction.rb +52 -103
- data/lib/sentry/transaction_event.rb +4 -9
- data/lib/sentry/transport.rb +2 -5
- data/lib/sentry/utils/encoding_helper.rb +6 -0
- data/lib/sentry/utils/logging_helper.rb +25 -9
- 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 +53 -30
- data/sentry-ruby-core.gemspec +1 -1
- data/sentry-ruby.gemspec +2 -1
- metadata +27 -16
- data/lib/sentry/metrics/aggregator.rb +0 -248
- data/lib/sentry/metrics/configuration.rb +0 -57
- 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-ruby.rb
CHANGED
|
@@ -26,8 +26,8 @@ require "sentry/threaded_periodic_worker"
|
|
|
26
26
|
require "sentry/session_flusher"
|
|
27
27
|
require "sentry/backpressure_monitor"
|
|
28
28
|
require "sentry/cron/monitor_check_ins"
|
|
29
|
-
require "sentry/metrics"
|
|
30
29
|
require "sentry/vernier/profiler"
|
|
30
|
+
require "sentry/metrics"
|
|
31
31
|
|
|
32
32
|
[
|
|
33
33
|
"sentry/rake",
|
|
@@ -59,7 +59,6 @@ module Sentry
|
|
|
59
59
|
logger
|
|
60
60
|
session_flusher
|
|
61
61
|
backpressure_monitor
|
|
62
|
-
metrics_aggregator
|
|
63
62
|
exception_locals_tp
|
|
64
63
|
].freeze
|
|
65
64
|
|
|
@@ -93,10 +92,6 @@ module Sentry
|
|
|
93
92
|
# @return [BackpressureMonitor, nil]
|
|
94
93
|
attr_reader :backpressure_monitor
|
|
95
94
|
|
|
96
|
-
# @!attribute [r] metrics_aggregator
|
|
97
|
-
# @return [Metrics::Aggregator, nil]
|
|
98
|
-
attr_reader :metrics_aggregator
|
|
99
|
-
|
|
100
95
|
##### Patch Registration #####
|
|
101
96
|
|
|
102
97
|
# @!visibility private
|
|
@@ -252,7 +247,6 @@ module Sentry
|
|
|
252
247
|
@background_worker = Sentry::BackgroundWorker.new(config)
|
|
253
248
|
@session_flusher = config.session_tracking? ? Sentry::SessionFlusher.new(config, client) : nil
|
|
254
249
|
@backpressure_monitor = config.enable_backpressure_handling ? Sentry::BackpressureMonitor.new(config, client) : nil
|
|
255
|
-
@metrics_aggregator = config.metrics.enabled ? Sentry::Metrics::Aggregator.new(config, client) : nil
|
|
256
250
|
exception_locals_tp.enable if config.include_local_variables
|
|
257
251
|
at_exit { close }
|
|
258
252
|
end
|
|
@@ -273,12 +267,6 @@ module Sentry
|
|
|
273
267
|
@backpressure_monitor = nil
|
|
274
268
|
end
|
|
275
269
|
|
|
276
|
-
if @metrics_aggregator
|
|
277
|
-
@metrics_aggregator.flush(force: true)
|
|
278
|
-
@metrics_aggregator.kill
|
|
279
|
-
@metrics_aggregator = nil
|
|
280
|
-
end
|
|
281
|
-
|
|
282
270
|
if client = get_current_client
|
|
283
271
|
client.flush
|
|
284
272
|
|
|
@@ -635,24 +623,27 @@ module Sentry
|
|
|
635
623
|
#
|
|
636
624
|
# @see https://develop.sentry.dev/sdk/telemetry/logs/ Sentry SDK Telemetry Logs Protocol
|
|
637
625
|
#
|
|
638
|
-
# @return [StructuredLogger
|
|
626
|
+
# @return [StructuredLogger] The structured logger instance or nil if logs are disabled
|
|
639
627
|
def logger
|
|
640
|
-
@logger ||=
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
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
|
|
656
647
|
end
|
|
657
648
|
|
|
658
649
|
##### Helpers #####
|
|
@@ -675,6 +666,38 @@ module Sentry
|
|
|
675
666
|
META
|
|
676
667
|
end
|
|
677
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
|
+
|
|
678
701
|
# @!visibility private
|
|
679
702
|
def utc_now
|
|
680
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
|
|
@@ -105,16 +120,9 @@ files:
|
|
|
105
120
|
- lib/sentry/log_event.rb
|
|
106
121
|
- lib/sentry/log_event_buffer.rb
|
|
107
122
|
- lib/sentry/logger.rb
|
|
123
|
+
- lib/sentry/metric_event.rb
|
|
124
|
+
- lib/sentry/metric_event_buffer.rb
|
|
108
125
|
- lib/sentry/metrics.rb
|
|
109
|
-
- lib/sentry/metrics/aggregator.rb
|
|
110
|
-
- lib/sentry/metrics/configuration.rb
|
|
111
|
-
- lib/sentry/metrics/counter_metric.rb
|
|
112
|
-
- lib/sentry/metrics/distribution_metric.rb
|
|
113
|
-
- lib/sentry/metrics/gauge_metric.rb
|
|
114
|
-
- lib/sentry/metrics/local_aggregator.rb
|
|
115
|
-
- lib/sentry/metrics/metric.rb
|
|
116
|
-
- lib/sentry/metrics/set_metric.rb
|
|
117
|
-
- lib/sentry/metrics/timing.rb
|
|
118
126
|
- lib/sentry/net/http.rb
|
|
119
127
|
- lib/sentry/profiler.rb
|
|
120
128
|
- lib/sentry/profiler/helpers.rb
|
|
@@ -127,11 +135,13 @@ files:
|
|
|
127
135
|
- lib/sentry/release_detector.rb
|
|
128
136
|
- lib/sentry/rspec.rb
|
|
129
137
|
- lib/sentry/scope.rb
|
|
138
|
+
- lib/sentry/sequel.rb
|
|
130
139
|
- lib/sentry/session.rb
|
|
131
140
|
- lib/sentry/session_flusher.rb
|
|
132
141
|
- lib/sentry/span.rb
|
|
133
142
|
- lib/sentry/std_lib_logger.rb
|
|
134
143
|
- lib/sentry/structured_logger.rb
|
|
144
|
+
- lib/sentry/telemetry_event_buffer.rb
|
|
135
145
|
- lib/sentry/test_helper.rb
|
|
136
146
|
- lib/sentry/threaded_periodic_worker.rb
|
|
137
147
|
- lib/sentry/transaction.rb
|
|
@@ -152,21 +162,22 @@ files:
|
|
|
152
162
|
- lib/sentry/utils/real_ip.rb
|
|
153
163
|
- lib/sentry/utils/request_id.rb
|
|
154
164
|
- lib/sentry/utils/sample_rand.rb
|
|
165
|
+
- lib/sentry/utils/telemetry_attributes.rb
|
|
155
166
|
- lib/sentry/utils/uuid.rb
|
|
156
167
|
- lib/sentry/vernier/output.rb
|
|
157
168
|
- lib/sentry/vernier/profiler.rb
|
|
158
169
|
- lib/sentry/version.rb
|
|
159
170
|
- sentry-ruby-core.gemspec
|
|
160
171
|
- sentry-ruby.gemspec
|
|
161
|
-
homepage: https://github.com/getsentry/sentry-ruby/tree/
|
|
172
|
+
homepage: https://github.com/getsentry/sentry-ruby/tree/6.3.1/sentry-ruby
|
|
162
173
|
licenses:
|
|
163
174
|
- MIT
|
|
164
175
|
metadata:
|
|
165
|
-
homepage_uri: https://github.com/getsentry/sentry-ruby/tree/
|
|
166
|
-
source_code_uri: https://github.com/getsentry/sentry-ruby/tree/
|
|
167
|
-
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
|
|
168
179
|
bug_tracker_uri: https://github.com/getsentry/sentry-ruby/issues
|
|
169
|
-
documentation_uri: http://www.rubydoc.info/gems/sentry-ruby/
|
|
180
|
+
documentation_uri: http://www.rubydoc.info/gems/sentry-ruby/6.3.1
|
|
170
181
|
rdoc_options: []
|
|
171
182
|
require_paths:
|
|
172
183
|
- lib
|
|
@@ -174,7 +185,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
|
174
185
|
requirements:
|
|
175
186
|
- - ">="
|
|
176
187
|
- !ruby/object:Gem::Version
|
|
177
|
-
version: '2.
|
|
188
|
+
version: '2.7'
|
|
178
189
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
179
190
|
requirements:
|
|
180
191
|
- - ">="
|
|
@@ -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,57 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
module Sentry
|
|
4
|
-
module Metrics
|
|
5
|
-
class Configuration
|
|
6
|
-
include ArgumentCheckingHelper
|
|
7
|
-
include LoggingHelper
|
|
8
|
-
|
|
9
|
-
# Enable metrics usage.
|
|
10
|
-
# Starts a new {Sentry::Metrics::Aggregator} instance to aggregate metrics
|
|
11
|
-
# and a thread to aggregate flush every 5 seconds.
|
|
12
|
-
# @return [Boolean]
|
|
13
|
-
attr_reader :enabled
|
|
14
|
-
|
|
15
|
-
# Enable code location reporting.
|
|
16
|
-
# Will be sent once per day.
|
|
17
|
-
# True by default.
|
|
18
|
-
# @return [Boolean]
|
|
19
|
-
attr_accessor :enable_code_locations
|
|
20
|
-
|
|
21
|
-
# Optional Proc, called before emitting a metric to the aggregator.
|
|
22
|
-
# Use it to filter keys (return false/nil) or update tags.
|
|
23
|
-
# Make sure to return true at the end.
|
|
24
|
-
#
|
|
25
|
-
# @example
|
|
26
|
-
# config.metrics.before_emit = lambda do |key, tags|
|
|
27
|
-
# return nil if key == 'foo'
|
|
28
|
-
# tags[:bar] = 42
|
|
29
|
-
# tags.delete(:baz)
|
|
30
|
-
# true
|
|
31
|
-
# end
|
|
32
|
-
#
|
|
33
|
-
# @return [Proc, nil]
|
|
34
|
-
attr_reader :before_emit
|
|
35
|
-
|
|
36
|
-
def initialize(sdk_logger)
|
|
37
|
-
@sdk_logger = sdk_logger
|
|
38
|
-
@enabled = false
|
|
39
|
-
@enable_code_locations = true
|
|
40
|
-
end
|
|
41
|
-
|
|
42
|
-
def enabled=(value)
|
|
43
|
-
log_warn <<~MSG
|
|
44
|
-
`config.metrics` is now deprecated and will be removed in the next major.
|
|
45
|
-
MSG
|
|
46
|
-
|
|
47
|
-
@enabled = value
|
|
48
|
-
end
|
|
49
|
-
|
|
50
|
-
def before_emit=(value)
|
|
51
|
-
check_callable!("metrics.before_emit", value)
|
|
52
|
-
|
|
53
|
-
@before_emit = value
|
|
54
|
-
end
|
|
55
|
-
end
|
|
56
|
-
end
|
|
57
|
-
end
|
|
@@ -1,25 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
module Sentry
|
|
4
|
-
module Metrics
|
|
5
|
-
class CounterMetric < Metric
|
|
6
|
-
attr_reader :value
|
|
7
|
-
|
|
8
|
-
def initialize(value)
|
|
9
|
-
@value = value.to_f
|
|
10
|
-
end
|
|
11
|
-
|
|
12
|
-
def add(value)
|
|
13
|
-
@value += value.to_f
|
|
14
|
-
end
|
|
15
|
-
|
|
16
|
-
def serialize
|
|
17
|
-
[value]
|
|
18
|
-
end
|
|
19
|
-
|
|
20
|
-
def weight
|
|
21
|
-
1
|
|
22
|
-
end
|
|
23
|
-
end
|
|
24
|
-
end
|
|
25
|
-
end
|
|
@@ -1,25 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
module Sentry
|
|
4
|
-
module Metrics
|
|
5
|
-
class DistributionMetric < Metric
|
|
6
|
-
attr_reader :value
|
|
7
|
-
|
|
8
|
-
def initialize(value)
|
|
9
|
-
@value = [value.to_f]
|
|
10
|
-
end
|
|
11
|
-
|
|
12
|
-
def add(value)
|
|
13
|
-
@value << value.to_f
|
|
14
|
-
end
|
|
15
|
-
|
|
16
|
-
def serialize
|
|
17
|
-
value
|
|
18
|
-
end
|
|
19
|
-
|
|
20
|
-
def weight
|
|
21
|
-
value.size
|
|
22
|
-
end
|
|
23
|
-
end
|
|
24
|
-
end
|
|
25
|
-
end
|
|
@@ -1,35 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
module Sentry
|
|
4
|
-
module Metrics
|
|
5
|
-
class GaugeMetric < Metric
|
|
6
|
-
attr_reader :last, :min, :max, :sum, :count
|
|
7
|
-
|
|
8
|
-
def initialize(value)
|
|
9
|
-
value = value.to_f
|
|
10
|
-
@last = value
|
|
11
|
-
@min = value
|
|
12
|
-
@max = value
|
|
13
|
-
@sum = value
|
|
14
|
-
@count = 1
|
|
15
|
-
end
|
|
16
|
-
|
|
17
|
-
def add(value)
|
|
18
|
-
value = value.to_f
|
|
19
|
-
@last = value
|
|
20
|
-
@min = [@min, value].min
|
|
21
|
-
@max = [@max, value].max
|
|
22
|
-
@sum += value
|
|
23
|
-
@count += 1
|
|
24
|
-
end
|
|
25
|
-
|
|
26
|
-
def serialize
|
|
27
|
-
[last, min, max, sum, count]
|
|
28
|
-
end
|
|
29
|
-
|
|
30
|
-
def weight
|
|
31
|
-
5
|
|
32
|
-
end
|
|
33
|
-
end
|
|
34
|
-
end
|
|
35
|
-
end
|