sentry-rails 5.17.3 → 5.18.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/Gemfile +5 -2
- data/lib/generators/sentry_generator.rb +19 -0
- data/lib/sentry/rails/action_cable.rb +8 -1
- data/lib/sentry/rails/active_job.rb +7 -1
- data/lib/sentry/rails/capture_exceptions.rb +7 -1
- data/lib/sentry/rails/configuration.rb +10 -0
- data/lib/sentry/rails/controller_transaction.rb +3 -1
- data/lib/sentry/rails/tracing/action_controller_subscriber.rb +2 -0
- data/lib/sentry/rails/tracing/action_view_subscriber.rb +8 -1
- data/lib/sentry/rails/tracing/active_record_subscriber.rb +80 -24
- data/lib/sentry/rails/tracing/active_storage_subscriber.rb +9 -1
- data/lib/sentry/rails/version.rb +1 -1
- data/sentry-rails.gemspec +1 -1
- metadata +6 -5
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: f37e46eaedbb7739f9def9819546a7f4b1457c7fefd076bb394104fdf18910c1
|
4
|
+
data.tar.gz: f7360c8d456797deef74837c24ee44ae7a5fd875cc602b83382ac345d6498c44
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: c5ec2e4609761381f9414e88c58559086dae1fb177d8f7b9a91e52bce88b3ea82f9f201d4aabd498e39d52f91637a7a1d5778f726d91afa1138f429136a96294
|
7
|
+
data.tar.gz: e58d7d66bb42a9dc0ff8679d7667c9c9bcb1fe268313c1fced2a6bd8713bf04b2dd95d407547b95052a8f57a9e93a393ace8e03108c9d9491bbc727414983944
|
data/Gemfile
CHANGED
@@ -17,8 +17,11 @@ rails_version = Gem::Version.new(rails_version)
|
|
17
17
|
if rails_version < Gem::Version.new("6.0.0")
|
18
18
|
gem "sqlite3", "~> 1.3.0", platform: :ruby
|
19
19
|
else
|
20
|
-
|
21
|
-
|
20
|
+
if Gem::Version.new(RUBY_VERSION) >= Gem::Version.new("2.7.0")
|
21
|
+
gem "sqlite3", "~> 1.7.3", platform: :ruby
|
22
|
+
else
|
23
|
+
gem "sqlite3", "~> 1.6.9", platform: :ruby
|
24
|
+
end
|
22
25
|
end
|
23
26
|
|
24
27
|
if rails_version >= Gem::Version.new("7.2.0.alpha")
|
@@ -0,0 +1,19 @@
|
|
1
|
+
require "rails/generators/base"
|
2
|
+
|
3
|
+
class SentryGenerator < ::Rails::Generators::Base
|
4
|
+
class_option :dsn, type: :string, desc: "Sentry DSN"
|
5
|
+
|
6
|
+
def copy_initializer_file
|
7
|
+
dsn = options[:dsn] ? "'#{options[:dsn]}'" : "ENV['SENTRY_DSN']"
|
8
|
+
|
9
|
+
create_file "config/initializers/sentry.rb", <<~RUBY
|
10
|
+
# frozen_string_literal: true
|
11
|
+
|
12
|
+
Sentry.init do |config|
|
13
|
+
config.breadcrumbs_logger = [:active_support_logger]
|
14
|
+
config.dsn = #{dsn}
|
15
|
+
config.enable_tracing = true
|
16
|
+
end
|
17
|
+
RUBY
|
18
|
+
end
|
19
|
+
end
|
@@ -3,6 +3,7 @@ module Sentry
|
|
3
3
|
module ActionCableExtensions
|
4
4
|
class ErrorHandler
|
5
5
|
OP_NAME = "websocket.server".freeze
|
6
|
+
SPAN_ORIGIN = "auto.http.rails.actioncable"
|
6
7
|
|
7
8
|
class << self
|
8
9
|
def capture(connection, transaction_name:, extra_context: nil, &block)
|
@@ -33,7 +34,13 @@ module Sentry
|
|
33
34
|
end
|
34
35
|
|
35
36
|
def start_transaction(env, scope)
|
36
|
-
options = {
|
37
|
+
options = {
|
38
|
+
name: scope.transaction_name,
|
39
|
+
source: scope.transaction_source,
|
40
|
+
op: OP_NAME,
|
41
|
+
origin: SPAN_ORIGIN
|
42
|
+
}
|
43
|
+
|
37
44
|
transaction = Sentry.continue_trace(env, **options)
|
38
45
|
Sentry.start_transaction(transaction: transaction, **options)
|
39
46
|
end
|
@@ -17,6 +17,7 @@ module Sentry
|
|
17
17
|
|
18
18
|
class SentryReporter
|
19
19
|
OP_NAME = "queue.active_job".freeze
|
20
|
+
SPAN_ORIGIN = "auto.queue.active_job".freeze
|
20
21
|
|
21
22
|
class << self
|
22
23
|
def record(job, &block)
|
@@ -27,7 +28,12 @@ module Sentry
|
|
27
28
|
if job.is_a?(::Sentry::SendEventJob)
|
28
29
|
nil
|
29
30
|
else
|
30
|
-
Sentry.start_transaction(
|
31
|
+
Sentry.start_transaction(
|
32
|
+
name: scope.transaction_name,
|
33
|
+
source: scope.transaction_source,
|
34
|
+
op: OP_NAME,
|
35
|
+
origin: SPAN_ORIGIN
|
36
|
+
)
|
31
37
|
end
|
32
38
|
|
33
39
|
scope.set_span(transaction) if transaction
|
@@ -2,6 +2,7 @@ module Sentry
|
|
2
2
|
module Rails
|
3
3
|
class CaptureExceptions < Sentry::Rack::CaptureExceptions
|
4
4
|
RAILS_7_1 = Gem::Version.new(::Rails.version) >= Gem::Version.new("7.1.0.alpha")
|
5
|
+
SPAN_ORIGIN = 'auto.http.rails'.freeze
|
5
6
|
|
6
7
|
def initialize(_)
|
7
8
|
super
|
@@ -32,7 +33,12 @@ module Sentry
|
|
32
33
|
end
|
33
34
|
|
34
35
|
def start_transaction(env, scope)
|
35
|
-
options = {
|
36
|
+
options = {
|
37
|
+
name: scope.transaction_name,
|
38
|
+
source: scope.transaction_source,
|
39
|
+
op: transaction_op,
|
40
|
+
origin: SPAN_ORIGIN
|
41
|
+
}
|
36
42
|
|
37
43
|
if @assets_regexp && scope.transaction_name.match?(@assets_regexp)
|
38
44
|
options.merge!(sampled: false)
|
@@ -126,6 +126,14 @@ module Sentry
|
|
126
126
|
|
127
127
|
attr_accessor :tracing_subscribers
|
128
128
|
|
129
|
+
# When the ActiveRecordSubscriber is enabled, capture the source location of the query in the span data.
|
130
|
+
# This is enabled by default, but can be disabled by setting this to false.
|
131
|
+
attr_accessor :enable_db_query_source
|
132
|
+
|
133
|
+
# The threshold in milliseconds for the ActiveRecordSubscriber to capture the source location of the query
|
134
|
+
# in the span data. Default is 100ms.
|
135
|
+
attr_accessor :db_query_source_threshold_ms
|
136
|
+
|
129
137
|
# sentry-rails by default skips asset request' transactions by checking if the path matches
|
130
138
|
#
|
131
139
|
# ```rb
|
@@ -157,6 +165,8 @@ module Sentry
|
|
157
165
|
Sentry::Rails::Tracing::ActiveRecordSubscriber,
|
158
166
|
Sentry::Rails::Tracing::ActiveStorageSubscriber
|
159
167
|
])
|
168
|
+
@enable_db_query_source = true
|
169
|
+
@db_query_source_threshold_ms = 100
|
160
170
|
@active_support_logger_subscription_items = Sentry::Rails::ACTIVE_SUPPORT_LOGGER_SUBSCRIPTION_ITEMS_DEFAULT.dup
|
161
171
|
end
|
162
172
|
end
|
@@ -1,6 +1,8 @@
|
|
1
1
|
module Sentry
|
2
2
|
module Rails
|
3
3
|
module ControllerTransaction
|
4
|
+
SPAN_ORIGIN = 'auto.view.rails'.freeze
|
5
|
+
|
4
6
|
def self.included(base)
|
5
7
|
base.prepend_around_action(:sentry_around_action)
|
6
8
|
end
|
@@ -11,7 +13,7 @@ module Sentry
|
|
11
13
|
if Sentry.initialized?
|
12
14
|
transaction_name = "#{self.class}##{action_name}"
|
13
15
|
Sentry.get_current_scope.set_transaction_name(transaction_name, source: :view)
|
14
|
-
Sentry.with_child_span(op: "view.process_action.action_controller", description: transaction_name) do |child_span|
|
16
|
+
Sentry.with_child_span(op: "view.process_action.action_controller", description: transaction_name, origin: SPAN_ORIGIN) do |child_span|
|
15
17
|
if child_span
|
16
18
|
begin
|
17
19
|
result = yield
|
@@ -9,6 +9,7 @@ module Sentry
|
|
9
9
|
|
10
10
|
EVENT_NAMES = ["process_action.action_controller"].freeze
|
11
11
|
OP_NAME = "view.process_action.action_controller".freeze
|
12
|
+
SPAN_ORIGIN = "auto.view.rails".freeze
|
12
13
|
|
13
14
|
def self.subscribe!
|
14
15
|
Sentry.logger.warn <<~MSG
|
@@ -22,6 +23,7 @@ module Sentry
|
|
22
23
|
|
23
24
|
record_on_current_span(
|
24
25
|
op: OP_NAME,
|
26
|
+
origin: SPAN_ORIGIN,
|
25
27
|
start_timestamp: payload[START_TIMESTAMP_NAME],
|
26
28
|
description: "#{controller}##{action}",
|
27
29
|
duration: duration
|
@@ -6,10 +6,17 @@ module Sentry
|
|
6
6
|
class ActionViewSubscriber < AbstractSubscriber
|
7
7
|
EVENT_NAMES = ["render_template.action_view"].freeze
|
8
8
|
SPAN_PREFIX = "template.".freeze
|
9
|
+
SPAN_ORIGIN = "auto.template.rails".freeze
|
9
10
|
|
10
11
|
def self.subscribe!
|
11
12
|
subscribe_to_event(EVENT_NAMES) do |event_name, duration, payload|
|
12
|
-
record_on_current_span(
|
13
|
+
record_on_current_span(
|
14
|
+
op: SPAN_PREFIX + event_name,
|
15
|
+
origin: SPAN_ORIGIN,
|
16
|
+
start_timestamp: payload[START_TIMESTAMP_NAME],
|
17
|
+
description: payload[:identifier],
|
18
|
+
duration: duration
|
19
|
+
)
|
13
20
|
end
|
14
21
|
end
|
15
22
|
end
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require "sentry/rails/tracing/abstract_subscriber"
|
2
4
|
|
3
5
|
module Sentry
|
@@ -5,41 +7,95 @@ module Sentry
|
|
5
7
|
module Tracing
|
6
8
|
class ActiveRecordSubscriber < AbstractSubscriber
|
7
9
|
EVENT_NAMES = ["sql.active_record"].freeze
|
8
|
-
SPAN_PREFIX = "db."
|
10
|
+
SPAN_PREFIX = "db."
|
11
|
+
SPAN_ORIGIN = "auto.db.rails"
|
9
12
|
EXCLUDED_EVENTS = ["SCHEMA", "TRANSACTION"].freeze
|
10
13
|
|
11
|
-
|
12
|
-
subscribe_to_event(EVENT_NAMES) do |event_name, duration, payload|
|
13
|
-
next if EXCLUDED_EVENTS.include? payload[:name]
|
14
|
+
SUPPORT_SOURCE_LOCATION = ActiveSupport::BacktraceCleaner.method_defined?(:clean_frame)
|
14
15
|
|
15
|
-
|
16
|
-
|
16
|
+
if SUPPORT_SOURCE_LOCATION
|
17
|
+
class_attribute :backtrace_cleaner, default: (ActiveSupport::BacktraceCleaner.new.tap do |cleaner|
|
18
|
+
cleaner.add_silencer { |line| line.include?("sentry-ruby/lib") || line.include?("sentry-rails/lib") }
|
19
|
+
end)
|
20
|
+
end
|
17
21
|
|
18
|
-
|
22
|
+
class << self
|
23
|
+
def subscribe!
|
24
|
+
record_query_source = SUPPORT_SOURCE_LOCATION && Sentry.configuration.rails.enable_db_query_source
|
25
|
+
query_source_threshold = Sentry.configuration.rails.db_query_source_threshold_ms
|
19
26
|
|
20
|
-
|
21
|
-
|
27
|
+
subscribe_to_event(EVENT_NAMES) do |event_name, duration, payload|
|
28
|
+
next if EXCLUDED_EVENTS.include? payload[:name]
|
22
29
|
|
23
|
-
|
24
|
-
|
25
|
-
|
30
|
+
record_on_current_span(
|
31
|
+
op: SPAN_PREFIX + event_name,
|
32
|
+
origin: SPAN_ORIGIN,
|
33
|
+
start_timestamp: payload[START_TIMESTAMP_NAME],
|
34
|
+
description: payload[:sql],
|
35
|
+
duration: duration
|
36
|
+
) do |span|
|
37
|
+
span.set_tag(:cached, true) if payload.fetch(:cached, false) # cached key is only set for hits in the QueryCache, from Rails 5.1
|
26
38
|
|
27
|
-
|
39
|
+
connection = payload[:connection]
|
28
40
|
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
connection.
|
41
|
+
if payload[:connection_id]
|
42
|
+
span.set_data(:connection_id, payload[:connection_id])
|
43
|
+
|
44
|
+
# we fallback to the base connection on rails < 6.0.0 since the payload doesn't have it
|
45
|
+
connection ||= ActiveRecord::Base.connection_pool.connections.find { |conn| conn.object_id == payload[:connection_id] }
|
34
46
|
end
|
35
47
|
|
36
|
-
|
48
|
+
next unless connection
|
49
|
+
|
50
|
+
db_config =
|
51
|
+
if connection.pool.respond_to?(:db_config)
|
52
|
+
connection.pool.db_config.configuration_hash
|
53
|
+
elsif connection.pool.respond_to?(:spec)
|
54
|
+
connection.pool.spec.config
|
55
|
+
end
|
56
|
+
|
57
|
+
next unless db_config
|
58
|
+
|
59
|
+
span.set_data(Span::DataConventions::DB_SYSTEM, db_config[:adapter]) if db_config[:adapter]
|
60
|
+
span.set_data(Span::DataConventions::DB_NAME, db_config[:database]) if db_config[:database]
|
61
|
+
span.set_data(Span::DataConventions::SERVER_ADDRESS, db_config[:host]) if db_config[:host]
|
62
|
+
span.set_data(Span::DataConventions::SERVER_PORT, db_config[:port]) if db_config[:port]
|
63
|
+
span.set_data(Span::DataConventions::SERVER_SOCKET_ADDRESS, db_config[:socket]) if db_config[:socket]
|
64
|
+
|
65
|
+
next unless record_query_source
|
66
|
+
|
67
|
+
# both duration and query_source_threshold are in ms
|
68
|
+
next unless duration >= query_source_threshold
|
37
69
|
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
70
|
+
source_location = query_source_location
|
71
|
+
|
72
|
+
if source_location
|
73
|
+
backtrace_line = Sentry::Backtrace::Line.parse(source_location)
|
74
|
+
span.set_data(Span::DataConventions::FILEPATH, backtrace_line.file) if backtrace_line.file
|
75
|
+
span.set_data(Span::DataConventions::LINENO, backtrace_line.number) if backtrace_line.number
|
76
|
+
span.set_data(Span::DataConventions::FUNCTION, backtrace_line.method) if backtrace_line.method
|
77
|
+
# Only JRuby has namespace in the backtrace
|
78
|
+
span.set_data(Span::DataConventions::NAMESPACE, backtrace_line.module_name) if backtrace_line.module_name
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
# Thread.each_caller_location is an API added in Ruby 3.2 that doesn't always collect the entire stack like
|
85
|
+
# Kernel#caller or #caller_locations do. See https://github.com/rails/rails/pull/49095 for more context.
|
86
|
+
if SUPPORT_SOURCE_LOCATION && Thread.respond_to?(:each_caller_location)
|
87
|
+
def query_source_location
|
88
|
+
Thread.each_caller_location do |location|
|
89
|
+
frame = backtrace_cleaner.clean_frame(location)
|
90
|
+
return frame if frame
|
91
|
+
end
|
92
|
+
nil
|
93
|
+
end
|
94
|
+
else
|
95
|
+
# Since Sentry is mostly used in production, we don't want to fallback to the slower implementation
|
96
|
+
# and adds potentially big overhead to the application.
|
97
|
+
def query_source_location
|
98
|
+
nil
|
43
99
|
end
|
44
100
|
end
|
45
101
|
end
|
@@ -19,9 +19,17 @@ module Sentry
|
|
19
19
|
analyze.active_storage
|
20
20
|
].freeze
|
21
21
|
|
22
|
+
SPAN_ORIGIN = "auto.file.rails".freeze
|
23
|
+
|
22
24
|
def self.subscribe!
|
23
25
|
subscribe_to_event(EVENT_NAMES) do |event_name, duration, payload|
|
24
|
-
record_on_current_span(
|
26
|
+
record_on_current_span(
|
27
|
+
op: "file.#{event_name}".freeze,
|
28
|
+
origin: SPAN_ORIGIN,
|
29
|
+
start_timestamp: payload[START_TIMESTAMP_NAME],
|
30
|
+
description: payload[:service],
|
31
|
+
duration: duration
|
32
|
+
) do |span|
|
25
33
|
payload.each do |key, value|
|
26
34
|
span.set_data(key, value) unless key == START_TIMESTAMP_NAME
|
27
35
|
end
|
data/lib/sentry/rails/version.rb
CHANGED
data/sentry-rails.gemspec
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: sentry-rails
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 5.
|
4
|
+
version: 5.18.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Sentry Team
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2024-
|
11
|
+
date: 2024-07-02 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: railties
|
@@ -30,14 +30,14 @@ dependencies:
|
|
30
30
|
requirements:
|
31
31
|
- - "~>"
|
32
32
|
- !ruby/object:Gem::Version
|
33
|
-
version: 5.
|
33
|
+
version: 5.18.1
|
34
34
|
type: :runtime
|
35
35
|
prerelease: false
|
36
36
|
version_requirements: !ruby/object:Gem::Requirement
|
37
37
|
requirements:
|
38
38
|
- - "~>"
|
39
39
|
- !ruby/object:Gem::Version
|
40
|
-
version: 5.
|
40
|
+
version: 5.18.1
|
41
41
|
description: A gem that provides Rails integration for the Sentry error logger
|
42
42
|
email: accounts@sentry.io
|
43
43
|
executables: []
|
@@ -57,6 +57,7 @@ files:
|
|
57
57
|
- app/jobs/sentry/send_event_job.rb
|
58
58
|
- bin/console
|
59
59
|
- bin/setup
|
60
|
+
- lib/generators/sentry_generator.rb
|
60
61
|
- lib/sentry-rails.rb
|
61
62
|
- lib/sentry/rails.rb
|
62
63
|
- lib/sentry/rails/action_cable.rb
|
@@ -105,7 +106,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
105
106
|
- !ruby/object:Gem::Version
|
106
107
|
version: '0'
|
107
108
|
requirements: []
|
108
|
-
rubygems_version: 3.
|
109
|
+
rubygems_version: 3.5.11
|
109
110
|
signing_key:
|
110
111
|
specification_version: 4
|
111
112
|
summary: A gem that provides Rails integration for the Sentry error logger
|