16

我遇到了一个问题,在我看来,这对大多数 Rails 用户来说肯定是个问题,但我还没有找到任何解决方案。

例如,当执行一个可能很大的二进制文件的文件上传并将其存储在数据库中时,您肯定不希望 rails 或 ActiveRecord 在开发模式下记录此特定字段(日志文件,stdout)。如果文件相当大,这会导致查询执行中断并几乎杀死我的终端。

是否有任何可靠且非 hacky 的方法来禁用特定字段的日志记录?请记住,我不是在谈论禁用请求参数的日志记录 - 这已经很好地解决了。

感谢您提供有关这方面的任何信息!

4

10 回答 10

6

如果这对任何人都有帮助,这里是上面代码片段的 Rails 4.1 兼容版本,它还包括对非二进制绑定参数(例如文本或 json 列)的编辑,并在编辑之前将日志记录增加到 100 个字符。在此感谢大家的帮助!

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
于 2014-02-12T15:13:17.020 回答
5

Create a file in config/initializers whitch modifies ActiveRecord::ConnectionAdapters::AbstractAdapter like so:

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

This will trunkate all fields that are longer than 20 chars in the output log.

于 2012-10-31T11:57:31.317 回答
4

注意:适用于 rails 3,但显然不是 4(回答此问题时未发布)

在您的 application.rb 文件中:

config.filter_parameters << :parameter_name

这将从您的日志中删除该属性,将其替换[FILTERED] 为过滤参数的常见用例当然是密码,但我认为它没有理由不适用于您的二进制文件字段。

于 2012-10-24T18:33:26.353 回答
3

这是@Patrik 建议的方法的实现,适用于针对 PostgreSQL 的插入和更新。正则表达式可能需要根据其他数据库的 SQL 格式进行调整。

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

我对此并不满意,但现在已经足够了。它保留二进制字符串的第一个和最后 10 个字节,并指示从中间删除了多少字节/字符。除非编辑的文本比替换的文本长,否则它不会编辑(即,如果没有至少 20 个字符要删除,那么“[REDACTED xx chars]”将比替换的文本长,所以没有意义) . 我没有进行性能测试来确定对编辑块使用贪婪或懒惰重复是否更快。我的本能是懒惰,所以我做了,但贪婪可能会更快,特别是如果 SQL 中只有一个二进制字段。

于 2013-07-25T21:45:13.233 回答
2

在 rails 5 你可以把它放在初始化程序中:

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,例如。valuetimeline

于 2017-05-05T10:18:20.377 回答
1

这是一个 Rails 5 版本。开箱即用的 Rails 5 会截断二进制数据,但不会截断长文本列。

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
于 2017-02-06T19:12:49.507 回答
0

我遇到了同样的问题,但我无法找到一个干净的解决方案。我最终为 Rails 记录器编写了一个自定义格式化程序,用于过滤掉 blob。

上面的代码需要放在 config/initializers 中,并将file_data 替换为要删除file_name的列和正则表达式中出现的列。

于 2013-01-03T06:48:24.823 回答
0

我也没有找到太多关于此的内容,尽管您可以做的一件事是

ActiveRecord::Base.logger = nil

完全禁用日志记录,尽管您可能不想这样做。更好的解决方案可能是将 ActiveRecord 记录器设置为一些自定义子类,该子类不会记录超过一定大小的消息,或者做一些更聪明的事情来解析太大的消息的特定部分。

这似乎并不理想,但它似乎是一个可行的解决方案,尽管我没有查看具体的实现细节。我真的很想听到任何更好的解决方案。

于 2012-10-24T15:19:56.073 回答
0

这对我来说适用于 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为您的列的名称。

于 2020-07-29T22:45:37.420 回答
0

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
于 2018-10-17T22:24:58.133 回答