Обогащение событий: mutate, date, geoip, drop
Набор фильтров, которые доводят разобранное событие до пригодного для аналитики вида.
Обогащение — добавление к событию новых полезных полей и приведение существующих к правильному виду уже после первичного разбора строки.
Зачем обогащать
grok извлёк поля — но этого мало. Время в логе записано строкой, а нужен настоящий @timestamp. По IP хочется видеть страну на карте. Лишние технические поля надо выкинуть, имена — привести к единому стилю. Этим занимается набор фильтров обогащения.
date: правильное время события
Самый важный фильтр. По умолчанию @timestamp события — это момент, когда Logstash обработал строку, а не когда событие произошло. Если логи пришли с задержкой или переотправились, время поедет. Фильтр date берёт извлечённое grok'ом текстовое поле времени и кладёт его в @timestamp:
filter {
date {
match => [ "timestamp", "dd/MMM/yyyy:HH:mm:ss Z" ]
target => "@timestamp"
}
}Теперь событие на временной шкале Kibana стоит там, где реально случилось.
mutate: правка полей
Универсальный фильтр-редактор. Умеет переименовывать, удалять, преобразовывать тип, приводить регистр, обрезать пробелы:
filter {
mutate {
rename => { "client_ip" => "source.ip" }
convert => { "bytes" => "integer" }
remove_field => [ "timestamp", "http_version" ]
uppercase => [ "method" ]
}
}Здесь мы переименовали поле под стандарт ECS, привели bytes к числу, удалили уже ненужное текстовое timestamp (его значение мы перенесли в @timestamp) и привели метод к верхнему регистру.
geoip: география по IP
Фильтр geoip по IP-адресу добавляет страну, город и координаты из встроенной базы MaxMind. Это превращает скучный список IP в карту запросов в Kibana:
filter {
geoip {
source => "source.ip"
target => "geo"
}
}В событие добавятся geo.country_name, geo.city_name, geo.location (координаты для карты). Сразу видно, откуда идёт трафик и не льётся ли подозрительный поток из одной страны.
drop: отсев шума
Не всё стоит хранить. Шумные DEBUG-логи или health-check'и (GET /health каждую секунду) раздувают индекс и кошелёк. Фильтр drop выбрасывает событие по условию ещё до индексации:
filter {
if [path] == "/health" or [level] == "DEBUG" {
drop { }
}
}Это один из самых дешёвых способов сократить объём логов: не собирать то, что не нужно.
Как работает под капотом: всё это просто мутация хеша
Событие внутри Logstash — это структура (по сути словарь полей). Каждый фильтр получает её, что-то меняет и передаёт дальше. geoip лезет в локальную базу MaxMind (никаких внешних запросов на каждое событие — иначе было бы катастрофически медленно), date парсит строку в timestamp, mutate правит ключи. Поскольку фильтры идут по цепочке, важно ставить drop как можно раньше: нет смысла тратить geoip и grok на событие, которое всё равно выбросим.
Порядок и стоимость обогащения
Фильтры обогащения сильно различаются по стоимости, и это влияет на их порядок. mutate и date дёшевы — это операции в памяти над уже распарсенным событием. geoip дороже, потому что обращается к базе данных (пусть и локальной) на каждое событие. Поэтому общая стратегия такая: сначала максимально дёшево отсеять ненужное (drop в самом начале), затем разобрать (grok или json), привести время (date), почистить поля (mutate), и только в конце применять дорогие обогащения вроде geoip к тем событиям, которые точно сохранятся. Каждое дорогое обогащение, применённое к событию, которое потом выбросят, — это зря потраченные ресурсы, умноженные на объём потока.
Отдельно стоит сказать про обогащение из внешних источников. Помимо geoip, в Logstash есть фильтры вроде translate (подмена значения по словарю — например, код ошибки в человекочитаемое описание) и elasticsearch (дотянуть данные из другого индекса). Они мощные, но каждое внешнее обращение замедляет конвейер, поэтому такие обогащения кэшируют и применяют осмотрительно. Идея обогащения в том, чтобы сделать лог самодостаточным для чтения в момент записи, а не заставлять читателя потом гадать, что означает загадочный код, — но платить за это разумную цену.
Частые ошибки
- Не настроить date. Без
dateвсе события встают на временной шкале по времени обработки — анализ инцидента «что было в 14:32» становится бессмысленным. - drop в конце цепочки. Если отбрасывать шум после geoip и grok, вы зря тратите ресурсы на обработку выбрасываемых событий. Ставьте
dropв начало. - geoip на приватные IP. Для
10.x/192.168.xgeoip ничего не найдёт и создаст пустые поля. Применяйте geoip только к публичным адресам.
Итоги
dateпереносит реальное время события в@timestamp— без него временная шкала врёт.mutateпереименовывает, удаляет и приводит типы полей;geoipдобавляет географию по IP.dropотсеивает шум (DEBUG, health-check) до индексации и экономит объём — ставьте его раньше grok/geoip.- Событие — это структура полей; фильтры по цепочке её мутируют.