-
Notifications
You must be signed in to change notification settings - Fork 6
/
metrics.rb
151 lines (136 loc) · 4.82 KB
/
metrics.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
require 'json'
module Sensu::Extension
# Sensu::Extension::Metrics
#
# This mutator is meant to be used in conjuction with Sensu::Extension::Relay
# It prepares metrics for relay over persistent TCP connections to metric
# stores.
#
# Metrics sent to sensu in the following JSON format will be mutated
# accordingly based on the endpoints defined in the "relay" config section.
# Currently supported endpoints are Graphite and OpenTSDB.
#
# { name: "metric.name", value: 1, tags: { host: "hostname" } }
#
# Metric checks also need to specify their output type, e.g.
#
# output_type: "json" Or output_type: "graphite"
#
# Checks should also specify whether or not the hostname should be
# automatically added to the formatted metric output. In the case of graphite,
# the hostname is prepended to the metric name, e.g. host.fqdn.com.metric.name
# In the case of OpenTSDB, the hostname is added with the "host" tag.
#
# DEFAULTS:
#
# output_type: "graphite" auto_tag_host: "yes"
#
# Metrics sent in graphite format will be mutated to OpenTSDB if an OpenTSDB
# endpoint is defined.
#
# Author: Greg Poirier http://github.com/grepory and @grepory on Twitter
# greg.poirier at opower.com
#
# Many thanks to Sean Porter, Zach Dunn, Brett Witt, and Jesse Kempf,
# and Jeff Kolesky for feedback and review.
class Metrics < Mutator
def initialize
@endpoints = {}
@mutators = {
graphite: method(:graphite),
opentsdb: method(:opentsdb),
}
@event = nil
end
def definition
{
type: 'extension',
name: 'metrics',
}
end
def name
'metrics'
end
def description
'mutates metrics for relay to metric stores'
end
def run(event)
@event = event
logger.debug("metrics.run(): Handling event - #{event}")
# The unwritten standard is graphite, if they don't specify it, assume that's
# the case.
event[:check][:output_type] ||= 'graphite'
# We also assume that people want to auto tag their metrics.
event[:check][:auto_tag_host] ||= 'yes'
settings[:relay].keys.each do |endpoint_name|
ep_name = endpoint_name.intern
mutator = @mutators[ep_name] || next
mutate(mutator, ep_name)
end unless settings[:relay].nil? # keys.each
# if we aren't configured we simply pass nil to the handler which it then
# guards against. fail silently.
yield(@endpoints, 0)
end # run
def mutate(mutator, ep_name)
logger.debug("metrics.run mutating for #{ep_name.inspect}")
check = @event[:check]
output = check[:output]
output_type = check[:output_type]
endpoint_name = ep_name.to_s
# if we receive json, we mutate based on the endpoint name
if output_type == 'json'
@endpoints[ep_name] = ''
metrics = JSON.parse(output)
if metrics.is_a?(Hash)
metrics = [metrics]
end
metrics.each do |metric|
mutated = mutator.call(metric)
@endpoints[ep_name] << mutated
end
# don't mutate
elsif output_type == endpoint_name
@endpoints[ep_name] = output
elsif output_type == 'graphite' && endpoint_name == 'opentsdb'
@endpoints[:opentsdb] = graphite_to_opentsdb
end
end
private
def graphite(metric)
out = ''
out << "#{@event[:client][:name].split(/\./).reverse.join('.')}." unless @event[:check][:auto_tag_host] == 'no'
out << "#{metric['name']}\t#{metric['value']}\t#{metric['timestamp']}\n"
out
end
def opentsdb(metric)
check = @event[:check]
out = "put #{metric['name']} #{metric['timestamp']} #{metric['value']}"
out << " check_name=#{check[:name]}" unless check[:name].nil?
out << " host=#{@event[:client][:name]}" unless check[:auto_tag_host] == 'no'
metric['tags'].each do |tag, value|
out << " " << [tag, value].join('=')
end if metric.key?('tags')
out << "\n"
end
def graphite_to_opentsdb
out = ''
metrics = @event[:check][:output]
client_name = @event[:client][:name]
metrics.split("\n").each do |output_line|
(metric_name, metric_value, epoch_time) = output_line.split
# Sometimes checks outputthings we don't want or expect.
# Only attempt to parse things that look like they might make sense.
next unless metric_name && metric_value && epoch_time
metric_value = metric_value.rstrip
# attempt to strip complete hostname from the beginning, otherwise
# passthrough metric name as-is
metric_name = metric_name.sub(/^#{client_name}\./, '')
out << "put #{metric_name} #{epoch_time} #{metric_value} host=#{client_name}\n"
end
out
end
def logger
Sensu::Logger.get
end
end # Metrics
end # Sensu::Extension