5 # directory to hold conditions and their corresponding metric
6 # {condition => metric}
7 attr_accessor :directory
12 def self.attach(condition, metric)
13 self.directory[condition] = metric
18 Timer.get.schedule(condition, 0)
19 when EventCondition, TriggerCondition
24 def self.detach(condition)
25 self.directory.delete(condition)
29 Timer.get.unschedule(condition)
30 when EventCondition, TriggerCondition
35 def self.trigger(condition)
38 self.handle_poll(condition)
39 when EventCondition, TriggerCondition
40 self.handle_event(condition)
44 def self.handle_poll(condition)
47 metric = self.directory[condition]
49 # it's possible that the timer will trigger an event before it can be cleared
50 # by an exiting metric, in which case it should be ignored
54 watch.mutex.synchronize do
56 result = condition.test
59 messages = self.log(watch, metric, condition, result)
62 if condition.notify && self.trigger?(metric, result)
63 self.notify(condition, messages.last)
71 if result && condition.transition
76 metric.destination && metric.destination[result]
79 # transition or reschedule
84 rescue EventRegistrationFailedError
85 msg = watch.name + ' Event registration failed, moving back to previous state'
86 applog(watch, :info, msg)
93 Timer.get.schedule(condition)
98 message = format("Unhandled exception (%s): %s\n%s",
99 e.class, e.message, e.backtrace.join("\n"))
100 applog(nil, :fatal, message)
105 def self.handle_event(condition)
108 metric = self.directory[condition]
113 watch.mutex.synchronize do
115 messages = self.log(watch, metric, condition, true)
118 if condition.notify && self.trigger?(metric, true)
119 self.notify(condition, messages.last)
122 # get the destination
124 if condition.transition
129 metric.destination && metric.destination[true]
137 rescue Exception => e
138 message = format("Unhandled exception (%s): %s\n%s",
139 e.class, e.message, e.backtrace.join("\n"))
140 applog(nil, :fatal, message)
147 def self.trigger?(metric, result)
148 (metric.destination && metric.destination.keys.size == 2) || result == true
151 def self.log(watch, metric, condition, result)
153 if self.trigger?(metric, result)
161 # log info if available
163 Array(condition.info).each do |condition_info|
164 messages << "#{watch.name} #{status} #{condition_info} (#{condition.base_name})"
165 applog(watch, :info, messages.last % [])
168 messages << "#{watch.name} #{status} (#{condition.base_name})"
169 applog(watch, :info, messages.last % [])
173 debug_message = watch.name + ' ' + condition.base_name + " [#{result}] " + self.dest_desc(metric, condition)
174 applog(watch, :debug, debug_message)
179 def self.dest_desc(metric, condition)
180 if condition.transition
181 {true => condition.transition}.inspect
183 if metric.destination
184 metric.destination.inspect
191 def self.notify(condition, message)
192 spec = Contact.normalize(condition.notify)
197 spec[:contacts].inject([]) do |acc, contact_name_or_group|
198 cons = Array(God.contacts[contact_name_or_group] || God.contact_groups[contact_name_or_group])
199 unmatched << contact_name_or_group if cons.empty?
204 # warn about unmatched contacts
205 unless unmatched.empty?
206 msg = "#{condition.watch.name} no matching contacts for '#{unmatched.join(", ")}'"
207 applog(condition.watch, :warn, msg)
210 # notify each contact
211 resolved_contacts.each do |c|
212 host = `hostname`.chomp rescue 'none'
213 c.notify(message, Time.now, spec[:priority], spec[:category], host)
215 msg = "#{condition.watch.name} #{c.info ? c.info : "notification sent for contact: #{c.name}"} (#{c.base_name})"
217 applog(condition.watch, :info, msg % [])