Skip to main content

Graylog — Документация по развёртыванию и настройке

Оглавление

  1. Архитектура Graylog
  2. Компоненты системы
  3. Ключевые концепции
  4. Потоки данных — от источника до индекса
  5. Источник 1: MikroTik (Syslog)
  6. Источник 2: Серверные логи (Vector → GELF)
  7. Pipeline Rules — синтаксис и примеры
  8. Index Sets — управление хранением
  9. Рекомендации по масштабированию

Архитектура Graylog

Итоговая структура стейджей


Компоненты системы

Graylog Server

Основной сервер обработки логов. Принимает сообщения через Inputs, обрабатывает их через Pipelines, маршрутизирует в Streams и записывает в OpenSearch. Предоставляет веб-интерфейс для поиска, дашбордов и управления.

OpenSearch

Поисковый движок и хранилище логов. Все сообщения индексируются и хранятся здесь. Graylog управляет индексами через Index Sets — создаёт новые, ротирует и удаляет старые автоматически. OpenSearch обеспечивает полнотекстовый поиск и агрегации для дашбордов.

MongoDB

Хранит только метаданные Graylog: конфигурацию системы, определения pipeline rules, настройки стримов, дашборды, учётные записи пользователей. Логи в MongoDB не хранятся.


Ключевые концепции

Input

Точка входа для логов в Graylog. Определяет протокол и порт приёма. Примеры: Syslog UDP на порту 5140, GELF UDP на порту 12201. Input сам по себе не фильтрует и не маршрутизирует — он просто принимает данные и помещает их в Default Stream.

Default Stream

Встроенный стрим "All messages", куда попадают все входящие сообщения без исключения. Является точкой входа для pipeline-обработки. Все pipelines для маршрутизации подключаются именно сюда. Имеет свой Default Index Set, куда пишутся сообщения, не направленные ни в один другой стрим.

Stream

Именованный подмножество логов, отфильтрованный по определённым критериям. Каждый стрим привязан к своему Index Set, что определяет как долго хранятся его данные. Стримы также используются для разграничения доступа, алертов и дашбордов.

Pipeline

Набор правил (rules), подключённый к одному или нескольким стримам. Выполняет две основные задачи:

  • Маршрутизация — направление сообщений из Default Stream в целевые стримы через route_to_stream()
  • Обогащение — извлечение и добавление полей, трансформация данных

Pipeline состоит из стейджей (stages), которые выполняются последовательно (Stage 0 → Stage 1 → ...). Правила внутри одного стейджа выполняются параллельно.

Важно: Stream rules — это legacy-функция. Graylog рекомендует использовать Pipeline rules для маршрутизации.

Index Set

Конфигурация хранения группы логов в OpenSearch. Определяет:

  • Rotation — когда создавать новый индекс (по времени, размеру или количеству документов)
  • Retention — сколько хранить данные перед удалением
  • Физические параметры — количество шардов, реплик

Разные Index Sets позволяют хранить разные типы логов с разными сроками и оптимизировать поиск (при запросе к стриму OpenSearch просматривает только его индексы).


Потоки данных — от источника до индекса

Общая схема обработки сообщения

1. Источник отправляет лог → Input принимает
2. Сообщение попадает в Default Stream
3. Pipelines на Default Stream обрабатывают сообщение:
a. Stage 0: извлечение полей, обогащение, дефолтные значения
b. Stage 1+: классификация, маршрутизация через route_to_stream()
4. Сообщение попадает в целевой Stream
5. Pipelines на целевом Stream (если есть) дополнительно обрабатывают
6. Сообщение записывается в Index Set, привязанный к стриму
7. OpenSearch индексирует и хранит сообщение

При использовании route_to_stream(name: "...", remove_from_default: true) сообщение удаляется из Default Stream и не дублируется в Default Index Set.


Источник 1: MikroTik (Syslog)

Настройка на стороне MikroTik (ROS7)

/system/identity/set name=ccr-office

