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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: d004cdd6b80d76424d8684494a6a0565b5f5440ada51a6878280baa0a0c174e7
4
- data.tar.gz: 747ed1691526ff296e90d697ab61bf6f6abfe5c63687904f7a1a12afa9a833d6
3
+ metadata.gz: f37e46eaedbb7739f9def9819546a7f4b1457c7fefd076bb394104fdf18910c1
4
+ data.tar.gz: f7360c8d456797deef74837c24ee44ae7a5fd875cc602b83382ac345d6498c44
5
5
  SHA512:
6
- metadata.gz: dec86deec8fd26222ddc97c7568d471bdea6c75caaae49b4793f37e5235d81537b026e336136ad3b734101b15f16ebeeeb546d99c6255a4a93ed305ef758be74
7
- data.tar.gz: b0ff142977ad7ece0e73a40cd3f434a2bb967efb6cc2040e5feb9448cf3963acfa535efb14d095efaf2f235923c3780f2f1a532cb9287344774e33b955618263
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
- # 1.7.0 dropped support for ruby < 3.0, remove later after upgrading craft setup
21
- gem "sqlite3", "1.6.9", platform: :ruby
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 = { 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
@@ -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.".freeze
10
+ SPAN_PREFIX = "db."
11
+ SPAN_ORIGIN = "auto.db.rails"
9
12
  EXCLUDED_EVENTS = ["SCHEMA", "TRANSACTION"].freeze
10
13
 
11
- def self.subscribe!
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
- 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
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
- connection = payload[:connection]
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
- if payload[:connection_id]
21
- span.set_data(:connection_id, payload[:connection_id])
27
+ subscribe_to_event(EVENT_NAMES) do |event_name, duration, payload|
28
+ next if EXCLUDED_EVENTS.include? payload[:name]
22
29
 
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
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
- next unless connection
39
+ connection = payload[:connection]
28
40
 
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
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
- next unless db_config
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
- 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]
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(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.1"
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.1"
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.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-04-11 00:00:00.000000000 Z
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.17.3
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.17.3
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.1.6
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