2017-01-19 8 views
3

Мы используем Serilog HTTP sink для отправки сообщений в Logstash. Но тело сообщения HTTP таково:Serilog HTTP sink + Logstash: разбиение массива сообщений Serilog на отдельные события журнала

{ 
    "events": [ 
    { 
     "Timestamp": "2016-11-03T00:09:11.4899425+01:00", 
     "Level": "Debug", 
     "MessageTemplate": "Logging {@Heartbeat} from {Computer}", 
     "RenderedMessage": "Logging { UserName: \"Mike\", UserDomainName: \"Home\" } from \"Workstation\"", 
     "Properties": { 
     "Heartbeat": { 
      "UserName": "Mike", 
      "UserDomainName": "Home" 
     }, 
     "Computer": "Workstation" 
     } 
    }, 
    { 
     "Timestamp": "2016-11-03T00:09:12.4905685+01:00", 
     "Level": "Debug", 
     "MessageTemplate": "Logging {@Heartbeat} from {Computer}", 
     "RenderedMessage": "Logging { UserName: \"Mike\", UserDomainName: \"Home\" } from \"Workstation\"", 
     "Properties": { 
     "Heartbeat": { 
      "UserName": "Mike", 
      "UserDomainName": "Home" 
     }, 
     "Computer": "Workstation" 
     } 
    } 
    ] 
} 

ie. события регистрации группируются в массиве. Можно отправлять сообщения один за другим, но он по-прежнему является массивом из одного элемента.

событие будет отображаться в Kibana как имеющий поле message со значением

{ 
    "events": [ 
    { 
     // ... 
    }, 
    { 
     // ... 
    } 
    ] 
} 

т.е.. буквально то, что исходило от ввода HTTP.

Как я могу разделить элементы в events массива отдельных событий лесозаготовительных и «подтянуть» свойства до верхнего уровня, так что я бы две записи событий в ElasticSearch:


"Timestamp": "2016-11-03T00:09:11.4899425+01:00", 
    "Level": "Debug", 
    "MessageTemplate": "Logging {@Heartbeat} from {Computer}", 
    "RenderedMessage": "Logging { UserName: \"Mike\", UserDomainName: \"Home\" } from \"Workstation\"", 
    "Properties": { 
    "Heartbeat": { 
     "UserName": "Mike", 
     "UserDomainName": "Home" 
    }, 
    "Computer": "Workstation" 
    } 

"Timestamp": "2016-11-03T00:09:12.4905685+01:00", 
    "Level": "Debug", 
    "MessageTemplate": "Logging {@Heartbeat} from {Computer}", 
    "RenderedMessage": "Logging { UserName: \"Mike\", UserDomainName: \"Home\" } from \"Workstation\"", 
    "Properties": { 
    "Heartbeat": { 
     "UserName": "Mike", 
     "UserDomainName": "Home" 
    }, 
    "Computer": "Workstation" 
    } 

Я попытался Logstash json и split, но я не могу это сделать Работа.

ответ

0

После обновления к Logstash 5.0 Val's solution прекратил работу из-за изменения Event API: обновление event.to_hash не было отражено в оригинале event. Для Logstash 5.0+ event.get('field') и event.set('field', value) должны использоваться аксессоры.

Обновленное решение теперь:

input { 
    http { 
    port => 8080 
    codec => json 
    } 
} 

filter { 
    split { 
    field => "events" 
    } 
    ruby { 
    code => " 
     event.get('events').each do |k, v| 
     event.set(k, v) 
     end 
    " 
    } 
    mutate { 
    remove_field => [ "events" ] 
    } 
} 
+0

Вы должны были обновить свой вопрос, чтобы упомянуть версию Logstash, в которой работало предыдущее решение, и создал новый вопрос с новой версией и вашим новым решением. – Val

+0

@Val Я не думаю, что это общий консенсус: https://meta.stackoverflow.com/q/265433/466738 https://meta.stackoverflow.com/q/268466/466738. Тем не менее, я ценю ваш ответ для Logstash 2.x! –

+0

Достаточно честный, не беспокойтесь – Val

3

Вы можете добиться того, что вы ожидаете, используя дополнительный ruby фильтр, чтобы подтянуть поля из суб-структуры:

filter { 
    split { 
    field => "events" 
    } 
    ruby { 
    code => " 
     event.to_hash.update(event['events'].to_hash) 
     event.to_hash.delete_if {|k, v| k == 'events'}  
    " 
    } 
} 

В результате событие будет выглядеть следующим образом:

{ 
      "@version" => "1", 
     "@timestamp" => "2017-01-20T04:51:39.223Z", 
       "host" => "iMac.local", 
      "Timestamp" => "2016-11-03T00:09:12.4905685+01:00", 
       "Level" => "Debug", 
    "MessageTemplate" => "Logging {@Heartbeat} from {Computer}", 
    "RenderedMessage" => "Logging { UserName: \"Mike\", UserDomainName: \"Home\" } from \"Workstation\"", 
     "Properties" => { 
     "Heartbeat" => { 
        "UserName" => "Mike", 
      "UserDomainName" => "Home" 
     }, 
     "Computer" => "Workstation" 
    } 
} 
+0

Спасибо, что помогли. Я должен был начать с добавления 'json {source =>" message "}', чтобы вытащить «события» как объект в корень события регистрации. По-видимому, Serilog отправляет сообщение без типа содержимого 'application/json', поэтому весь HTTP-объект выгружается как строка в поле 'message'. После этого ваше решение работало отлично. –

+1

Прохладный, либо вы используете фильтр 'json', либо код' json' на вашем входе. Твой выбор. Рад, что это помогло! – Val

+0

О, я пропустил решение кодека! Я действительно использую кодек 'json', чувствую себя чище. Благодаря! –

Смежные вопросы