обрезать большой файл журнала

Я выполняю тесты производительности для нескольких java-приложений. При тестировании приложения производят очень большие файлы журнала (это может быть 7-10 ГБ). Мне нужно обрезать эти файлы журналов между определенными датами и временем. В настоящее время я использую скрипт python, который анализирует временные метки журнала в объекте python datetime и печатает только согласованные строки. Но это решение очень медленно. Журнал 5 ГБ анализируется около 25 минут. Очевидно, записи в файле журнала последовательно, и мне не нужно читать все файлы по строкам. Я думал о чтении файла с самого начала и с конца, пока условие не будет согласовано и не будет печатать файлы между количеством строк. Но я не знаю, как я могу читать файл с обратной стороны, не загружая его в память.

Пожалуйста, вы можете предложить мне какое-нибудь решение для этого случая.

вот часть скрипта python:

lfmt = '%Y-%m-%d %H:%M:%S' file = open(filename, 'rU') normal_line = '' for line in file: if line[0] == '[': ltimestamp = datetime.strptime(line[1:20], lfmt) if ltimestamp >= str and ltimestamp <= end: normal_line = 'True' else: normal_line = '' if normal_line: print line, 

4 Solutions collect form web for “обрезать большой файл журнала”

Поскольку данные являются последовательными, если начало и конец интересующей области находятся рядом с началом файла, чтение из конца файла (чтобы найти подходящую конечную точку) по-прежнему является плохим решением!

Я написал код, который быстро найдет начальную и конечную точки по вашему требованию, этот подход называется бинарным поиском и похож на игру с угадыванием «выше или ниже» в класическом детстве!

Сценарий читает пробную строку на середине между lower_bounds и upper_bounds (первоначально SOF и EOF) и проверяет критерии соответствия. Если искомая строка раньше, то она снова угадывает, читая строку на полпути между lower_bound и предыдущим чтением (если она выше, то она делится между ее догадкой и верхней границей). Таким образом, вы продолжаете итерацию между верхней и нижней границами – это дает максимально возможное «среднее» решение.

Это должно быть реальным быстрым решением (войдите в базу 2 из числа строк!). Например, в худшем возможном случае (вывод строки 999 из 1000 строк), используя двоичный поиск, потребуется всего 9 строк. (из миллиарда строк потребуется всего 30 …)

Предположения для приведенного ниже кода:

  • Каждая строка начинается с информации о времени.
  • Время уникальное. Если нет, то при совпадении вам нужно будет проверить назад или вперед, чтобы включить или исключить все записи с подходящим временем (при необходимости).
  • Потрясающе это рекурсивная функция, поэтому количество строк вашего файла ограничено 2 ** 1000 (к счастью, это позволяет довольно большой файл …)

В дальнейшем:

  • Это может быть адаптировано для чтения в произвольных блоках, а не по линии, если это необходимо. Как предложил Дж. Ф. Себастьян.
  • В моем первоначальном ответе я предложил этот подход, но используя linecache.getline , хотя это возможно для его неприемлемых для больших файлов, поскольку он считывает весь файл в память (таким образом, file.seek() превосходит), благодаря TerryE и JF Sebastian для указания, что вне.

import datetime

 def match(line): lfmt = '%Y-%m-%d %H:%M:%S' if line[0] == '[': return datetime.datetime.strptime(line[1:20], lfmt) def retrieve_test_line(position): file.seek(position,0) file.readline() # avoids reading partial line, which will mess up match attempt new_position = file.tell() # gets start of line position return file.readline(), new_position def check_lower_bound(position): file.seek(position,0) new_position = file.tell() # gets start of line position return file.readline(), new_position def find_line(target, lower_bound, upper_bound): trial = int((lower_bound + upper_bound) /2) inspection_text, position = retrieve_test_line(trial) if position == upper_bound: text, position = check_lower_bound(lower_bound) if match(text) == target: return position return # no match for target within range matched_position = match(inspection_text) if matched_position == target: return position elif matched_position < target: return find_line(target, position, upper_bound) elif matched_position > target: return find_line(target, lower_bound, position) else: return # no match for target within range lfmt = '%Y-%m-%d %H:%M:%S' # start_target = # first line you are trying to find: start_target = datetime.datetime.strptime("2012-02-01 13:10:00", lfmt) # end_target = # last line you are trying to find: end_target = datetime.datetime.strptime("2012-02-01 13:39:00", lfmt) file = open("log_file.txt","r") lower_bound = 0 file.seek(0,2) # find upper bound upper_bound = file.tell() sequence_start = find_line(start_target, lower_bound, upper_bound) if sequence_start or sequence_start == 0: #allow for starting at zero - corner case sequence_end = find_line(end_target, sequence_start, upper_bound) if not sequence_end: print "start_target match: ", sequence_start print "end match is not present in the current file" else: print "start match is not present in the current file" if (sequence_start or sequence_start == 0) and sequence_end: print "start_target match: ", sequence_start print "end_target match: ", sequence_end print print start_target, 'target' file.seek(sequence_start,0) print file.readline() print end_target, 'target' file.seek(sequence_end,0) print file.readline() 

