How to get curb error stacktrace - ruby

How can I display the entire stacktrace where there is an error when using Curb's curl::easy? I've tried looking at http://www.rubydoc.info/github/taf2/curb/Curl/Err to get information, but don't see anything.

begin
curl.perform
rescue Curl::Err::CurlError => e
puts e.backtrace.inspect # or log it
end
UPDATE
I tried that however that didn't seem to give me a detailed error
message. Right now I'm getting a generic error message and it's hard
to debug
As Tin Man already mentioned "rubydoc.info/github/taf2/curb/Curl/Err is the list of possible errors that could be returned if Curb has a problem.".
To catch specific errors you need to rescue them individually. As you can see in my example above I am trying to rescue specifically Curl::Err::CurlError. Similarly you can add more errors as follows:
rescue Curl::Err::CurlError => e
puts "CURLERROR: === " + e.backtrace.inspect
rescue Curl::Err::AccessDeniedError => e
puts "ACCESSDENIED: === " + e.backtrace.inspect
rescue Curl::Err::TimeoutError => e
puts "TIMEOUT: === " + e.backtrace.inspect
...
end

Related

In RoR, how do I catch an exception if I get no response from a server?

I’m using Rails 4.2.3 and Nokogiri to get data from a web site. I want to perform an action when I don’t get any response from the server, so I have:
begin
content = open(url).read
if content.lstrip[0] == '<'
doc = Nokogiri::HTML(content)
else
begin
json = JSON.parse(content)
rescue JSON::ParserError => e
content
end
end
rescue Net::OpenTimeout => e
attempts = attempts + 1
if attempts <= max_attempts
sleep(3)
retry
end
end
Note that this is different than getting a 500 from the server. I only want to retry when I get no response at all, either because I get no TCP connection or because the server fails to respond (or some other reason that causes me not to get any response). Is there a more generic way to take account of this situation other than how I have it? I feel like there are a lot of other exception types I’m not thinking of.
This is generic sample how you can define timeout durations for HTTP connection, and perform several retries in case of any error while fetching content (edited)
require 'open-uri'
require 'nokogiri'
url = "http://localhost:3000/r503"
openuri_params = {
# set timeout durations for HTTP connection
# default values for open_timeout and read_timeout is 60 seconds
:open_timeout => 1,
:read_timeout => 1,
}
attempt_count = 0
max_attempts = 3
begin
attempt_count += 1
puts "attempt ##{attempt_count}"
content = open(url, openuri_params).read
rescue OpenURI::HTTPError => e
# it's 404, etc. (do nothing)
rescue SocketError, Net::ReadTimeout => e
# server can't be reached or doesn't send any respones
puts "error: #{e}"
sleep 3
retry if attempt_count < max_attempts
else
# connection was successful,
# content is fetched,
# so here we can parse content with Nokogiri,
# or call a helper method, etc.
doc = Nokogiri::HTML(content)
p doc
end
When it comes to rescuing exceptions, you should aim to have a clear understanding of:
Which lines in your system can raise exceptions
What is going on under the hood when those lines of code run
What specific exceptions could be raised by the underlying code
In your code, the line that's fetching the content is also the one that could see network errors:
content = open(url).read
If you go to the documentation for the OpenURI module you'll see that it uses Net::HTTP & friends to get the content of arbitrary URIs.
Figuring out what Net::HTTP can raise is actually very complicated but, thankfully, others have already done this work for you. Thoughtbot's suspenders project has lists of common network errors that you can use. Notice that some of those errors have to do with different network conditions than what you had in mind, like the connection being reset. I think it's worth rescuing those as well, but feel free to trim the list down to your specific needs.
So here's what your code should look like (skipping the Nokogiri and JSON parts to simplify things a bit):
require 'net/http'
require 'open-uri'
HTTP_ERRORS = [
EOFError,
Errno::ECONNRESET,
Errno::EINVAL,
Net::HTTPBadResponse,
Net::HTTPHeaderSyntaxError,
Net::ProtocolError,
Timeout::Error,
]
MAX_RETRIES = 3
attempts = 0
begin
content = open(url).read
rescue *HTTP_ERRORS => e
if attempts < MAX_RETRIES
attempts += 1
sleep(2)
retry
else
raise e
end
end
I would think about using a Timeout that raises an exception after a short period:
MAX_RESPONSE_TIME = 2 # seconds
begin
content = nil # needs to be defined before the following block
Timeout.timeout(MAX_RESPONSE_TIME) do
content = open(url).read
end
# parsing `content`
rescue Timeout::Error => e
attempts += 1
if attempts <= max_attempts
sleep(3)
retry
end
end

