Last active
March 27, 2018 10:34
-
-
Save maurorappa/9c242f7ff9a18f1ba8ab6e82cb76ce77 to your computer and use it in GitHub Desktop.
Sensu check for ElasticSearch document query
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/opt/sensu/embedded/bin/ruby | |
require 'rubygems' if RUBY_VERSION < '1.9.0' | |
require 'sensu-plugin/check/cli' | |
require 'net/http' | |
require 'json' | |
# Scenario: you are shipping logs to the ES cluster and you want periodically look for errors | |
# the script will run any query and count the occurences, with the flags -c (critical) and -w (warning) you can trigger SENSU alarms | |
# the following example will look for Fatal errors occurred between 017-02-12 and 017-02-12 and raise a critical alert if at least one is found | |
# es_log_search.rb -h localhost -p 9200 -q '{"match_phrase": { "message": "FATAL ERROR" } }' -t '{"gte":"2017-02-12T00:00:00","lte":"2017-02-14T00:00:00"}' -c 1 | |
class ElasticsearchCountCheck < Sensu::Plugin::Check::CLI | |
option :host, | |
:required => true, | |
:short => '-h hostname', | |
:default => 'localhost' | |
option :port, | |
:required => true, | |
:short => '-p port', | |
:default => 9200, | |
:proc => proc(&:to_i) | |
option :debug, | |
:short => '-d', | |
:long => '--debug', | |
:default => false | |
option :index, | |
:short => '-i index e.g logstash-*', | |
:long => '--index', | |
:default => 'logstash-*' | |
option :query, | |
:required => true, | |
:short => '-q QUERY e.g {"bool":{"must":[{"match":{"_type":"test"}},{"match":{"level": "error"}}]}}', | |
:long => '--query' | |
option :timeframe, | |
:required => true, | |
:short => '-t e.g {\"gte\":\"2017-02-12T00:00:00\",\"lte\":\"2017-02-14T00:00:00\"}}', | |
:long => '--timeframe' | |
option :critical, | |
:required => true, | |
:short => '-c CRIT', | |
:proc => proc(&:to_i) | |
option :warning, | |
:short => '-w WARN', | |
:proc => proc(&:to_i) | |
def search | |
transport = Net::HTTP.new(config[:host], config[:port]) | |
request = Net::HTTP::Get.new("/#{config[:index]}/_search") | |
request.initialize_http_header({'Accept' => 'application/json', 'ContentType' => 'application/json'}) | |
request.body = "{\"query\": #{config[:query]},\"filter\":{\"range\":{\"@timestamp\":#{config[:timeframe]} }}}" | |
puts "ES Query: #{request.body}\n\n" if config[:debug] | |
response = transport.request request | |
end | |
def run | |
begin | |
response = search | |
puts "ES Reply: #{response.body}\n\n" if config[:debug] | |
data = JSON.parse(response.body) | |
rescue Exception => e | |
critical e.message | |
end | |
# ensure the ES answer contains the result counter | |
if data.has_key?('hits') | |
count = data['hits']['total'].to_i | |
puts "Result count: #{count}" if config[:debug] | |
else | |
unknown "count field not found in response" | |
end | |
# compare with defined thresholds | |
if config[:critical] && count >= config[:critical] | |
critical "count (#{count}) exceeds threshold (#{config[:critical]})" | |
end | |
if config[:warning] && count >= config[:warning] | |
warning "count (#{count}) exceeds threshold (#{config[:warning]})" | |
end | |
# test passed! | |
ok | |
end | |
end |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/opt/sensu/embedded/bin/ruby | |
require 'rubygems' if RUBY_VERSION < '1.9.0' | |
require 'sensu-plugin/check/cli' | |
require 'net/http' | |
require 'json' | |
require 'time' | |
# Scenario: you are shipping logs to the ES cluster and you want periodically look for errors | |
# the script will run any query and count the occurences, with the flags -c (critical) and -w (warning) you can trigger SENSU alarms | |
# the timeframe can be specifed as now minus X minutes, this ideal for a periodic sensu check | |
class ElasticsearchCountCheck < Sensu::Plugin::Check::CLI | |
option :host, | |
:required => true, | |
:short => '-h hostname', | |
:default => 'localhost' | |
option :port, | |
:required => true, | |
:short => '-p port', | |
:default => 9200, | |
:proc => proc(&:to_i) | |
option :debug, | |
:short => '-d', | |
:long => '--debug', | |
:default => false | |
option :index, | |
:short => '-i index e.g logstash-*', | |
:long => '--index', | |
:default => 'logstash-*' | |
option :query, | |
:required => true, | |
:short => '-q QUERY e.g {"bool":{"must":[{"match":{"_type":"test"}},{"match":{"level": "error"}}]}}', | |
:long => '--query' | |
option :timeframe, | |
:short => '-t e.g {\"gte\":\"2017-02-12T00:00:00\",\"lte\":\"2017-02-14T00:00:00\"}}', | |
:long => '--timeframe' | |
option :last, | |
:short => '-l number of minutes', | |
:proc => proc(&:to_i), | |
:long => '--lastminutes', | |
:default => 0 | |
option :critical, | |
:required => true, | |
:short => '-c CRIT', | |
:proc => proc(&:to_i) | |
option :warning, | |
:short => '-w WARN', | |
:proc => proc(&:to_i) | |
def search | |
transport = Net::HTTP.new(config[:host], config[:port]) | |
request = Net::HTTP::Get.new("/#{config[:index]}/_search") | |
now = Time.now | |
# if we specified the last n minutes we create the following json query | |
if config[:last] > 0 | |
from = now - ( config[:last] * 60 ) | |
now = now.strftime("%FT%T") | |
from = from.strftime("%FT%T") | |
request.body = "{\"query\": #{config[:query]},\"filter\":{\"range\":{\"@timestamp\": {\"gte\":\"#{from}\",\"lte\":\"#{now}\"}} }}}" | |
# we specified a timeframe (interval between dates) | |
elsif config.has_key? :timeframe | |
request.body = "{\"size\":1,\"query\": #{config[:query]},\"filter\":{\"range\":{\"@timestamp\": #{config[:timeframe]} }}}" | |
else | |
puts "You need to specify one option between 'last' or 'timeframe'!" | |
exit | |
end | |
request.initialize_http_header({'Accept' => 'application/json', 'ContentType' => 'application/json'}) | |
puts "ES Query: #{request.body}\n\n" if config[:debug] | |
response = transport.request request | |
end | |
def run | |
begin | |
response = search | |
puts "ES Reply: #{response.body}\n\n" if config[:debug] | |
data = JSON.parse(response.body) | |
rescue Exception => e | |
critical e.message | |
end | |
# if the query faild on some shards we may have missing data, so we exit with a critical | |
if data['_shards']['failed'] > 0 | |
critical "query failed on some shards" | |
end | |
# ensure the data is what we expect | |
if data.has_key?('hits') | |
count = data['hits']['total'].to_i | |
puts "Result count: #{count}" if config[:debug] | |
else | |
unknown "count field not found in response" | |
end | |
# compare with defined thresholds | |
if config[:critical] && count >= config[:critical] | |
critical "count (#{count}) exceeds threshold (#{config[:critical]})" | |
end | |
if config[:warning] && count >= config[:warning] | |
warning "count (#{count}) exceeds threshold (#{config[:warning]})" | |
end | |
# test passed! | |
ok | |
end | |
end | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment