sentry-rails 5.17.3 → 5.18.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: d004cdd6b80d76424d8684494a6a0565b5f5440ada51a6878280baa0a0c174e7
4
- data.tar.gz: 747ed1691526ff296e90d697ab61bf6f6abfe5c63687904f7a1a12afa9a833d6
3
+ metadata.gz: 0eadc69b5a0f19fb1fbde82ca580c5385990637ab2080a21c81c4a03ffeb81a5
4
+ data.tar.gz: 44bf3e89893bde541f38ad15974d6e91db4cf153ad9e37f167ee54b7c32bd00e
5
5
  SHA512:
6
- metadata.gz: dec86deec8fd26222ddc97c7568d471bdea6c75caaae49b4793f37e5235d81537b026e336136ad3b734101b15f16ebeeeb546d99c6255a4a93ed305ef758be74
7
- data.tar.gz: b0ff142977ad7ece0e73a40cd3f434a2bb967efb6cc2040e5feb9448cf3963acfa535efb14d095efaf2f235923c3780f2f1a532cb9287344774e33b955618263
6
+ metadata.gz: b1924626717ccb8103871a7f24bf77c36db5e98c232a3a60cf3b0bab69d6a27f0bce3bf97bd326bdadddd3172354d8616c35c5e0087a08e0a44b6e0c7f7f706f
7
+ data.tar.gz: 41fc7b2f3573624f821807ff0409b2138a1d249ef8c5fa7e6a271b5225ba20da61c7912933665554c72ffd3ea80356d57534f17ce9ad3f7a7bb1050ceb5a5da4
@@ -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 = { name: scope.transaction_name, source: scope.transaction_source, op: OP_NAME }
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(name: scope.transaction_name, source: scope.transaction_source, op: OP_NAME)
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 = { name: scope.transaction_name, source: scope.transaction_source, op: transaction_op }
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(op: SPAN_PREFIX + event_name, start_timestamp: payload[START_TIMESTAMP_NAME], description: payload[:identifier], duration: duration)
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
@@ -6,40 +6,98 @@ module Sentry
6
6
  class ActiveRecordSubscriber < AbstractSubscriber
7
7
  EVENT_NAMES = ["sql.active_record"].freeze
8
8
  SPAN_PREFIX = "db.".freeze
9
+ SPAN_ORIGIN = "auto.db.rails".freeze
9
10
  EXCLUDED_EVENTS = ["SCHEMA", "TRANSACTION"].freeze
10
11
 
11
- def self.subscribe!
12
- subscribe_to_event(EVENT_NAMES) do |event_name, duration, payload|
13
- next if EXCLUDED_EVENTS.include? payload[:name]
12
+ SUPPORT_SOURCE_LOCATION = ActiveSupport::BacktraceCleaner.method_defined?(:clean_frame)
14
13
 
15
- record_on_current_span(op: SPAN_PREFIX + event_name, start_timestamp: payload[START_TIMESTAMP_NAME], description: payload[:sql], duration: duration) do |span|
16
- span.set_tag(:cached, true) if payload.fetch(:cached, false) # cached key is only set for hits in the QueryCache, from Rails 5.1
14
+ if SUPPORT_SOURCE_LOCATION
15
+ # Need to be specific down to the lib path so queries generated in specs don't get ignored
16
+ SENTRY_RUBY_PATH = File.join(Gem::Specification.find_by_name("sentry-ruby").full_gem_path, "lib")
17
+ SENTRY_RAILS_PATH = File.join(Gem::Specification.find_by_name("sentry-rails").full_gem_path, "lib")
17
18
 
18
- connection = payload[:connection]
19
+ class_attribute :backtrace_cleaner, default: (ActiveSupport::BacktraceCleaner.new.tap do |cleaner|
20
+ cleaner.add_silencer { |line| line.include?(SENTRY_RUBY_PATH) || line.include?(SENTRY_RAILS_PATH) }
21
+ end)
22
+ end
19
23
 
20
- if payload[:connection_id]
21
- span.set_data(:connection_id, payload[:connection_id])
24
+ class << self
25
+ def subscribe!
26
+ record_query_source = SUPPORT_SOURCE_LOCATION && Sentry.configuration.rails.enable_db_query_source
27
+ query_source_threshold = Sentry.configuration.rails.db_query_source_threshold_ms
22
28
 
23
- # we fallback to the base connection on rails < 6.0.0 since the payload doesn't have it
24
- connection ||= ActiveRecord::Base.connection_pool.connections.find { |conn| conn.object_id == payload[:connection_id] }
25
- end
29
+ subscribe_to_event(EVENT_NAMES) do |event_name, duration, payload|
30
+ next if EXCLUDED_EVENTS.include? payload[:name]
31
+
32
+ record_on_current_span(
33
+ op: SPAN_PREFIX + event_name,
34
+ origin: SPAN_ORIGIN,
35
+ start_timestamp: payload[START_TIMESTAMP_NAME],
36
+ description: payload[:sql],
37
+ duration: duration
38
+ ) do |span|
39
+ 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
40
 
