Giving the following logstash pipeline:
input
{
generator
{
lines => [
'{"name" : "search", "product" : { "module" : "search" , "name" : "api"}, "data" : { "query" : "toto"}}',
'{"name" : "user_interaction", "product" : { "module" : "search" , "name" : "front"}, "data" : { "query" : "toto"}}',
'{"name" : "search", "product" : { "module" : "search" , "name" : "api"}, "data" : { "query" : "toto"}}',
'{"hello": "world"}',
'{"name" :"wrong data", "data" : "I am wrong !"}',
'{"name" :"wrong data", "data" : { "hello" : "world" }}'
]
codec => json
count => 1
}
}
filter
{
mutate
{
remove_field => ["sequence", "host", "#version"]
}
}
output
{
elasticsearch
{
hosts => ["elasticsearch:9200"]
index => "events-dev6-test"
document_type => "_doc"
manage_template => false
}
stdout
{
codec => rubydebug
}
}
elasticsearch has a strict mapping for this index, hence, some events are giving 400 error "mapping set to strict, dynamic introduction of [hello] within [data] is not allowed" (which is normal).
How to send failed events elsewhere (text logs or another elasticsearch index) (so I dont lost events) ?
Logstash 6.2 introduced Dead Letter Queues that can be used to do what you want. You'll need to enable dead_letter_queue.enable: true in your logstash.yml.
And then just deal with it as an input:
input {
dead_letter_queue {
path => "/path/to/data/dead_letter_queue"
commit_offsets => true
pipeline_id => "main"
}
}
output {
file {
path => ...
codec => line { format => "%{message}"}
}
}
Prior to 6.2, I don't believe there was a way to do what you want.
Related
I'm trying to parse JSON logs being generated from a system in the form of:
[{"name" : "test1", "state" : "success"},
{"name" : "test2", "state" : "fail"},
{"name" : "test3", "state" : "success"}]
This is a valid JSON file however I am not able to parse it using the json filter.
My current logstash.config file looks like this:
input {
file {
path => "C:/elastic_stack/json_stream/*.json"
start_position => "beginning"
sincedb_path => "NUL"
}
}
filter{
mutate {
gsub => [ "message","\]",""]
gsub => [ "message","\[",""]
gsub => [ "message","\},","}"]
}
json{
source=> "message"
}
}
output {
elasticsearch {
hosts => ["localhost:9200"]
index => "logstashindex"
}
stdout { codec => rubydebug }
}
I have tried adding a bunch of regex filters with gsub but since I'm pretty new I'm not sure if that's the cleanest and most efficient parsing solution. Any suggestion?
Trying to parse this multiline JSON file
{
"eventSource" : { "objectName": "SYSTEM.ADMIN.CHANNEL.EVENT",
"objectType" : "Queue" },
"eventType" : {
"name" : "Channel Event",
"value" : 46
},
"eventReason" : {
"name" : "Channel Blocked",
"value" : 2577
},
"eventCreation" : {
"timeStamp" : "2018/03/07 05:50:19.06 GMT",
"epoch" : 1520401819
},
"eventData" : {
"queueMgrName" : "QMG1",
"connectionName" : "localhost (192.168.10.1)",
"connectionNameList" : [
"localhost"
],
"reasonQualifier" : "Channel Blocked Noaccess",
"channelName" : "SVR.TEST",
"clientUserId" : "test1",
"applName" : "WebSphere MQ Client for Java",
"applType" : "Java"
}
}
filebeat is configured as
filebeat.prospectors:
- type: log
paths:
- /var/log/test2.log
fields:
tags: ['json']
logsource: mqjson
fields_under_root: true
input beats conf is as below.
input {
beats {
port => 5400
host => "192.168.205.11"
ssl => false
#ssl_certificate => "/etc/pki/tls/certs/logstash-beats.crt"
#ssl_key => "/etc/pki/tls/private/logstash-beats.key"
}
}
filter {
if [tags][json] {
json {
source => "message"
}
}
}
In elastic each line is a record.
Question:
How do i parse this multi line json
Also is there an option to extract certain keys, like eventData section.
by adding as below converts the json. There was an issue opened in elastic which was corrected in 6.0
processors:
- decode_json_fields:
fields: ['message']
target: json
I want to convert a field to a json list, sg like this:
"person": {
"name":"XX",
"adress":"124"
}
to
"person": [{"name":"XX",
"adress":"124"}]
Thank you for help.
A bit of ruby magic will do here:
input {
stdin{}
}
filter {
ruby {
code => "
require 'json'
event['res'] = [JSON.parse(event['message'])['person']]
"
}
}
output {
stdout { codec => rubydebug }
}
This will simply parse your message field containing your Json document, then extract the person object and add it to a field.
The test looks as such:
artur#pandaadb:~/dev/logstash$ ./logstash-2.3.2/bin/logstash -f conf_json_list/
Settings: Default pipeline workers: 8
Pipeline main started
{ "person": { "name":"XX", "adress":"124" }}
{
"message" => "{ \"person\": { \"name\":\"XX\", \"adress\":\"124\" }}",
"#version" => "1",
"#timestamp" => "2017-03-15T11:34:37.424Z",
"host" => "pandaadb",
"res" => [
[0] {
"name" => "XX",
"adress" => "124"
}
]
}
As you can see, your hash now lives in a list on index 0.
Hope that helps,
Artur
I wanted to make a copy of a nested field in a Logstash filter but I can't figure out the correct syntax.
Here is what I try:
incorrect syntax:
mutate {
add_field => { "received_from" => %{beat.hostname} }
}
beat.hostname is not replaced
mutate {
add_field => { "received_from" => "%{beat.hostname}" }
}
beat.hostname is not replaced
mutate {
add_field => { "received_from" => "%{[beat][hostname]}" }
}
beat.hostname is not replaced
mutate {
add_field => { "received_from" => "%[beat][hostname]" }
}
No way. If I give a non nested field it works as expected.
The data structure received by logstash is the following:
{
"#timestamp" => "2016-08-24T13:01:28.369Z",
"beat" => {
"hostname" => "etg-dbs-master-tmp",
"name" => "etg-dbs-master-tmp"
},
"count" => 1,
"fs" => {
"device_name" => "/dev/vdb",
"total" => 5150212096,
"used" => 99287040,
"used_p" => 0.02,
"free" => 5050925056,
"avail" => 4765712384,
"files" => 327680,
"free_files" => 326476,
"mount_point" => "/opt/ws-etg/datas"
},
"type" => "filesystem",
"#version" => "1",
"tags" => [
[0] "topbeat"
],
"received_at" => "2016-08-24T13:01:28.369Z",
"received_from" => "%[beat][hostname]"
}
EDIT:
Since you didn't show your input message I worked off your output. In your output the field you are trying to copy into already exists, which is why you need to use replace. If it does not exist, you do in deed need to use add_field. I updated my answer for both cases.
EDIT 2: I realised that your problem might be to access the value that is nested, so I added that as well :)
you are using the mutate filter wrong/backwards.
First mistake:
You want to replace a field, not add one. In the docs, it gives you the "replace" option. See: https://www.elastic.co/guide/en/logstash/current/plugins-filters-mutate.html#plugins-filters-mutate-replace
Second mistake, you are using the syntax in reverse. It appears that you believe this is true:
"text I want to write" => "Field I want to write it in"
While this is true:
"myDestinationFieldName" => "My Value to be in the field"
With this knowledge, we can now do this:
mutate {
replace => { "[test][a]" => "%{s}"}
}
or if you want to actually add a NEW NOT EXISTING FIELD:
mutate {
add_field => {"[test][myNewField]" => "%{s}"}
}
Or add a new existing field with the value of a nested field:
mutate {
add_field => {"some" => "%{[test][a]}"}
}
Or more details, in my example:
input {
stdin {
}
}
filter {
json {
source => "message"
}
mutate {
replace => { "[test][a]" => "%{s}"}
add_field => {"[test][myNewField]" => "%{s}"}
add_field => {"some" => "%{[test][a]}"}
}
}
output {
stdout { codec => rubydebug }
}
This example takes stdin and outputs to stdout. It uses a json filter to parse the message, and then the mutate filter to replace the nested field. I also add a completely new field in the nested test object.
And finally creates a new field "some" that has the value of test.a
So for this message:
{"test" : { "a": "hello"}, "s" : "to_Repalce"}
We want to replace test.a (value: "Hello") with s (Value: "to_Repalce"), and add a field test.myNewField with the value of s.
On my terminal:
artur#pandaadb:~/dev/logstash$ ./logstash-2.3.2/bin/logstash -f conf2/
Settings: Default pipeline workers: 8
Pipeline main started
{"test" : { "a": "hello"}, "s" : "to_Repalce"}
{
"message" => "{\"test\" : { \"a\": \"hello\"}, \"s\" : \"to_Repalce\"}",
"#version" => "1",
"#timestamp" => "2016-08-24T14:39:52.002Z",
"host" => "pandaadb",
"test" => {
"a" => "to_Repalce",
"myNewField" => "to_Repalce"
},
"s" => "to_Repalce"
"some" => "to_Repalce"
}
The value has succesfully been replaced.
A field "some" with the replaces value has been added
A new field in the nested array has been added.
if you use add_field, it will convert a into an array and append your value there.
Hope this solves your issue,
Artur
I am trying to get kibana-4 geo map to work with ELB logs
when i click the discover tab i can clearly see a field geoip.location with values of [lat, lon]
but when i click the visualise tab -> Tile map -> new search -> Geo coordinates
i get an error (not showing anywhere what is the error i've also checked the kibana logs - but nothing is there)
I checked inspect element - also nothing
I then select GeoHash, but the field is empty (when i click on it its blank with a check icon)
How can i see what is the error ?
How can get this map to work ?
my config is:
input {
file {
path => "/logstash_data/logs/elb/**/*"
exclude => "*.gz"
type => "elb"
start_position => "beginning"
sincedb_path => "log_sincedb"
}
}
filter {
if [type] == "elb" {
grok {
match => [
"message", '%{TIMESTAMP_ISO8601:timestamp} %{NGUSERNAME:loadbalancer} %{IP:client_ip}:%{POSINT:client_port} (%{IP:backend_ip}:%{POSINT:backend_port}|-) %{NUMBER:request_processing_time} %{NUMBER:backend_processing_time} %{NUMBER:response_processing_time} %{POSINT:elb_status_code} %{INT:backend_status_code} %{NUMBER:received_bytes} %{NUMBER:sent_bytes} \\?"%{WORD:method} https?://%{WORD:request_subdomain}.server.com:%{POSINT:request_port}%{URIPATH:request_path}(?:%{URIPARAM:query_string})? %{NOTSPACE}"'
]
}
date {
match => [ "timestamp", "ISO8601" ]
target => "#timestamp"
}
if [query_string] {
kv {
field_split => "&?"
source => "query_string"
prefix => "query_string_"
}
mutate {
remove => [ "query_string" ]
}
}
if [client_ip] {
geoip {
source => "client_ip"
add_tag => [ "geoip" ]
}
}
if [timestamp] {
ruby { code => "event['log_timestamp'] = event['#timestamp'].strftime('%Y-%m-%d')"}
}
}
}
}
output {
elasticsearch {
cluster => "ElasticSearch"
host => "elasticsearch.server.com"
port => 9300
protocol => "node"
manage_template => true
template => "/etc/logstash/lib/logstash/outputs/elasticsearch/elasticsearch-template.json"
index => "elb-%{log_timestamp}"
}
}
geo_ip index did not work in my case because my index names did not started with logstash-
if you want the custom index name to get the geo-ip, you must create a template for that index name
in the output for elasticsearch use it
elasticsearch {
manage_template => true
template => "/etc/logstash/templates/custom_template.json"
}
your template should look like this
{
"template" : "index_name-*",
"settings" : {
"index.refresh_interval" : "5s"
},
"mappings" : {
"_default_" : {
"_all" : {"enabled" : true, "omit_norms" : true},
"dynamic_templates" : [ {
"message_field" : {
"match" : "message",
"match_mapping_type" : "string",
"mapping" : {
"type" : "string", "index" : "analyzed", "omit_norms" : true
}
}
}, {
"string_fields" : {
"match" : "*",
"match_mapping_type" : "string",
"mapping" : {
"type" : "string", "index" : "analyzed", "omit_norms" : true,
"fields" : {
"raw" : {"type": "string", "index" : "not_analyzed", "ignore_above" : 256}
}
}
}
} ],
"properties" : {
"#version": { "type": "string", "index": "not_analyzed" },
"geoip" : {
"type" : "object",
"dynamic": true,
"properties" : {
"location" : { "type" : "geo_point" }
}
}
}
}
}
}
On our maps, we specify a field geoip.location which according to the documentation is automatically created by the geoip filter.
Can you see that field in discover? If not, can you try amending your geoip filter to
if [client_ip] {
geoip {
source => "client_ip"
add_tag => [ "geoip" ]
target => "geoip"
}
}
and see if you can now see geoip.location in new entries?
The elasticsearch templates look for the "geoip" target when creating the associated geoip fields.
Once we have the geoip.location being created, we can create a new map with the following steps in Kibana 4.
Click on visualise
Choose 'Tile Map' from the list of visualisation types
Select either new search or saved - we're using a saved search that filters out Apache entries, but as long as the data contains geoip.location you should be good
Select the 'geo coordinates' bucket type - you'll have an error flagged at this point
In 'aggregation' dropdown, select 'geohash'
In 'field' dropdown, select 'geoip.location'