Как стать автором
Обновить

Комментарии 12

Приветствуем в мире Python и на хабре!

Вот это

    with open(status_log, "r") as file:
        for line in file:
            if "TITLE" in line:
                continue
            elif "TIME" in line:
                continue
            elif "HEADER" in line:
                continue
            elif "ROUTING_TABLE" in line:
                break

            log_connection.append(line)

можно заменить на

    with open(status_log, "r") as file:
        for line in file:
            if "CLIENT_LIST" in line:
                log_connection.append(line)

При этом я сохранил возможность обрабатывать несколько журналов разом

Лучше сразу выкидывать код, который "может быть потом пригодится". Не пригодится в 99% случаев.

Чтение логов и парсинг красивее и экономичнее по памяти делать в один проход, а не двумя циклами. Парсинг одной строки можно вынести в отдельную функцию.

Что это за duration у вас? Как получатель этой информации, я бы подумал, что это длительность клиентской сессии, но у вас это время прошедшее от подключения клиента до обработки логов:

....
    time_now = int(time.time())
    seconds = time_now - int(parsing[8])
...
    "unix_time": parsing[8],
    "duration": seconds,

Обращатся к внешней серверам при обработки логов чревато не только долгой работой, но и баном ввиду слишком частых подключений, так что решение выкачать GeoIP совершенно правильное.

В целом идёте по граблям но в правильном направлении!

Спасибо за подсказку с фильтрацией строк исходного лог файла, учту!)
Про duration вы правильно поняли, вернее использовать временную метку из лог файла. Лог файл обрабатывается каждые несколько минут, потому погрешность не очень большая, но стоит этот момент переделать, спасибо!

Извините за мою настойчивость, но получается, что Duration это не время, в течении которого пользователь (был) подключен к VPN, а время, через которое вы логи обработали. А если вы логи через год обработали, то получится что пользователь год от VPN не отключался.
Смысл этой метрики для мониторинга для меня непонятен.
Узнать о том, как давно клиент подключился?
Но что если он уже отключился?

Почему? Если в момент времени обработки лога в нём есть запись о пользователе, значит он всё ещё подключен, в противном случае о нём просто не будет информации. Я согласен, что вернее брать метку времени из самого файла, когда он последний раз был изменен, а меняется он очень часто. Стоит понимать, что обрабатываемый лог - это динамичная сущность. Пользователь подключился - появилась о нём запись, отключился - из файла он пропал.

Скажите, рассматривался ли mgmt интерфейс OpenVPN для сбора статистики? Если да то почему был выбран статус лог файл?

Нет, не рассматривался.

для больших файлов иногда полезно использовать yield

def status_log_reader(status_log):
    with open(status_log, "r") as file:
        for line in file:
            yield line
    for status_log in status_files: 
        log_connection = []
        with open(status_log, "r") as file:
            y_file = status_log_reader(file) # status_log_reader
            for line in y_file:
                if "CLIENT_LIST" in line:
                    log_connection.append(line)

У вас две ошибки, одна семантическая, а другая логическая.

Во-первых вы пытаетесь открыть файл дважды, когда передаёте уже открытый файл (объект TextIOWrapper ) в status_log_reader, которая ожидает путь к файлу и выбросит исключение.

Во вторых, если все строки в конце концов складываются в List, они будут занимать одинаковое количество памяти независимо от того, читаете вы их обычным перебором или Yield.

    for status_log in status_files:
        log_connection = tuple()
        y_file = status_log_reader(status_log)
        for line in y_file:
            if "CLIENT_LIST" in line:
                log_connection += (line,)

А на счёт памяти, когда все строки попадают в List - отдельный случай, т.е. могут и не все строки + можно использовать tuple.

Текущая программа автора выглядит в псевдокоде так

read and filter all lines
make big json
print big json

Я предполагаю, что вы советуете испльзовать Yield, для того чтобы сократить потребление памяти?

Но у вас все отфильтрованные строки также загружаются в память, и неважно, через FOR они загружаются или через YIELD и не так уж важно в List или в Tuple.
Размер занимаемой памяти в любом случае будет O(n), а точнее
N * (StringSize + StructSize)
То есть количество строк умножить на сумму длинны строки и размера элемента структуры, который эту строку содержит.

Если вы хотите сократить потребление до 1(n) то вместо складывания строк в память, их нужно фильтровать и сразу передавать дальше.

В псевдокоде такая программа будет выглядеть как

read line
filter line
make json line
print json line
"group1": list(range(0, 1)),

А зачем range с одним элементом на выходе? Просто для единообразия?

Это упрощенный пример для статьи, в действительности диапазоны больше)

Зарегистрируйтесь на Хабре, чтобы оставить комментарий