Как извлечь объекты Serilog .ForContext (), зарегистрированные в Elasticsearch, используя ElasticClient (NEST)? - PullRequest
0 голосов
/ 14 марта 2019

Мы стремимся к лучшей централизации, структурированию и поиску в наших журналах с использованием стека ELK. В настоящее время мы регистрируемся в таблице базы данных, и у нас есть EventLog POCO, который захватывает поля, необходимые для заполнения этих строк. Нашей первой попыткой получить эти данные в Elasticsearch было простое ведение журнала событий через Serilog следующим образом:

EventLog eventLog = ...;
log.ForContext("EventLog", eventLog, true).Write(eventLog.EventMessage);

В результате получается документ типа «logevent» Serilog с «полем», которое содержит нашу пользовательскую структуру и дает нам большую часть того, что мы хотим:

{
  "_index": "logstash-2019.03.14",
  "_type": "logevent",
  "_id": "p_amfGkBaphWeJXGjjSW",
  "_version": 1,
  "_score": null,
  "_source": {
    "@timestamp": "2019-03-14T09:41:21.6924251-05:00",
    "level": "Information",
    "messageTemplate": "blah",
    "message": "blah",
    "fields": {
      "EventLog": {
        "_typeTag": "EventLog",
        "EventMessage": "blah",
        "EventId": 3112,
...

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

Теперь мы хотим программно выполнить поиск в наших журналах, и мы ищем NEST для этого, но мы не можем точно определить правильные заклинания. ElasticClient.Search<T> кажется нам тем, что нам нужно, мы не уверены, что нужно T, и как NEST интерпретирует критерии поиска или возвращенные данные.

Что мы пробовали:

client.Search<EventLog>();   // returns zero results
client.Search<Serilog.Events.LogEvent>();   // throws: Error converting string to Serilog.Events.MessageTemplate
client.Search<EventLog>(s => s.Type("logevent")); // returns results!, but the EventLog objects are all empty (properties all have default values)

Единственный звонок, который, кажется, приближает нас к чему-либо, это:

client.Search<Object>(s => s.Type("logevent")); // returns the raw JSON source of each log entry

Итак, мы чувствуем, что упускаем что-то простое и фундаментальное здесь. Все документы NEST основаны на типе Project, который индексируется непосредственно в Elasticsearch, но что нам нужно сделать, чтобы иметь возможность извлекать и запрашивать наши пользовательские структуры, которые мы добавляем в журнал, используя ForContext? * Serilog? 1022 *

ОБНОВЛЕНИЕ: Похоже, нам может понадобиться создать отдельный набор POCO, которые повторяют структуру события Serilog, например:

        private class DummyFields
        {
            public EventLog EventLog { get; set; }
        }

        private class Dummy
        {
            public DummyFields Fields { get; set; }
        }

Запросы на Dummy в NEST действительно дают нам доступ к нашей внутренней структуре, но, похоже, должен быть лучший способ сделать это ...

ОБНОВЛЕНИЕ 2: Мы в порядке с реализацией POCO только для сопоставления ответов на запросы, но теперь кажется, что есть другая проблема с сопоставлением полей внутри нашей вложенной структуры:

client.Search<Dummy>(s => s
    .AllTypes()
    .Query(q => q
        .Match(t => t
            .Field(f => f.Message).Query("blah"); // returns some results

client.Search<Dummy>(s => s
    .AllTypes()
    .Query(q => q
        .Match(t => t
            .Field(f => f.Fields.EventLog.EventMessage).Query("blah"); // returns zero results

Почему второй запрос не "видит" наше вложенное сообщение о событии?

ОБНОВЛЕНИЕ 3:

Любопытно, что это также работает:

client.Search<Dummy>(s => s
    .AllTypes()
    .Query(q => q
        .Match(t => t
            .Field(f => new Field("fields.EventLog.EventMessage")).Query("blah");

Так что похоже, что NEST просто не соответствует типобезопасной версии вложенного поля. Очевидно, мы бы предпочли не полагаться на магические нити здесь ...

ОБНОВЛЕНИЕ 4:

Я включил ведение журнала запросов в ES и увидел, что между запросами литералов и выражений есть разница в регистре. Литеральный запрос (со свойствами PascalCased) рассматривается ES как Pascal и находит документы, в то время как безопасное от типа выражение преобразуется в camelCase ("fields.eventLog.eventMessage") и ничего не соответствует. Конечно же, если я сделаю запрос с буквенным путем поля camelCased, я тоже ничего не получу.

Как мне сказать NEST, чтобы использовать правильную чувствительность к регистру?

1 Ответ

0 голосов
/ 15 марта 2019

Исходный документ JSON, отправленный в Elasticsearch, содержится в свойстве _source каждого попадания в поисковом ответе

{
    "@timestamp": "2019-03-14T09:41:21.6924251-05:00",
    "level": "Information",
    "messageTemplate": "blah",
    "message": "blah",
    "fields": {
      "EventLog": {
        "_typeTag": "EventLog",

"EventLog", к которому, я полагаю, вы пытаетесь добраться, находится по адресупуть JSON "fields.EventLog", поэтому любой CLR POCO, который будет отображаться в этом и в который будет десериализован JSON, должен соответствовать этой структуре, как ваш тип Dummy выше.

Там будетЧтобы изменить это, можно воспользоваться двумя подходами:

  1. сериализовать "EventLog" как весь документ JSON в _source.После этого вы сможете напрямую сопоставить его с вашим "EventLog" типом.

  2. Реализовать пользовательский сериализатор, который десериализует только "fields.EventLog" в данный тип EventLog POCO.Вы сможете сделать это с JsonNetSerializer и реализовать пользовательский тип JsonConverter для EventLog.Есть две сложности, чтобы сделать это, хотя;во-первых, это будет работать только как модель чтения, потому что при повторной сериализации при желании результирующий JSON будет only быть JSON для EventLog, а во-вторых, использование JsonNetSerializer снижает производительность по сравнению свнутренний сериализатор используется NEST.

...