Skip to content

Instantly share code, notes, and snippets.

@maurorappa
Last active March 27, 2018 10:34
Show Gist options
  • Save maurorappa/9c242f7ff9a18f1ba8ab6e82cb76ce77 to your computer and use it in GitHub Desktop.
Save maurorappa/9c242f7ff9a18f1ba8ab6e82cb76ce77 to your computer and use it in GitHub Desktop.
Sensu check for ElasticSearch document query
#!/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
#!/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