I am attempting to rescue RuntimeErrors from within my controller using rescue_from, log it as a HealthNotice, and then redirect the user with a flash message, but I'm finding that in certain circumstances, the rescue_from method is being run twice (I end up with 2 duplicate HealthNotices). If the exception is rasied from a function called in a method (such as the test method below), it only creates one, but if I raise the exception directly within the method (such as test2), the rescue function is running twice (although strangely I don't get a double render error -- ??)
class Portal::BluerimController < PortalController
rescue_from RuntimeError, with: :error_handler
def error_handler(e)
hn_long_message = "#{e.class}: #{e.message}\n#{e.backtrace.join("\n")}\n\nPARAMS:\n#{params}"
HealthNotice.create(name: "API Exception Handled", severity: "WARNING", short_message: e.message, long_message: hn_long_message)
flash[:notice] = e.message
redirect_to :action => :index and return
end
def test
Sotal.new.test # a method that raises a RuntimeError -- only 1 HealthNotice
end
def test2
raise "an error" # results in duplicate HealthNotice
end
end
Any suggestions on what I'm doing wrong? Thanks!
Related
I have problem with handling exceptions. I know how to do it, but I'm not sure what's the correct place to rescue them. For example:
class ExampleService
def call
...
raise ExampleServiceError, 'ExampleService message'
end
end
class SecondExampleService
def call
raise SecondExampleServiceError if something
ExampleService.call
rescue ExampleService::ExampleServiceError => e ---> should I rescue it here?
raise SecondExampleServiceError, e.message
end
end
Class ExampleController
def update
SecondExampleService.call
rescue ExampleService::ExampleServiceError, SecondExampleService::SecondExampleServiceError => e
render json: { error: e.message }
end
end
As you can see in example I have two Services. ExampleService raises his own exception. SecondExampleService call the ExampleService, can raise exception and is used in ExampleController. Where should I rescue ExampleServiceError? In ExampleController or SecondExampleService? If in ExampleController, what about when we use such a Service in multiple controllers (rescue code will be repeated many times)?
When the controller only calls SecondExampleService directly and doesn't know anything about ExampleService, then it should not rescue from ExampleServiceError.
Only SecondExampleService knows that ExampleService is used internally, and therefore SecondExampleService should rescue from ExampleServiceError and translate it into a SecondExampleServiceError.
That is my interpretation of the law of demeter.
In our Rails application we use Airbrake connected to a hosted Errbit server.
We use rescue and rescue_from in a lot of places where we want to handle any exceptions in a particular way and then we log the exception ourselves before returning a response.
Some examples we have in our ApplicationController:
rescue_from CanCan::AccessDenied do |e|
Rails.logger.error "CanCan exception: #{e.message}"
render 'errors/401', status: 401
end
rescue_from ActionController::InvalidAuthenticityToken do |e|
Rails.logger.error "Authenticity exception: #{e.message}"
render 'errors/csrf', status: 400
end
And then we also have some in individual methods such as an API:
def try_request
Response.new(yield)
rescue RestClient::Unauthorized,
RestClient::ExceptionWithResponse,
RestClient::InternalServerError,
RestClient::BadRequest => e
Rails.logger.error "API exception: #{e.message}"
Response.new(e.response)
end
However by using rescue we noticed that Errbit was no longer picking up our execeptions because we were capturing them... makes sense... but we want to still see these in our Errbit reports!
You can manually notify Airbrake with:
Airbrake.notify(e)
But we were hoping to avoid having to put this code in every single rescue block.
Is it possible to have a higher-level statement that can notify Airbrake automatically when manually logging errors? Perhaps middleware that is called when Rails.logger.error is called?
rescue is ruby clause, you cannot override it directly (and even if you could - it would not be a great idea)
In all your examples there's Rails.logger.error, you can make yourself a helper like:
module MyErrorNotifier
def self.notify(message, exception, params = {}, &block)
Rails.logger.error("#{message}: #{exception.message}")
Airbrake.notify(exception, params, &block)
end
end
and instead of logger call it:
def try_request
...
rescue e
MyErrorNotifier.notify("Foo exception happened", e)
Response.new(e.response)
end
I have a vanilla Rails app that consists just of a simple Post model with a title and a content attribute.
I took the ActiveJob exception example from the official Rails guide, which looks as follows:
class ExceptionTestJob < ApplicationJob
queue_as :default
rescue_from(ActiveRecord::RecordNotFound) do |exception|
logger.error("Could not find given post")
end
def perform(post_id)
post = Post.find(post_id)
end
end
If I try to run the job with a post id that doesn’t exist, I get the full stack trace and the error message "Could not find given post".
I do not want to get the stack trace. I just want to get the code executed that's in the rescue_from block. How do I suppress the stack trace?
Edit:
If I use the regular rescue inside the method like this:
class ExceptionTestJob < ApplicationJob
def perform(post_id)
begin
Post.find(post_id)
rescue ActiveRecord::RecordNotFound
logger.error("Could not find given post")
end
end
end
the exception will get caught without the backtrace (like intended), but the job will finish as successful: Performed ExceptionTestJob (Job ID: c21ce397-b19b-4464-9e53-696707b87dde) from Async(default) in 13.84ms.
I think it will help you
begin
raise
rescue => e
logger.error e.message
logger.error e.backtrace.join("\n")
end
I have a webhook sent to a page on my app, and I am catching SOAP errors from processing the data, but I want to send to the webhook a 200 status. Is this possible in the controller?
controller
def example_method
...
rescue Savon::SOAPFault => error
...
# Respond to the HTTP POST request by giving the 'all clear'
head 200
raise
end
First off don't place soap calls directly into your controller - instead create a client class or service object that handles it.
Your controller should only really know that it calls some kind of service and that it may fail or succeed - if you are catching library specific errors in your controller you are on your way down the rabbit hole.
So how do you rescue exceptions in your controller?
You can rescue them inline:
class SomethingController
def do_something
begin
# Something.this_may_blow_up!
rescue SomeError => error
head :ok and return
end
end
end
The key here is and return which halts the controller flow and prevents double render errors.
The other method is by using rescue_from.
class ThingsController
rescue_from ActiveRecord::RecordNotFound do
render :not_found and return
end
def show
#thing = Thing.find(params[:id])
end
end
The above is an example of how you can create a custom 404 page per controller. What happens is that when the action on the controller is invoked it is wrapped in a begin..rescue block that looks for rescue_from handlers in the controller.
How do I rescue from a
undefined method
error in this code
for user in #users do
#customer = Stripe::Customer.retrieve(user.stripe_customer_token)
if #customer.subscription.nil?
elsif #customer.subscription.plan.id == 2
user.silver_reset
elsif #customer.subscription.plan.id == 3
user.gold_reset
end
end
I have tried a plain rescue call, but rake isn't liking it.
What is way to rescue from the error?
UPDATE:
The way I was doing it
for user in #users do
#customer = Stripe::Customer.retrieve(user.stripe_customer_token)
rescue_from Exception => exception
# Logic
end
if #customer.subscription.nil?
elsif #customer.subscription.plan.id == 2
user.silver_reset
elsif #customer.subscription.plan.id == 3
user.gold_reset
end
end
The Error
/home/user/rails_projects/assignitapp/lib/tasks/daily.rake:25: syntax error, unexpected keyword_rescue, expecting keyword_end
rescue Exception => exception
Rake 0.9.2.2
Rails 3.2.5
Use try to wrap the problem method and return nil if it doesn't exist. E.g.:
unless #customer = Stripe::Customer.try(:retrieve, user.stripe_customer_token)
# Logic
end
Alternatively, this catches more errors:
unless #customer = Stripe::Customer.retrieve(user.stripe_customer_token) rescue nil
# Logic
end
Or this is more what you were trying for:
#users.each do |user|
begin
#customer = Stripe::Customer.retrieve(user.stripe_customer_token)
rescue StandardError => error
# handle error
end
end
I still haven't enough reputation to comment, but regarding the above answer's third option: Don't rescue from Exception!
Exception is the root of Ruby's exception hierarchy, so when you rescue Exception you rescue from everything, including subclasses such as SyntaxError, LoadError, and Interrupt.
If you want to learn more, check out Why is it a bad style to `rescue Exception => e` in Ruby?