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
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
According to the family tree of Exception. SyntaxError is child to ScriptError
I wish to handle Syntax and/or ScriptError in my rails application.
Exception
NoMemoryError
ScriptError
LoadError
NotImplementedError
SyntaxError
SignalException
Interrupt
StandardError
ArgumentError
IOError
EOFError
IndexError
StopIteration
LocalJumpError
NameError
NoMethodError
RangeError
FloatDomainError
RegexpError
RuntimeError
SecurityError
SystemCallError
SystemStackError
ThreadError
TypeError
ZeroDivisionError
SystemExit
fatal
I did:
rescue_from ScriptError, :with => :notify_on_landing_page
but didn't worked.
Error raised on screen :
SyntaxError in Bc::Muse::User::ProfileController#show
I have created an explicit syntax error, it should gracefully rescue it and do the things I want to.
Unfortunately, I don't think it works that way.
Rescuing from exceptions with rescue_from works only after creating a controller instance during request processing (see source code here and here). Your SyntaxError is probably raised much earlier - during autoloading of the given controller and dependent classes / modules. So unless you were trying to rescue from a syntax error of code being loaded during a controller action execution, you are out of luck, I'm afraid.
Test: when you explicitly load a file with a syntax error in a controller action and the rescue_from will work as expected:
class MyController < ApplicationController
rescue_from(::SyntaxError) { Rails.logger.error "SYNTAX ERROR!" }
def index
load "#{Rails.root}/test.rb"
end
end
If you save a test.rb file in the rails root and add a deliberate syntax error in it, you will see the exception is handled correctly by rescue_from and the error message will be present in the log file.
On the other hand, if you look at the full stack trace of your SyntaxError, you will probably see that it does not even reach the ActionController methods for request processing.
Thanks!
I added a middleware to achieve this.
class RescueSyntaxError
def initialize(app)
#app = app
end
def call(env)
#app.call(env)
rescue SyntaxError => error
request = Rack::Request.new(env)
session = request.env['rack.session']
params = request.params
if session.try(:[], :user_object)
##Do validation stuff
...
[302, {'Location' => '/'}, []]
end
end
end
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!
I have a Rails web app with some forms users can use to comment etc. They are frequently being used by spammers trying to create spam comments. Nothing new about that.
Sometimes I get CSRF-hack attempts which causes an ActionController::InvalidAuthenticityToken exception. This happens quite a lot and I would like to rescue and send the user/bot to a "You failed to comment"-page.
This has turned out to be a tricky exception to catch though since I can't recreate the error myself. I first put a rescue in the #create when the model (created by the form) was to be saved but it doesn't catch the exception. In order to do so I consider having it cover the entire controller section but that seems over the top.
My two questions:
Is there a way to re-create an ActionController::InvalidAuthenticityToken error by myself, so I can test it?
When is the exception raised? During .save? On Comment.new? Basically, where should I put my begin/rescue?
Thanks!
You can rescue from this exception in your controller. One way you can set this up is to add a rescue in your ApplicationController.
class ApplicationController < ActionController::Base
rescue_from ActionController::InvalidAuthenticityToken, :with => :invalid_auth_token
private
def record_not_found
render :text => "You failed to comment", :status => 422
end
end
You could also just capture the exception locally in your controller action.
def create
begin
# Create your comment
rescue ActionController::InvalidAuthenticityToken
# Render your last view with some error text.
end
end
You can add raise ActionController::InvalidAuthenticityToken inside your action if you want to test it out.