How do I send Bugsnag notifices to stderr? - ruby

I'm working on a Ruby software which may catch some errors (exceptions) and use Bugsnag to record the event in the Bugsnag logs.
For example, I may have something like this:
begin
[...snip...]
rescue StandardError => e
Bugsnag.notify(e)
end
What I'd like to be able to do is redirect the message logged by that line of code to my console. That way I could get it to my log file and then search on it and see what's before/after it and make sure things are working as expected.
Is there a way to setup Bugsnag to get such functionality?

I suggest using an On Error Callback. This callback will be executed for every handled and unhandled exception.
Bugsnag.configure do |config|
config.add_on_error(proc do |event|
# redirect message to your console here
end)
end

Related

Rack-timeout results in 500 instead of 503

I'm using heroku's rack-timeout gem, along with dynamic error pages as described here.
However, when timeout raises an exception, it get's routed as a 500 error rather than 503.
I could catch the exception with a rescue_from in my application controller and manually route to errors#503, but that would prevent plugins like Rollbar from recording the exception.
Is there a way to get the correct error page rendered and ensure plugins like Rollbar still get wind of the exception?
I ended up using the rambulance gem, which provides a simple configuration option to solve this:
# config/initializers/rambulance.rb
Rambulance.setup do |config|
config.rescue_responses = {
"Rack::Timeout::RequestTimeoutException" => :service_unavailable
}
end
The author has also written up some good reasons why not to use the approach I was previously using:
Remove custom errors page section from the guides
I know this is an old question, but there's no need to add a gem dependency for this.
rack-timeout raises an exception; the 500 results from that exception being unhandled. To handle that exception and get a 503 or whatever else you might want, add:
config.action_dispatch.rescue_responses["Rack::Timeout::RequestTimeoutException"] = :service_unavailable
to your application.rb file.

Why can't I terminate Sinatra server?

Why can't I stop Sinatra server using the following:
post '/terminate' do
Thread.current.kill
end
I type in my browser:
localhost:<port>/terminate
but it never terminates the server, Sinatra says that it does not know such path. What could be the reason?
adzdavies is partially right, you don't hit the route because your browser is issuing a GET request and you've defined a post route, but exit won't work either, it'll just spit out an error at you. As will raising an Interrupt exception. Thread.current.kill just ends execution of the current thread, which seems to mean killing the current instance and the server will just spawn a new instance on the next request, it won't kill the server, the server has its own process.
require 'sinatra/base'
class SillyWaysToKillTheServer < Sinatra::Base
enable :inline_templates
get "/" do
erb :index
end
get '/terminate' do
exit # system exit!
end
get "/threadkill" do
Thread.current.kill
end
get "/exception" do
raise Interrupt, "Kill the server, please!"
end
run! if __FILE__ == $0
end
__END__
## layout
<html>
<body>
<%= yield %>
</body>
</html>
## index
<p>terminate</p>
<p>threadkill</p>
<p>exception</p>
Sinatra is a framework, not a server. The server has its own process and runs a little loop that starts new threads or forks (Thin uses threading as its model, Unicorn uses preforking, for example) or whatever which run the Sinatra code you've provided. To stop the server, either use Ctrl+c to interrupt it, or find the process number and use kill or send a SIGHUP via something like everyone else does. There may be some good reason for doing stopping a webserver like this, but I can't think of one, and maybe different servers would respond differently to the Thread kills and exits etc, but they still won't stop your server.
Go with the flow.
The browser will be doing a "GET" http request.
If you change to
get '/terminate' do
exit # system exit!
end
I think it will work.

How to test 404 Sinatra::NotFound errors with Rack::Test?

I have an application that raises 404 Sinatra::NotFound errors on production when the route is missing.
However, the following test passes:
it 'should not raise error' do
expect{ get '/unknown_path' }.to_not raise_error
end
Why don't 404 Sinatra::NotFound errors get raised in the test?
The following does raise the error and cause the test to fail:
get '/unknown_path' do
raise 'error'
end
How to test for 404 Sinatra::NotFound errors?
The issue is that get /unknown_path' is not actually raising an error – it's only receiving a 404 request.
From Sinatra: README
The error handler is invoked any time an exception is raised from a route block or a filter. The exception object can be obtained from the sinatra.error Rack variable...
Your test is testing for an actual error (this is what raise_error does), while Sinatra is squashing the error – otherwise, every time someone 404'd the server would crash!
Check out the Sinatra testing guide for better direction on forming your tests. The basic idea is that using get ... in a test sets the last_response local variable, which can then be tested for equality, etc.
last_response has a status attribute, so (like was mentioned in another answer) you can just test to make sure that last_response.status equals 404.
Edit:
I wasn't really clear about this. In testing mode, the application does actually raise errors.
From Sinatra Settings
raise_errors
raise exceptions (will stop application). Enabled by default when environment is set to "test", disabled otherwise.
So you don't actually want to raise just any error, but raise Sinatra::NotFound.new("Not found!") to raise the specific type of error. The Sinatra::NotFound error will trigger the 404 handler. Sinatra Error Handling
Try this:
get '/unknown_path'
assert_equal last_response.status, 404

how to halt from inside stream block in sinatra?

