Skip to content

Commit bcc0fe7

Browse files
authored
fix(logging): improve error handling in AR subscriber (#2798)
* fix(logging): rescue from unexpected errors when getting binds * fix(logging): don't crash if getting db connection info failed * Update CHANGELOG * Just rescue from any error * Fix spec and remove redundant resques
1 parent 6081a29 commit bcc0fe7

File tree

4 files changed

+58
-15
lines changed

4 files changed

+58
-15
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
### Fixes
88

99
- Prevent logging from crashing main thread ([2795](https://github.com/getsentry/sentry-ruby/pull/2795))
10+
- Improve error handling in ActiveRecord subscriber ([2798](https://github.com/getsentry/sentry-ruby/pull/2798))
1011

1112
## 6.1.2
1213

sentry-rails/lib/sentry/rails/log_subscriber.rb

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
# frozen_string_literal: true
22

33
require "active_support/log_subscriber"
4+
require "sentry/utils/logging_helper"
45

56
module Sentry
67
module Rails
@@ -27,6 +28,8 @@ module Rails
2728
# end
2829
# end
2930
class LogSubscriber < ActiveSupport::LogSubscriber
31+
include Sentry::LoggingHelper
32+
3033
ORIGIN = "auto.log.rails.log_subscriber"
3134

3235
class << self

sentry-rails/lib/sentry/rails/log_subscribers/active_record_subscriber.rb

Lines changed: 18 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ class ActiveRecordSubscriber < Sentry::Rails::LogSubscriber
2424
include ParameterFilter
2525

2626
EXCLUDED_NAMES = ["SCHEMA", "TRANSACTION"].freeze
27+
EMPTY_ARRAY = [].freeze
2728

2829
# Handle sql.active_record events
2930
#
@@ -40,8 +41,6 @@ def sql(event)
4041
cached = event.payload.fetch(:cached, false)
4142
connection_id = event.payload[:connection_id]
4243

43-
db_config = extract_db_config(event.payload)
44-
4544
attributes = {
4645
sql: sql,
4746
duration_ms: duration_ms(event),
@@ -53,18 +52,18 @@ def sql(event)
5352
if Sentry.configuration.send_default_pii && (binds && !binds.empty?)
5453
type_casted_binds = type_casted_binds(event)
5554

56-
binds.each_with_index do |bind, index|
57-
key = bind.respond_to?(:name) ? bind.name : index.to_s
58-
value = type_casted_binds[index].to_s
55+
type_casted_binds.each_with_index do |value, index|
56+
bind = binds[index]
57+
name = bind.respond_to?(:name) ? bind.name : index.to_s
5958

60-
attributes["db.query.parameter.#{key}"] = value
59+
attributes["db.query.parameter.#{name}"] = value.to_s
6160
end
6261
end
6362

6463
attributes[:statement_name] = statement_name if statement_name && statement_name != "SQL"
6564
attributes[:connection_id] = connection_id if connection_id
6665

67-
add_db_config_attributes(attributes, db_config)
66+
maybe_add_db_config_attributes(attributes, event.payload)
6867

6968
message = build_log_message(statement_name)
7069

@@ -73,6 +72,8 @@ def sql(event)
7372
level: :info,
7473
attributes: attributes
7574
)
75+
rescue => e
76+
log_debug("[#{self.class}] failed to log sql event: #{e.message}")
7677
end
7778

7879
def type_casted_binds(event)
@@ -97,15 +98,9 @@ def build_log_message(statement_name)
9798
end
9899
end
99100

100-
def extract_db_config(payload)
101-
connection = payload[:connection]
102-
103-
return unless connection
104-
105-
extract_db_config_from_connection(connection)
106-
end
101+
def maybe_add_db_config_attributes(attributes, payload)
102+
db_config = extract_db_config(payload)
107103

108-
def add_db_config_attributes(attributes, db_config)
109104
return unless db_config
110105

111106
attributes[:db_system] = db_config[:adapter] if db_config[:adapter]
@@ -125,6 +120,14 @@ def add_db_config_attributes(attributes, db_config)
125120
attributes[:server_socket_address] = db_config[:socket] if db_config[:socket]
126121
end
127122

123+
def extract_db_config(payload)
124+
connection = payload[:connection]
125+
126+
return unless connection
127+
128+
extract_db_config_from_connection(connection)
129+
end
130+
128131
if ::Rails.version.to_f >= 6.1
129132
def extract_db_config_from_connection(connection)
130133
if connection.pool.respond_to?(:db_config)

sentry-rails/spec/sentry/rails/log_subscribers/active_record_subscriber_spec.rb

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -178,6 +178,42 @@
178178
type: "string", value: "test"
179179
)
180180
end
181+
182+
it "does not choke on type_casted_binds errors" do
183+
ActiveSupport::Notifications.instrument("sql.active_record",
184+
sql: "SELECT error",
185+
name: "SQL",
186+
connection: ActiveRecord::Base.connection,
187+
binds: ["foo"],
188+
type_casted_binds: -> { raise StandardError.new("boom") }
189+
)
190+
191+
Sentry.get_current_client.flush
192+
193+
log_event = sentry_logs.find { |log| log[:attributes].dig(:sql, :value) == "SELECT error" }
194+
195+
expect(log_event).to be_nil
196+
end
197+
198+
it "does not choke on retrieving connection info" do
199+
connection = double(ActiveRecord::Base.connection.class)
200+
201+
expect(connection).to receive(:pool).and_raise(StandardError.new("boom"))
202+
203+
ActiveSupport::Notifications.instrument("sql.active_record",
204+
sql: "SELECT error",
205+
name: "SQL",
206+
connection: connection,
207+
binds: [:foo],
208+
type_casted_binds: -> { ["foo"] }
209+
)
210+
211+
Sentry.get_current_client.flush
212+
213+
log_event = sentry_logs.find { |log| log[:attributes].dig(:sql, :value) == "SELECT error" }
214+
215+
expect(log_event).to be_nil
216+
end
181217
end
182218

183219
context "when send_default_pii is disabled" do

0 commit comments

Comments
 (0)