bin/metrics-haproxy.rb in sensu-plugins-haproxy-1.3.0 vs bin/metrics-haproxy.rb in sensu-plugins-haproxy-1.4.0
- old
+ new
@@ -32,10 +32,106 @@
#
# HA Proxy Metrics
#
class HAProxyMetrics < Sensu::Plugin::Metric::CLI::Graphite
+ # Check http://cbonte.github.io/haproxy-dconv/1.7/management.html#9.1 for
+ # haproxy stats CSV format.
+
+ TYPE_FRONTEND = '0'.freeze
+ TYPE_BACKEND = '1'.freeze
+ TYPE_SERVER = '2'.freeze
+ TYPE_LISTENER = '3'.freeze
+ # All fields are listed here for ease of long term maintenance.
+ # Format: field_index => %w(haproxy-name friendly-name)
+ CSV_FIELDS = {
+ 0 => %w(pxname proxy_name),
+ 1 => %w(svname service_name),
+ 2 => %w(qcur queued_requests_current),
+ 3 => %w(qmax queued_requests_max),
+ 4 => %w(scur session_current),
+ 5 => %w(smax session_max),
+ 6 => %w(slim session_limit),
+ 7 => %w(stot session_total),
+ 8 => %w(bin bytes_in),
+ 9 => %w(bout bytes_out),
+ 10 => %w(dreq request_denied_security),
+ 11 => %w(dresp response_denied_security),
+ 12 => %w(ereq request_errors),
+ 13 => %w(econ connection_errors),
+ 14 => %w(eresp response_errors),
+ 15 => %w(wretr warning_retries),
+ 16 => %w(wredis warning_redispatched),
+ 17 => %w(status status),
+ 18 => %w(weight weight),
+ 19 => %w(act servers_active),
+ 20 => %w(bck servers_backup),
+ 21 => %w(chkfail healthcheck_failed),
+ 22 => %w(chkdown healthcheck_transitions),
+ 23 => %w(lastchg healthcheck_seconds_since_change),
+ 24 => %w(downtime healthcheck_downtime),
+ 25 => %w(qlimit server_queue_limit),
+ 26 => %w(pid process_id),
+ 27 => %w(iid proxy_id),
+ 28 => %w(sid server_id),
+ 29 => %w(throttle server_throttle_percent),
+ 30 => %w(lbtot server_selected),
+ 31 => %w(tracked tracked_server_id),
+ 32 => %w(type type),
+ 33 => %w(rate session_rate),
+ 34 => %w(rate_lim session_rate_limit),
+ 35 => %w(rate_max session_rate_max),
+ 36 => %w(check_status check_status),
+ 37 => %w(check_code check_code),
+ 38 => %w(check_duration healthcheck_duration),
+ 39 => %w(hrsp_1xx response_1xx),
+ 40 => %w(hrsp_2xx response_2xx),
+ 41 => %w(hrsp_3xx response_3xx),
+ 42 => %w(hrsp_4xx response_4xx),
+ 43 => %w(hrsp_5xx response_5xx),
+ 44 => %w(hrsp_other response_other),
+ 45 => %w(hanafail failed_healthcheck_details),
+ 46 => %w(req_rate requests_per_second),
+ 47 => %w(req_rate_max requests_per_second_max),
+ 48 => %w(req_tot requests_total),
+ 49 => %w(cli_abrt trasfer_aborts_client),
+ 50 => %w(srv_abrt trasfer_aborts_server),
+ 51 => %w(comp_in compressor_in),
+ 52 => %w(comp_out compressor_out),
+ 53 => %w(comp_byp compressor_bytes),
+ 54 => %w(comp_rsp compressor_responses),
+ 55 => %w(lastsess session_last_assigned_seconds),
+ 56 => %w(last_chk healthcheck_contents),
+ 57 => %w(last_agt agent_check_contents),
+ 58 => %w(qtime queue_time),
+ 59 => %w(ctime connect_time),
+ 60 => %w(rtime response_time),
+ 61 => %w(ttime average_time),
+ 62 => %w(agent_status agent_status),
+ 63 => %w(agent_code agent_code),
+ 64 => %w(agent_duration agent_duration),
+ 65 => %w(check_desc check_desc),
+ 66 => %w(agent_desc agent_desc),
+ 67 => %w(check_rise check_rise),
+ 68 => %w(check_fall check_fall),
+ 69 => %w(check_health check_health),
+ 70 => %w(agent_rise agent_rise),
+ 71 => %w(agent_fall agent_fall),
+ 72 => %w(agent_health agent_health),
+ 73 => %w(addr address),
+ 74 => %w(cookie cookie),
+ 75 => %w(mode mode),
+ 76 => %w(algo algorithm),
+ 77 => %w(conn_rate conn_rate),
+ 78 => %w(conn_rate_max conn_rate_max),
+ 79 => %w(conn_tot conn_tot),
+ 80 => %w(intercepted requests_intercepted),
+ 81 => %w(dcon requests_denied_connection),
+ 82 => %w(dses requests_denied_session)
+ }.freeze
+ NON_NUMERIC_FIELDS = [0, 1, 17, 26, 27, 28, 31, 32, 36, 37, 45, 56, 57, 62, 63, 65, 66, 73, 74, 75, 76].freeze
+
option :connection,
short: '-c HOSTNAME|SOCKETPATH',
long: '--connect HOSTNAME|SOCKETPATH',
description: 'HAproxy web stats hostname or path to stats socket',
required: true
@@ -100,10 +196,29 @@
short: '-i SECONDS',
long: '--retry_interval SECONDS',
default: 1,
proc: proc(&:to_i)
+ option :expose_all,
+ description: 'Expose all possible metrics, includes "--server-metrics", "--backends" will still in effect',
+ short: '-a',
+ long: '--expose-all',
+ boolean: true,
+ default: false
+
+ option :use_haproxy_names,
+ description: 'Use raw names as used in haproxy CSV format definition rather than human friendly names',
+ long: '--use-haproxy-names',
+ boolean: true,
+ default: false
+
+ option :use_explicit_names,
+ description: 'Use explicit names for frontend, backend, server, listener',
+ long: '--use-explicit-names',
+ boolean: true,
+ default: false
+
def acquire_stats
uri = URI.parse(config[:connection])
if uri.is_a?(URI::Generic) && File.socket?(uri.path)
socket = UNIXSocket.new(config[:connection])
@@ -123,15 +238,34 @@
return out
rescue
return nil
end
- def output(*args)
- super(*args) unless args[1].nil?
+ def render_output(type, pxname, svname, index, value)
+ return if value.nil?
+ field_index = config[:use_haproxy_names] ? 0 : 1
+ field_name = CSV_FIELDS[index][field_index]
+ if config[:use_explicit_names]
+ if type == TYPE_FRONTEND
+ output "#{config[:scheme]}.frontend.#{pxname}.#{field_name}", value
+ elsif type == TYPE_BACKEND
+ output "#{config[:scheme]}.backend.#{pxname}.#{field_name}", value
+ elsif type == TYPE_SERVER
+ output "#{config[:scheme]}.backend.#{pxname}.server.#{svname}.#{field_name}", value
+ elsif type == TYPE_LISTENER
+ output "#{config[:scheme]}.listener.#{pxname}.#{svname}.#{field_name}", value
+ end
+ else
+ if type == TYPE_BACKEND # rubocop:disable IfInsideElse
+ output "#{config[:scheme]}.#{pxname}.#{field_name}", value
+ else
+ output "#{config[:scheme]}.#{pxname}.#{svname}.#{field_name}", value
+ end
+ end
end
- def run #rubocop:disable all
+ def run
out = nil
1.upto(config[:retries]) do |_i|
out = acquire_stats
break unless out.to_s.length.zero?
sleep(config[:retry_interval])
@@ -143,53 +277,39 @@
up_by_backend = {}
parsed = CSV.parse(out)
parsed.shift
parsed.each do |line|
+ pxname = line[0]
+ svname = line[1]
+ type = line[32]
+
if config[:backends].length > 0
next unless config[:backends].include? line[0]
end
- if line[1] == 'BACKEND'
- output "#{config[:scheme]}.#{line[0]}.session_current", line[4]
- output "#{config[:scheme]}.#{line[0]}.session_total", line[7]
- output "#{config[:scheme]}.#{line[0]}.bytes_in", line[8]
- output "#{config[:scheme]}.#{line[0]}.bytes_out", line[9]
- output "#{config[:scheme]}.#{line[0]}.connection_errors", line[13]
- output "#{config[:scheme]}.#{line[0]}.warning_retries", line[15]
- output "#{config[:scheme]}.#{line[0]}.warning_redispatched", line[16]
- output "#{config[:scheme]}.#{line[0]}.response_1xx", line[39]
- output "#{config[:scheme]}.#{line[0]}.response_2xx", line[40]
- output "#{config[:scheme]}.#{line[0]}.response_3xx", line[41]
- output "#{config[:scheme]}.#{line[0]}.response_4xx", line[42]
- output "#{config[:scheme]}.#{line[0]}.response_5xx", line[43]
- output "#{config[:scheme]}.#{line[0]}.response_other", line[44]
- unless line[46].nil?
- output "#{config[:scheme]}.#{line[0]}.requests_per_second", line[46]
- end
- unless line[47].nil?
- output "#{config[:scheme]}.#{line[0]}.requests_per_second_max", line[47]
- end
- output "#{config[:scheme]}.#{line[0]}.queue_time", line[58]
- output "#{config[:scheme]}.#{line[0]}.connect_time", line[59]
- output "#{config[:scheme]}.#{line[0]}.response_time", line[60]
- output "#{config[:scheme]}.#{line[0]}.average_time", line[61]
+ indices = []
+ if config[:expose_all]
+ indices = CSV_FIELDS.keys - NON_NUMERIC_FIELDS
+ elsif type == TYPE_BACKEND
+ indices = [4, 7, 8, 9, 13, 15, 16, 39, 40, 41, 42, 43, 44, 46, 47, 58, 59, 60, 61]
elsif config[:server_metrics]
- output "#{config[:scheme]}.#{line[0]}.#{line[1]}.session_total", line[7]
- output "#{config[:scheme]}.#{line[0]}.#{line[1]}.session_current", line[4]
- output "#{config[:scheme]}.#{line[0]}.#{line[1]}.requests_per_second", line[46]
- output "#{config[:scheme]}.#{line[0]}.#{line[1]}.requests_per_second_max", line[47]
- output "#{config[:scheme]}.#{line[0]}.#{line[1]}.requests_total", line[48]
+ indices = [4, 7, 46, 47, 48]
end
+ indices.each { |i| render_output type, pxname, svname, i, line[i] }
- if line[1] != 'BACKEND' && !line[1].nil?
- up_by_backend[line[0]] ||= 0
- up_by_backend[line[0]] += line[17].start_with?('UP') ? 1 : 0
+ if type == TYPE_SERVER
+ up_by_backend[pxname] ||= 0
+ up_by_backend[pxname] += line[17].start_with?('UP') ? 1 : 0
end
end
up_by_backend.each_pair do |backend, count|
- output "#{config[:scheme]}.#{backend}.num_up", count
+ if config[:use_explicit_names]
+ output "#{config[:scheme]}.backend.#{backend}.num_up", count
+ else
+ output "#{config[:scheme]}.#{backend}.num_up", count
+ end
end
ok
end
end