我对Ruby很陌生,而且我对一个看似简单的问题遇到了一些困难。
代码就在这里......
https://github.com/sensu/sensu-community-plugins/blob/master/plugins/graphite/check-stats.rb
...但我在最后包含了当前源的完整副本,因为它可能会随着新版本提交给Github而改变。
这是一个Sensu插件。它通过HTTP请求从Graphite收集数据。将回复存储在body中,然后将JSON.parse()存储到数据中。
对于数据中的每个指标,它会收集数据点,并对数据点执行平均值。如果平均值高于某些阈值(选项-w或-c),则会发出警告或严重警告。
有时Graphite商店有点落后。某些指标可能缺少最新的数据点。发生这种情况时,数据点为零。
问题是,当计算平均值(数据点)时,nil被计为零。这会人为地降低平均值,有时会降低插件不会触发的效果。
从平均值的计算中消除零值的最佳方法是什么?
理想情况下,消除nils应该以这样的方式发生:如果所有数据点都是nil,那么它应该触发datapoints.empty条件。基本上,在达到“除非datapoints.empty?”之前杀死所有的nils。因为如果所有都是零,那么我们实际上没有任何数据点。
或者某种方式,metric.collect {}应该跳过零值。
我试过使用.compact,但这似乎没什么区别(可能我用错了)。
这是代码的当前版本:
#!/usr/bin/env ruby
#
# Checks metrics in graphite, averaged over a period of time.
#
# The fired sensu event will only be critical if a stat is
# above the critical threshold. Otherwise, the event will be warning,
# if a stat is above the warning threshold.
#
# Multiple stats will be checked if * are used
# in the "target" query.
#
# Author: Alan Smith (alan@asmith.me)
# Date: 08/28/2014
#
require 'rubygems' if RUBY_VERSION < '1.9.0'
require 'json'
require 'net/http'
require 'sensu-plugin/check/cli'
class CheckGraphiteStat < Sensu::Plugin::Check::CLI
option :host,
:short => "-h HOST",
:long => "--host HOST",
:description => "graphite hostname",
:proc => proc {|p| p.to_s },
:default => "graphite"
option :period,
:short => "-p PERIOD",
:long => "--period PERIOD",
:description => "The period back in time to extract from Graphite. Use -24hours, -2days, -15mins, etc, same format as in Graphite",
:proc => proc {|p| p.to_s },
:required => true
option :target,
:short => "-t TARGET",
:long => "--target TARGET",
:description => "The graphite metric name. Can include * to query multiple metrics",
:proc => proc {|p| p.to_s },
:required => true
option :warn,
:short => "-w WARN",
:long => "--warn WARN",
:description => "Warning level",
:proc => proc {|p| p.to_f },
:required => false
option :crit,
:short => "-c Crit",
:long => "--crit CRIT",
:description => "Critical level",
:proc => proc {|p| p.to_f },
:required => false
def average(a)
total = 0
a.to_a.each {|i| total += i.to_f}
total / a.length
end
def danger(metric)
datapoints = metric['datapoints'].collect {|p| p[0].to_f}
unless datapoints.empty?
avg = average(datapoints)
if !config[:crit].nil? && avg > config[:crit]
return [2, "#{metric['target']} is #{avg}"]
elsif !config[:warn].nil? && avg > config[:warn]
return [1, "#{metric['target']} is #{avg}"]
end
end
[0, nil]
end
def run
body =
begin
uri = URI("http://#{config[:host]}/render?format=json&target=#{config[:target]}&from=#{config[:period]}")
res = Net::HTTP.get_response(uri)
res.body
rescue Exception => e
warning "Failed to query graphite: #{e.inspect}"
end
status = 0
message = ''
data =
begin
JSON.parse(body)
rescue
[]
end
unknown "No data from graphite" if data.empty?
data.each do |metric|
s, msg = danger(metric)
message += "#{msg} " unless s == 0
status = s unless s < status
end
if status == 2
critical message
elsif status == 1
warning message
end
ok
end
end
答案 0 :(得分:3)
好吧,如果你想在收集之前消除nils,你可以做
metric['datapoints'].reject { |p| p.nil? }.collect {|p| p[0].to_f}
而不是
metric['datapoints'].collect {|p| p[0].to_f}
顺便说一句,您average
也可以改写为
def average(a)
a.reduce(0,:+)/a.size
end
答案 1 :(得分:2)
您可以使用Array#compact
完全相同的内容:
["a", nil, "b", nil, "c", nil].compact
#=> [ "a", "b", "c" ]