Watcher alert if no records matching filter in x minutes - elasticsearch

I need to get ElasticSearch watcher to alert if there is no record matching a pattern inserted into the index in a time frame, it needs to be able to do this whilst grouping on another pair of field.
i.e. the records will be of the pattern:
Date Timestamp Level Message Client Site
It needs to check that Message matches "is running" for each Client's site(s) (i.e. Google Maps and Bing Maps have the same site of Maps). I tihnk the best(?) way to do this right now is to run a wacher per client site.
Sofar I have this, assume the task should write is running into the log every 20 minutes :
{
"trigger" : {
"schedule" : {
"interval" : "25m"
}
},
"input" : {
"search" : {
"request" : {
"search_type" : "count",
"indices" : "<logstash-{now/d}>",
"body" : {
"filtered" : {
"query" : {
"match_phrase" : { "Message" : "Is running" }
},
"filter" : {
"match" : { "Client" : "Example" } ,
"match" : { "Site" : "SomeSite" }
}
}
}
}
}
},
"condition" : {
"script" : "return ctx.payload.hits.total < 1"
},
"actions" : {
},
"email_administrator" : {
"email" : {
"to" : "me#host.tld",
"subject" : "Tasks are not running for {{ctx.payload.client}} on their site {{ctx.payload.site}}",
"body" : "Too many error in the system, see attached data",
"attach_data" : true,
"priority" : "high"
}
}
}
}

For anyone looking how to do this in the future, a few things need nesting in query as part of filter and match becomes term. Fun!...
{
"trigger": {
"schedule": {
"interval": "25m"
}
},
"input": {
"search": {
"request": {
"search_type": "count",
"indices": "<logstash-{now/d}>",
"body": {
"query": {
"filtered": {
"query": {
"match_phrase": {
"Message": "Its running"
}
},
"filter": {
"query": {
"term": {
"Client": "Example"
}
},
"query": {
"term": {
"Site": "SomeSite"
}
},
"query": {
"range": {
"event_timestamp": {
"gte": "now-25m",
"lte": "now"
}
}
}
}
}
}
}
}
}
},
"condition": {
"compare": {
"ctx.payload.hits.total": {
"lte": 1
}
}
},
"actions": {
"email_administrator": {
"email": {
"to": "me#host.tld",
"subject": "Tasks are not running for {{ctx.payload.client}} on their site {{ctx.payload.site}}",
"body": "Tasks are not running for {{ctx.payload.client}} on their site {{ctx.payload.site}}",
"attach_data": true,
"priority": "high"
}
}
}
}

You have to change your condition,It support json format:
"condition" : {
"script" : "return ctx.payload.hits.total : 1"
}
Please refer below link,
https://www.elastic.co/guide/en/watcher/current/condition.html

Related

How to build a watcher in Elasticsearch for generating OTRS ticket?

