在具有到不同数据库的多个连接的ActiveRecord应用程序中,日志中没有任何内容指示哪个查询进入哪个数据库。这些查询分离数据库:
Base1.connection.select_value("select * from foo")
Base2.connection.select_value("select * from foo")
发出这些日志条目:
D, [2017-03-13T09:27:11.844395 #22112] DEBUG -- : (0.6ms) select * from foo
D, [2017-03-13T09:27:11.844539 #22112] DEBUG -- : (0.1ms) select * from foo
如何使ActiveRecord数据库日志指示正在对哪个数据库执行查询?
begin
require "bundler/inline"
rescue LoadError => e
$stderr.puts "Bundler version 1.10 or later is required."
raise e
end
gemfile(true) do
source "https://rubygems.org"
# Activate the gem you are reporting the issue against.
gem "activerecord", "4.2.8"
gem "sqlite3"
end
require "active_record"
require "logger"
class Base1 < ActiveRecord::Base
self.abstract_class = true
end
class Base2 < ActiveRecord::Base
self.abstract_class = true
end
Base1.establish_connection(adapter: "sqlite3", database: ":memory:")
Base2.establish_connection(adapter: "sqlite3", database: ":memory:")
ActiveRecord::Base.logger = Logger.new(STDOUT)
Base1.connection.execute("create table foo(i int)")
Base2.connection.execute("create table foo(i int)")
Base1.connection.execute("insert into foo(i) values (1)")
Base2.connection.execute("insert into foo(i) values (2)")
raise unless Base1.connection.select_value("select * from foo") == 1
raise unless Base2.connection.select_value("select * from foo") == 2
输出:
D, [2017-03-13T09:27:11.842939 #22112] DEBUG -- : (0.2ms) create table foo(i int)
D, [2017-03-13T09:27:11.843478 #22112] DEBUG -- : (0.2ms) create table foo(i int)
D, [2017-03-13T09:27:11.843612 #22112] DEBUG -- : (0.1ms) insert into foo(i) values (1)
D, [2017-03-13T09:27:11.843720 #22112] DEBUG -- : (0.0ms) insert into foo(i) values (2)
D, [2017-03-13T09:27:11.844395 #22112] DEBUG -- : (0.6ms) select * from foo
D, [2017-03-13T09:27:11.844539 #22112] DEBUG -- : (0.1ms) select * from foo
我尝试为每个连接提供自己的记录器,以便我可以更改每个日志的格式:
Base1.logger = Logger.new(STDOUT)
Base2.logger = Logger.new(STDOUT)
但是,不幸的是,ActiveRecord中似乎只有一个记录器,如此行所示,它不会引发异常:
raise unless Base1.logger.object_id == Base2.logger.object_id
(1)此问题并非特定于任何特定数据库适配器。我已经列出了适配器版本的完整性。
答案 0 :(得分:5)
据我所知,你无法在本机上使用ActiveRecord。但是,如果您真的需要,可以在log
中覆盖AbstractAdapter
方法:
class ActiveRecord::ConnectionAdapters::AbstractAdapter
alias :original_log :log
def log(sql, name = "SQL", binds = [], statement_name = nil)
#add info that you want to display to name
name = "#{name} #{@connection.hash}"
original_log(sql, name, binds, statement_name) { yield }
end
end
输出:
D, [2017-03-15T20:55:59.200533 #73440] DEBUG -- : -4111614587995646180 (0.5ms) create table foo(i int)
D, [2017-03-15T20:55:59.201178 #73440] DEBUG -- : -4097137311320758185 (0.1ms) create table foo(i int)
D, [2017-03-15T20:55:59.201298 #73440] DEBUG -- : -4111614587995646180 (0.0ms) insert into foo(i) values (1)
D, [2017-03-15T20:55:59.201426 #73440] DEBUG -- : -4097137311320758185 (0.1ms) insert into foo(i) values (2)
D, [2017-03-15T20:55:59.202229 #73440] DEBUG -- : -4111614587995646180 (0.7ms) select * from foo
D, [2017-03-15T20:55:59.202360 #73440] DEBUG -- : -4097137311320758185 (0.0ms) select * from foo
上面的代码记录了连接哈希值,这足以告诉另一个连接。如果你想记录比这更人性化的东西,你需要有点棘手。
我们可以做的是使用一种方法来装饰抽象适配器,以返回一个人性化的连接名称。在程序初始化期间,将#log_name方法添加到每个连接适配器:
Base1.connection.define_singleton_method(:log_name) { "one" }
Base2.connection.define_singleton_method(:log_name) { "two" }
猴子补丁现在可以使用#log_name方法:
class ActiveRecord::ConnectionAdapters::AbstractAdapter
alias :original_log :log
def log(sql, name = "SQL", binds = [], statement_name = nil)
connection_name = respond_to?(:log_name) ? log_name : nil
name = [connection_name, name].compact.join(" ")
original_log(sql, name, binds, statement_name) { yield }
end
end
输出:
D, [2017-03-21T10:10:53.330021 #22147] DEBUG -- : one (0.3ms) create table foo(i int)
D, [2017-03-21T10:10:53.330380 #22147] DEBUG -- : two (0.2ms) create table foo(i int)
D, [2017-03-21T10:10:53.330464 #22147] DEBUG -- : one (0.0ms) insert into foo(i) values (1)
D, [2017-03-21T10:10:53.330536 #22147] DEBUG -- : two (0.0ms) insert into foo(i) values (2)
D, [2017-03-21T10:10:53.331002 #22147] DEBUG -- : one (0.4ms) select * from foo
D, [2017-03-21T10:10:53.331104 #22147] DEBUG -- : two (0.0ms) select * from foo
答案 1 :(得分:2)
@idej答案很好,但我还要添加另一种方法。如果您也使用ActiveSupport,则可以订阅sql.active_record
事件。
ActiveSupport::Notifications.subscribe("sql.active_record") do |*args|
event = ActiveSupport::Notifications::Event.new(*args)
puts event.inspect
end
在event.payload
中,您可以访问一些有用的属性,例如sql
,connection_id
,binds
等。问题是您需要自己记录这些属性并留言任何SQL查询都会在日志中出现两次。但是你不必修改任何类。
您可以在docs中找到更多详细信息。
答案 2 :(得分:1)
你必须(猴子)补丁ActiveRecord::ConnectionAdapters::AbstractAdapter
你可以在新的初始化程序中执行它:
# config/initializers/adapter_monkeypatch.rb
class ActiveRecord::ConnectionAdapters::AbstractAdapter
alias :original_log :log
def log(sql, *args, &block)
prefix = self.class.to_s.demodulize
original_log("#{prefix}: #{sql}", *args, &block)
end
end
当您使用不同的数据库时,您可以使用适配器的class_name作为前缀: