Статьи

Полнотекстовое хранилище журналов с возможностью поиска с использованием Cassandra и Lucene

Аннотация

Лесозаготовка является общей сквозной проблемой практически для всех областей применения. Для этого у нас есть надежные библиотеки, такие как log4j / logback или jdk logging. Для многих проектов журналы хранятся в некоторой базе данных для последующего анализа.

В этой статье мы предлагаем такую ​​постоянную, доступную для поиска, масштабируемую инфраструктуру ведения журналов, которую можно настраивать или просто использовать в качестве плагина для расширения существующей среды ведения журналов приложения. Мы обсудим это как плагин Log4j, использующий пользовательское приложение Log4j для использования фреймворка.

Рабочий код можно найти по адресу:

https://github.com/javanotes/weblogs

https://github.com/javanotes/weblog4jappender

В проекте используется дополнительный индексный плагин Stratio для Cassandra; основанная на  Lucene реализация полнотекстового поиска на Cassandra. Базовый проект разрабатывается как загрузочное приложение Spring, которое может работать как встроенный контейнер сервлета или развертываться как веб-приложение. Это обеспечивает:

  1. RESTful API для приема запросов журнала. Например, приложение Log4j будет отправлять запросы регистрации POST к API
  2. Веб-консоль для просмотра и поиска по журналам, а также некоторая визуализация данных

Эта проблема

Сервис регистрации, который должен быть:

  • Постоянный — сгенерированные журналы должны быть сохранены как данные временных рядов на диске
  • Масштабируемость — сгенерированные журналы могут расти произвольно. Таким образом, инфраструктура должна быть достаточно масштабируемой, чтобы сохранять огромное количество данных.
  • Не навязчивый — регистрация должна быть «не навязчивой» сквозной задачей приложения. То есть производительность и функциональность приложения не должны быть затронуты / незначительно затронуты службой ведения журнала.
  • Централизованный . Инфраструктура должна быть централизованной, и ее могут использовать несколько клиентов.
  • Доступный для поиска — постоянные данные журнала должны быть доступны для полнотекстового поиска и поиска по дате, а также могут быть просмотрены по результатам.
  • Съемный — Решение должно быть расширяемым , так что он может быть подключен / адаптирован с любой популярной средой журналирования легко.
  • Анализируемый — некоторые аналитики, как тренды подсчета ошибок.

Решение

Постоянное и доступное для поиска хранилище данных должно быть базой данных. Хотя некоторые традиционные РСУБД (MySql, о которых я знаю) поддерживают полнотекстовый поиск, но хранение огромного количества данных с высокой скоростью записи может быстро стать узким местом. Таким образом, мы выходим из решения RDBMS.

Хранилища данных на основе Lucene, такие как SolR / Elastic Search, могут быть хорошим предложением, предпочтительно ElasticSearch, если мы примем простоту использования. Однако эти инструменты индексируют документ мудро; то есть полная запись со всеми ее полями индексируется, проще говоря. Эти решения хорошо подходят для сложных (структурно) поисковых возможностей записи, но могут быть излишними для ограниченного поискового объекта (только для поиска по фразе или термину) и без необходимости, например, поиска по релевантности.

Это приводит нас к опциям nosql. Ближайшими кандидатами, которые приходят на ум, исключительно на основе популярности (ну, по крайней мере, в результатах поиска в Интернете!), Являются Кассандра и Монгодб. На самом деле Mongo имеет встроенную поддержку полнотекстового поиска. Однако, учитывая природу временных рядов данных, кажется, что хранилище значений ключей (Cassandra) должно лучше подходить, чем ориентированное на документы (Mongo). Кроме того, учитывая высокую природу операции записи, мне показалось, что Кассандра — лучший кандидат. Последний вывод просто основан на предыдущем проекте, над которым я работал, где мы создавали прототип большой платформы для приема данных на Cassandra. Итак .. Кассандра!

Подход

При использовании Cassandra необходимо помнить следующее:

  • Основная проблема в том, что Cassandra (v2.2.3) не имеет встроенной поддержки полнотекстового поиска.
  • Пагинация набора данных, которая не является тривиальной при использовании Cassandra и имеет некоторые ограничения на предыдущие / следующие выборки.
  • Модель данных Cassandra должна быть разработана сверху вниз, то есть мы проектируем, как мы храним данные, основываясь на том, что мы хотим видеть, а не иначе
  • Ключ раздела должен основываться на чем-то, что всегда будет предоставляться при запросе. Это также должно быть достаточно для равномерного распределения данных по кластеру.
  • Столбец метки времени должен быть первым ключом кластеризации с типом данных timeuuid. Использование timeuuid будет служить двойной цели естественного упорядочения данных временных рядов, а также обеспечит уникальный ‘rowid’, который может быть полезен для запросов на нумерацию страниц.
  • Любое другое поле поиска, скажем, уровень ведения журнала, может быть сохранено в качестве последующего ключа кластеризации.

Полнотекстовый поиск

Для полнотекстового поиска мы будем использовать пользовательский дополнительный индексный плагин для Cassandra. Пользовательский вторичный индекс в Кассандре является внешним расширением Java , который использует Кассандра в качестве библиотеки динамической. Некоторые обсуждения здесь .

Stratio разработала реализацию пользовательского вторичного индекса на основе lucene в качестве части своей основной платформы больших данных, и она имеет открытый исходный код . Из их Github вики:

Это плагин для Apache Cassandra, который расширяет функциональность своего индекса для обеспечения поиска в режиме реального времени, такого как ElasticSearch или Solr, включая возможности полнотекстового поиска и бесплатный многопараметрический, геопространственный и битемпоральный поиск.

 Учитывая вышеперечисленные моменты, простая модель данных может быть следующей:

CREATE TABLE data_points (

    app_id text,

    event_ts timeuuid,

    level text,

    log_text text,

    lucene text,

    PRIMARY KEY (app_id, event_ts)

) WITH CLUSTERING ORDER BY (event_ts DESC)

The column ‘lucene’ is a dummy column that will be used by Stratio plugin to index designated fields. More details can be found in their documentation.

Then queries can be formed to fetch time-series ordered data as follows:

SELECT * FROM data_points WHERE app_id=<app_log_id> AND lucene='{filter:{type:"boolean", must:[{type:"phrase",field:"log_text",value:"<search_phrase_or_term>"},{type:"match",field:"level",value:"INFO"}],should:[],not:[]},sort:{fields:[{field:"event_ts",reverse:false}]}}' AND event_ts>=minTimeuuid(<lower_bound_timeuuid>) AND event_ts<=maxTimeuuid(<upper_bound_timeuuid>) LIMIT <page_size>;

 The important points to note for pagination:

  1. For NEXT — <upper_bound> is from the last record in current page
  2. For PREVIOUS — <lower_bound> is from the first record in current page
  3. For NEXT, the limit is from ‘head‘ and for PREVIOUS it will be from ‘tail‘. Accordingly for a PREVIOUS query, the timeuuid should be sorted in reverse
  4. Skipping to page numbers is not supported, since we do not have a concept of ‘offset’