Парсинг логов с grok

Главный инструмент превращения сырой строки в структуру — фильтр grok и его язык шаблонов.

grok — фильтр, который сопоставляет строку лога с шаблоном из именованных кусочков-регулярок и извлекает совпавшие части в отдельные поля.

Проблема, которую решает grok

Логи многих сервисов — это неструктурированный текст. Строка лога nginx выглядит так:

192.168.1.10 - - [24/Jun/2026:14:32:07 +0000] "GET /api/orders HTTP/1.1" 500 234

Для человека читаемо, для машины — мусор: это одно поле message, по которому нельзя ни отфильтровать по status: 500, ни построить график ошибок по path. grok разбирает её на поля.

Анатомия grok-шаблона

Базовый синтаксис — %{ШАБЛОН:имя_поля}. ШАБЛОН — это готовая именованная регулярка (например, IP, NUMBER, WORD), имя_поля — куда положить результат. Разберём ту же строку nginx вручную:

filter {
  grok {
    match => { "message" => "%{IPORHOST:client_ip} - - \[%{HTTPDATE:timestamp}\] \"%{WORD:method} %{URIPATHPARAM:path} HTTP/%{NUMBER:http_version}\" %{NUMBER:status:int} %{NUMBER:bytes:int}" }
  }
}

Результат — структурированный документ:

{
  "client_ip": "192.168.1.10",
  "method": "GET",
  "path": "/api/orders",
  "status": 500,
  "bytes": 234
}

Обратите внимание на %{NUMBER:status:int} — третий сегмент :int приводит поле к числу, иначе status будет строкой и по нему нельзя считать числовые агрегаты.

Готовые шаблоны

Писать всё с нуля не нужно: Logstash несёт сотни готовых паттернов, и для типовых форматов есть составные. Тот же лог nginx целиком покрывается одним %{COMBINEDAPACHELOG}. Полезные базовые шаблоны:

ШаблонЧто ловит
%{IP}IP-адрес
%{NUMBER}число
%{WORD}слово без пробелов
%{HTTPDATE}дата в формате логов Apache
%{LOGLEVEL}уровень (INFO, ERROR...)
%{GREEDYDATA}«всё остальное» до конца строки

Как работает под капотом

Под капотом grok — это просто регулярные выражения. Каждый %{ШАБЛОН} разворачивается в кусок regex, шаблоны вкладываются друг в друга (COMBINEDAPACHELOG состоит из десятка мелких), и итоговая большая регулярка применяется к строке. Отсюда и главная слабость: grok может быть медленным и «прожорливым» на сложных строках, потому что движок regex перебирает варианты с откатами (backtracking).

Отладка grok

Составить рабочий grok с первого раза почти никогда не выходит. В Kibana есть Grok Debugger (Dev Tools → Grok Debugger): вставляете образец строки, пишете шаблон и сразу видите, что извлеклось, а что нет. Это незаменимый инструмент — отлаживайте шаблон там, а не методом «поправил конфиг → перезапустил Logstash → посмотрел». Если строка не совпала с шаблоном, Logstash вешает на событие тег _grokparsefailure — по нему легко найти все строки, которые не распарсились.

  Grok Debugger:
  +---------------------------+
  | образец: 192.168.1.10 ... |
  | шаблон:  %{IP:client_ip}..|
  +---------------------------+
  | результат: { client_ip:.. } |  -- видно сразу
  +---------------------------+

Стратегия построения шаблона

Хороший рабочий процесс с grok выглядит так. Сначала возьмите несколько разных образцов строк лога, а не одну — форматы часто варьируются (есть строки с дополнительным полем, есть с пустыми значениями). Затем стройте шаблон от крупного к мелкому: сперва опишите общую структуру составными шаблонами, и только если их не хватает — спускайтесь к мелким WORD, DATA. Завершайте строку шаблоном GREEDYDATA для «хвоста», чтобы видеть, что осталось неразобранным, — это помогает не потерять данные молча. И обязательно прогоните на всех образцах в Grok Debugger: шаблон, идеально совпавший с одной строкой, нередко спотыкается на соседней с чуть иным форматом.

Важно также понимать DATA против GREEDYDATA. Первый — «нежадный», берёт минимум символов до следующего якоря в шаблоне; второй — «жадный», хватает всё до конца. Неправильный выбор между ними — частая причина, почему grok извлекает не то: жадный шаблон в середине строки «съедает» поля, которые вы хотели разобрать отдельно. Осознанное чередование жадных и нежадных кусков — половина искусства написания grok. А если grok оказывается слишком медленным или хрупким, это нередко сигнал, что пора не оттачивать регулярку, а перейти к структурированному логированию из следующего раздела — лучший grok тот, которого нет.

Частые ошибки

  • Забыть :int / :float. Числа без приведения типа индексируются как строки — агрегации и сравнения status >= 500 не работают.
  • Злоупотреблять GREEDYDATA. Жадный %{GREEDYDATA} в середине шаблона провоцирует катастрофический backtracking и тормозит весь Logstash.
  • Игнорировать _grokparsefailure. Если формат лога чуть изменился, шаблон молча перестаёт совпадать. Мониторьте тег _grokparsefailure, чтобы ловить это.

Итоги

  • grok превращает неструктурированную строку лога в поля через шаблоны вида %{ШАБЛОН:имя}.
  • Суффикс :int/:float приводит поле к числу — без него не будет числовых агрегаций.
  • Под капотом grok — это вложенные регулярки; жадные шаблоны опасны замедлением.
  • Отлаживайте в Kibana Grok Debugger и следите за тегом _grokparsefailure.
Проверьте себя
1. Что делает фильтр grok?
AСжимает строки логов
BСопоставляет строку с шаблоном из именованных регулярок и извлекает части в отдельные поля
CОтправляет логи в Kibana
DУдаляет дубликаты событий
2. Зачем в шаблоне писать %{NUMBER:status:int}, а не %{NUMBER:status}?
AЭто просто стилистическое предпочтение
BСуффикс :int приводит поле к числу, иначе оно будет строкой и числовые агрегации/сравнения не сработают
Cint ускоряет парсинг
DБез int поле не извлечётся вовсе
3. Каким тегом Logstash помечает событие, строка которого не совпала с grok-шаблоном?
A_nomatch
B_grokparsefailure
C_error
D_skipped