27
- next unless connection
41
+ connection = payload[:connection]
28
42
 
29
- db_config =
30
- if connection.pool.respond_to?(:db_config)
31
- connection.pool.db_config.configuration_hash
32
- elsif connection.pool.respond_to?(:spec)
33
- connection.pool.spec.config
43
+ if payload[:connection_id]
44
+ span.set_data(:connection_id, payload[:connection_id])
45
+
46
+ # we fallback to the base connection on rails < 6.0.0 since the payload doesn't have it
47
+ connection ||= ActiveRecord::Base.connection_pool.connections.find { |conn| conn.object_id == payload[:connection_id] }
34
48
  end
35
49
 
36
- next unless db_config
50
+ next unless connection
51
+
52
+ db_config =
53
+ if connection.pool.respond_to?(:db_config)
54
+ connection.pool.db_config.configuration_hash
55
+ elsif connection.pool.respond_to?(:spec)
56
+ connection.pool.spec.config
57
+ end
58
+
59
+ next unless db_config
60
+
61
+ span.set_data(Span::DataConventions::DB_SYSTEM, db_config[:adapter]) if db_config[:adapter]
62
+ span.set_data(Span::DataConventions::DB_NAME, db_config[:database]) if db_config[:database]
63
+ span.set_data(Span::DataConventions::SERVER_ADDRESS, db_config[:host]) if db_config[:host]
64
+ span.set_data(Span::DataConventions::SERVER_PORT, db_config[:port]) if db_config[:port]
65
+ span.set_data(Span::DataConventions::SERVER_SOCKET_ADDRESS, db_config[:socket]) if db_config[:socket]
66
+
67
+ next unless record_query_source
68
+
69
+ # both duration and query_source_threshold are in ms
70
+ next unless duration >= query_source_threshold
37
71
 
38
- span.set_data(Span::DataConventions::DB_SYSTEM, db_config[:adapter]) if db_config[:adapter]
39
- span.set_data(Span::DataConventions::DB_NAME, db_config[:database]) if db_config[:database]
40
- span.set_data(Span::DataConventions::SERVER_ADDRESS, db_config[:host]) if db_config[:host]
41
- span.set_data(Span::DataConventions::SERVER_PORT, db_config[:port]) if db_config[:port]
42
- span.set_data(Span::DataConventions::SERVER_SOCKET_ADDRESS, db_config[:socket]) if db_config[:socket]
72
+ source_location = query_source_location
73
+
74
+ if source_location
75
+ backtrace_line = Sentry::Backtrace::Line.parse(source_location)
76
+ span.set_data(Span::DataConventions::FILEPATH, backtrace_line.file) if backtrace_line.file
77
+ span.set_data(Span::DataConventions::LINENO, backtrace_line.number) if backtrace_line.number
78
+ span.set_data(Span::DataConventions::FUNCTION, backtrace_line.method) if backtrace_line.method
79
+ # Only JRuby has namespace in the backtrace
80
+ span.set_data(Span::DataConventions::NAMESPACE, backtrace_line.module_name) if backtrace_line.module_name
81
+ end
82
+ end
83
+ end
84
+ end
85
+
86
+ # Thread.each_caller_location is an API added in Ruby 3.2 that doesn't always collect the entire stack like
87
+ # Kernel#caller or #caller_locations do. See https://github.com/rails/rails/pull/49095 for more context.
88
+ if SUPPORT_SOURCE_LOCATION && Thread.respond_to?(:each_caller_location)
89
+ def query_source_location
90
+ Thread.each_caller_location do |location|
91
+ frame = backtrace_cleaner.clean_frame(location)
92
+ return frame if frame
93
+ end
94
+ nil
95
+ end
96
+ else
97
+ # Since Sentry is mostly used in production, we don't want to fallback to the slower implementation
98
+ # and adds potentially big overhead to the application.
99
+ def query_source_location
100
+ nil
43
101
  end
44
102
  end
45
103
  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(op: "file.#{event_name}".freeze, start_timestamp: payload[START_TIMESTAMP_NAME], description: payload[:service], duration: duration) do |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
@@ -1,5 +1,5 @@
1
1
  module Sentry
2
2
  module Rails
3
- VERSION = "5.17.3"
3
+ VERSION = "5.18.0"
4
4
  end
5
5
  end
data/sentry-rails.gemspec CHANGED
@@ -23,5 +23,5 @@ Gem::Specification.new do |spec|
23
23
  spec.require_paths = ["lib"]
24
24
 
25
25
  spec.add_dependency "railties", ">= 5.0"
26
- spec.add_dependency "sentry-ruby", "~> 5.17.3"
26
+ spec.add_dependency "sentry-ruby", "~> 5.18.0"
27
27
  end
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.17.3
4
+ version: 5.18.0
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-04-11 00:00:00.000000000 Z
11
+ date: 2024-06-25 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.17.3
33
+ version: 5.18.0
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.17.3
40
+ version: 5.18.0
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