/system/logging/action/add name=graylog target=remote remote=172.20.109.253 remote-port=5140 bsd-syslog=yes

/system/logging/add action=graylog topics=info
/system/logging/add action=graylog topics=warning
/system/logging/add action=graylog topics=error
/system/logging/add action=graylog topics=critical

Input в Graylog

Тип: Syslog UDP, порт: 5140, Bind address: 0.0.0.0.

Как выглядит сырое сообщение

Из MikroTik в формате BSD syslog приходит строка вида:

ccr-office <l2tp-KAR_Kungrad>: connected

или

ccr-office login failure for user admin from 38.180.71.32 via winbox

Формат: <hostname> <текст сообщения>. MikroTik в BSD syslog режиме не передаёт topic (system, firewall, l2tp) как отдельное структурированное поле — topic либо входит в текст сообщения, либо теряется.

Обработанное сообщение в Graylog

{
"timestamp": "2026-04-13T11:57:21.000Z",
"source": "ccr-office",
"facility": "syslogd",
"facility_num": 5,
"level": 6,
"message": "ccr-office <l2tp-KAR_Kungrad>: connected",
"mt_service": "l2tp",
"mt_detail": "<l2tp-KAR_Kungrad>: connected",
"streams": ["69d7a9b3b759cc2c88a9cff8"],
"gl2_source_input": "69d7a69bb759cc2c88a9bfc0",
"gl2_remote_ip": "10.21.10.1"
}

Поля mt_service и mt_detail добавляются pipeline-правилами (см. раздел Pipeline Rules).

Pipeline: Mikrotik (подключён к стриму Mikrotik)

Stage 0 — Извлечение полей:

rule "MT detail extract"
when
has_field("message")
then
let msg = to_string($message.message);
let content = grok("%{WORD} %{GREEDYDATA:content}", msg);
set_field("mt_detail", to_string(content["content"]));
set_field("mt_service", "other");
end

Логика: первое слово в message — hostname (дублирует source), всё после него — детали сообщения. По умолчанию mt_service ставится "other".

Stage 1 — Классификация по ключевым словам:

Каждый сервис — отдельное правило. Все правила в Stage 1 выполняются параллельно и перезаписывают mt_service если совпали.

rule "MT service L2TP"
when
contains(to_string($message.message), "l2tp", true)
then
set_field("mt_service", "l2tp");
end

rule "MT service wireguard"
when
contains(to_string($message.message), "wireguard", true)
then
set_field("mt_service", "wireguard");
end

rule "MT service account"
when
contains(to_string($message.message), "logged in", true) ||
contains(to_string($message.message), "logged out", true)
then
set_field("mt_service", "account");
end

rule "MT service dhcp"
when
contains(to_string($message.message), "dhcp", true)
then
set_field("mt_service", "dhcp");
end

rule "MT service firewall"
when
contains(to_string($message.message), "firewall", true)
then
set_field("mt_service", "firewall");
end

Третий параметр true в contains() включает регистронезависимый поиск.

Итоговая структура стейджей

Для добавления нового сервиса: найти в Search записи с mt_service: other, определить ключевое слово, создать новое правило в Stage 1.

Почему keyword matching, а не парсинг topic

MikroTik в BSD syslog режиме не передаёт topics (system, firewall, l2tp и т.д.) как структурированные поля. Topic встроен в текст сообщения в непредсказуемом формате. Переключение на RFC 5424 (non-BSD) формат может не работать корректно с Graylog. Keyword matching — надёжный и расширяемый подход для данного случая.

Итоговая иллюстрация пути лога

Путь лога от MikroTik до graylog

Источник 2: Серверные логи (Vector → GELF)

Установка Vector на сервере

bash -c "$(curl -L https://setup.vector.dev)"
sudo apt install vector -y

Или через deb-пакет:

curl -LO https://github.com/vectordotdev/vector/releases/latest/download/vector_<version>_amd64.deb
sudo dpkg -i vector_<version>_amd64.deb

