Проблема с анализатором журнала часто возникает при разработке программного обеспечения. Многие системы создают файлы журналов во время нормальной работы, и иногда вам необходимо проанализировать эти файлы, чтобы найти аномалии или общую информацию о работающей системе.
Для этой проблемы вам нужно проанализировать файл журнала в указанном формате и создать отчет:
# logparse.py """ log parser Accepts a filename on the command line. The file is a Linux-like log file from a system you are debugging. Mixed in among the various statements are messages indicating the state of the device. They look like this: Jul 11 16:11:51:490 [139681125603136] dut: Device State: ON The device state message has many possible values, but this program cares about only three: ON, OFF, and ERR. Your program will parse the given log file and print out a report giving how long the device was ON and the timestamp of any ERR conditions. """
Обратите внимание, что предоставленный скелет кода не включает модульные тесты. Это было опущено, поскольку точный формат отчета зависит от вас. Подумайте и напишите свой собственный в процессе.
Прилагается файл test.log с примером. Решение, которое вы исследуете, дает следующий результат:
$./logparse.py test.log Device was on for 7 seconds Timestamps of error events: Jul 11 16:11:54:661 Jul 11 16:11:56:067
Хотя этот формат генерируется решением Real Python, вы можете создать свой собственный формат для вывода. Образец входного файла должен генерировать эквивалентную информацию.
Поскольку это решение длиннее, чем то, что вы видели для целых сумм или задач с шифром Цезаря, давайте начнем с полной программы:
# logparse.py """ log parser Accepts a filename on the command line. The file is a Linux-like log file from a system you are debugging. Mixed in among the various statements are messages indicating the state of the device. They look like this: Jul 11 16:11:51:490 [139681125603136] dut: Device State: ON The device state message has many possible values, but this program cares about only three: ON, OFF, and ERR. Your program will parse the given log file and print out a report giving how long the device was ON and the timestamp of any ERR conditions. """ import datetime import sys def get_next_event(filename): with open(filename, "r") as datafile: for line in datafile: if "dut: Device State: " in line: line = line.strip() # Parse out the action and timestamp action = line.split()[-1] timestamp = line[:19] yield (action, timestamp) def compute_time_diff_seconds(start, end): format = "%b %d %H:%M:%S:%f" start_time = datetime.datetime.strptime(start, format) end_time = datetime.datetime.strptime(end, format) return (end_time - start_time).total_seconds() def extract_data(filename): time_on_started = None errs = [] total_time_on = 0 for action, timestamp in get_next_event(filename): # First test for errs if "ERR" == action: errs.append(timestamp) elif ("ON" == action) and (not time_on_started): time_on_started = timestamp elif ("OFF" == action) and time_on_started: time_on = compute_time_diff_seconds(time_on_started, timestamp) total_time_on += time_on time_on_started = None return total_time_on, errs if __name__ == "__main__": total_time_on, errs = extract_data(sys.argv[1]) print(f"Device was on for {total_time_on} seconds") if errs: print("Timestamps of error events:") for err in errs: print(f"t{err}") else: print("No error events found.")
Это ваше полное решение. Вы можете видеть, что программа состоит из трех функций и основного раздела. Вы будете работать через них сверху.
Во-первых, это get_next_event():
# logparse.py def get_next_event(filename): with open(filename, "r") as datafile: for line in datafile: if "dut: Device State: " in line: line = line.strip() # Parse out the action and timestamp action = line.split()[-1] timestamp = line[:19] yield (action, timestamp)
Потому что он содержит в себе оператор yield, эта функция является генератор. Это означает, что вы можете использовать его для создания одного события из файла журнала За один раз.
Вы могли бы просто использовать for line in datafile, но вместо этого вы добавляете немного фильтрации. Вызов рутины будет получать только те события, которые имеют dut: Device State: в них. Это позволяет сохранить весь специфичный для файла синтаксический анализ, содержащийся в одной функции.
Это может сделать get_next_event() немного сложнее, но это относительно небольшая функция, поэтому она остается достаточно короткой, чтобы читать и понимать. Он также сохраняет этот сложный код инкапсулированным в одном месте.
Вам может быть интересно, когда datafile закрывается. До тех пор, пока вы не вызовете генератор, пока все строки не будут прочитаны из datafile, for петля завершится, позволив вам покинуть with заблокируйте и выйдите из функции.
Вторая функция – это compute_time_diff_seconds(), которая, как следует из названия, вычисляет количество секунд между двумя метками времени:
# logparse.py def compute_time_diff_seconds(start, end): format = "%b %d %H:%M:%S:%f" start_time = datetime.datetime.strptime(start, format) end_time = datetime.datetime.strptime(end, format) return (end_time - start_time).total_seconds()
В этой функции есть несколько интересных моментов. Во-первых, это вычитание двух datetime объекты приводят к datetime.timedelta. Для этой проблемы вы сообщите общее количество секунд, так что возвращайтесь .total_seconds() из timedelta, это уместно.
Во-вторых, следует отметить, что в Python существует множество пакетов, упрощающих обработку дат и времени. В этом случае ваша модель использования достаточно проста, чтобы она не гарантировала сложность привлечения внешней библиотеки, когда стандартных библиотечных функций будет достаточно.
Вот и все, datetime.datetime.strptime() это достойно упоминания. При передаче строки и определенного формата, .strptime() анализирует эту строку с заданным форматом и производит datetime объект.
Это еще одно место, где в ситуации интервью важно не паниковать, если вы не можете вспомнить точные названия стандартных библиотечных функций Python.
Следующий шаг-это extract_data(), который выполняет основную часть работы в этой программе. Прежде чем вы погрузитесь в код, давайте вернемся назад и поговорим о государственных машинах.
Государственная машина являются ли программные (или аппаратные) устройства, которые переходят от одного государство к другому в зависимости от конкретных входных данных. Это действительно широкое определение, которое может быть трудно понять, поэтому давайте посмотрим на диаграмму государственной машины, которую вы будете использовать ниже:
На этой диаграмме состояния представлены помеченными прямоугольниками. Здесь есть только два Device State: ON и OFF, которые соответствуют состоянию устройства. Есть также два входных сигнала, Device State: ON и Device State: OFF. Диаграмма использует стрелки, чтобы показать, что происходит, когда ввод происходит, пока машина находится в каждом состоянии.
Например, если машина находится в Device State: ON происходит ввод данных, после чего машина остается в ON. Никаких изменений не происходит. И наоборот, если машина получает Device State: OFF ввод, когда он находится в ON состояние, то оно будет переходить в OFF.
В то время как машина состояний здесь-это только два состояния с двумя входами, машины состояний часто гораздо сложнее. Создание диаграммы ожидаемого поведения может помочь сделать код, реализующий машину состояний, более сжатым.
Давайте вернемся к extract_data():
# logparse.py def extract_data(filename): time_on_started = None errs = [] total_time_on = 0 for action, timestamp in get_next_event(filename): # First test for errs if "ERR" == action: errs.append(timestamp) elif ("ON" == action) and (not time_on_started): time_on_started = timestamp elif ("OFF" == action) and time_on_started: time_on = compute_time_diff_seconds(time_on_started, timestamp) total_time_on += time_on time_on_started = None return total_time_on, errs
Может быть, здесь трудно увидеть. Обычно машины состояний требуют переменной для хранения состояния. В этом случае вы используете time_on_started служить двум целям:
Верхняя часть extract_data() настройка переменной состояния, time_on_started, а также два выхода, которые вы хотите. errs это список меток времени, при которых ERR сообщение было найдено, и total_time_on это сумма всех периодов, когда устройство было включено.
Как только вы завершите начальную настройку, вы вызовете генератор get_next_event() для извлечения каждого события и метки времени. action. Он получает используется для управления машиной состояний, но прежде чем он проверяет наличие изменений состояния, он сначала использует if блок для отфильтровывания любых ERR условия и добавить их к errs.
После проверки ошибок, первая elif блок обрабатывает переходы к ON. Вы можете перейти к следующему ON только когда вы находитесь в OFF состояние, о котором сигнализирует time_on_started существование False. Если вы еще не находитесь в ON состояние и действие есть “ON”, то вы храните timestamp, кладя машину в ON State.
Второй elif обрабатывает переход к OFF State. Об этом переходе, extract_data() необходимо вычислить количество секунд, в течение которых устройство было включено. Он делает это с помощью compute_time_diff_seconds(), вы видели выше. Это добавляет это время к бегу total_time_on и наборы time_on_started вернуться к None, эффектно переводят машину назад в OFF State.
Наконец, вы можете перейти к следующему разделу __main__. Этот последний раздел проходит sys.argv[1], который является первым аргумент командной строки, extract_data() а затем представляет отчет о результатах:
# logparse.py if __name__ == "__main__": total_time_on, errs = extract_data(sys.argv[1]) print(f"Device was on for {total_time_on} seconds") if errs: print("Timestamps of error events:") for err in errs: print(f"t{err}") else: print("No error events found.")
Чтобы вызвать это решение, вы запускаете скрипт и передаете имя файла журнала. Запуск вашего примера кода приводит к такому результату:
$ python3 logparse.py test.log Device was on for 7 seconds Timestamps of error events: Jul 11 16:11:54:661 Jul 11 16:11:56:067
Ваше решение может иметь различное форматирование, но информация должна быть одинаковой для примера файла журнала.
Есть много способов решить подобную проблему. Помните, что в ситуации интервью обсуждение проблемы и ваш мыслительный процесс могут быть более важными, чем решение, которое вы решите реализовать.
Это все, что касается решения для анализа журналов.
Предыдущие статьи:
Продолжение: