我遇到了一个问题,我认为这对大多数Rails用户来说肯定是个问题,但我还没有找到任何解决方案。
例如,当上传一个可能很大的二进制文件并将其存储在数据库中时,你肯定不希望Rails或ActiveRecord在开发模式下记录此特定字段(日志文件,标准输出)。在处理相当大的文件时,这会导致查询执行失败,几乎让我的终端崩溃。
有没有可靠且非hacky的方法来禁用特定字段的日志记录?请记住,我不是要禁用请求参数的日志记录 - 这已经得到了很好的解决。
感谢任何相关信息!
我遇到了一个问题,我认为这对大多数Rails用户来说肯定是个问题,但我还没有找到任何解决方案。
例如,当上传一个可能很大的二进制文件并将其存储在数据库中时,你肯定不希望Rails或ActiveRecord在开发模式下记录此特定字段(日志文件,标准输出)。在处理相当大的文件时,这会导致查询执行失败,几乎让我的终端崩溃。
有没有可靠且非hacky的方法来禁用特定字段的日志记录?请记住,我不是要禁用请求参数的日志记录 - 这已经得到了很好的解决。
感谢任何相关信息!
class ActiveRecord::ConnectionAdapters::AbstractAdapter
protected
def log_with_binary_truncate(sql, name="SQL", binds=[], statement_name = nil, &block)
binds = binds.map do |col, data|
if data.is_a?(String) && data.size > 100
data = "#{data[0,10]} [REDACTED #{data.size - 20} bytes] #{data[-10,10]}"
end
[col, data]
end
sql = sql.gsub(/(?<='\\x[0-9a-f]{100})[0-9a-f]{100,}?(?=[0-9a-f]{100}')/) do |match|
"[REDACTED #{match.size} chars]"
end
log_without_binary_truncate(sql, name, binds, statement_name, &block)
end
alias_method_chain :log, :binary_truncate
end
注意: 适用于Rails 3,但显然不适用于4(当回答此问题时尚未发布)
在你的application.rb文件中:
config.filter_parameters << :parameter_name
这将从日志中删除该属性,并用[FILTERED]
替换
过滤参数的常见用例当然是密码,但我认为它也适用于二进制文件字段。
在config/initializers中创建一个文件,修改ActiveRecord::ConnectionAdapters::AbstractAdapter
如下:
class ActiveRecord::ConnectionAdapters::AbstractAdapter
protected
def log_with_trunkate(sql, name="SQL", binds=[], &block)
b = binds.map {|k,v|
v = v.truncate(20) if v.is_a? String and v.size > 20
[k,v]
}
log_without_trunkate(sql, name, b, &block)
end
alias_method_chain :log, :trunkate
end
class ActiveRecord::ConnectionAdapters::AbstractAdapter
protected
def log_with_binary_truncate(sql, name="SQL", binds=[], &block)
binds = binds.map do |col, data|
if col.type == :binary && data.is_a?(String) && data.size > 27
data = "#{data[0,10]}[REDACTED #{data.size - 20} bytes]#{data[-10,10]}"
end
[col, data]
end
sql = sql.gsub(/(?<='\\x[0-9a-f]{20})[0-9a-f]{20,}?(?=[0-9a-f]{20}')/) do |match|
"[REDACTED #{match.size} chars]"
end
log_without_binary_truncate(sql, name, binds, &block)
end
alias_method_chain :log, :binary_truncate
end
module SqlLogFilter
FILTERS = Set.new(%w(geo_data value timeline))
def render_bind(attribute)
return [attribute.name, '<filtered>'] if FILTERS.include?(attribute.name)
super
end
end
ActiveRecord::LogSubscriber.prepend SqlLogFilter
例如,对于筛选器属性geo_data
、value
和timeline
。
module LogTruncater
def render_bind(attribute)
num_chars = Integer(ENV['ACTIVERECORD_SQL_LOG_MAX_VALUE']) rescue 120
half_num_chars = num_chars / 2
value = if attribute.type.binary? && attribute.value
if attribute.value.is_a?(Hash)
"<#{attribute.value_for_database.to_s.bytesize} bytes of binary data>"
else
"<#{attribute.value.bytesize} bytes of binary data>"
end
else
attribute.value_for_database
end
if value.is_a?(String) && value.size > num_chars
value = "#{value[0,half_num_chars]} [REDACTED #{value.size - num_chars} chars] #{value[-half_num_chars,half_num_chars]}"
end
[attribute.name, value]
end
end
class ActiveRecord::LogSubscriber
prepend LogTruncater
end
我也没找到太多相关信息,但你可以尝试做一件事情:
ActiveRecord::Base.logger = nil
禁用日志可能不是最好的解决方案,但你可以设置ActiveRecord日志记录器为一些自定义子类,使其不记录超过特定大小的消息或执行一些更智能的操作,以解析出消息中的特定部分。
虽然这似乎不是理想的解决方案,但它看起来是可行的,尽管我还没有研究具体的实现细节。我很想听听是否有更好的解决方案。
适用于Rails 5.2及以上版本
module LogTruncater
def render_bind(attr, value)
num_chars = Integer(ENV['ACTIVERECORD_SQL_LOG_MAX_VALUE']) rescue 120
half_num_chars = num_chars / 2
if attr.is_a?(Array)
attr = attr.first
elsif attr.type.binary? && attr.value
value = "<#{attr.value_for_database.to_s.bytesize} bytes of binary data>"
end
if value.is_a?(String) && value.size > num_chars
value = "#{value[0,half_num_chars]} [REDACTED #{value.size - num_chars} chars] #{value[-half_num_chars,half_num_chars]}"
end
[attr && attr.name, value]
end
end
class ActiveRecord::LogSubscriber
prepend LogTruncater
end
file_dat
替换为您想要删除的列,将 file_name
替换为正则表达式中出现在其后的列。这是我在Rails 6中使用的方法:
# initializers/scrub_logs.rb
module ActiveSupport
module TaggedLogging
module Formatter # :nodoc:
# Hide PlaygroundTemplate#yaml column from SQL queries because it's huge.
def scrub_yaml_source(input)
input.gsub(/\["yaml", ".*, \["/, '["yaml", "REDACTED"], ["')
end
alias orig_call call
def call(severity, timestamp, progname, msg)
orig_call(severity, timestamp, progname, scrub_yaml_source(msg))
end
end
end
end
将yaml
替换为您的列名。