Прочитайте файл gz и получите последние 24 часа строки Python - PullRequest
0 голосов
/ 29 января 2019

У меня есть три файла: 2 .gz файлы и 1 .log файл.Эти файлы довольно большие.Ниже у меня есть образец копии моих оригинальных данных.Я хочу извлечь записи, которые соответствуют последним 24 часам.

a.log.1.gz

2018/03/25-00:08:48.638553  508     7FF4A8F3D704     snononsonfvnosnovoosr
2018/03/25-10:08:48.985053 346K     7FE9D2D51706     ahelooa afoaona woom
2018/03/25-20:08:50.486601 1.5M     7FE9D3D41706     qojfcmqcacaeia
2018/03/25-24:08:50.980519  16K     7FE9BD1AF707     user: number is 93823004
2018/03/26-00:08:50.981908 1389     7FE9BDC2B707     user 7fb31ecfa700
2018/03/26-10:08:51.066967    0     7FE9BDC91700     Exit Status = 0x0
2018/03/26-15:08:51.066968    1     7FE9BDC91700     std:ZMD:

a.log.2.gz
2018/03/26-20:08:48.638553  508     7FF4A8F3D704     snononsonfvnosnovoosr
2018/03/26-24:08:48.985053 346K     7FE9D2D51706     ahelooa afoaona woom
2018/03/27-00:08:50.486601 1.5M     7FE9D3D41706     qojfcmqcacaeia
2018/03/27-10:08:50.980519  16K     7FE9BD1AF707     user: number is 93823004
2018/03/27-20:08:50.981908 1389     7FE9BDC2B707     user 7fb31ecfa700
2018/03/27-24:08:51.066967    0     7FE9BDC91700     Exit Status = 0x0
2018/03/28-00:08:51.066968    1     7FE9BDC91700     std:ZMD:

a.log
2018/03/28-10:08:48.638553  508     7FF4A8F3D704     snononsonfvnosnovoosr
2018/03/28-20:08:48.985053 346K     7FE9D2D51706     ahelooa afoaona woom

** Desired Result**
result.txt
2018/03/27-20:08:50.981908 1389     7FE9BDC2B707     user 7fb31ecfa700
2018/03/27-24:08:51.066967    0     7FE9BDC91700     Exit Status = 0x0
2018/03/28-00:08:51.066968    1     7FE9BDC91700     std:ZMD:
2018/03/28-10:08:48.638553  508     7FF4A8F3D704     snononsonfvnosnovoosr
2018/03/28-20:08:48.985053 346K     7FE9D2D51706     ahelooa afoaona woom

Я не уверен, как получить записи, которые покрывают последние 24 часа.

Ответы [ 2 ]

0 голосов
/ 29 января 2019

Работа с файлами журналов часто включает в себя довольно большие объемы данных, поэтому чтение в порядке возрастания и чтение всего, что угодно, нежелательно, так как тратит много ресурсов.

Самый быстрый способ достичь своей цели, который немедленноМне пришло в голову (лучшие подходы, безусловно, будут существовать) очень простой случайный поиск: мы ищем лог-файл (ы) в обратном порядке, таким образом, начиная с самого нового.Вместо того, чтобы посещать все строки, вы просто произвольно выбираете несколько 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>

0 голосов
/ 29 января 2019

Примерно так должно работать.

from datetime import datetime, timedelta
import glob
import gzip
from pathlib import Path
import shutil


def open_file(path):
    if Path(path).suffix == '.gz':
        return gzip.open(path, mode='rt', encoding='utf-8')
    else:
        return open(path, encoding='utf-8')


def parsed_entries(lines):
    for line in lines:
        yield line.split(' ', maxsplit=1)


def earlier():
    return (datetime.now() - timedelta(hours=24)).strftime('%Y/%m/%d-%H:%M:%S')


def get_files():
    return ['a.log'] + list(reversed(sorted(glob.glob('a.log.*'))))


output = open('output.log', 'w', encoding='utf-8')


files = get_files()


cutoff = earlier()


for i, path in enumerate(files):
    with open_file(path) as f:
        lines = parsed_entries(f)
        # Assumes that your files are not empty
        date, line = next(lines)
        if cutoff <= date:
            # Skip files that can just be appended to the output later
            continue
        for date, line in lines:
            if cutoff <= date:
                # We've reached the first entry of our file that should be
                # included
                output.write(line)
                break
        # Copies from the current position to the end of the file
        shutil.copyfileobj(f, output)
        break
else:
    # In case ALL the files are within the last 24 hours
    i = len(files)

for path in reversed(files[:i]):
    with open_file(path) as f:
        # Assumes that your files have trailing newlines.
        shutil.copyfileobj(f, output)

# Cleanup, it would get closed anyway when garbage collected or process exits.
output.close()

Тогда, если мы создадим несколько файлов журнала испытаний:

#!/bin/sh
echo "2019/01/15-00:00:00.000000 hi" > a.log.1
echo "2019/01/31-00:00:00.000000 hi2" > a.log.2
echo "2019/01/31-19:00:00.000000 hi3" > a.log
gzip a.log.1 a.log.2

и запустим наш скрипт, он выдаст ожидаемый результат (для этого моментавовремя)

2019/01/31-00:00:00.000000 hi2
2019/01/31-19:00:00.000000 hi3
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...