Ruby/Webdriver : Error handling log output

Here's what I'm currently doing:
def wait_for(timeout = 5)
Selenium::WebDriver::Wait.new(:timeout => timeout).until { yield }
rescue Selenium::WebDriver::Error::TimeOutError => e
puts 'Timeout Error'
rescue Selenium::WebDriver::Error::NoSuchElementError => ex
puts 'No Such Element Error'
end
Two questions:
1) Why is it showing me the Timeout Error twice? It should just time out and end the test
2) How do I get it to not show me all that extra information at the bottom? Ideally what I'd like is just for it to say "Timeout" or "No such element" and that's it.**
My log spits out alllll of this:
Loaded suite C:/2oh/qt Started
First Run Timeout Error Timeout Error E
======================================================================================================================================================================================================== Error: test_18a(Tests):
Selenium::WebDriver::Error::NoSuchElementError: no such element
(Session info: chrome=43.0.2357.134)
(Driver info: chromedriver=2.15.322448 (52179c1b310fec1797c81ea9a20326839860b7d3),platform=Windows NT 6.1 SP1
x86_64)
C:/Ruby22/lib/ruby/gems/2.2.0/gems/selenium-webdriver-2.46.2/lib/selenium/webdriver/remote/response.rb:71:in
assert_ok'
C:/Ruby22/lib/ruby/gems/2.2.0/gems/selenium-webdriver-2.46.2/lib/selenium/webdriver/remote/response.rb:34:in
initialize'
C:/Ruby22/lib/ruby/gems/2.2.0/gems/selenium-webdriver-2.46.2/lib/selenium/webdriver/remote/http/common.rb:78:in
new'
C:/Ruby22/lib/ruby/gems/2.2.0/gems/selenium-webdriver-2.46.2/lib/selenium/webdriver/remote/http/common.rb:78:in
create_response'
C:/Ruby22/lib/ruby/gems/2.2.0/gems/selenium-webdriver-2.46.2/lib/selenium/webdriver/remote/http/default.rb:90:in
request'
C:/Ruby22/lib/ruby/gems/2.2.0/gems/selenium-webdriver-2.46.2/lib/selenium/webdriver/remote/http/common.rb:59:in
call'
C:/Ruby22/lib/ruby/gems/2.2.0/gems/selenium-webdriver-2.46.2/lib/selenium/webdriver/remote/bridge.rb:657:in
raw_execute'
C:/Ruby22/lib/ruby/gems/2.2.0/gems/selenium-webdriver-2.46.2/lib/selenium/webdriver/remote/bridge.rb:635:in
execute'
C:/Ruby22/lib/ruby/gems/2.2.0/gems/selenium-webdriver-2.46.2/lib/selenium/webdriver/remote/bridge.rb:603:in
find_element_by'
C:/Ruby22/lib/ruby/gems/2.2.0/gems/selenium-webdriver-2.46.2/lib/selenium/webdriver/common/search_context.rb:61:in
find_element' C:/2oh/qttests.rb:28:in test_hdesrbf'
C:/2oh/qt.rb:13:intest_18a'
10:
11: def test_18a
12: puts "First Run" => 13: test_hdesrbf
14: end
15:
16:
The output you are seeing is telling you more information about the error you are rescuing. Don't think there is any escaping it, although it is handy because it will tell you what line of code has failed, and why. I'm not actually 100% sure why you would want to simplify the error!
The reason you are seeing the errors twice is because you are not telling the program to end. You are saying 'Hey, rescue this error type and output this text.' you're not telling it to stop. It will continue and I guess timeout finding the next element? To rectify this, instead of using puts to output your error text, use raise. This will halt the program once the text has been output. E.g
def wait_for(timeout = 5)
Selenium::WebDriver::Wait.new(:timeout => timeout).until { yield }
rescue Selenium::WebDriver::Error::TimeOutError
raise 'Timeout Error'
rescue Selenium::WebDriver::Error::NoSuchElementError
raise 'No Such Element Error'
end

