Скрипт разбора лого...
 
Уведомления
Очистить всё

Скрипт разбора логов съедает 6 ГБ памяти на файле в 2 ГБ

4 Сообщения
3 Пользователи
2 Реакции
606 Просмотры
Василий1992
Сообщения: 7
Автор темы
Дружит с bundler data engineer
Присоединился: 3 года назад
[#21]

Я в основном на Python, Ruby у нас живёт в мелких скриптах вокруг выгрузок в ClickHouse. Есть лог сервиса в JSON, по строке на событие, файл за сутки около 2 ГБ. Нужно вытащить топ путей с ошибками. На маленьком файле скрипт работает, на суточном машина уходит в своп, в пике видел больше 6 ГБ. Ruby 3.2.

require "json"

lines = File.read(ARGV[0]).split("\n")
events = lines.map { |l| JSON.parse(l) }
errors = events.select { |e| e["level"] == "error" }

by_path = errors.group_by { |e| e["path"] }
by_path.sort_by { |_, list| -list.size }.first(20).each do |path, list|
  puts "#{list.size}\t#{path}"
end

Где я столько держу?


3 ответа
kirill_spb
Сообщения: 27
Собрал свой gem разработчик инструментов
Присоединился: 4 года назад

Ты держишь файл трижды: одной строкой целиком, массивом строк и массивом хэшей. А нужен тебе только счётчик. Читай построчно и считай на ходу:

require "json"

counts = Hash.new(0)

File.foreach(ARGV[0]) do |line|
  next unless line.include?('"level":"error"')

  event = JSON.parse(line)
  counts[event["path"]] += 1 if event["level"] == "error"
end

counts.sort_by { |_, n| -n }.first(20).each do |path, n|
  puts "#{n}\t#{path}"
end

File.foreach отдаёт по строке, и память почти не растёт. Проверку include? я поставил до JSON.parse: парсить каждую строку дорого, а ошибок обычно процент-два. Только сверь со своим форматом, если после двоеточия в логе стоит пробел, подстрока будет другой.


Ответ
TimurK62
Сообщения: 31
Читает чужие PR бэкенд-разработчик
Присоединился: 3 года назад

Прогнал оба варианта на синтетическом файле в 500 МБ, Ruby 3.2.2, мерил через /usr/bin/time -v. Исходный: 1,9 ГБ RSS и 41 секунда. Построчный с include?: 38 МБ и 9 секунд. Построчный без include?: те же 38 МБ, но 33 секунды, почти всё время уходит на JSON.parse. То есть по памяти выигрыш даёт foreach, а по времени фильтр по подстроке.


Ответ
Василий1992
Сообщения: 7
Автор темы
Дружит с bundler data engineer
Присоединился: 3 года назад

Переделал по образцу kirill_spb, пробелов после двоеточия у нас нет, так что фильтр работает. Суточный файл теперь проходит за 40 секунд и держит меньше 100 МБ, раньше я его на ноутбуке дома в Гатчине вообще не мог дождаться. Обидно, что на Python я бы написал так же неправильно: дело было не в языке, а в File.read.


Ответ