I want to configure a elasticsearch webhook watcher , which will look for the keyword "error" in my indices and genarate an OTRS ticket, if found.
Right now I have following configuration :
{
"trigger": {
"schedule": {"interval": "1m"}
},
"input": {
"search": {
"request": {
"body": {
"size": 0,
"query": {"match_all": "Error"}
},
"indices": ["*"]
}
}
},
"condition": {
"compare": {
"ctx.payload.hits.total": {
"gte": 1
}
}
},
"actions" : {
"create_otrs" : {
"transform": {
"script": """{"Ticket":{"Queue":"EngineeringTeam","Priority":"P3","CustomerUser":"root#localhost","Title":"RESTCreateTest","State":"new","Type":"Incident"},"Article":{"ContentType":"text/plain;charset=utf8","Subject":"RestCreateTest","Body":"Thisisonlyatest"}}"""
},
"webhook" : {
"method" : "POST",
"host" : "http://myotrs.com/otrs/nph-genericinterface.pl/Webservice/GenericTicketConnectorREST/Ticket?UserLogin=<user>&Password=<pass>",
"port": 9200,
"body": "{{#toJson}}ctx.payload{{/toJson}}",
"auth" : {
"basic" : {
"username" : "elastic",
"password" : "<elasticsearch pass>"
}
}
}
}
}
}
This gives me Error saving watch : compile error and watcher will not simulate. There is no syntax error in the json by the way. What is wrong in the configuration? A curl operation successfully generates the OTRS ticket but I am getting a hard time configuring it with elasticsearch.
Tldr;
Your transform script is wrong.
As per the documentation:
The executed script may either return a valid model that is the equivalent of a Java™ Map or a JSON object (you will need to consult the documentation of the specific scripting language to find out what this construct is).
Solution
You can do something as simple as, converting your json into a string
{
"Ticket": {
"Queue": "EngineeringTeam",
"Priority": "P3",
"CustomerUser": "root#localhost",
"Title": "RESTCreateTest",
"State": "new",
"Type": "Incident"
},
"Article": {
"ContentType": "text/plain;charset=utf8",
"Subject": "RestCreateTest",
"Body": "Thisisonlyatest"
}
}
Becomes:
"{\"Ticket\":{\"Queue\":\"EngineeringTeam\",\"Priority\":\"P3\",\"CustomerUser\":\"root#localhost\",\"Title\":\"RESTCreateTest\",\"State\":\"new\",\"Type\":\"Incident\"},\"Article\":{\"ContentType\":\"text/plain;charset=utf8\",\"Subject\":\"RestCreateTest\",\"Body\":\"Thisisonlyatest\"}}"
And use the Json.load function to convert the string into a proper object.
Your watch will look like:
{
"watch" : {
"trigger": {
"schedule": {"interval": "1m"}
},
"input": {
"search": {
"request": {
"body": {
"size": 0,
"query": {"match_all": "Error"}
},
"indices": ["*"]
}
}
},
"condition": {
"compare": {
"ctx.payload.hits.total": {
"gte": 1
}
}
},
"actions" : {
"create_otrs" : {
"transform": {
"script": """return Json.load("{\"Ticket\":{\"Queue\":\"EngineeringTeam\",\"Priority\":\"P3\",\"CustomerUser\":\"root#localhost\",\"Title\":\"RESTCreateTest\",\"State\":\"new\",\"Type\":\"Incident\"},\"Article\":{\"ContentType\":\"text/plain;charset=utf8\",\"Subject\":\"RestCreateTest\",\"Body\":\"Thisisonlyatest\"}}");"""
},
"webhook" : {
"method" : "POST",
"host" : "http://myotrs.com/otrs/nph-genericinterface.pl/Webservice/GenericTicketConnectorREST/Ticket?UserLogin=<user>&Password=<pass>",
"port": 9200,
"body": "{{#toJson}}ctx.payload{{/toJson}}",
"auth" : {
"basic" : {
"username" : "elastic",
"password" : "<elasticsearch pass>"
}
}
}
}
}
}
}
Then another error you have in your watch is the query
{
"search": {
"request": {
"body": {
"size": 0,
"query": {"match_all": "Error"}
},
"indices": ["*"]
}
}
}
match_all should take an object such as {} so "Error" is not going to work.
So in the end the watcher looks like:
{
"watch" : {
"trigger": {
"schedule": {"interval": "1m"}
},
"input": {
"search": {
"request": {
"body": {
"size": 0,
"query": {"match_all": {}}
},
"indices": ["*"]
}
}
},
"condition": {
"compare": {
"ctx.payload.hits.total": {
"gte": 1
}
}
},
"actions" : {
"create_otrs" : {
"transform": {
"script": """return Json.load("{\"Ticket\":{\"Queue\":\"EngineeringTeam\",\"Priority\":\"P3\",\"CustomerUser\":\"root#localhost\",\"Title\":\"RESTCreateTest\",\"State\":\"new\",\"Type\":\"Incident\"},\"Article\":{\"ContentType\":\"text/plain;charset=utf8\",\"Subject\":\"RestCreateTest\",\"Body\":\"Thisisonlyatest\"}}");"""
},
"webhook" : {
"method" : "POST",
"host" : "http://myotrs.com/otrs/nph-genericinterface.pl/Webservice/GenericTicketConnectorREST/Ticket?UserLogin=<user>&Password=<pass>",
"port": 9200,
"body": "{{#toJson}}ctx.payload{{/toJson}}",
"auth" : {
"basic" : {
"username" : "elastic",
"password" : "<elasticsearch pass>"
}
}
}
}
}
}
}

