Opensearch, Logstash и dynamic mapping

6725d85aefa9226aff3adaa21f75ce9d.jpg

У нас в Домклик огромное количество микросервисов, около 5000. Все они пишут какие‑то логи. В этой статье я хочу рассказать о том, как у нас настроен маппинг в индексах Opensearch и какие «фишки» мы используем, чтобы минимизировать работы по настройке маппинга.

Введение

Конечно же, логи не пишутся в один общий индекс. Для разделения мы используем понятие «продукты», то есть несколько сервисов объединяются в продукт. Всего у нас около 200 продуктов и 200 индексов. Мы не регулируем наименование и вложенность полей, только количество (не более 200 на индекс).

В Opensearch есть чудесная функция — динамический маппинг. Его суть в том, что Opensearch по умолчанию пытается типизировать поле как текст. Но есть и ложка дёгтя (даже половник), маппинг не работает для вложенных полей, если родительские поля не заданы явно как object. Например, для поля host.name потребуется такая конфигурация, иначе будет ошибка:

"mappings": {  
  "properties": {  
    "host": {  
      "type": "object"  
    }
  }  
}

Что делать, чтобы ненастраивать маппинг сотен полей в сотнях индексах руками, а также не маппить всё как текст, а получить хоть какую‑то типизацию автоматически? Вот вам пара фишек.

Фишка №1. logstash-filter-flatten_json

Фильтр для Logstash. Нашли его на github и чуть‑чуть доработали. Превращает поля JSON в «плоские», то есть добавляет в название поля всех его «родителей» через точку и выносит его на самый верхний уровень. Например, такой JSON:

{
    "key_1": "value_1",
    "key_3": {
        "nested_key_1": "nested_value_1"
    }
}

превращается в такой:

{
    "key_1": "value_1",
    "key_3.nested_key_1": "nested_value_1"
}

Если поле содержит массив, то значения в нём приводятся к строке и дальше вложенность не парсится. Если вам такое не подходит, то можно взять исходный плагин из оригинального репозитория, в котором значения массивов парсятся как отдельные поля. Этот плагин решает проблему с динамическим маппингом, гарантируя, что все поля будут проиндексированы как минимум как текст.

Фишка №2. Динамическое распознавание типов

Две функции динамического маппинга:

"mappings": {
  "date_detection": true,
  "dynamic_date_formats": ["yyyy-MM-dd HH:mm:ss.SSS 'MSK'"],
  "numeric_detection": true
}

date_detection — распознавание дат. В массиве dynamic_date_formats можно указать любые форматы, подробнее описано в тут (в документации Opensearch этого нет, но есть в документации Elasticsearch).

numeric_detection — распознавание чисел (long, float и т. д.)

Фишка №3. Очистка пустых полей

Бывает, что какое‑то поле приходит пустым или с прочерком. Прочерк считается текстом и проиндексировать такое поле как, например, число не получится. Для этого написали небольшой фильтр для Logstash на Ruby:

ruby {  
    code => "  
        Hash[event].each { |k, v|  
          if v.kind_of?(Array)  
              if v.empty? or v.include? '-'
                  event.remove(k)  
              end  
          else  
              if v == '-'  
                event.remove(k)  
              end  
          end  
        }  
    "  
}

Это «лечит» логи, которые не попали бы в индекс из‑за неверной типизации.

Фишка №4. Индексирование поля с несколькими типами

Для построения дашбордов со статистикой требуется тип keyword. Но поля с таким типом плохо подходят для полнотекстового поиска. Решением будет индексировать их с двумя типами одновременно. Для этого добавляем блок fields:

"mappings": {  
  "dynamic_templates": [{  
    "string_fields": {
      "mapping": {
        "type": "text",
        "fields": {
          "keyword": {
            "ignore_above": 256,
            "type": "keyword"
          }
        }
      },
      "match_mapping_type": "string",
      "match": "*"
    }
  }]
}

Таким образом мы сможем и построить дашборд по таким полям, и использовать все функции полнотекстового поиска.

Подведем итоги

Благодаря этим «фишкам» мы не настраиваем около 90% индексов. Все поля в них автоматически распознаются и типизируются как текст, даты и числа. Для остальных 10% приходится настраивать небольшой маппинг руками. Например, если поле содержит IP‑адрес.

Правильная типизация позволяет делать дашборды со статистикой по логам, более гибкий поиск со сравнением чисел и дат, вхождение IP по маске и так далее.

© Habrahabr.ru