Конфигурация Vector

Файл: /etc/vector/vector.yaml

sources:
journald:
type: journald

mysql_error:
type: file
include:
- /var/log/mysql/error.log

mysql_slow:
type: file
include:
- /var/lib/mysql/*-slow.log
multiline:
start_pattern: '^# Time:|^# User@Host:'
mode: halt_before
condition_pattern: '^# Time:|^# User@Host:'
timeout_ms: 1000

transforms:
tag_system:
type: remap
inputs:
- journald
source: |
.log_type = "system"
.source_host = get_hostname!()

tag_mysql:
type: remap
inputs:
- mysql_error
- mysql_slow
source: |
.log_type = "mysql"
.source_host = get_hostname!()

sinks:
graylog:
type: socket
inputs:
- tag_system
- tag_mysql
address: "172.20.109.253:12201"
mode: udp
encoding:
codec: gelf

Ключевой элемент — секция transforms, где Vector проставляет поле log_type ("system" или "mysql") до отправки в Graylog. Это поле используется pipeline'ом для маршрутизации.

Управление Vector

sudo vector validate /etc/vector/vector.yaml   # проверка конфига
sudo systemctl enable --now vector # запуск
sudo journalctl -u vector -f # просмотр логов

Чекпоинты и устойчивость

Vector хранит позицию чтения (checkpoint) в /var/lib/vector/. При перезапуске продолжает с того места где остановился. При падении сервера и последующей загрузке Vector отправит все пропущенные сообщения из journald включая записи о причине падения (kernel panic, OOM killer).

Input в Graylog

Тип: GELF UDP, порт: 12201. Обязательно проверить проброс порта 12201:12201/udp в docker-compose.

Как выглядит сырое сообщение от Vector

Пример системного лога (journald → Vector → GELF → Graylog):

{
"timestamp": "2026-04-13T12:17:01.096Z",
"source": "ideaconc-idea-website-database",
"message": "(root) CMD (cd / && run-parts --report /etc/cron.hourly)",

"log_type": "system",
"source_host": "ideaconc-idea-website-database",
"source_type": "journald",

"SYSLOG_IDENTIFIER": "CRON",
"SYSTEMD_UNIT": "cron.service",
"SYSTEMD_SLICE": "system.slice",
"SYSTEMD_CGROUP": "/system.slice/cron.service",
"CMDLINE": "/bin/sh -c \"cd / && run-parts --report /etc/cron.hourly\"",
"EXE": "/usr/bin/dash",
"COMM": "sh",
"PID": "154760",
"UID": "0",
"GID": "0",
"PRIORITY": "6",
"SYSLOG_FACILITY": "9",
"SYSLOG_PID": "154760",
"SYSLOG_TIMESTAMP": "Apr 13 12:17:01",
"BOOT_ID": "95473fbb6f4144f28a646fa5019a354f",
"MACHINE_ID": "77fc4b77772f42b198159ebc5b6fb325",
"RUNTIME_SCOPE": "system",
"SELINUX_CONTEXT": "unconfined",
"AUDIT_SESSION": "4142",
"AUDIT_LOGINUID": "0",
"CAP_EFFECTIVE": "1ffffffffff",
"SYSTEMD_INVOCATION_ID": "96050e21f2f1471a8a6c76b91758a78d",

"gl2_source_input": "69d8fec885ff8306a5fc1252",
"gl2_remote_ip": "172.18.10.6",
"streams": ["69d90ac785ff8306a5fc3322", "69d8ff6e85ff8306a5fc15bc"]
}

Поля log_type, source_host, source_type — проставлены Vector'ом. Поля с заглавными буквами (SYSTEMD_UNIT, SYSLOG_IDENTIFIER и т.д.) — метаданные journald. Поля gl2_* — добавлены Graylog при приёме.

Pipeline: Vector Router (подключён к Default Stream)

rule "Route system logs"
when
has_field("log_type") AND to_string($message.log_type) == "system"
then
route_to_stream(name: "Servers", remove_from_default: true);
end

rule "Route MySQL logs"
when
has_field("log_type") AND to_string($message.log_type) == "mysql"
then
route_to_stream(name: "MySQL", remove_from_default: true);
end

Флаг remove_from_default: true убирает сообщение из Default Stream, чтобы оно не дублировалось в Default Index Set.


Pipeline Rules — синтаксис и примеры

Структура правила

rule "Название правила"
when
<условие — boolean выражение>
then
<действия>
end

Ограничения синтаксиса

  • Нет else if и else — только простой if/then/end внутри then-блока
  • Каждый if должен завершаться end; (точка с запятой после end)
  • Для реализации логики "если A — сделай X, если B — сделай Y" используйте отдельные правила в одном Stage

Полезные функции

ФункцияОписание
has_field("name")Проверяет наличие поля
to_string($message.field)Получает значение поля как строку
contains(str, substr, ignore_case)Проверяет содержание подстроки (третий параметр true = регистронезависимо)
set_field("name", value)Создаёт или перезаписывает поле
route_to_stream(name: "...", remove_from_default: true)Направляет в стрим
grok(pattern, string)Парсит строку по grok-паттерну
split(delimiter, string)Разбивает строку на массив

Стратегия: Stages vs Rules

Stage 0 — извлечение полей, установка дефолтных значений. Выполняется первым.

Stage 1+ — классификация, маршрутизация. Использует поля, установленные на Stage 0.

Правила внутри одного Stage выполняются параллельно. Если несколько правил совпали — все выполнятся (последнее перезапишет поле).

Куда подключать Pipelines

  • Pipeline маршрутизации (route_to_stream) → подключать к Default Stream
  • Pipeline обогащения (set_field, grok) → подключать к целевому стриму

Index Sets — управление хранением

Рекомендуемая конфигурация

Index SetПрефиксRetentionНазначение
Defaultgraylog40 днейНераспределённые сообщения
Networkmikrotik7 днейСетевое оборудование (MikroTik, SNR, Kerio)
Serversservers30 днейСистемные логи (journald)
MySQLmysql60 днейMySQL error/slow logs
Applicationsapps30-60 днейLaravel, Next.js, другие приложения

Почему разные Index Sets

  • Разный retention — сетевые логи теряют актуальность за дни, slow queries полезны месяцами
  • Оптимизация поиска — при запросе к стриму OpenSearch просматривает только релевантные индексы
  • Управление диском — видно какой тип логов занимает больше всего места

Параметры для single-node

  • Shards: 1 (больше не нужно на одном ноде)
  • Replicas: 0 (нет второго нода для реплик)
  • Rotation strategy: Data Tiering (дефолт Graylog 7)

Рекомендации по масштабированию

Добавление нового MikroTik

  1. Задать identity: /system/identity/set name=shop-NNN-gw
  2. Настроить logging action на Graylog IP/порт
  3. Логи автоматически попадут в стрим Mikrotik через существующий pipeline

Добавление нового сервера

  1. Установить Vector нативно (apt install vector)
  2. Скопировать шаблон /etc/vector/vector.yaml с нужными sources
  3. Указать log_type в transforms для каждого источника
  4. Логи автоматически попадут в нужные стримы через pipeline "Vector Router"

Добавление Laravel (idea-store)

Вариант 1 — GELF напрямую из Laravel через Monolog:

// config/logging.php
'graylog' => [
'driver' => 'monolog',
'handler' => \Monolog\Handler\GelfHandler::class,
'handler_with' => [
'publisher' => new \Gelf\Publisher(
new \Gelf\Transport\UdpTransport('graylog-server', 12201)
),
],
'level' => 'debug',
],

Вариант 2 — Vector читает storage/logs/laravel.log и шлёт в Graylog. Проще в настройке, но теряются structured-поля.