Продвинутый механизм файлового логирования

Приветствую тех кто повязан в вебе. Сегодня обсуждение небольшого вопроса связанного с попыткой организовать правильное файловое логирование
Очередное нестандартное решение стандартной проблемы. Кому интересны необычные подходы - прошу. Кто считает что если есть стандартные решения и нужно всегда ими пользоваться - проходите мимо.

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

Каких показателей мы хотим достигнуть?
Хочется быстро получить доступ к последним записям в логах. Хочется иметь возможность программно парсить данные, для дальнейшего анализа логов. А также хочется не вредить при этом существующим механизмам для работы с логами - утилиты tail и текстовому представлению.

Если вы когда либо что-то логировали - вы знаете - взаимодействие с файлами происходит у программы на уровне ссылки на позицию в файле и чтению или записи данных относительно этой позиции.

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

Таким образом работают все базовые механизмы для логирования веб сервера, базы данных и самих скриптов. Каждая новая запись пишется с новой строки и перевод строки как правило и является разделителем.

И Вам наверное когда надо было что-либо залогировать - вы использовали конструкцию вида

function log($filename, $string)
{
file_put_contents($filename, file_get_contents($filename) . "\n" . $string);
}

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

function log($filename, $string)
{
file_put_contents($filename, "\n" . $string, FILE_APPEND | LOCK_EX);
}


Классический вариант, вполне себе уместный. Какие тут могут быть недостатки?
В первую очередь затруднительно смотреть последние самые свежие логи. Для линуксов есть замечательная команда tail, которая показывает и следит за последними строчками в файле. Она вполне себе уместна

Но она немного ограничена - у вас должен быть доступ к ssh чтобы пользоваться ею и на сервере должен быть linux.

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

Если вы хотите анализировать логи, а я рекомендую вам делать логи которые можно впоследствии агрегировать как-либо - вам нужно использовать серилизацию, но такую, чтобы легко читалась и через tail. Рекомендую использовать для этой цели JSON. Как самый универсальный и понятный и доступный механизм на всех языках.
Всё выглядит хорошо, но как насчет реализации?

Делать массив из данных - неудобно и опасно, нужно стирать последние завершающие кавычки для этого. Да и многогигабайтный файл читать нужно весь в память, чтобы с ним как-то работать. Значит новый элемент массива нужно дописывать как отдельный массив. Очень хорошо.
А как делить массивы между собой? Проблемы tail всё еще актуальны, а серилизованные массивы могут быть очень длинные
Здесь и начинается волшебство.

Итак, нам известно что ранее записанные строки не могут меняться. А это значит что все позиции указателя всех начал и длину добавленной фразы мы можем где-либо отдельно списком вести. Получается некий hashmap, проглотить который будет уже гораздо проще и читать построчно его можно гораздо проще как с начала так и с конца

Куда его положить - вопрос совершенно отдельный. Если вести файлы отдельно - у нас может  нарушится атомарность, а операции с блокировками двух файлов будут значительно сложнее. Плюс проблемы с копированием, сжатием. Чтобы избежать вновь образовавшихся проблем - предлагаю вести указатели прямо внутри файла. Но как?

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

Что же такое ссылка, если вдруг есть непонимание - это позиция (число байт) на место начала порции полезных данных относительно известной величины. То есть это целое положительное число и оно не может быть больше чем размер файла. Вопрос лишь как создать компактный и лаконичный протокол.
Исходим из того что длина файла нам известна. А это значит - нам известна позиция указателя для чтения самого конца файла. Очевидно, что в каждом дополнении "полезные данные - ссылка" - ссылка должна идти после данных (потому что она имеет ограниченную длинну). Поскольку следующий поток полезных данных будет идти сразу после текущей ссылки - то и длину массива данных можно не хранить, она будет равна разности между положением текущего указателя и положением предыдущего указателя, а значит в указателе может храниться сама длина блока полезных данных. Таким образом можно легко прыгать по блокам начиная с конца файла.

Остается только один вопрос - как узнать длину самого указателя. Если вы подумали что достаточно делать проверку на то является ли символ числом и отматывать по одному символу - может оказаться что полезные данные - это тоже число, а число в JSON даже кавычками не обрамляется.
Есть одно предложение - добавить в протокол еще один символ - а именно длину указателя - всегда 1 байт. Число от 1 до 9. Это позволит хранить указатели до 9 знаков, а это 999 гб на один массов (при условии что хранится именно длина), мне кажется хватит с запасом.
Таким образом решены все проблемы записи и чтения файлов, поиска по большому файлу множества данных и открываются возможности для анализа этих данных, т.к. парсинг данных уже реализован стандартным JSON.

Для организации постраничника - можно запомнить текущую абсолютную позицию курсора последней записи и перемещаться сразу туда.
Можно пойти еще дальше и сжать ссылку, поскольку кащунственно использовать 256 возможных значений в байте лишь 10ю значениями. Достаточно избегать спецсимволов в ascii, а все остальное пространство можно использовать для сжатия. Но это экономия на спичках и оно упрется обязательно в скорость пробега по ссылкам.

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

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

Данный механизм используется в фреймаворке Core Framework

Больше материалов выкладываю на своем Дзен канале

  • Автор: kosmom
  • Рейтинг: 0
  • Просмотров: 517
  • Комментариев: 0
  • Создан: 22.01.2021 11:30

Комментарии (0)

Ваши предложения и пожелания пишите на pro@kosmom.ru

Теги

ajax axios backup bootstrap core framework eloquent excel home project html ios javascript keep-alive kpi laravel legacy mvp orm php rip scroll solid timestamp undefined vue vuetify watch безопасность биометрический паспорт ваша любаша для путешествий загран на 10 лет загран паспорт загранпаспорт нового образца зимние книги как заполнить анкеты кеширование книги на новый год логирование мцф недвижимость новогодние книги образец заполнения антеты паспорт для путешествий паспорт нового поколения печать продукт проектирование прокси разработка ремонт ремонт в апартаментах ремонт нежилого помещения самокат сдача сколько стоил ремонт апартаментов спорт стандарты таблица финансы хостинг цена ремонта что почитать зимой юзабилити

Случайный пост

07.12.2015 17:22
Книги за ноябрь 2015 года