Bodybuilder js: Bucket Filter Aggregation

I would like to make buckets based of keyword-occurences in a field.
I checked elasticsearch documentation and found Filters Aggregation should be a good fit:
https://www.elastic.co/guide/en/elasticsearch/reference/current/search-aggregations-bucket-filters-aggregation.html#search-aggregations-bucket-filters-aggregation
Currently we're using bodybuilder.js to build queries. I found in the source code (https://github.com/danpaz/bodybuilder/blob/master/src/aggregation-builder.js#L87) an undocumented function:
bodybuilder()
.aggregation('terms', 'title', {
_meta: { color: 'blue' }
}, 'titles')
.build()
what results in:
{
"aggs": {
"titles": {
"terms": {
"field": "title"
},
"meta": {
"color": "blue"
}
}
}
}
But that's actually not the same structure like described in ES documentation:
GET logs/_search
{
"size": 0,
"aggs" : {
"messages" : {
"filters" : {
"filters" : [
{ "match" : { "body" : "error" }},
{ "match" : { "body" : "warning" }}
]
}
}
}
}
Any idea how to achieve Filters Aggregations with bodybuilder.js ?
This is not a solution for bodybuilder.js but one for javascript using a library - very similar to bodybuilder.js. Its called elastic-builder.js.
Regarding your question this code:
esb.requestBodySearch()
.agg(esb.filtersAggregation('messages')
.filter('errors', esb.matchQuery('body', 'error'))
.filter('warnings', esb.matchQuery('body', 'warning')));
will give you this response:
{
"aggs": {
"messages": {
"filters": {
"filters": {
"errors": { "match": { "body": "error" }},
"warnings": { "match": { "body": "warning" }}
}
}
}
}
}
It looks like bodybuilder.js is mostly build for queries and its easier to use for such purpose. If you want more control without the heavy json overhead elastic-builder could be a solution.
We can do it like this in bodybuilder.js:
bodybuilder().aggregation('filters', {
"filters": {
"errors": { "term": { "body": "error" } },
"warnings": { "term": { "body": "warning" } }
}
}, 'messages').build();
above code will generate the query:
{
"aggs" : {
"messages" : {
"filters" : {
"filters" : {
"errors" : { "term" : { "body" : "error" }},
"warnings" : { "term" : { "body" : "warning" }}
}
},
}
}
}

ElasticSearch nested query score

I have an index :
PUT my_index2
{
"mappings": {
"my_type": {
"properties": {
"user": {
"type": "nested"
}
}
}
}
}
I have two documents:
POST my_index2/my_type/
{
"user": [
{
"name": "Alice Don"
},
{
"name": "Smith"
}
]
}
POST my_index2/my_type/
{
"user": [
{
"name": "Alice David"
}
]
}
When I search it:
GET my_index2/_search
{
"query": {
"nested" : {
"path" : "user",
"query" : {
"bool" : {
"should" : [
{ "match" : {"user.name" : "Alice"} }
]
}
}
}
}
}
Although both documents have one "Alice", the score of the first one is higher. How could that possible?
Your first document has shorter "name", so you got more same chars between "query" and "name"

Cannot divide multiple context values in Elasticsearch watcher

