Logstash - convert JSON to readable format - during logging to a file - json

I have a Logstash configuration as given below:
input {
udp {
port => 5043
codec => json
}
}
output {
file {
path => "/logfile.log"
}
}
I am trying to log messages in the "logfile.log" which are more readable.
So if my input data is like {"attr1":"val1","attr2":"val2"}
I want to write it in the log as:
attr1_val1 | attr2_val2
Basically converting data from JSON to a readable format.
What should I be modifying in my Logstash configuration to do that?

The message_format option of the file output allows you to specify how each message should be formatted. If the keys of your messages are fixed and known you can simply do this:
output {
file {
message_format => "attr1_%{attr1} | attr2_%{attr2}"
...
}
}
To handle arbitrary fields you'll probably have to write some custom Ruby code using the ruby filter. The following filter, for example, produces the same results as above but doesn't require you to hardcode the names of the fields:
filter {
ruby {
code => '
values = []
event.to_hash.each { |k, v|
next if k.start_with? "#"
values << "#{k}_#{v.to_s}"
}
event["myfield"] = values.join(" | ")
'
}
}
output {
file {
message_format => "%{myfield}"
...
}
}

Related

Parsing JSON objects with arbitrary keys in Logstash

Consider a subset of a sample output from http://demo.nginx.com/status:
{
"timestamp": 1516053885198,
"server_zones": {
"hg.nginx.org": {
... // Data for "hg.nginx.org"
},
"trac.nginx.org": {
... // Data for "trac.nginx.org"
}
}
}
The keys "hg.nginx.org" and "track.nginx.org" are quite arbitrary, and I would like to parse them into something meaningful for Elasticsearch. In other words, each key under "server_zones" should be transformed into a separate event. Logstash should thus emit the following events:
[
{
"timestamp": 1516053885198,
"server_zone": "hg.nginx.org",
... // Data for "hg.nginx.org"
},
{
"timestamp": 1516053885198,
"server_zone": "trac.nginx.org",
... // Data for "trac.nginx.org"
}
]
What is the best way to go about doing this?
You can try using the ruby filter. Get the server zones and create a new object using the key value pairs you want to include. From the top of my head, something like below should work. Obviously you then need to map the object to your field in the index. Change the snipped based on your custom format i.e. build the array or object as you want.
filter {
ruby {
code => " time = event.get('timestamp')
myArr = []
event.to_hash.select {|k,v| ['server_zones'].include?(k)}.each do |key,value|
myCustomObject = {}
#map the key value pairs into myCustomObject
myCustomObject[timestamp] = time
myCustomObject[key] = value
myArr.push(myCustomObject) #you'd probably move this out based on nesting level
end
map['my_indexed_field'] = myArr
"
}
}
In the output section use rubydebug for error debugging
output {
stdout { codec => rubydebug }
}

How to include the filter in Logstash config file?

I am using LogStash which accepts data from a log file, which has different types of logs.
The first row represents a custom log, whereas the second row represents a log in JSON format.
Now, I want to write a filter which will parse the logs on the basis of content and finally direct all the JSON format logs to a file called jsonformat.log and the other logs into a seperate file.
You can leverage the json filter and check if it failed or not to decide where to send the event.
input {
file {
path => "/Users/mysystem/Desktop/abc.log"
start_position => beginning
ignore_older => 0
}
}
filter {
json {
source => "message"
}
}
output {
# this condition will be true if the log line is not valid JSON
if "_jsonparsefailure" in [tags] {
file {
path => "/Users/mysystem/Desktop/nonjson.log"
}
}
# this condition will be true if the log line is valid JSON
else {
file {
path => "/Users/mysystem/Desktop/jsonformat.log"
}
}
}

How do I pretty-print JSON for an email body in logstash?

