【问题标题】:How to disable ActiveRecord logging for a certain column?如何禁用某个列的 ActiveRecord 日志记录?
【发布时间】:2012-10-14 15:42:28
【问题描述】:

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

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

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

感谢您提供任何相关信息!

【问题讨论】:

  • 有一个选项可以过滤喜欢的密码,不知道是否有帮助。你会考虑覆盖 ActiveRecord 执行 hacky 吗?如果没有,那么有你的提示。

标签: ruby-on-rails activerecord logging upload blob


【解决方案1】:

我也没有找到太多关于这个的东西,虽然你可以做的一件事是

ActiveRecord::Base.logger = nil

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

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

【讨论】:

  • 是的,这是我想到的唯一可能性,但我发现这个解决方案非常丑陋(正如你所提到的),因为你肯定希望记录呼叫而不是这个字段。如果没有“完美”的答案,我可能会继续实现这样一个自定义记录器来解析日志语句......也很丑。
  • 看起来你想重写 Logger 上的“add”方法,子类版本可以做你的解析,然后调用超类的 add 方法,传递解析的消息和其他参数。
【解决方案2】:

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

在您的 application.rb 文件中:

config.filter_parameters << :parameter_name

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

【讨论】:

  • 这不只是过滤HTTP请求参数而不是SQL日志中的参数吗?
  • 不,它会过滤 Rails 日志中的所有内容。传入请求的参数哈希和记录的 SQL 语句都将显示 [FILTERED]
  • 谢谢!我会尝试并尽快报告。
  • 这不会隐藏活动记录日志中的内容。使用 Rails 4 测试。请参阅@tovodeverett 解决方案。
  • 我不认为这在 Rails 3.2 中有效,但很可能我只是愚蠢。
【解决方案3】:

在 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

这将在输出日志中将所有长度超过 20 个字符的字段合并。

【讨论】:

  • 还应注意,这只会截断插入请求。在更新的情况下,所有内容都被放入 sql 参数中,因此也需要被截断。
  • Rails 3.0.9 中 'log_without_trunkate' 调用中的参数数量错误(3 对 2)。
  • 3.0.x rails 的 log 方法使用的 log 方法只接受两个参数 'sql' 和 'name'。所以从参数中删除绑定和块,然后从 sql 参数中过滤掉。
  • Rails 4.1.4 中“log_without_trunkate”调用中的参数数量错误(1..3 为 4 个)
  • @Colin 请参阅下面针对 rails 4.1 的 dbortz 解决方案
【解决方案4】:

我遇到了同样的问题,但我想不出一个干净的解决方案。我最终为过滤掉 blob 的 Rails 记录器写了a custom formatter

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

【讨论】:

    【解决方案5】:

    这是@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 中只有一个二进制字段。

    【讨论】:

    • 恕我直言。这是这个问题的最佳解决方案。
    • 这是 mysql 的正则表达式。您想将其添加到您的解决方案中吗? /(?
    【解决方案6】:

    如果这对任何人都有帮助,这里是上述 sn-p 的 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
    

    【讨论】:

    • 这绝对适用于 Rails 4.2,但不适用于 5.0+。据我所知,Rails 5 也不需要它,因为 Rails 似乎内置了类似的功能。
    • @Ritchie 内置功能是什么?
    【解决方案7】:

    这是一个 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
    

    【讨论】:

    • Rails 5.2 已更改,它需要 2 个参数。但是根据文档,render_bind 方法似乎完全消失了......
    【解决方案8】:

    在 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_datavaluetimeline

    【讨论】:

      【解决方案9】:

      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
      

      【讨论】:

        【解决方案10】:

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

        【讨论】:

          猜你喜欢
          • 2012-07-07
          • 1970-01-01
          • 1970-01-01
          • 2012-05-28
          • 1970-01-01
          • 2018-12-24
          • 2020-01-17
          • 2016-07-04
          • 2021-06-27
          相关资源
          最近更新 更多