@@ -144,6 +144,10 @@ class Agent < ActiveRecord::Base |
||
144 | 144 |
AgentLog.log_for_agent(self, message, options) |
145 | 145 |
end |
146 | 146 |
|
147 |
+ def error(message, options = {}) |
|
148 |
+ log(message, options.merge(:level => 4)) |
|
149 |
+ end |
|
150 |
+ |
|
147 | 151 |
# Class Methods |
148 | 152 |
class << self |
149 | 153 |
def cannot_be_scheduled! |
@@ -201,9 +205,14 @@ class Agent < ActiveRecord::Base |
||
201 | 205 |
# and Event ids instead of a literal ActiveRecord models because it is preferable to serialize delayed_jobs with ids. |
202 | 206 |
def async_receive(agent_id, event_ids) |
203 | 207 |
agent = Agent.find(agent_id) |
204 |
- agent.receive(Event.where(:id => event_ids)) |
|
205 |
- agent.last_receive_at = Time.now |
|
206 |
- agent.save! |
|
208 |
+ begin |
|
209 |
+ agent.receive(Event.where(:id => event_ids)) |
|
210 |
+ agent.last_receive_at = Time.now |
|
211 |
+ agent.save! |
|
212 |
+ rescue => e |
|
213 |
+ agent.error "Exception during receive: #{e.message} -- #{e.backtrace}" |
|
214 |
+ raise |
|
215 |
+ end |
|
207 | 216 |
end |
208 | 217 |
handle_asynchronously :async_receive |
209 | 218 |
|
@@ -228,9 +237,14 @@ class Agent < ActiveRecord::Base |
||
228 | 237 |
# id instead of a literal Agent because it is preferable to serialize delayed_jobs with ids. |
229 | 238 |
def async_check(agent_id) |
230 | 239 |
agent = Agent.find(agent_id) |
231 |
- agent.check |
|
232 |
- agent.last_check_at = Time.now |
|
233 |
- agent.save! |
|
240 |
+ begin |
|
241 |
+ agent.check |
|
242 |
+ agent.last_check_at = Time.now |
|
243 |
+ agent.save! |
|
244 |
+ rescue => e |
|
245 |
+ agent.error "Exception during check: #{e.message} -- #{e.backtrace}" |
|
246 |
+ raise |
|
247 |
+ end |
|
234 | 248 |
end |
235 | 249 |
handle_asynchronously :async_check |
236 | 250 |
end |
@@ -69,7 +69,7 @@ module Agents |
||
69 | 69 |
log "Fetching #{options[:url]}" |
70 | 70 |
request = Typhoeus::Request.new(options[:url], :followlocation => true) |
71 | 71 |
request.on_failure do |response| |
72 |
- log "Failed: #{response.inspect}" |
|
72 |
+ error "Failed: #{response.inspect}" |
|
73 | 73 |
end |
74 | 74 |
request.on_success do |response| |
75 | 75 |
doc = parse(response.body) |
@@ -84,7 +84,7 @@ module Agents |
||
84 | 84 |
elsif extraction_details[:text] |
85 | 85 |
node.text() |
86 | 86 |
else |
87 |
- log ":attr or :text is required on HTML or XML extraction patterns" |
|
87 |
+ error ":attr or :text is required on HTML or XML extraction patterns" |
|
88 | 88 |
return |
89 | 89 |
end |
90 | 90 |
} |
@@ -95,7 +95,7 @@ module Agents |
||
95 | 95 |
num_unique_lengths = options[:extract].keys.map { |name| output[name].length }.uniq |
96 | 96 |
|
97 | 97 |
if num_unique_lengths.length != 1 |
98 |
- log "Got an uneven number of matches for #{options[:name]}: #{options[:extract].inspect}", :level => 4 |
|
98 |
+ error "Got an uneven number of matches for #{options[:name]}: #{options[:extract].inspect}" |
|
99 | 99 |
return |
100 | 100 |
end |
101 | 101 |
|
@@ -125,25 +125,40 @@ describe Agent do |
||
125 | 125 |
end |
126 | 126 |
|
127 | 127 |
describe ".async_check" do |
128 |
- it "records last_check_at and calls check on the given Agent" do |
|
129 |
- checker = Agents::SomethingSource.new(:name => "something") |
|
130 |
- checker.user = users(:bob) |
|
131 |
- checker.save! |
|
128 |
+ before do |
|
129 |
+ @checker = Agents::SomethingSource.new(:name => "something") |
|
130 |
+ @checker.user = users(:bob) |
|
131 |
+ @checker.save! |
|
132 |
+ end |
|
132 | 133 |
|
133 |
- mock(checker).check.once { |
|
134 |
- checker.options[:new] = true |
|
134 |
+ it "records last_check_at and calls check on the given Agent" do |
|
135 |
+ mock(@checker).check.once { |
|
136 |
+ @checker.options[:new] = true |
|
135 | 137 |
} |
136 | 138 |
|
137 |
- mock(Agent).find(checker.id) { checker } |
|
139 |
+ mock(Agent).find(@checker.id) { @checker } |
|
140 |
+ |
|
141 |
+ @checker.last_check_at.should be_nil |
|
142 |
+ Agents::SomethingSource.async_check(@checker.id) |
|
143 |
+ @checker.reload.last_check_at.should be_within(2).of(Time.now) |
|
144 |
+ @checker.reload.options[:new].should be_true # Show that we save options |
|
145 |
+ end |
|
138 | 146 |
|
139 |
- checker.last_check_at.should be_nil |
|
140 |
- Agents::SomethingSource.async_check(checker.id) |
|
141 |
- checker.reload.last_check_at.should be_within(2).of(Time.now) |
|
142 |
- checker.reload.options[:new].should be_true # Show that we save options |
|
147 |
+ it "should log exceptions" do |
|
148 |
+ mock(@checker).check.once { |
|
149 |
+ raise "foo" |
|
150 |
+ } |
|
151 |
+ mock(Agent).find(@checker.id) { @checker } |
|
152 |
+ lambda { |
|
153 |
+ Agents::SomethingSource.async_check(@checker.id) |
|
154 |
+ }.should raise_error |
|
155 |
+ log = @checker.logs.first |
|
156 |
+ log.message.should =~ /Exception/ |
|
157 |
+ log.level.should == 4 |
|
143 | 158 |
end |
144 | 159 |
end |
145 | 160 |
|
146 |
- describe ".receive!" do |
|
161 |
+ describe ".receive! and .async_receive" do |
|
147 | 162 |
before do |
148 | 163 |
stub_request(:any, /wunderground/).to_return(:body => File.read(Rails.root.join("spec/data_fixtures/weather.json")), :status => 200) |
149 | 164 |
stub.any_instance_of(Agents::WeatherAgent).is_tomorrow?(anything) { true } |
@@ -155,6 +170,19 @@ describe Agent do |
||
155 | 170 |
Agent.receive! |
156 | 171 |
end |
157 | 172 |
|
173 |
+ it "should log exceptions" do |
|
174 |
+ mock.any_instance_of(Agents::TriggerAgent).receive(anything).once { |
|
175 |
+ raise "foo" |
|
176 |
+ } |
|
177 |
+ Agent.async_check(agents(:bob_weather_agent).id) |
|
178 |
+ lambda { |
|
179 |
+ Agent.receive! |
|
180 |
+ }.should raise_error |
|
181 |
+ log = agents(:bob_rain_notifier_agent).logs.first |
|
182 |
+ log.message.should =~ /Exception/ |
|
183 |
+ log.level.should == 4 |
|
184 |
+ end |
|
185 |
+ |
|
158 | 186 |
it "should track when events have been seen and not received them again" do |
159 | 187 |
mock.any_instance_of(Agents::TriggerAgent).receive(anything).once |
160 | 188 |
Agent.async_check(agents(:bob_weather_agent).id) |