Timing out Watir with Timeout::timeout

I’ve been writing a script to monitor our production app that has been playing up a lot lately. The problem is that the service on the server continues to run but when trying to access the main page it just sits there loading indefinitely.

The idea is to write a Watir script that brings up the main page every minute and notifies us if it displays an error, or, as has been happening, sits there loading indefinitely.

One of the things I love about Watir is how it handles browser synchronization: it’s really neat. To quote Bret’s design objective:

“Watir is deterministic. Watir does not wait X seconds. It waits until the page is loaded. Period.”
- Bret Pettichord

So this is great when you’re writing a test script, but as my page sometimes loads indefinitely, so does my Watir script:

require 'watir'
check_url = 'www.google.com'
ie = Watir::IE.new()
ie.goto(check_url)
puts ie.check_for_http_error()

I did some research and found a ruby Timeout class that I figured I could use. My first attempt was to do something like this:

require 'watir'
check_url = 'www.google.com'
ie = Watir::IE.new()
begin</pre>
Timeout::timeout(30) do
 ie.goto(check_url)
 puts ie.check_for_http_error()
 end
rescue
 puts 'timed out'
end

This seemed to work when the page didn’t timeout, but for some reason it wasn’t catching the timeout. So I did a bit of digging and found out a rescue clause in ruby with no following class only catches exceptions of type StandardError, a subclass of Exception. So it wasn’t catching the Timeout::Error exception. The way to catch this exception is to always include the Exception clause, and to pass it to a variable such as ‘e’.

The final script looked something like:

require 'watir'

check_url = 'www.google.com'
ie = Watir::IE.new()
begin
 Timeout::timeout(30) do
 ie.goto(check_url)
 puts ie.check_for_http_error()
 end
rescue Exception => e
 puts 'timed out: '+e
end

In my research on Timeout::timeout I found this article that explains how it’s actually dangerous to use Timeout::timeout, but until I figure out a better solution it seems to work pretty well for me.

Simple web application monitoring with Watir

One of things I love about Watir is its flexibility. For example, you can quickly and easily write a script to monitor your web application availability and schedule it to repeat periodically.

I like the idea of application monitoring that acts like a real user. It’s all well and good to monitor a server’s CPU and memory but if the user can’t access the logon page then the application is not doing its job.

I use SMTP to send an email if a page is unavailable. There is some good information about using SMTP connections in ruby available here. You need to specify an SMTP server which most organisations already have running, otherwise you can run one locally or use a public one such as Gmail. You can set up notification groups which then send text messages as well.


require "watir" # For connecting to web pages
require "socket" # For getting host name
require "net/smtp" # For sending email

MONITORED_URLS = ["http://www.google.com","http://www.ruby-lang.org/en/"] #This is a list of urls
EMAIL_FROM = "your.email@example.com"
EMAILS_TO = ["your.email@example.com"] # This must be a list of addresses.
SMTP_SERVER_NAME = "localhost"
LOG_FILE_NAME = "./watir_monitor_log.txt"

def check_page_available(url)
   begin
      start_time = Time.now
      ie_page = Watir::IE.start(url)
      load_time = (Time.now - start_time).to_s
      if (ie_page.check_for_http_error() or ie_page.text.include?('The page cannot be displayed')) then
         result = false
      else
         result = true
      end
   rescue
      puts "EXCEPTION RAISED: #{$!}"
      result = false
   end
   ie_page.close
   return result, load_time
end

def send_email(subject)
   Net::SMTP.start(SMTP_SERVER_NAME) do |smtp_server|
      EMAILS_TO.each do |email_address|
         email_message = "From: Web Site Monitoring Script \n"
         email_message << "To: #{email_address}\n"
         email_message << "Subject: #{subject}\n"
         email_message << "#{subject}\n\n"
         smtp_server.send_message email_message, EMAIL_FROM, email_address
      end
   end
end

# Start of Script
host_name = Socket.gethostname
MONITORED_URLS.each do |url|
	puts url
	date_time = Time.now.strftime( "%A %d/%m/%Y %I:%M %p" )
	result, load_time = check_page_available(url)
	if not result then
		send_email( "Could not connect to URL: . There may be a problem with this site." )
	end
		
	File.open(LOG_FILE_NAME, File::WRONLY|File::APPEND|File::CREAT, 0666) do |log_file|
		log_file.puts "#{date_time},#{host_name},#{url},#{result},#{load_time}"
	end # File closes automatically
end
# End of Script

I like to record page load times for historical data collection. I have logged this information locally for simplicity but you can also easily log this information to a wiki page (such as Confluence) so that it is easy for others to access as well.

Once you have the ruby script ready you can simply schedule it to run as a Windows task every 10 minutes or so. I usually create a one line batch file to call the script with the -b flag so the browser doesn’t display during execution.