Так уж вышло, что мне регулярно приходится просматривать много логов.
Одно радует, не так много как у людей работающих вместе со мной у которых порой это основная работа.
Логи эти не лежат в в какой либо централизованной системе, а хранятся в s3 и смотрим мы их скачивая с перенаправлением вывода в less
less установлен у всех, все привыкли с ним работать, знают о базовых вещах, как поиск вперед-назад, фильтрация по &, переход в конец(G) файла, переход в начало(g) и так далее.
А так же, все уже смирились с тем, что в любой момент, при добавлении фильтра less может подвиснуть на неопределенный срок, выводить по строчке в 5 секунд и так далее. В конечном счете, особенно при считывании логов с stdin — приходится быть аккуратным. Фильтр может сработать, а может и не сработать
Собственно, в тот момент, что и мне выпала участь в течении нескольких дней пройтись через этак пару сотен лог-файлов — стало очевидно — мир нужно менять к лучшему…
Для начала небольшое демо (2.2mb):
Тем кто уже готов: github.com/tigrawap/slit
Кто нет, прошу под кат…
Основных претензий к less было 3:
1) Фильтр может подвести в любой момент
2) Фильтр в принципе работает достаточно медленно, особенно с stdin, особенно при логах > 5MB
3) Невозможно добавлять несколько фильтров
Поискав на просторах сети — понял что ничего нет. Есть один «лог навигатор» — но, не интуитивен, много лишнего, а то что нужно очень неудобно
Приступив к написанию своего PAGER-a на Go — в первую очередь принялся сравнивать различные подходы
Первый был с наскока, мысль о том — что памяти у всех на машинах много, можно просто загрузить весь файл в память, подготовить его — и потом быстро там все искать.
Все оказалось не так банально, на 50мб файл, с построчным его разбиением, распознованием контрольных символов для отделения текста от цвета, выделением памяти под получившиеся данные(отдельно информация о цветах, отдельно текст для быстрого поиска в тексте) — уходило больше времени, чем если просто все считывать, разбивать, искать, но не выделять память под хранение.
При потоковой обработке данные переходят от функции к функции в стеке, что работает намного быстрее
В конечном счете, пришел к тому, что память выделяется только для хранения оффсета каждой строки, а отделение текста от цветов и передача готовых подходящих строк в буфер происходит в потоковом режиме. В буфере всегда хранится 2-3 окна в каждом направлении, для быстрой навигации вперед-назад без дополнительных считываний. По возможности поиск производится в этом буфере, если этого недостаточно — уже тогда повторно считывается файл с нужной позиции.
Само собой плюс такого подхода так же то, что не нужно ждать в начале подгрузку всего файла.
В принципе, в будущем подумываю добавить флаг, который позволит в любом случае, держать весь файл в памяти, но с потоковой обработкой. Но не уверен что это нужно :)
В целом, поиск, фильтрация, в том числе многоуровневая — получилось намного быстрее, чем в less-е и намного стабильнее. Примерно раза в 4 простой поиск, разница в фильтрации достигает бесконечности, так как less может просто повиснуть. Но даже если нет — то там уже на порядок
К слову о киллер-фичах
Многоуровневые фильтры
- & — пересечение с предыдущей выборкой. Т.е то же самое что в less, но с учетом всех предыдущих фильтров
- + Добавить совпадения к выборке
- — Исключить совпадения из выборки
Последовательность этих фильтров поможет быстро оставить на экране то что нужно
Также, для более детального изучения активно помогут:
- U(shift+u) — убрать последний фильтр
- = — убрать все фильтры
- C(shift+c, Context) — временно отключить все фильтры, позволяет увидеть все что было после данной строки, данной строкой считается первая на экране
Еще одна полезная возможность:
Shift+K — Keep. Позволяет указать кол-во символов с начала строки, которые будут отображаться при горизонтальном сдвиге экрана. Таким образом можно зафиксировать время, hash коммита итд
Можно либо ввести кол-во, либо стрелками вверх-вниз корректировать кол-во зафиксированных символов
— В основном горячие клавиши следуют less-у
— Поиск-фильтры сохраняют истории между сессиями. Пока что нет поиска по ctrl+r, это в планах.
— Файлы (пока-что) можно просматривать только по одному либо с stdin-a
— Если slit запущен с перенаправлением вывода — то входящий поток будет попросту перенаправлен, без какой либо логики, аналогично cat и less
— Активно протестировано на linux, osx, немного на windows, совсем не тестировалось на freebsd. Собрать под него собрал, но проверить не на чем. По идеи должно работать
Проект на очень ранней стадии, лично мне уже заменил PAGER в системе.
Но возможно кому либо не хватает чего то, казалось бы очевидного. Не молчите :)
Есть еще ряд планов:
— Добавить переключение чувствительности к регистру. На данный момент только регистро-зависимый поиск
— Добавить переключение типа поиска (regex/plain/extended glob)
— Добавить меню фильтров, для обзора того что есть на данный момент с выборочным удалением-отключением
— Добавить поддержку нескольких файлов. Для начала с переключением между ними как в less
— CTRL+R поиск по истории
— Удаление подстроки из каждой строки, для удаления шума, без удаления самих строк
— Прямой вывод в терминал если текст меньше экрана(аналог флага -F в less-e), пожалуй с поддержкой -F из переменной окружения LESS
Что под вопросом:
— Считывать весь stdin либо нет? Лично мне это здорово помогает, что он весь считывается еще до того, как я решил что мне это нужно. Но обычно PAGER-ы этого не делают.
— неХитрые возможности по работе с временем, т.е распозновать timestamp-ы и давать возможность по ним фильтровать. Не знаю нужно ли это кому либо в логах
Что обязано остаться несмотря на добавление возможностей:
— Забирать не больше одной строчки с терминала
— На открытие файла не должно тратиться больше времени, чем в случае с less-ом
И немного о Go
Это мой второй проект на Go, первый был тут:
habrahabr.ru/post/314220
Отсутствие различного функционала, вроде пресловутых generic-ов, порой необходимость реализовывать казалось бы банальные вещи (atomic boolean) — иногда расстраивает
На python я пишу намного больше, но каждый раз притрагиваясь к go я на самом деле радуюсь ограничениям которые на меня наложили :)
Но больше всего меня радует экосистема. О ней почему то часто забывают должным образом упомянуть, сравнивая либо ругая язык. Возможно язык замедляет написание, до момента нажатия кнопки «run» первый раз. После нее go стремительно вырывается вперед
1) go build/run/install --race
Приложение будет бежать в режима поиска race condition, обращения к данным из разных потоков без синхронизации. Это на самом деле очень здорово помогает отловить баги, которые достаточно сложно отловить. И порой понять, что зря решил пойти легким путем и передать указатель, а не добавить дополнительный уровень общения между частями приложения через потоки
2) net/http/pprof — это прекрасно. Простой импорт, регистрация локального порта и при возникновении дедлока можно зайти через браузер на выбранный порт и посмотреть всю информацию о goroutine-ах которые сейчас бегут
3) Кросс-компиляция. Об этом сказано конечно больше, но каждый раз не перестает радовать. Что не нужно танцевать с бубном, ставить дополнительные зависимости — оно просто работает
4) Goglang, тут спасибо Jetbrains, что собрали все воедино, опять таки, не нужно танцевать с бубном, а также как и в случае с Pycharm, продолжаю работать в схожей среде, с vim mode, автоимпортом, авто go get, объявлением методов и так далее.
Перед очередным выбором языка, сравнил как обстоят дела у некоторых других восходящих языков и стало очевидно, им еще есть куда расти…
И о главном, где брать:
Если у вас уже настроено окружение Go, то лучше всего просто собрать:
go get github.com/tigrawap/slit
Если предпочитаете готовые бинарники, то со странички релизов
github.com/tigrawap/slit/releases
И вопрос в студию, хотелось бы как то уведомлять пользователей о том, что есть более новая версия. Неприятно, в случае если я делаю релиз, вдруг замечаю ошибку, через 5 минут выпускаю новый релиз, но тот уже скачали. Какие есть варианты о ненавязчивом но понятном уведомлении?
Пока что думаю лезть в сеть не раньше, чем раз в час и при запуске, пока не будет произведено какое либо действие показывать сообщение в навигационной панеле. Может будут более элегантные идеи.
Так же рад идеям что стоит улучшить-добавить. Но все не обещаю :)
Автор: Anton Bykov