Сложно изучать новый язык/фреймворк без пет-проджекта. Всегда стараюсь сделать то, чем потом буду сам пользоваться. При изучении языка rust, я решил реализовать давнюю задумку, и назвал ее logextractor. Этот пост отвечает на вопросы “зачем мне это?” и “как это сделано”? В заключение выскажу свое впечатление о языке rust.

Этот проект - утилита командной строки. Как нетрудно догадаться из названия, logextractor что то вытаскивает из логов. Под логами подразумевается stdin. А вытаскивает он строки обернутые другими строками(на самом деле байты, обернутые байтами). Проще показать на примере. Представим, что мы хотим вытащить значение из рантайма программы, и нет ни какого желания задумываться о том, как его сохранить, куда сохранить, как потом скачать, где хранить счетчик для порядкового номера файла, как не писать дубликаты, если таких значений много…

Всё что нужно на стороне софта - оборачивание значения в набор уникальных символов, которые гарантированно не встречаются в логах:

logger.info(s"long-awaited result calculated ==-->$result<--==")

Вытаскивание значения result:

cat logs.txt | logextractor --prefix '==-->' --suffix '<--=='

В stdout напечатается только значение из $result.

Да эту задачу можно решить простым грепом! (Подумаете вы). Наверное, можно. Но кроме grep’a понадобится что то ещё.

Опционально можно писать в файлы, игнорировать дубликаты, добавлять файлам порядковый номер. Так же можно делать дополнительную обработку для каждого результата. Например, на стороне софта, можно бинарник преобразовать в base64 и напечатать в консоль. Распаковка будет осуществляться в дочернем процессе (процесс будет запущен для каждого из значений).

echo '<(aGVsbG8K)><(d29ybGQK)>' | logextractor -p '<(' -s ')>' -d ''  base64 --decode

hello
world

Самое важное для меня - сохранение файлов. Читать можно откуда угодно, например из docker-compose logs -f. Каждый файл пишется в директорию, указанную в параметре -o, --output-dir [PATH]. В имени файла обязательно содержится sha1 этого файла (Дубликаты ищутся именно по sha1 в именах файлов, что сильно эффективнее вычисления настоящего sha1). По умолчанию имя файла это {sha1}. Можно добавить порядковый номер -e. Порядковый номер встречается в начале имени файла, после него следует нижнее подчеркивание. Полезно для просмотра последних N файлов ls | sort -nr | head -n 5 | xargs cat. Параметр -u, --unique работает не только для файлов, но и для вывода в stdout. В памяти хранится hashset из хэш-сумм всех результатов.

В качестве бонуса - возможность писать не только в файлы, но и в мессенджеры: slack, telegram… Но в таком случае нужно учитывать ограничение на длинну сообщения.

docker-compose logs -f | logextractor --prefix '#>' --suffix '<#' telegram-send --stdin

Несколько слов про имплементацию logextractor.

  • Имя файла это {опциональный порядковый номер}_{sha1}
  • При использовании режима с нумерацией файлов, работает следующая логика При старте приложения считываются имена файлов из –output-dir, затем вычисляется максимальный номер файла и при каждой новой записи инкрементится
  • Аргумент –unique включает сохранение sha1 результатов в hashset
  • Процесс, при чтении stdin, использует байтовый буффер фиксированного размера, а значит не должно быть утечек по памяти. Для временного сохранения данных, которые находятся между prefix и suffix, используется динамически выделенная память (можно было бы писать сразу в stdout или в файл для экономии памяти, но мне было достаточно такого решения). Еще память аллоцируется динамически для хранения хэшей результатов.

Несколько слов про rust.

  • Rust собирает приложения в один бинарник - очень удобно
  • Можно устанавливать и обновлять софт из исходников на гитхабе одной командой cargo install --git https://github.com/d10xa/logextractor.git --force
  • Бинарник с hello world весит 268K
  • Бинарник logextractor весит 1.6M (На момент, когда подключены 4 библиотеки)
  • Моментальный запуск, в отличии от jvm…
  • Относительно долгая компиляция, но только первый раз. Хорошо работает инкрементальная сборка
  • В intellij idea community edition нет дебаггера для rust
  • Rust это не тот язык, который можно изучить за пару вечеров

Если вдруг вам эта поделка понравилась, буду рад звезде на гитхабе. Если есть другие интересные идеи для использования, или замечания - добро пожаловать в issues на гитхабе