I have a Logstash configuration that I've been using to forward log messages in emails. It uses json and json_encode to parse and re-encode JSON log messages.
json_encode used to pretty-print the JSON, which made for very nice looking emails. Unfortunately, with recent Logstash upgrades, it no longer pretty prints.
Is there any way I can get a pretty form of the event into a field that I can use for the email bodies? I'm fine with JSON, Ruby debug, or most other human readable formats.
filter {
if [type] == "bunyan" {
# Save a copy of the message, in case we need to pretty-print later
mutate {
add_field => { "#orig_message" => "%{message}" }
}
json {
source => "message"
add_tag => "json"
}
}
// other filters that might add an "email" tag
if "email" in [tags] {
# pretty-print JSON for the email
if "json" in [tags] {
# re-parse the message into a field we can encode
json {
source => "#orig_message"
target => "body"
}
# encode the message, but pretty this time
json_encode {
source => "body"
target => "body"
}
}
# escape the body for HTML output
mutate {
add_field => { htmlbody => "%{body}" }
}
mutate {
gsub => [
'htmlbody', '&', '&',
'htmlbody', '<', '<'
]
}
}
}
output {
if "email" in [tags] and "throttled" not in [tags] {
email {
options => {
# config stuff...
}
body => "%{body}"
htmlbody => "
<table>
<tr><td>host:</td><td>%{host}</td></tr>
<tr><td>when:</td><td>%{#timestamp}</td></tr>
</table>
<pre>%{htmlbody}</pre>
"
}
}
}
As said by approxiblue, this issue is caused by logstash's new JSON parser (JrJackson). You can use the old parser as a workaround until pretty-print support is added again. Here is how:
You need to change two lines of the plugin's ruby file. Path should be something like:
LS_HOME/vendor/bundle/jruby/1.9/gems/logstash-filter-json_encode-0.1.5/lib/logstash/filters/json_encode.rb
Change line 5
require "logstash/json"
into
require "json"
And change line 44
event[#target] = LogStash::Json.dump(event[#source])
into
event[#target] = JSON.pretty_generate(event[#source])
That's all. After restarting logstash should pretty-print again.
Supplement:
In case you don't like changing your ruby sources you could also use a ruby filter instead of json_encode:
# encode the message, but pretty this time
ruby {
init => "require 'json'"
code => "event['body'] = JSON.pretty_generate(event['body'])"
}

Using JSON with LogStash

I'm going out of my mind here. I have an app that writes logs to a file. Each log entry is a JSON object. An example of my .json file looks like the following:
{"Property 1":"value A","Property 2":"value B"}
{"Property 1":"value x","Property 2":"value y"}
I'm trying desperately to get the log entries into LogStash. In an attempt to do this, I've created the following LogStash configuration file:
input {
file {
type => "json"
path => "/logs/mylogs.log"
codec => "json"
}
}
output {
file {
path => "/logs/out.log"
}
}
Right now, I'm manually adding records to mylogs.log to try and get it working. However, they appear oddly in the stdout. When I look open out.log, I see something like the following:
{"message":"\"Property 1\":\"value A\", \"Property 2\":\"value B\"}","#version":"1","#timestamp":"2014-04-08T15:33:07.519Z","type":"json","host":"ip-[myAddress]","path":"/logs/mylogs.log"}
Because of this, if I send the message to ElasticSearch, I don't get the fields. Instead I get a jumbled mess. I need my properties to still be properties. I do not want them crammed into the message portion or the output. I have a hunch this has something to do with Codecs. Yet, I'm not sure. I'm not sure if I should change the codec on the logstash input configuration. Or, if I should change the input on the output configuration.
Try removing the json codec and adding a json filter:
input {
file {
type => "json"
path => "/logs/mylogs.log"
}
}
filter{
json{
source => "message"
}
}
output {
file {
path => "/logs/out.log"
}
}
You do not need the json codec because you do not want decode the source JSON but you want filter the input to get the JSON data in the #message field only.
By default tcp put everything to message field if json codec not specified.
An workaround to _jsonparsefailure of the message field after we specify the json codec also can be rectified by doing the following:
input {
tcp {
port => '9563'
}
}
filter{
json{
source => "message"
target => "myroot"
}
json{
source => "myroot"
}
}
output {
elasticsearch {
hosts => [ "localhost:9200" ]
}
}
It will parse message field to proper json string to field myroot
and then myroot is parsed to yield the json.
We can remove the redundant field like message as
filter {
json {
source => "message"
remove_field => ["message"]
}
}
Try with this one:
filter {
json {
source => "message"
target => "jsoncontent" # with multiple layers structure
}
}

Having Logstash reading JSON

I am trying to use logstash for analyzing a file containing JSON objects as follows:
{"Query":{"project_id":"a7565b911f324a9199a91854ea18de7e","timestamp":1392076800,"tx_id":"2e20a255448742cebdd2ccf5c207cd4e","token":"3F23A788D06DD5FE9745D140C264C2A4D7A8C0E6acf4a4e01ba39c66c7c9cbd6a123588b22dc3a24"}}
{"Response":{"result_code":"Success","project_id":"a7565b911f324a9199a91854ea18de7e","timestamp":1392076801,"http_status_code":200,"tx_id":"2e20a255448742cebdd2ccf5c207cd4e","token":"3F23A788D06DD5FE9745D140C264C2A4D7A8C0E6acf4a4e01ba39c66c7c9cbd6a123588b22dc3a24","targets":[]}}
{"Query":{"project_id":"a7565b911f324a9199a91854ea18de7e","timestamp":1392076801,"tx_id":"f7f68c7fb14f4959a1db1a206c88a5b7","token":"3F23A788D06DD5FE9745D140C264C2A4D7A8C0E6acf4a4e01ba39c66c7c9cbd6a123588b22dc3a24"}}
Ideally i'd expect Logstash to understand the JSON.
I used the following config:
input {
file {
type => "recolog"
format => json_event
# Wildcards work, here :)
path => [ "/root/isaac/DailyLog/reco.log" ]
}
}
output {
stdout { debug => true }
elasticsearch { embedded => true }
}
I built this file based on this Apache recipe
When running logstash with debug = true, it reads the objects like this:
How could i see stats in the kibana GUI based on my JSON file, for example number of Query objects and even queries based on timestamp.
For now it looks like it understand a very basic version of the data not the structure of it.
Thx in advance
I found out that logstash will automatically detect JSON byt using the codec field within the file input as follows:
input {
stdin {
type => "stdin-type"
}
file {
type => "prodlog"
# Wildcards work, here :)
path => [ "/root/isaac/Mylogs/testlog.log"]
codec => json
}
}
output {
stdout { debug => true }
elasticsearch { embedded => true }
}
Then Kibana showed the fields of the JSON perfectly.