Работа с файлами журналов часто включает в себя довольно большие объемы данных, поэтому чтение в порядке возрастания и чтение всего, что угодно, нежелательно, так как тратит много ресурсов.
Самый быстрый способ достичь своей цели, который немедленноМне пришло в голову (лучшие подходы, безусловно, будут существовать) очень простой случайный поиск: мы ищем лог-файл (ы) в обратном порядке, таким образом, начиная с самого нового.Вместо того, чтобы посещать все строки, вы просто произвольно выбираете несколько stepsize
и смотрите только несколько строк в каждой stepsize
.Таким образом, вы можете выполнять поиск в гигабайтах данных за очень короткое время.
Кроме того, этот подход не требует сохранения каждой строки файла в памяти, а только некоторых строк иокончательный результат.
Когда a.log
является текущим файлом журнала, мы начинаем поиск здесь:
with open("a.log", "rb+") as fh:
Поскольку нас интересуют только последние 24 часа, мы переходим к концуСначала сохраните метку времени для поиска в виде отформатированной строки:
timestamp = datetime.datetime.now() - datetime.timedelta(days=1) # last 24h
# jump to logfile's end
fh.seek(0, 2) # <-- '2': search relative to file's end
index = fh.tell() # current position in file; here: logfile's *last* byte
Теперь мы можем начать наш случайный поиск.Ваша строка в среднем имеет длину около 65 символов, поэтому мы перемещаем ее кратно.
average_line_length = 65
stepsize = 1000
while True:
# we move a step back
fh.seek(index - average_line_length * stepsize, 2)
# save our current position in file
index = fh.tell()
# we try to read a "line" (multiply avg. line length times a number
# large enough to cover even large lines. Ignore largest lines here,
# since this is an edge cases ruining our runtime. We rather skip
# one iteration of the loop then)
r = fh.read(average_line_length * 10)
# our results now contains (on average) multiple lines, so we
# split first
lines = r.split(b"\n")
# now we check for our timestring
for l in lines:
# your timestamps are formatted like '2018/03/28-20:08:48.985053'
# I ignore minutes, seconds, ... here, just for the sake of simplicity
timestr = l.split(b":") # this gives us b'2018/03/28-20' in timestr[0]
# next we convert this to a datetime
found_time = datetime.datetime.strptime(timestr[0], "%Y/%m/%d-%H")
# finally, we compare if the found time is not inside our 24hour margin
if found_time < timestamp:
break
С помощью этого кода мы будем искать только несколько строк stepsize
(здесь: 1000 строк) пока мы находимся внутри наших последних 24 часов.После того, как мы покинули эти 24 часа, мы знаем , что в большинстве случаев мы зашли точно stepsize
* average_line_length
слишком далеко в файл.
Фильтрация этого "зашла слишком далеко" становится оченьтогда просто:
# read in file's contents from current position to end
contents = fh.read()
# split for lines
lines_of_contents = contents.split(b"\n")
# helper function for removing all lines older than 24 hours
def check_line(line):
# split to extract datestr
tstr = line.split(b":")
# convert this to a datetime
ftime = datetime.datetime.strptime(tstr[0], "%Y/%m/%d-%H")
return ftime > timestamp
# remove all lines that are older than 24 hours
final_result = filter(check_line, lines_of_contents)
Поскольку contents
охватывает все оставшееся содержимое нашего файла (и lines
все строки, которые просто contents
разбиваются на разрывы строк \n
), мы можем легко использоватьfilter
, чтобы получить желаемый результат.
Каждая строка в lines
будет передана в check_line
, которая возвращает True
, если время строки > timestamp
и timestamp
- наш объект datetimeточно описывая now - 1day
.Это означает, что check_line
вернет False
для всех строк старше timestamp
, а filter
удалит эти строки.
Очевидно, что это далеко не оптимально, но легко понять и легкорасширяемый на фильтрацию в течение нескольких минут, секунд, ...
Кроме того, охват нескольких файлов также прост: вам просто нужно glob.glob
, чтобы найти все возможные файлы, начать с новейшего файла и добавить еще один цикл: вы быищите файлы до тех пор, пока наш цикл while не выйдет из строя в первый раз, затем прервите и прочитайте все оставшееся содержимое из текущего файла + все содержимое всех файлов, которые были просмотрены ранее.
Примерно что-то вроде этого:
final_lines = lst()
for file in logfiles:
# our while-loop
while True:
...
# if while-loop did not break all of the current logfile's content is
# <24 hours of age
with open(file, "rb+") as fh:
final_lines.extend(fh.readlines())
Таким образом, вы просто сохраняете все строки файла журнала, если возраст всех строк <24 часов.Если цикл прерывается в какой-то момент, то есть мы нашли лог-файл и точную строку> 24-часового возраста, продлим final_lines
на final_result
, поскольку это будет охватывать только строки <24-часового возраста. </p>