I have created a watch in Elasticsearch to alert me if the ratio of http errors is greater than 15% of total requests over 60 minutes.
I am using chain inputs to generate the dividend and divisor values for my ratio calculation.
In my condition I am using scripting to do the division and check if it is greater than my ratio.
However, whenever I use 2 ctx parameters to do the division, it always equals to zero.
If I play with it and only use one of ctx param, then it works fine.
It seems that we cannot use 2 ctx params in a condition.
Does anyone know how to get around this?
Below is my watch.
Thanks.
{
"trigger" : {
"schedule" : {
"interval" : "5m"
}
},
"input" : {
"chain":{
"inputs": [
{
"first": {
"search" : {
"request" : {
"indices" : [ "logstash-*" ],
"body" : {
"query" : {
"bool":{
"must": [
{
"match" : {"d.uri": "xxxxxxxx"}
},
{
"match" : {"topic": "xxxxxxxx"}
}
],
"filter": {
"range": {
"#timestamp": {
"gte": "now-60m"
}
}
}
}
}
}
},
"extract": ["hits.total"]
}
}
},
{
"second": {
"search" : {
"request" : {
"body" : {
"query" : {
"bool":{
"must": [
{
"match" : {"d.uri": "xxxxxxxx"}
},
{
"match" : {"topic": "xxxxxxxx"}
},
{
"match" : {"d.status": "401"}
}
],
"filter": {
"range": {
"#timestamp": {
"gte": "now-60m"
}
}
}
}
}
}
},
"extract": ["hits.total"]
}
}
}
]
}
},
"condition" : {
"script" : {
"source" : "return (ctx.payload.second.hits.total / ctx.payload.first.hits.total) == 0"
}
}
}
The issue comes in fact from the fact that I was doing an integer division to get to a ratio in the form of 0.xx.
I reversed the operation and it is working fine.

Send all the aggregations as text with Watcher (ElasticSearch)

I'm configuring right now Watcher to search in the access logs and see how many error is so far and send it to a slack account.
Well, the problem that I have is because I can't know how many aggregations I will have when the query is done and in my configurations is something like "hardcoded" to send just like 5 at maximum , but if the result is grather than 5 not works.
I'm searching for 404 status code in the query and filter only for one server, then I just need send all bucket results as notification as:
Total: Total-number-of-its
Logs:
log1: number-of-results
log2: number-of-results
log3: number-of-results
log4: number-of-results
log5: number-of-results
log6: number-of-results
Here my configuration:
"trigger" : {
"schedule" : { "interval" : "1h" }
},
"input" : {
"search" : {
"request": {
"body": {
"query": {
"bool": {
"must": [
{ "range": {
"#timestamp": {
"gte": "now-1h",
"lte": "now"
}
}
},
{
"match": {
"beat.hostname": "someserver"
}
}
],
"filter": {
"term": {
"response": "404"
}
}
}
},
"aggs": {
"host": {
"terms": {
"field": "beat.hostname",
"size": 1
}
},
"logs_list": {
"terms": {
"field": "source",
"size": 10
}
}
}
}
}
}
},
"condition": {
"compare" : { "ctx.payload.hits.total" : { "gt" : 0 }}
},
"actions" : {
"notify-slack" : {
"throttle_period" : "30m",
"slack" : {
"message" : {
"from": "Watcher",
"to" : [ "somechannel" ],
"attachments" : [
{
"title" : "400 code status found",
"text" : "Encountered: {{ctx.payload.hits.total}} in the last hour on {{ctx.payload.aggregations.host.buckets.0.key}} \n Files: \n {{ctx.payload.aggregations.logs_list.buckets.0.key}}: {{ctx.payload.aggregations.logs_list.buckets.0.doc_count}} \n {{ctx.payload.aggregations.logs_list.buckets.1.key}}: {{ctx.payload.aggregations.logs_list.buckets.1.doc_count}} \n {{ctx.payload.aggregations.logs_list.buckets.2.key}}: {{ctx.payload.aggregations.logs_list.buckets.2.doc_count}} \n {{ctx.payload.aggregations.logs_list.buckets.3.key}}: {{ctx.payload.aggregations.logs_list.buckets.3.doc_count}} \n {{ctx.payload.aggregations.logs_list.buckets.4.key}}: {{ctx.payload.aggregations.logs_list.buckets.4.doc_count}} \n {{ctx.payload.aggregations.logs_list.buckets.5.key}}: {{ctx.payload.aggregations.logs_list.buckets.5.doc_count}}",
"color" : "danger"
}
]
}
}
}
}
I don't know how should I send the "text" in actions, any ideas how should I pass all buckets result?
Thanks in advance, i'm using xpack, ELK and logstash.
If I understand your question correctly, you want to loop over your aggregation in the action. Try this:
{{#ctx.payload.aggregations.myAggName.buckets}}
{{key}}: {{doc_count}}
{{/ctx.payload.aggregations.myAggName.buckets}}

Resources