Журнал 5 ГБ анализируется около 25 минут

Это ~ 3 МБ / с. Даже последовательное сканирование O(n) в Python может значительно улучшить (~ 500 МБ / с для wc-l.py ), т. wc-l.py Производительность должна ограничиваться только ввода- wc-l.py .

Чтобы выполнить двоичный поиск в файле, вы можете адаптировать FileSearcher, который использует фиксированные записи для использования строк вместо этого, используя подход, аналогичный реализации tail -n в Python (это O(n) для сканирования для '\n' ).

Чтобы избежать O(n) (если диапазон дат выбирает только небольшую часть журнала), вы можете использовать приблизительный поиск, который использует большие фиксированные куски и позволяет пропустить некоторые записи из-за того, что они лежат на границе фрагмента, например, используйте немодифицированные FileSearcher с record_size=1MB и пользовательский класс Query :

 class Query(object): def __init__(self, query): self.query = query # eg, '2012-01-01' def __lt__(self, chunk): # assume line starts with a date; find the start of line i = chunk.find('\n') # assert '\n' in chunk and len(chunk) > (len(self.query) + i) # eg, '2012-01-01' < '2012-03-01' return self.query < chunk[i+1:i+1+len(self.query)] 

Чтобы принять во внимание, что диапазон дат может охватывать несколько фрагментов, вы можете изменить FileSearcher.__getitem__ для возврата (filepos, chunk) и поиска дважды ( bisect_left() , bisect_right() ), чтобы найти примерный filepos_mindate , filepos_maxdate . После этого вы можете выполнить линейный поиск (например, используя подход tail -n ) вокруг заданных позиций файла, чтобы найти точные первые и последние записи журнала.

7-10 ГБ – большой объем данных. Если бы мне пришлось анализировать такие данные, я бы либо сделал журнал приложений в базе данных, либо загрузил файлы журнала в базу данных. Тогда есть множество анализов, которые вы можете эффективно выполнять в базе данных. Если вы используете стандартный инструмент ведения журнала, такой как Log4J, ведение журнала в базе данных должно быть довольно простым. Просто предлагаю альтернативное решение.

Для получения дополнительной информации о регистрации базы данных вы можете обратиться к этому сообщению:

Хорошее приложение для регистрации базы данных для Java?

Если у вас есть доступ к среде Windows, вы можете использовать MS LogParser для чтения файлов и сбора любой информации, которая вам может понадобиться. Он использует синтаксис SQL, который делает использование этого инструмента радостью. Он также поддерживает большое количество типов ввода.

В качестве дополнительного бонуса он также поддерживает переключатель iCheckPoint, который создает файл контрольной точки при работе с последовательными файлами журнала. Для получения дополнительной информации зайдите в справку «Анализ паролей» в разделе «Дополнительные функции ->« Разбор входных данных »,

Смотрите также:

  • Функции логического анализа
  • Пример использования
  • Python - используя подпроцесс для вызова sed?
  • Разбор скобок с помощью sed с использованием регулярного выражения
  • Как я могу разбить эту строку?
  • Как извлечь комментарий из файла заголовка с помощью python, perl или sed?
  • извлекать каждый файл последовательности в виде отдельного файла
  • Несоответствие между регулярными выражениями sed и python
  • разбиение файла на более мелкие файлы с использованием количества полей
  • Как конкатенировать идентификатор, указанный в двух строках?
  • Python - лучший язык программирования в мире.