Сервисы RX, nginx, PostgreSQL, RabbitMQ и системный журнал пишут каждый в своём формате и в свой часовой пояс. Генератор собирает конфигурацию Vector, которая разбирает всё это на поля, приводит время к одному виду и отправляет в Elasticsearch, Loki или Splunk.
Один файл конфигурации на сервер. Vector читает новые записи с момента запуска: старые файлы он не перечитывает и хранилище не заваливает.
Сохраните как vector.yaml. Паролей в нём нет: Vector читает их из отдельных файлов в каталоге secrets.
Включите JavaScript, чтобы увидеть конфигурацию.
Журналы монтируются только на чтение. Команда tap в конце показывает первые события после разбора: видно, на какие поля разложилась запись.
Вместо паролей в командах стоит CHANGE_ME. Для Vector заведите отдельного пользователя, который может только создавать свои индексы и писать в них. Команды для этого в блоке проверки.
Собрать журналы в одно место несложно. Сложно собрать так, чтобы через месяц по ним можно было найти ответ, а хранилище не остановилось.
Один небольшой процесс вместо пары Filebeat и Logstash: читает файлы и системный журнал, разбирает записи на месте и отправляет сразу в хранилище. Разбор пишется на его языке VRL, который проверяется при запуске: опечатка в правиле не даст Vector стартовать, а не тихо испортит данные. На сервер уходит 100–200 МБ памяти, в команде запуска стоит предел 512 МБ.
Сервисы RX пишут журнал строками JSON, одна запись на строку. Состав полей описан в документации вендора: t время с часовым поясом, l уровень, mt текст сообщения, ex исключение со стеком, span операция и её длительность, lg логгер, tr трассировка, un учётная запись. Конфигурация выносит наверх то, по чему ищут: сервис (из имени файла), уровень, текст, логгер, пользователя, тип исключения, стек, метод, в котором оно случилось, операцию и её длительность в миллисекундах. Остальное лежит в поле rx.
У предупреждений и ошибок появляется поле genericMessage: тот же текст, где адреса, пути, GUID, числа и строки в кавычках заменены заглушками. «Document 18234 not found» и «Document 90211 not found» становятся одной строкой, и по ней видно, какая ошибка самая частая, а не какая последняя. Ошибки уходят в отдельный индекс: их хранят дольше, а ищут чаще.
Поля mt, args, cust и span у разных записей бывают то строкой, то объектом. Elasticsearch такую запись отвергает с конфликтом типов, поэтому они сохраняются строкой JSON. Веб-агент пишет пути Windows с одиночным обратным слешем, строгий разбор JSON на этом падает: конфигурация такие строки чинит, а не теряет.
Без единого времени события с разных серверов не сопоставить. PostgreSQL по умолчанию пишет зону сокращением вроде MSK, которое однозначно не разбирается. Поэтому для таких записей берётся часовой пояс из формы. Если log_timezone в PostgreSQL выставлен в UTC, конфигурация это распознает сама.
Ошибка PostgreSQL занимает несколько строк: сама ошибка, DETAIL с подробностями, STATEMENT с текстом запроса, иногда HINT и CONTEXT. Если отправлять их по отдельности, в хранилище окажется «deadlock detected» без запроса, который его вызвал. Конфигурация склеивает их в одну запись и раскладывает по полям: pg.statement, pg.detail. Ещё она определяет вид записи pg.kind: медленный запрос с длительностью, чекпоинт с числом буферов и временем, автовакуум с таблицей, временный файл с размером, блокировка, подключение. По этим полям строятся панели без разбора текста на лету. Строки pg_probackup, которые попадают в тот же журнал без префикса, тоже распознаются.
Одно событие auditd это несколько строк с общим номером, связать их можно по полю audit.id. Поле auid показывает того, кто вошёл в систему, и не меняется после su и sudo, поэтому отвечает на вопрос «кто это сделал» точнее, чем uid. Команду sudo auditd записывает шестнадцатеричной строкой: конфигурация переводит её в текст, иначе её не прочитать ни глазами, ни поиском. Неудачные попытки получают уровень warn.
Индексы называются по семейству источника: журналы RX, nginx, PostgreSQL и так далее, по одному в сутки. Отдельный индекс на каждый сервис RX быстро съедает лимит шардов. Подробно эта ловушка разобрана в статье о журналах RX, а срок хранения под ваш диск считает калькулятор Elasticsearch: начало имени индексов в нём то же.
Во многих инструкциях пароль подставляют в конфигурацию из переменной окружения записью вида ${ES_PASSWORD}. В свежих версиях Vector так больше не работает: подстановка выключена по умолчанию, и в хранилище уходит буквальная строка с долларом, а в ответ приходит ошибка 401. Включить её можно флагом с говорящим названием --dangerously-allow-env-var-interpolation. Мы используем встроенное хранилище секретов Vector: каждый пароль лежит отдельным файлом, читать который может только сам Vector.
Если хранилище недоступно, Vector складывает события в буфер на диске объёмом до гигабайта и дошлёт их, когда хранилище вернётся. Без буфера события за время недоступности пропадут, а это как раз то время, когда журналы нужнее всего.
Отладочные записи RX и запросы nginx к статике и проверкам доступности. Из системного журнала исключён журнал самого Vector: его ошибки отправки, отправленные в то же хранилище, дают петлю ровно тогда, когда хранилищу и так плохо. Записи длиннее 64 КБ обрезаются с пометкой message_truncated. На нагруженной системе это больше половины объёма, а при разборе инцидента они не нужны. Ответы nginx с ошибкой не отбрасываются никогда, даже для статики.
Сборщик журналов тоже ломается, и узнают об этом обычно в момент, когда журналы понадобились. На порту 9598 Vector отдаёт метрики для Prometheus: ошибки разбора и отправки, заполнение буфера, число событий по источникам. Правило тревоги простое: ошибки отправки растут или буфер заполняется.
Чего генератор не делает: не настраивает панели и поиск в хранилище и не разбирает журналы Windows. Под Windows Vector работает так же, но источники там другие: журнал событий и файлы IIS.
Девять мест, где сборщик теряет записи молча. Разбор
Запись не уходит в хранилище, Elasticsearch отвечает ошибкой. Справочник ошибок