sentry-ruby 5.26.0 → 6.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/Gemfile +26 -4
- data/README.md +2 -2
- data/lib/sentry/background_worker.rb +1 -4
- data/lib/sentry/backtrace/line.rb +99 -0
- data/lib/sentry/backtrace.rb +44 -76
- data/lib/sentry/breadcrumb.rb +1 -1
- data/lib/sentry/breadcrumb_buffer.rb +2 -2
- data/lib/sentry/check_in_event.rb +2 -2
- data/lib/sentry/client.rb +59 -136
- data/lib/sentry/configuration.rb +168 -78
- data/lib/sentry/cron/monitor_check_ins.rb +3 -3
- data/lib/sentry/cron/monitor_config.rb +2 -2
- data/lib/sentry/cron/monitor_schedule.rb +2 -2
- data/lib/sentry/debug_structured_logger.rb +94 -0
- data/lib/sentry/dsn.rb +32 -0
- data/lib/sentry/envelope/item.rb +3 -3
- data/lib/sentry/error_event.rb +3 -3
- data/lib/sentry/event.rb +4 -10
- data/lib/sentry/graphql.rb +1 -1
- data/lib/sentry/hub.rb +29 -5
- data/lib/sentry/interface.rb +1 -1
- data/lib/sentry/interfaces/exception.rb +2 -2
- data/lib/sentry/interfaces/request.rb +2 -0
- data/lib/sentry/interfaces/single_exception.rb +4 -4
- data/lib/sentry/interfaces/stacktrace.rb +3 -3
- data/lib/sentry/interfaces/stacktrace_builder.rb +0 -8
- data/lib/sentry/interfaces/threads.rb +2 -2
- data/lib/sentry/log_event.rb +33 -138
- data/lib/sentry/log_event_buffer.rb +13 -60
- data/lib/sentry/metric_event.rb +49 -0
- data/lib/sentry/metric_event_buffer.rb +28 -0
- data/lib/sentry/metrics.rb +47 -42
- data/lib/sentry/profiler.rb +4 -5
- data/lib/sentry/propagation_context.rb +55 -18
- data/lib/sentry/rspec.rb +1 -1
- data/lib/sentry/scope.rb +32 -5
- data/lib/sentry/sequel.rb +35 -0
- data/lib/sentry/span.rb +2 -17
- data/lib/sentry/std_lib_logger.rb +10 -1
- data/lib/sentry/telemetry_event_buffer.rb +130 -0
- data/lib/sentry/test_helper.rb +30 -0
- data/lib/sentry/transaction.rb +72 -95
- data/lib/sentry/transaction_event.rb +4 -9
- data/lib/sentry/transport/debug_transport.rb +70 -0
- data/lib/sentry/transport/dummy_transport.rb +1 -0
- data/lib/sentry/transport/http_transport.rb +9 -5
- data/lib/sentry/transport.rb +3 -5
- data/lib/sentry/utils/encoding_helper.rb +6 -0
- data/lib/sentry/utils/logging_helper.rb +25 -9
- data/lib/sentry/utils/sample_rand.rb +97 -0
- data/lib/sentry/utils/telemetry_attributes.rb +30 -0
- data/lib/sentry/vernier/profiler.rb +4 -3
- data/lib/sentry/version.rb +1 -1
- data/lib/sentry-ruby.rb +25 -30
- data/sentry-ruby-core.gemspec +1 -1
- data/sentry-ruby.gemspec +1 -1
- metadata +17 -17
- data/lib/sentry/metrics/aggregator.rb +0 -248
- data/lib/sentry/metrics/configuration.rb +0 -47
- data/lib/sentry/metrics/counter_metric.rb +0 -25
- data/lib/sentry/metrics/distribution_metric.rb +0 -25
- data/lib/sentry/metrics/gauge_metric.rb +0 -35
- data/lib/sentry/metrics/local_aggregator.rb +0 -53
- data/lib/sentry/metrics/metric.rb +0 -19
- data/lib/sentry/metrics/set_metric.rb +0 -28
- data/lib/sentry/metrics/timing.rb +0 -51
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: ea228d904661a633e6bdfd3eb0ea4dc6ac129a0fed8f4a0e378e9a6e84de30e0
|
|
4
|
+
data.tar.gz: 71c6db91301a24a1b05afa798a249de12b31a5d68c45d16f138f73c0999b3183
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 07adab1cfe625b662c7cc89867225f40f07a36b0b73b70c916969ad5bac6ec1516bda791b767398e0b48246e5f23e31601d954cec9f724a79d33980e45d6e4d5
|
|
7
|
+
data.tar.gz: 5072c6873787d7e070050df7aeeb3e9040ca3add4c04f1c4e21c63d12fb241ee6615eced7f71fac5edeee4381d60272069bdc38ce967033ead0e2e76894b2573
|
data/Gemfile
CHANGED
|
@@ -3,15 +3,16 @@
|
|
|
3
3
|
source "https://rubygems.org"
|
|
4
4
|
git_source(:github) { |name| "https://github.com/#{name}.git" }
|
|
5
5
|
|
|
6
|
-
eval_gemfile "../Gemfile"
|
|
6
|
+
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?
|
|
12
|
-
gem "rack", "~> #{Gem::Version.new(rack_version)}" unless rack_version == "0"
|
|
13
14
|
|
|
14
|
-
gem "
|
|
15
|
+
gem "rack", "~> #{Gem::Version.new(rack_version)}" unless rack_version == "0"
|
|
15
16
|
|
|
16
17
|
redis_rb_version = ENV.fetch("REDIS_RB_VERSION", "5.0")
|
|
17
18
|
gem "redis", "~> #{redis_rb_version}"
|
|
@@ -22,7 +23,7 @@ gem "timecop"
|
|
|
22
23
|
gem "stackprof" unless RUBY_PLATFORM == "java"
|
|
23
24
|
gem "vernier", platforms: :ruby if RUBY_VERSION >= "3.2.1"
|
|
24
25
|
|
|
25
|
-
gem "graphql", ">= 2.2.6"
|
|
26
|
+
gem "graphql", ">= 2.2.6"
|
|
26
27
|
|
|
27
28
|
gem "benchmark-ips"
|
|
28
29
|
gem "benchmark_driver"
|
|
@@ -34,3 +35,24 @@ gem "webrick"
|
|
|
34
35
|
gem "faraday"
|
|
35
36
|
gem "excon"
|
|
36
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,9 +105,9 @@ 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
|
-
* [](https://x.com/intent/follow?screen_name=sentry)
|
|
111
111
|
|
|
112
112
|
## Contributing to the SDK
|
|
113
113
|
|
|
@@ -23,10 +23,7 @@ module Sentry
|
|
|
23
23
|
@shutdown_callback = nil
|
|
24
24
|
|
|
25
25
|
@executor =
|
|
26
|
-
if
|
|
27
|
-
log_debug("config.async is set, BackgroundWorker is disabled")
|
|
28
|
-
Concurrent::ImmediateExecutor.new
|
|
29
|
-
elsif @number_of_threads == 0
|
|
26
|
+
if @number_of_threads == 0
|
|
30
27
|
log_debug("config.background_worker_threads is set to 0, all events will be sent synchronously")
|
|
31
28
|
Concurrent::ImmediateExecutor.new
|
|
32
29
|
else
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Sentry
|
|
4
|
+
# @api private
|
|
5
|
+
class Backtrace
|
|
6
|
+
# Handles backtrace parsing line by line
|
|
7
|
+
class Line
|
|
8
|
+
RB_EXTENSION = ".rb"
|
|
9
|
+
# regexp (optional leading X: on windows, or JRuby9000 class-prefix)
|
|
10
|
+
RUBY_INPUT_FORMAT = /
|
|
11
|
+
^ \s* (?: [a-zA-Z]: | uri:classloader: )? ([^:]+ | <.*>):
|
|
12
|
+
(\d+)
|
|
13
|
+
(?: :in\s('|`)(?:([\w:]+)\#)?([^']+)')?$
|
|
14
|
+
/x
|
|
15
|
+
|
|
16
|
+
# org.jruby.runtime.callsite.CachingCallSite.call(CachingCallSite.java:170)
|
|
17
|
+
JAVA_INPUT_FORMAT = /^([\w$.]+)\.([\w$]+)\(([\w$.]+):(\d+)\)$/
|
|
18
|
+
|
|
19
|
+
# The file portion of the line (such as app/models/user.rb)
|
|
20
|
+
attr_reader :file
|
|
21
|
+
|
|
22
|
+
# The line number portion of the line
|
|
23
|
+
attr_reader :number
|
|
24
|
+
|
|
25
|
+
# The method of the line (such as index)
|
|
26
|
+
attr_reader :method
|
|
27
|
+
|
|
28
|
+
# The module name (JRuby)
|
|
29
|
+
attr_reader :module_name
|
|
30
|
+
|
|
31
|
+
attr_reader :in_app_pattern
|
|
32
|
+
|
|
33
|
+
# Parses a single line of a given backtrace
|
|
34
|
+
# @param [String] unparsed_line The raw line from +caller+ or some backtrace
|
|
35
|
+
# @return [Line] The parsed backtrace line
|
|
36
|
+
def self.parse(unparsed_line, in_app_pattern = nil)
|
|
37
|
+
ruby_match = unparsed_line.match(RUBY_INPUT_FORMAT)
|
|
38
|
+
|
|
39
|
+
if ruby_match
|
|
40
|
+
_, file, number, _, module_name, method = ruby_match.to_a
|
|
41
|
+
file.sub!(/\.class$/, RB_EXTENSION)
|
|
42
|
+
module_name = module_name
|
|
43
|
+
else
|
|
44
|
+
java_match = unparsed_line.match(JAVA_INPUT_FORMAT)
|
|
45
|
+
_, module_name, method, file, number = java_match.to_a
|
|
46
|
+
end
|
|
47
|
+
new(file, number, method, module_name, in_app_pattern)
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
# Creates a Line from a Thread::Backtrace::Location object
|
|
51
|
+
# This is more efficient than converting to string and parsing with regex
|
|
52
|
+
# @param [Thread::Backtrace::Location] location The location object
|
|
53
|
+
# @param [Regexp, nil] in_app_pattern Optional pattern to determine if the line is in-app
|
|
54
|
+
# @return [Line] The backtrace line
|
|
55
|
+
def self.from_source_location(location, in_app_pattern = nil)
|
|
56
|
+
file = location.absolute_path
|
|
57
|
+
number = location.lineno
|
|
58
|
+
method = location.base_label
|
|
59
|
+
|
|
60
|
+
label = location.label
|
|
61
|
+
index = label.index("#") || label.index(".")
|
|
62
|
+
module_name = label[0, index] if index
|
|
63
|
+
|
|
64
|
+
new(file, number, method, module_name, in_app_pattern)
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
def initialize(file, number, method, module_name, in_app_pattern)
|
|
68
|
+
@file = file
|
|
69
|
+
@module_name = module_name
|
|
70
|
+
@number = number.to_i
|
|
71
|
+
@method = method
|
|
72
|
+
@in_app_pattern = in_app_pattern
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
def in_app
|
|
76
|
+
return false unless in_app_pattern
|
|
77
|
+
|
|
78
|
+
if file =~ in_app_pattern
|
|
79
|
+
true
|
|
80
|
+
else
|
|
81
|
+
false
|
|
82
|
+
end
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
# Reconstructs the line in a readable fashion
|
|
86
|
+
def to_s
|
|
87
|
+
"#{file}:#{number}:in `#{method}'"
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
def ==(other)
|
|
91
|
+
to_s == other.to_s
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
def inspect
|
|
95
|
+
"<Line:#{self}>"
|
|
96
|
+
end
|
|
97
|
+
end
|
|
98
|
+
end
|
|
99
|
+
end
|
data/lib/sentry/backtrace.rb
CHANGED
|
@@ -1,86 +1,12 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
require "rubygems"
|
|
4
|
+
require "concurrent/map"
|
|
5
|
+
require "sentry/backtrace/line"
|
|
4
6
|
|
|
5
7
|
module Sentry
|
|
6
8
|
# @api private
|
|
7
9
|
class Backtrace
|
|
8
|
-
# Handles backtrace parsing line by line
|
|
9
|
-
class Line
|
|
10
|
-
RB_EXTENSION = ".rb"
|
|
11
|
-
# regexp (optional leading X: on windows, or JRuby9000 class-prefix)
|
|
12
|
-
RUBY_INPUT_FORMAT = /
|
|
13
|
-
^ \s* (?: [a-zA-Z]: | uri:classloader: )? ([^:]+ | <.*>):
|
|
14
|
-
(\d+)
|
|
15
|
-
(?: :in\s('|`)(?:([\w:]+)\#)?([^']+)')?$
|
|
16
|
-
/x
|
|
17
|
-
|
|
18
|
-
# org.jruby.runtime.callsite.CachingCallSite.call(CachingCallSite.java:170)
|
|
19
|
-
JAVA_INPUT_FORMAT = /^([\w$.]+)\.([\w$]+)\(([\w$.]+):(\d+)\)$/
|
|
20
|
-
|
|
21
|
-
# The file portion of the line (such as app/models/user.rb)
|
|
22
|
-
attr_reader :file
|
|
23
|
-
|
|
24
|
-
# The line number portion of the line
|
|
25
|
-
attr_reader :number
|
|
26
|
-
|
|
27
|
-
# The method of the line (such as index)
|
|
28
|
-
attr_reader :method
|
|
29
|
-
|
|
30
|
-
# The module name (JRuby)
|
|
31
|
-
attr_reader :module_name
|
|
32
|
-
|
|
33
|
-
attr_reader :in_app_pattern
|
|
34
|
-
|
|
35
|
-
# Parses a single line of a given backtrace
|
|
36
|
-
# @param [String] unparsed_line The raw line from +caller+ or some backtrace
|
|
37
|
-
# @return [Line] The parsed backtrace line
|
|
38
|
-
def self.parse(unparsed_line, in_app_pattern = nil)
|
|
39
|
-
ruby_match = unparsed_line.match(RUBY_INPUT_FORMAT)
|
|
40
|
-
|
|
41
|
-
if ruby_match
|
|
42
|
-
_, file, number, _, module_name, method = ruby_match.to_a
|
|
43
|
-
file.sub!(/\.class$/, RB_EXTENSION)
|
|
44
|
-
module_name = module_name
|
|
45
|
-
else
|
|
46
|
-
java_match = unparsed_line.match(JAVA_INPUT_FORMAT)
|
|
47
|
-
_, module_name, method, file, number = java_match.to_a
|
|
48
|
-
end
|
|
49
|
-
new(file, number, method, module_name, in_app_pattern)
|
|
50
|
-
end
|
|
51
|
-
|
|
52
|
-
def initialize(file, number, method, module_name, in_app_pattern)
|
|
53
|
-
@file = file
|
|
54
|
-
@module_name = module_name
|
|
55
|
-
@number = number.to_i
|
|
56
|
-
@method = method
|
|
57
|
-
@in_app_pattern = in_app_pattern
|
|
58
|
-
end
|
|
59
|
-
|
|
60
|
-
def in_app
|
|
61
|
-
return false unless in_app_pattern
|
|
62
|
-
|
|
63
|
-
if file =~ in_app_pattern
|
|
64
|
-
true
|
|
65
|
-
else
|
|
66
|
-
false
|
|
67
|
-
end
|
|
68
|
-
end
|
|
69
|
-
|
|
70
|
-
# Reconstructs the line in a readable fashion
|
|
71
|
-
def to_s
|
|
72
|
-
"#{file}:#{number}:in `#{method}'"
|
|
73
|
-
end
|
|
74
|
-
|
|
75
|
-
def ==(other)
|
|
76
|
-
to_s == other.to_s
|
|
77
|
-
end
|
|
78
|
-
|
|
79
|
-
def inspect
|
|
80
|
-
"<Line:#{self}>"
|
|
81
|
-
end
|
|
82
|
-
end
|
|
83
|
-
|
|
84
10
|
# holder for an Array of Backtrace::Line instances
|
|
85
11
|
attr_reader :lines
|
|
86
12
|
|
|
@@ -100,6 +26,48 @@ module Sentry
|
|
|
100
26
|
new(lines)
|
|
101
27
|
end
|
|
102
28
|
|
|
29
|
+
# Thread.each_caller_location is an API added in Ruby 3.2 that doesn't always collect
|
|
30
|
+
# the entire stack like Kernel#caller or #caller_locations do.
|
|
31
|
+
#
|
|
32
|
+
# @see https://github.com/rails/rails/pull/49095 for more context.
|
|
33
|
+
if Thread.respond_to?(:each_caller_location)
|
|
34
|
+
def self.source_location(&backtrace_cleaner)
|
|
35
|
+
Thread.each_caller_location do |location|
|
|
36
|
+
frame_key = [location.absolute_path, location.lineno]
|
|
37
|
+
cached_value = line_cache[frame_key]
|
|
38
|
+
|
|
39
|
+
next if cached_value == :skip
|
|
40
|
+
|
|
41
|
+
if cached_value
|
|
42
|
+
return cached_value
|
|
43
|
+
else
|
|
44
|
+
if cleaned_frame = backtrace_cleaner.(location)
|
|
45
|
+
line = Line.from_source_location(location)
|
|
46
|
+
line_cache[frame_key] = line
|
|
47
|
+
|
|
48
|
+
return line
|
|
49
|
+
else
|
|
50
|
+
line_cache[frame_key] = :skip
|
|
51
|
+
|
|
52
|
+
next
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
def self.line_cache
|
|
59
|
+
@line_cache ||= Concurrent::Map.new
|
|
60
|
+
end
|
|
61
|
+
else
|
|
62
|
+
# Since Sentry is mostly used in production, we don't want to fallback
|
|
63
|
+
# to the slower implementation and adds potentially big overhead to the
|
|
64
|
+
# application.
|
|
65
|
+
def self.source_location(*)
|
|
66
|
+
nil
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
|
|
103
71
|
def initialize(lines)
|
|
104
72
|
@lines = lines
|
|
105
73
|
end
|
data/lib/sentry/breadcrumb.rb
CHANGED
|
@@ -48,13 +48,13 @@ module Sentry
|
|
|
48
48
|
end
|
|
49
49
|
|
|
50
50
|
# @return [Hash]
|
|
51
|
-
def
|
|
51
|
+
def to_h
|
|
52
52
|
data = super
|
|
53
53
|
data[:check_in_id] = check_in_id
|
|
54
54
|
data[:monitor_slug] = monitor_slug
|
|
55
55
|
data[:status] = status
|
|
56
56
|
data[:duration] = duration if duration
|
|
57
|
-
data[:monitor_config] = monitor_config.
|
|
57
|
+
data[:monitor_config] = monitor_config.to_h if monitor_config
|
|
58
58
|
data
|
|
59
59
|
end
|
|
60
60
|
end
|
data/lib/sentry/client.rb
CHANGED
|
@@ -3,7 +3,10 @@
|
|
|
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"
|
|
9
|
+
require "sentry/utils/encoding_helper"
|
|
7
10
|
|
|
8
11
|
module Sentry
|
|
9
12
|
class Client
|
|
@@ -20,6 +23,9 @@ module Sentry
|
|
|
20
23
|
# @!visibility private
|
|
21
24
|
attr_reader :log_event_buffer
|
|
22
25
|
|
|
26
|
+
# @!visibility private
|
|
27
|
+
attr_reader :metric_event_buffer
|
|
28
|
+
|
|
23
29
|
# @!macro configuration
|
|
24
30
|
attr_reader :configuration
|
|
25
31
|
|
|
@@ -43,7 +49,11 @@ module Sentry
|
|
|
43
49
|
@spotlight_transport = SpotlightTransport.new(configuration) if configuration.spotlight
|
|
44
50
|
|
|
45
51
|
if configuration.enable_logs
|
|
46
|
-
@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)
|
|
47
57
|
end
|
|
48
58
|
end
|
|
49
59
|
|
|
@@ -60,8 +70,7 @@ module Sentry
|
|
|
60
70
|
return
|
|
61
71
|
end
|
|
62
72
|
|
|
63
|
-
|
|
64
|
-
data_category = Envelope::Item.data_category(event_type)
|
|
73
|
+
data_category = Envelope::Item.data_category(event.type)
|
|
65
74
|
|
|
66
75
|
is_transaction = event.is_a?(TransactionEvent)
|
|
67
76
|
spans_before = is_transaction ? event.spans.size : 0
|
|
@@ -78,9 +87,7 @@ module Sentry
|
|
|
78
87
|
transport.record_lost_event(:event_processor, "span", num: spans_delta) if spans_delta > 0
|
|
79
88
|
end
|
|
80
89
|
|
|
81
|
-
if
|
|
82
|
-
dispatch_async_event(async_block, event, hint)
|
|
83
|
-
elsif configuration.background_worker_threads != 0 && hint.fetch(:background, true)
|
|
90
|
+
if configuration.background_worker_threads != 0 && hint.fetch(:background, true)
|
|
84
91
|
unless dispatch_background_event(event, hint)
|
|
85
92
|
transport.record_lost_event(:queue_overflow, data_category)
|
|
86
93
|
transport.record_lost_event(:queue_overflow, "span", num: spans_before + 1) if is_transaction
|
|
@@ -100,7 +107,17 @@ module Sentry
|
|
|
100
107
|
# @return [LogEvent]
|
|
101
108
|
def buffer_log_event(event, scope)
|
|
102
109
|
return unless event.is_a?(LogEvent)
|
|
103
|
-
@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)
|
|
104
121
|
event
|
|
105
122
|
end
|
|
106
123
|
|
|
@@ -117,6 +134,7 @@ module Sentry
|
|
|
117
134
|
transport.flush if configuration.sending_to_dsn_allowed?
|
|
118
135
|
spotlight_transport.flush if spotlight_transport
|
|
119
136
|
@log_event_buffer&.flush
|
|
137
|
+
@metric_event_buffer&.flush
|
|
120
138
|
end
|
|
121
139
|
|
|
122
140
|
# Initializes an Event object with the given exception. Returns `nil` if the exception's class is excluded from reporting.
|
|
@@ -195,9 +213,19 @@ module Sentry
|
|
|
195
213
|
def event_from_log(message, level:, **options)
|
|
196
214
|
return unless configuration.sending_allowed?
|
|
197
215
|
|
|
198
|
-
attributes = options.reject { |k, _| k == :level || k == :severity }
|
|
216
|
+
attributes = options.reject { |k, _| k == :level || k == :severity || k == :origin }
|
|
217
|
+
origin = options[:origin]
|
|
218
|
+
body = Utils::EncodingHelper.safe_utf_8_string(message)
|
|
219
|
+
|
|
220
|
+
sanitized_attributes = attributes.transform_values do |value|
|
|
221
|
+
if value.is_a?(String)
|
|
222
|
+
Utils::EncodingHelper.safe_utf_8_string(value)
|
|
223
|
+
else
|
|
224
|
+
value
|
|
225
|
+
end
|
|
226
|
+
end
|
|
199
227
|
|
|
200
|
-
LogEvent.new(level: level, body:
|
|
228
|
+
LogEvent.new(level: level, body: body, attributes: sanitized_attributes, origin: origin)
|
|
201
229
|
end
|
|
202
230
|
|
|
203
231
|
# Initializes an Event object with the given Transaction object.
|
|
@@ -209,22 +237,13 @@ module Sentry
|
|
|
209
237
|
|
|
210
238
|
# @!macro send_event
|
|
211
239
|
def send_event(event, hint = nil)
|
|
212
|
-
|
|
213
|
-
data_category = Envelope::Item.data_category(event_type)
|
|
240
|
+
data_category = Envelope::Item.data_category(event.type)
|
|
214
241
|
spans_before = event.is_a?(TransactionEvent) ? event.spans.size : 0
|
|
215
242
|
|
|
216
|
-
if
|
|
243
|
+
if event.is_a?(ErrorEvent) && configuration.before_send
|
|
217
244
|
event = configuration.before_send.call(event, hint)
|
|
218
245
|
|
|
219
|
-
|
|
220
|
-
when ErrorEvent, CheckInEvent
|
|
221
|
-
# do nothing
|
|
222
|
-
when Hash
|
|
223
|
-
log_debug(<<~MSG)
|
|
224
|
-
Returning a Hash from before_send is deprecated and will be removed in the next major version.
|
|
225
|
-
Please return a Sentry::ErrorEvent object instead.
|
|
226
|
-
MSG
|
|
227
|
-
else
|
|
246
|
+
if !event.is_a?(ErrorEvent)
|
|
228
247
|
# Avoid serializing the event object in this case because we aren't sure what it is and what it contains
|
|
229
248
|
log_debug(<<~MSG)
|
|
230
249
|
Discarded event because before_send didn't return a Sentry::ErrorEvent object but an instance of #{event.class}
|
|
@@ -234,21 +253,10 @@ module Sentry
|
|
|
234
253
|
end
|
|
235
254
|
end
|
|
236
255
|
|
|
237
|
-
if
|
|
256
|
+
if event.is_a?(TransactionEvent) && configuration.before_send_transaction
|
|
238
257
|
event = configuration.before_send_transaction.call(event, hint)
|
|
239
258
|
|
|
240
|
-
if event.is_a?(TransactionEvent)
|
|
241
|
-
spans_after = event.is_a?(TransactionEvent) ? event.spans.size : 0
|
|
242
|
-
spans_delta = spans_before - spans_after
|
|
243
|
-
transport.record_lost_event(:before_send, "span", num: spans_delta) if spans_delta > 0
|
|
244
|
-
|
|
245
|
-
if event.is_a?(Hash)
|
|
246
|
-
log_debug(<<~MSG)
|
|
247
|
-
Returning a Hash from before_send_transaction is deprecated and will be removed in the next major version.
|
|
248
|
-
Please return a Sentry::TransactionEvent object instead.
|
|
249
|
-
MSG
|
|
250
|
-
end
|
|
251
|
-
else
|
|
259
|
+
if !event.is_a?(TransactionEvent)
|
|
252
260
|
# Avoid serializing the event object in this case because we aren't sure what it is and what it contains
|
|
253
261
|
log_debug(<<~MSG)
|
|
254
262
|
Discarded event because before_send_transaction didn't return a Sentry::TransactionEvent object but an instance of #{event.class}
|
|
@@ -257,6 +265,23 @@ module Sentry
|
|
|
257
265
|
transport.record_lost_event(:before_send, "span", num: spans_before + 1)
|
|
258
266
|
return
|
|
259
267
|
end
|
|
268
|
+
|
|
269
|
+
spans_after = event.is_a?(TransactionEvent) ? event.spans.size : 0
|
|
270
|
+
spans_delta = spans_before - spans_after
|
|
271
|
+
transport.record_lost_event(:before_send, "span", num: spans_delta) if spans_delta > 0
|
|
272
|
+
end
|
|
273
|
+
|
|
274
|
+
if event.is_a?(CheckInEvent) && configuration.before_send_check_in
|
|
275
|
+
event = configuration.before_send_check_in.call(event, hint)
|
|
276
|
+
|
|
277
|
+
if !event.is_a?(CheckInEvent)
|
|
278
|
+
# Avoid serializing the event object in this case because we aren't sure what it is and what it contains
|
|
279
|
+
log_debug(<<~MSG)
|
|
280
|
+
Discarded event because before_send_check_in didn't return a Sentry::CheckInEvent object but an instance of #{event.class}
|
|
281
|
+
MSG
|
|
282
|
+
transport.record_lost_event(:before_send, data_category)
|
|
283
|
+
return
|
|
284
|
+
end
|
|
260
285
|
end
|
|
261
286
|
|
|
262
287
|
transport.send_event(event) if configuration.sending_to_dsn_allowed?
|
|
@@ -270,53 +295,6 @@ module Sentry
|
|
|
270
295
|
raise
|
|
271
296
|
end
|
|
272
297
|
|
|
273
|
-
# Send an envelope with batched logs
|
|
274
|
-
# @param log_events [Array<LogEvent>] the log events to be sent
|
|
275
|
-
# @api private
|
|
276
|
-
# @return [void]
|
|
277
|
-
def send_logs(log_events)
|
|
278
|
-
envelope = Envelope.new(
|
|
279
|
-
event_id: Sentry::Utils.uuid,
|
|
280
|
-
sent_at: Sentry.utc_now.iso8601,
|
|
281
|
-
dsn: configuration.dsn,
|
|
282
|
-
sdk: Sentry.sdk_meta
|
|
283
|
-
)
|
|
284
|
-
|
|
285
|
-
discarded_count = 0
|
|
286
|
-
envelope_items = []
|
|
287
|
-
|
|
288
|
-
if configuration.before_send_log
|
|
289
|
-
log_events.each do |log_event|
|
|
290
|
-
processed_log_event = configuration.before_send_log.call(log_event)
|
|
291
|
-
|
|
292
|
-
if processed_log_event
|
|
293
|
-
envelope_items << processed_log_event.to_hash
|
|
294
|
-
else
|
|
295
|
-
discarded_count += 1
|
|
296
|
-
end
|
|
297
|
-
end
|
|
298
|
-
|
|
299
|
-
envelope_items
|
|
300
|
-
else
|
|
301
|
-
envelope_items = log_events.map(&:to_hash)
|
|
302
|
-
end
|
|
303
|
-
|
|
304
|
-
envelope.add_item(
|
|
305
|
-
{
|
|
306
|
-
type: "log",
|
|
307
|
-
item_count: envelope_items.size,
|
|
308
|
-
content_type: "application/vnd.sentry.items.log+json"
|
|
309
|
-
},
|
|
310
|
-
{ items: envelope_items }
|
|
311
|
-
)
|
|
312
|
-
|
|
313
|
-
send_envelope(envelope)
|
|
314
|
-
|
|
315
|
-
unless discarded_count.zero?
|
|
316
|
-
transport.record_lost_event(:before_send, "log_item", num: discarded_count)
|
|
317
|
-
end
|
|
318
|
-
end
|
|
319
|
-
|
|
320
298
|
# Send an envelope directly to Sentry.
|
|
321
299
|
# @param envelope [Envelope] the envelope to be sent.
|
|
322
300
|
# @return [void]
|
|
@@ -333,38 +311,6 @@ module Sentry
|
|
|
333
311
|
raise
|
|
334
312
|
end
|
|
335
313
|
|
|
336
|
-
# @deprecated use Sentry.get_traceparent instead.
|
|
337
|
-
#
|
|
338
|
-
# Generates a Sentry trace for distribted tracing from the given Span.
|
|
339
|
-
# Returns `nil` if `config.propagate_traces` is `false`.
|
|
340
|
-
# @param span [Span] the span to generate trace from.
|
|
341
|
-
# @return [String, nil]
|
|
342
|
-
def generate_sentry_trace(span)
|
|
343
|
-
return unless configuration.propagate_traces
|
|
344
|
-
|
|
345
|
-
trace = span.to_sentry_trace
|
|
346
|
-
log_debug("[Tracing] Adding #{SENTRY_TRACE_HEADER_NAME} header to outgoing request: #{trace}")
|
|
347
|
-
trace
|
|
348
|
-
end
|
|
349
|
-
|
|
350
|
-
# @deprecated Use Sentry.get_baggage instead.
|
|
351
|
-
#
|
|
352
|
-
# Generates a W3C Baggage header for distributed tracing from the given Span.
|
|
353
|
-
# Returns `nil` if `config.propagate_traces` is `false`.
|
|
354
|
-
# @param span [Span] the span to generate trace from.
|
|
355
|
-
# @return [String, nil]
|
|
356
|
-
def generate_baggage(span)
|
|
357
|
-
return unless configuration.propagate_traces
|
|
358
|
-
|
|
359
|
-
baggage = span.to_baggage
|
|
360
|
-
|
|
361
|
-
if baggage && !baggage.empty?
|
|
362
|
-
log_debug("[Tracing] Adding #{BAGGAGE_HEADER_NAME} header to outgoing request: #{baggage}")
|
|
363
|
-
end
|
|
364
|
-
|
|
365
|
-
baggage
|
|
366
|
-
end
|
|
367
|
-
|
|
368
314
|
private
|
|
369
315
|
|
|
370
316
|
def dispatch_background_event(event, hint)
|
|
@@ -372,28 +318,5 @@ module Sentry
|
|
|
372
318
|
send_event(event, hint)
|
|
373
319
|
end
|
|
374
320
|
end
|
|
375
|
-
|
|
376
|
-
def dispatch_async_event(async_block, event, hint)
|
|
377
|
-
# We have to convert to a JSON-like hash, because background job
|
|
378
|
-
# processors (esp ActiveJob) may not like weird types in the event hash
|
|
379
|
-
|
|
380
|
-
event_hash =
|
|
381
|
-
begin
|
|
382
|
-
event.to_json_compatible
|
|
383
|
-
rescue => e
|
|
384
|
-
log_error("Converting #{event.type} (#{event.event_id}) to JSON compatible hash failed", e, debug: configuration.debug)
|
|
385
|
-
return
|
|
386
|
-
end
|
|
387
|
-
|
|
388
|
-
if async_block.arity == 2
|
|
389
|
-
hint = JSON.parse(JSON.generate(hint))
|
|
390
|
-
async_block.call(event_hash, hint)
|
|
391
|
-
else
|
|
392
|
-
async_block.call(event_hash)
|
|
393
|
-
end
|
|
394
|
-
rescue => e
|
|
395
|
-
log_error("Async #{event_hash["type"]} sending failed", e, debug: configuration.debug)
|
|
396
|
-
send_event(event, hint)
|
|
397
|
-
end
|
|
398
321
|
end
|
|
399
322
|
end
|