I am trying to respond with a HTTP error code from my streaming block, but the web server throws an exception. what is the proper way to do it in this context?
/var/lib/gems/1.9.1/gems/sinatra-1.3.3/lib/sinatra/base.rb:803:in `throw':
uncaught throw :halt (ArgumentError)
my code:
require 'sinatra/base'
class App < Sinatra::Base
get '/' do
stream :keep_open do |out|
error 401
end
end
run! if app_file == $0
end
Based on my understanding of #stream(), the response Headers have already been sent. While you can continue streaming data (the body), and even close the connection, I don't think you can modify the header after they've already been sent. I'm digging through Sinatra YARD docs to verify, but I'm pretty sure that is your issue.

POSTing an HTML form to remote.cgi - written in Ruby?

I am working on a website hosted on microsoft's office live service. It has a contact form enabling visitors to get in touch with the owner. I want to write a Ruby script that sits on a seperate sever and which the form will POST to. It will parse the form data and email the details to a preset address. The script should then redirect the browser to a confirmation page.
I have an ubuntu hardy machine running nginx and postfix. Ruby is installed and we shall see about using Thin and it's Rack functionality to handle the script. Now it's come to writing the script and i've drawn a blank.
It's been a long time and if i remember rightly the process is something like;
read HTTP header
parse parameters
send email
send redirect header
Broadly speaking, the question has been answered. Figuring out how to use the answer was more complicated than expected and I thought worth sharing.
First Steps:
I learnt rather abruptly that nginx doesn't directly support cgi scripts. You have to use some other process to run the script and get nginx to proxy requests over. If I was doing this in php (which in hind sight i think would have been a more natural choice) i could use something like php-fcgi and expect life would be pretty straight forward.
Ruby and fcgi felt pretty daunting. But if we are abandoning the ideal of loading these things at runtime then Rack is probably the most straight forward solution and Thin includes all we need. Learning how to make basic little apps with them has been profoundly beneficial to a relative Rails newcomer like me. The foundations of a Rails app can seem hidden for a long time and Rack has helped me lift the curtain that little bit further.
Nonetheless, following Yehuda's advice and looking up sinatra has been another surprise. I now have a basic sinatra app running in a Thin instance. It communicates with nginx over a unix socket in what i gather is the standard way. Sinatra enables a really elegant way to handle different requests and routes into the app. All you need is a get '/' {} to start handling requests to the virtual host. To add more (in a clean fashion) we just include a routes/script.rb into the main file.
# cgi-bin.rb
# main file loaded as a sinatra app
require 'sinatra'
# load cgi routes
require 'routes/default'
require 'routes/contact'
# 404 behaviour
not_found do
"Sorry, this CGI host does not recognize that request."
end
These route files will call on functionality stored in a separate library of classes:
# routes/contact.rb
# contact controller
require 'lib/contact/contactTarget'
require 'lib/contact/contactPost'
post '/contact/:target/?' do |target|
# the target for the message is taken from the URL
msg = ContactPost.new(request, target)
redirect msg.action, 302
end
The sheer horror of figuring out such a simple thing will stay with me for a while. I was expecting to calmly let nginx know that .rb files were to be executed and to just get on with it. Now that this little sinatra app is up and running, I'll be able to dive straight in if I want to add extra functionality in the future.
Implementation:
The ContactPost class handles the messaging aspect. All it needs to know are the parameters in the request and the target for the email. ContactPost::action kicks everything off and returns an address for the controller to redirect to.
There is a separate ContactTarget class that does some authentication to make sure the specified target accepts messages from the URL given in request.referrer. This is handled in ContactTarget::accept? as we can guess from the ContactPost::action method;
# lib/contact/contactPost.rb
class ContactPost
# ...
def action
return failed unless #target.accept? #request.referer
if send?
successful
else
failed
end
end
# ...
end
ContactPost::successful and ContactPost::failed each return a redirect address by combining paths supplied with the HTML form with the request.referer URI. All the behaviour is thus specified in the HTML form. Future websites that use this script just need to be listed in the user's own ~/cgi/contact.conf and they'll be away. This is because ContactTarget looks in /home/:target/cgi/contact.conf for the details. Maybe oneday this will be inappropriate, but for now it's just fine for my purposes.
The send method is simple enough, it creates an instance of a simple Email class and ships it out. The Email class is pretty much based on the standard usage example given in the Ruby net/smtp documentation;
# lib/email/email.rb
require 'net/smtp'
class Email
def initialize(from_alias, to, reply, subject, body)
#from_alias = from_alias
#from = "cgi_user#host.domain.com"
#to = to
#reply = reply
#subject = subject
#body = body
end
def send
Net::SMTP.start('localhost', 25) do |smtp|
smtp.send_message to_s, #from, #to
end
end
def to_s
<<END_OF_MESSAGE
From: #{#from_alias}
To: #{#to}
Reply-To: #{#from_alias}
Subject: #{#subject}
Date: #{DateTime::now().to_s}
#{#body}
END_OF_MESSAGE
end
end
All I need to do is rack up the application, let nginx know which socket to talk to and we're away.
Thank you everyone for your helpful pointers in the right direction! Long live sinatra!
It's all in the Net module, here's an example:
#net = Net::HTTP.new 'http://www.foo.com', 80
#params = {:name => 'doris', :email => 'doris#foo.com'}
# Create HTTP request
req = Net::HTTP::Post.new( 'script.cgi', {} )
req.set_form_data #params
# Send request
response = #net.start do |http|
http.read_timeout = 5600
http.request req
end
Probably the best way to do this would be to use an existing Ruby library like Sinatra:
require "rubygems"
require "sinatra"
get "/myurl" do
# params hash available here
# send email
end
You'll probably want to use MailFactory to send the actual email, but you definitely don't need to be mucking about with headers or parsing parameters.
CGI class of Ruby can be used for writing CGI scripts. Please check: http://www.ruby-doc.org/stdlib/libdoc/cgi/rdoc/index.html
By the way, there is no need to read the HTTP header. Parsing parametres will be easy using CGI class. Then, send the e-mail and redirect.

Resources