Ruby rescue syntax error

I have the following line of code that is giving me an error:
rescue Timeout::Error => e
logs.puts("Rescued a timeout error...#{e}")
email_ids_all.each do |email_delete|
call= "/api/v2/emails/#{email_delete}/"
uri= HTTParty.delete("https://www.surveys.com#{call}",
:basic_auth => auth,
:headers => { 'ContentType' => 'application/x-www-form-urlencoded', 'Content-Length' => "0" }
)
puts "Deleted email #{email_delete}".green
log.puts("Deleted email #{email_delete}")
end
abort #abort entire script after deleting emails
end
The error I am receiving is this:
syntax error, unexpected keyword_rescue, expecting $end
rescue Timeout::Error => e
^
Essentially I am just trying to run an API delete call if the script times out. It doesn't seem to matter what I put in the block for rescue though, I receive the same error. What's wrong with my syntax on the rescue method?
The format for using rescue is as follows:
begin
# Code you want to run that might raise exceptions
rescue YourExceptionClass => e
# Code that runs in the case of YourExceptionClass
rescue ADifferentError => e
# Code that runs in the case of ADifferentError
else
# Code that runs if there was no error
ensure
# Code that always runs at the end regardless of whether or not there was an error
end
Here is a question with lots more information: Begin, Rescue and Ensure in Ruby?.

Ruby to read from a Server that resets frequently

I have a server that disconnects frequently
$echo "GET hosts" | nc localhost 323
a.host.com
b.host.com
c.host.com
While reading the same thing from ruby with following code
s = TCPSocket.new(host,port)
s.puts "GET hosts\n\n\r\r"
data = ""
begin
until s.closed?
l = s.gets
puts "Host:" + l
data = data + l
end
rescue Exception => e
puts "pp" + e.message
end
prints out
Host:a.host.com
Host:b.host.com
Host:c.host.com
Host:Error reading from 3: Connection reset by peer
And the application hangs, somehow.
Any heads up for this?? Weird thing is that it is not entering the exception handler.
You can try rescuing like that rescue Errno::ECONNRESET => e.
Check out How to catch error Connection reset by peer (Errno::ECONNRESET), especially the retry bit.

How do I get ruby to print a full backtrace instead of a truncated one?

