在日志中包含 Active Record 查询的作用
Include the role of Active Record queries in logs
Rails 现在包括对多个数据库角色的支持(by default,writing
用于主数据库,reading
用于副本):
ActiveRecord::Base.connected_to(role: :reading) do
# all code in this block will be connected to the reading role
end
在开发中,默认记录 Active Record 查询,例如:
> User.last
User Load (0.1ms) SELECT "users".* FROM "users" ORDER BY "users"."id" DESC LIMIT ? [["LIMIT", 1]]
如何在日志记录中包含用于查询的角色?例如:
> ActiveRecord::Base.connnected_to(role: :reading) { User.last }
[role: reading] User Load (0.1ms) SELECT "users".* FROM "users" ORDER BY "users"."id" DESC LIMIT ? [["LIMIT", 1]]
因为所有查询都将在最后一步通过方法 log of class AbstractAdapter 记录,无论您使用的是哪个数据库适配器:postgresql,mysql,..所以您可以重写该方法并前置 role
# lib/extent_dblog.rb
ActiveRecord::ConnectionAdapters::AbstractAdapter.class_eval do
alias_method(:origin_log, :log)
def log(sql, name = 'SQL', binds = [],
type_casted_binds = [], statement_name = nil, &block)
# prepend the current role before the log
name = "[role: #{ActiveRecord::Base.current_role}] #{name}"
origin_log(sql, name, binds, type_casted_binds, statement_name, &block)
end
end
# config/initializers/ext_log.rb
require File.join(Rails.root, "lib", "extent_dblog.rb")
演示
# config/application.rb
...
config.active_record.reading_role = :dev
config.active_record.reading_role = :test
# app/models/application_record.rb
class ApplicationRecord < ActiveRecord::Base
self.abstract_class = true
connects_to database: { dev: :development, test: :test }
end
ActiveRecord::Base.connected_to(role: :dev) do
Target.all
end
# [role: dev] (0.6ms) SELECT "targets".* FROM "targets"
使用以下代码创建一个新文件 config/initializers/multidb_logger.rb
:
ActiveRecord::ConnectionAdapters::AbstractAdapter.class_eval do
alias_method(:origin_log, :log)
def log(sql, name = 'SQL', binds = [], type_casted_binds = [], statement_name = nil, &block)
sql = "#{sql} /* #{@config[:replica] ? 'REPLICA' : 'MASTER'} DB */"
origin_log(sql, name, binds, type_casted_binds, statement_name, &block)
end
end
ActiveRecord::LogSubscriber.class_eval do
alias_method(:origin_extract_query_source_location, :extract_query_source_location)
def extract_query_source_location(locations)
new_locations = locations.reject { |loc| loc.include? File.basename(__FILE__) }
origin_extract_query_source_location(new_locations)
end
end
现在,在 ActiveRecord 记录的 SQL 查询中,您将在每个查询的末尾看到“REPLICA DB”或“MASTER DB”。
此解决方案使用与@Lam Phan 解决方案类似的方法,但它保留了所有标准日志记录行为:
- 它不记录 SCHEMA 查询
- 它正确显示触发查询的源文件和行
另请注意,我没有使用 ActiveRecord::Base.current_role
来获取角色,因为它没有显示可靠的信息(即它打印角色 writing
但查询转到副本数据库)。
可以使用 @config
哈希中可用的信息进一步自定义日志,例如 host
、port
、database
等
Rails 现在包括对多个数据库角色的支持(by default,writing
用于主数据库,reading
用于副本):
ActiveRecord::Base.connected_to(role: :reading) do
# all code in this block will be connected to the reading role
end
在开发中,默认记录 Active Record 查询,例如:
> User.last
User Load (0.1ms) SELECT "users".* FROM "users" ORDER BY "users"."id" DESC LIMIT ? [["LIMIT", 1]]
如何在日志记录中包含用于查询的角色?例如:
> ActiveRecord::Base.connnected_to(role: :reading) { User.last }
[role: reading] User Load (0.1ms) SELECT "users".* FROM "users" ORDER BY "users"."id" DESC LIMIT ? [["LIMIT", 1]]
因为所有查询都将在最后一步通过方法 log of class AbstractAdapter 记录,无论您使用的是哪个数据库适配器:postgresql,mysql,..所以您可以重写该方法并前置 role
# lib/extent_dblog.rb
ActiveRecord::ConnectionAdapters::AbstractAdapter.class_eval do
alias_method(:origin_log, :log)
def log(sql, name = 'SQL', binds = [],
type_casted_binds = [], statement_name = nil, &block)
# prepend the current role before the log
name = "[role: #{ActiveRecord::Base.current_role}] #{name}"
origin_log(sql, name, binds, type_casted_binds, statement_name, &block)
end
end
# config/initializers/ext_log.rb
require File.join(Rails.root, "lib", "extent_dblog.rb")
演示
# config/application.rb
...
config.active_record.reading_role = :dev
config.active_record.reading_role = :test
# app/models/application_record.rb
class ApplicationRecord < ActiveRecord::Base
self.abstract_class = true
connects_to database: { dev: :development, test: :test }
end
ActiveRecord::Base.connected_to(role: :dev) do
Target.all
end
# [role: dev] (0.6ms) SELECT "targets".* FROM "targets"
使用以下代码创建一个新文件 config/initializers/multidb_logger.rb
:
ActiveRecord::ConnectionAdapters::AbstractAdapter.class_eval do
alias_method(:origin_log, :log)
def log(sql, name = 'SQL', binds = [], type_casted_binds = [], statement_name = nil, &block)
sql = "#{sql} /* #{@config[:replica] ? 'REPLICA' : 'MASTER'} DB */"
origin_log(sql, name, binds, type_casted_binds, statement_name, &block)
end
end
ActiveRecord::LogSubscriber.class_eval do
alias_method(:origin_extract_query_source_location, :extract_query_source_location)
def extract_query_source_location(locations)
new_locations = locations.reject { |loc| loc.include? File.basename(__FILE__) }
origin_extract_query_source_location(new_locations)
end
end
现在,在 ActiveRecord 记录的 SQL 查询中,您将在每个查询的末尾看到“REPLICA DB”或“MASTER DB”。
此解决方案使用与@Lam Phan 解决方案类似的方法,但它保留了所有标准日志记录行为:
- 它不记录 SCHEMA 查询
- 它正确显示触发查询的源文件和行
另请注意,我没有使用 ActiveRecord::Base.current_role
来获取角色,因为它没有显示可靠的信息(即它打印角色 writing
但查询转到副本数据库)。
可以使用 @config
哈希中可用的信息进一步自定义日志,例如 host
、port
、database
等