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