Как отключить ведение журнала ActiveRecord для определенного столбца?
У меня возникла проблема, которая, на мой взгляд, должна быть проблемой для большинства пользователей rails, но я пока не нашел для нее никакого решения.
Когда, например, выполняя загрузку файла потенциально большого двоичного файла и сохраняя его в базе данных, вы, разумеется, не хотите, чтобы рельсы или ActiveRecord регистрировали это конкретное поле в режиме разработки (файл журнала, stdout), В случае довольно большого файла это приводит к тому, что выполнение запроса прерывается и почти убивает мой терминал.
Есть ли какой-либо надежный и не хаккий способ отключения ведения журнала для определенных полей? Помните, я не говорю об отключении ведения журнала для параметров запроса - это было решено довольно хорошо.
Спасибо за любую информацию об этом!
Ответы
Ответ 1
Если это кому-то помогает, вот версия вышеописанной версии Rails 4.1, которая также включает в себя редактирование не двоичных параметров привязки (например, текстового или json-столбца) и увеличивает регистрацию до 100 char перед редактированием. Спасибо всем за помощь!
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
Ответ 2
Создать файл в config/initializers whitch изменяет 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 символов в журнале вывода.
Ответ 3
ПРИМЕЧАНИЕ: Работает с рельсами 3, но, по-видимому, не 4 (который не был выпущен, когда был дан ответ на этот вопрос)
В файле application.rb:
config.filter_parameters << :parameter_name
Это приведет к удалению этого атрибута из отображения в ваших журналах, заменив его на [FILTERED]
Обычный вариант использования фильтрующих параметров - это, конечно, пароли, но я не вижу причин, по которым он не должен работать с вашим двоичным файлом.
Ответ 4
Здесь реализуется подход, предложенный @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 есть только одно двоичное поле.
Ответ 5
Я тоже не нашел этого, хотя одна вещь, которую вы могли бы сделать, это
ActiveRecord::Base.logger = nil
полностью отключить ведение журнала, хотя вы, вероятно, не захотите этого делать. Лучшее решение может заключаться в том, чтобы установить регистратор ActiveRecord на какой-либо пользовательский подкласс, который не регистрирует сообщения по определенному размеру, или делает что-то более умное, чтобы анализировать отдельные разделы слишком большого сообщения.
Это не кажется идеальным, но это похоже на работоспособное решение, хотя я не рассматривал конкретные детали реализации. Мне было бы очень интересно услышать лучшие решения.
Ответ 6
Я столкнулся с одной и той же проблемой, но я не мог понять, как решить проблему. Я закончил писать пользовательский форматировщик для журнала Rails, который отфильтровывает blob.
Вышеупомянутый код должен быть помещен в config/initializers и заменить file_dat
a на столбец, который вы хотите удалить, и file_name
на столбец, который появляется после в регулярном выражении.
Ответ 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
Ответ 8
В рельсах 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
, value
и timeline
например.