When I get exceptions, it is often from deep within the call stack. When this happens, more often than not, the actual offending line of code is hidden from me:
tmp.rb:7:in `t': undefined method `bar' for nil:NilClass (NoMethodError)
from tmp.rb:10:in `s'
from tmp.rb:13:in `r'
from tmp.rb:16:in `q'
from tmp.rb:19:in `p'
from tmp.rb:22:in `o'
from tmp.rb:25:in `n'
from tmp.rb:28:in `m'
from tmp.rb:31:in `l'
... 8 levels...
from tmp.rb:58:in `c'
from tmp.rb:61:in `b'
from tmp.rb:64:in `a'
from tmp.rb:67
That "... 8 levels..." truncation is causing me a great deal of trouble. I'm not having much success googling for this one: How do I tell ruby that I want dumps to include the full stack?
Exception#backtrace has the entire stack in it:
def do_division_by_zero; 5 / 0; end
begin
do_division_by_zero
rescue => exception
puts exception.backtrace
raise # always reraise
end
(Inspired by Peter Cooper's Ruby Inside blog)
You could also do this if you'd like a simple one-liner:
puts caller
This produces the error description and nice clean, indented stacktrace:
begin
# Some exception throwing code
rescue => e
puts "Error during processing: #{$!}"
puts "Backtrace:\n\t#{e.backtrace.join("\n\t")}"
end
IRB has a setting for this awful "feature", which you can customize.
Create a file called ~/.irbrc that includes the following line:
IRB.conf[:BACK_TRACE_LIMIT] = 100
This will allow you to see 100 stack frames in irb, at least. I haven't been able to find an equivalent setting for the non-interactive runtime.
Detailed information about IRB customization can be found in the Pickaxe book.
One liner for callstack:
begin; Whatever.you.want; rescue => e; puts e.message; puts; puts e.backtrace; end
One liner for callstack without all the gems's:
begin; Whatever.you.want; rescue => e; puts e.message; puts; puts e.backtrace.grep_v(/\/gems\//); end
One liner for callstack without all the gems's and relative to current directory
begin; Whatever.you.want; rescue => e; puts e.message; puts; puts e.backtrace.grep_v(/\/gems\//).map { |l| l.gsub(`pwd`.strip + '/', '') }; end
This mimics the official Ruby trace, if that's important to you.
begin
0/0 # or some other nonsense
rescue => e
puts e.backtrace.join("\n\t")
.sub("\n\t", ": #{e}#{e.class ? " (#{e.class})" : ''}\n\t")
end
Amusingly, it doesn't handle 'unhandled exception' properly, reporting it as 'RuntimeError', but the location is correct.
Almost everybody answered this. My version of printing any rails exception into logs would be:
begin
some_statement
rescue => e
puts "Exception Occurred #{e}. Message: #{e.message}. Backtrace: \n #{e.backtrace.join("\n")}"
Rails.logger.error "Exception Occurred #{e}. Message: #{e.message}. Backtrace: \n #{e.backtrace.join("\n")}"
end
I was getting these errors when trying to load my test environment (via rake test or autotest) and the IRB suggestions didn't help. I ended up wrapping my entire test/test_helper.rb in a begin/rescue block and that fixed things up.
begin
class ActiveSupport::TestCase
#awesome stuff
end
rescue => e
puts e.backtrace
end
[examine all threads backtraces to find the culprit]
Even fully expanded call stack can still hide the actual offending line of code from you when you use more than one thread!
Example: One thread is iterating ruby Hash, other thread is trying to modify it. BOOM! Exception! And the problem with the stack trace you get while trying to modify 'busy' hash is that it shows you chain of functions down to the place where you're trying to modify hash, but it does NOT show who's currently iterating it in parallel (who owns it)! Here's the way to figure that out by printing stack trace for ALL currently running threads. Here's how you do this:
# This solution was found in comment by #thedarkone on https://github.com/rails/rails/issues/24627
rescue Object => boom
thread_count = 0
Thread.list.each do |t|
thread_count += 1
err_msg += "--- thread #{thread_count} of total #{Thread.list.size} #{t.object_id} backtrace begin \n"
# Lets see if we are able to pin down the culprit
# by collecting backtrace for all existing threads:
err_msg += t.backtrace.join("\n")
err_msg += "\n---thread #{thread_count} of total #{Thread.list.size} #{t.object_id} backtrace end \n"
end
# and just print it somewhere you like:
$stderr.puts(err_msg)
raise # always reraise
end
The above code snippet is useful even just for educational purposes as it can show you (like x-ray) how many threads you actually have (versus how many you thought you have - quite often those two are different numbers ;)
You can also use backtrace Ruby gem (I'm the author):
require 'backtrace'
begin
# do something dangerous
rescue StandardError => e
puts Backtrace.new(e)
end

Resources