I want to send mail to an email address whenever any kind of error occurs. Until now I have been working on the development environment. To accomplish this
I used the code from here and implemented that code in app/Exceptions/Handler.php.
When an error occurs, it neither displays the error nor sends an email.
My code is below :
public function report(Exception $e)
{
if ($e instanceof \Exception) {
//dd($e->getLine()); <-- gives output
// emails.exception is the template of your email
// it will have access to the $error that we are passing below
$mail = Mail::send('emails.error_report', ['content' => $e->getMessage()], function ($m) {
$m->to('mail#domain.com', 'Name')->subject('Error Report');
});
return parent::report($e);
}
return parent::report($e);
}
After research of few hours, I have managed to get idea what could be the reason behind this. What I think is, we are already handling an error, and while handling this error, what happens if another error occurs? means, I had an error in the mail function view file. which could not be handled further, while already handling an error.
So the Answer was correct. it was my specific problem. But still i would like to share the code with some improvisations,though code is not much different from the linked thread.
public function report(Exception $e)
{
if ($e instanceof Exception) {
// whenever any error occurs, a mail will be sent to desired email.
// content variable will contain message that you can send to emails.error_report(or whatever) view.
// you can use this variable simply echoing $content.
$content = "There was an error occured on Your Website.following are details of Error:<br><br>Message : ".$e->getMessage()."<br>Line : ".$e->getLine()."<br>File : ".$e->getFile()."";
$mail_content = [
'content' => $content,
];
// if you want to mail that error message to multiple email addresses put those addresses in $emails array.
$emails = array('mail1#domain.com','mail2#gmail.com');
$mail = Mail::send('emails.error_report', $mail_content, function ($message) use ($emails) {
$message->to($emails, 'Maninder')->subject('Error Report');
});
}
return parent::report($e);
}
Please do not forget to add use Illuminate\Support\Facades\Mail; on the top of app/Exceptions/Handler.php file.
After using working code for 1 day, i noticed that i got tons of mails regarding errors that are not needed to be reported to the admin.so i got after making little modification, admin will get email only when any fatal error occurs.though, if you want to be notified for all exceptions you can keep the code as it is above or make modification as below:
if ($e instanceof \Symfony\Component\Debug\Exception\FatalErrorException) {
Instead of:
if ($e instanceof Exception) {
Happy coding for new comers like me! ;)
Related
I'm currently working on a project with my friend. He follows this method of writing codes. Is it good or bad? I'm also not that much experience developer.
public function store(Request $request)
{
try
{
$comment = new TicketComment();
$comment->content = $request['comment'];
$comment->user_id = Auth::user()->id;
$comment->ticket_id = $request['ticketId'];
$comment->save();
$ticket = Ticket::where('id', '=', $comment->ticket_id)->first();
$ticket->updated_at = $comment->created_at;
$ticket->update();
}
catch(Exception $e)
{
request()->session()->flash('unsuccessMessage', 'Failed to add comment !!!');
return redirect()->back();
}
request()->session()->flash('successMessage', 'Comment has been successfully added !!!');
return redirect()->back();
}
Don't have a broad Exception catch block. Only catch exceptions you expect to be thrown in that block that way you can properly log unexpected exceptions and fix any other bugs in your code that may have caused those, instead of hiding them from yourself.
If you must do this then it might be in the context of something like:
public function store(Request $request)
{
try
{
$comment = new TicketComment();
$comment->content = $request['comment'];
$comment->user_id = Auth::user()->id;
$comment->ticket_id = $request['ticketId'];
$comment->save();
$ticket = Ticket::where('id', '=', $comment->ticket_id)->first();
$ticket->updated_at = $comment->created_at;
$ticket->update();
}
catch(Exception $e)
{
if (!($e instanceof SQLException)) {
app()->make(\App\Exceptions\Handler::class)->report($e); // Report the exception if you don't know what actually caused it
}
request()->session()->flash('unsuccessMessage', 'Failed to add comment !!!');
return redirect()->back();
}
request()->session()->flash('successMessage', 'Comment has been successfully added !!!');
return redirect()->back();
}
This way any unexpected exceptions will still be reported and you can go through your logs later to fix any bugs that are causing them.
As a reminder since PHP 7.1 you can have a union of exceptions in a catch block (Reference) e.g.
try { }
catch (ExceptionType1|ExceptionType2 $e) {
}
This way you can handle the exceptions you know you can handle and let Laravel handle the ones you are not sure how to handle.
Note that you generally don't have to have try-catch blocks in controller code, you can always use the exception handler to do the flash/redirect back combination for all unhandled exceptions if that is your preferred way of handling them.
It is allways good to handle the errors. However, Laravel has built in error handling, which ease this process. This does not mean you dont need the try catch block.
Read more about laravel error handling: https://laravel.com/docs/5.8/errors
Even that laravel has his own built in error handling you better use try & catch.
Don't use it on development mode but on production mode. The use of try & catch is user friendly, which means that user will not see the traditional page with the error generated from laravel (in case of any error), but will see your default message.
Example of how to use it:
public function index()
{
try {
return view('index.blade.php');
} catch (\Exception $exception) {
\Log::error($exception);
return redirect()->back()->with(['message' => 'There was an error']);
}
}
If you see this part \Log::error($exception);, it means that the error generated from laravel will be registered on logs file under storage/logs/laravel.log so you as e developer can see the problem with that method. On the other part if something goes wrong with accessing the page: view('index.blade.php') the user will be redirected back to the previous page with a message saved in session, which is: 'There was an error'.
To catch the message in case of errors in your blade you should:
#if (session('message'))
<div class="container">
<div class="alert alert-danger text-center">{{ session('message') }}</div>
</div>
#endif
Happy coding!
here is how I handled the same. I logged the error instead of returning it to user interface.
public function postData()
{
try
{
//your logics to post goes here
}
catch (\Exception $exception) {
if (!($exception instanceof \SQLiteException)) {
app()->make(\App\Exceptions\Handler::class)->report($exception); // Report the exception if you don't know what actually caused it
}
Alert::toast('Error Toast', 'An error occurred');
\Log::error($exception);
return redirect()->back();
}
Alert::success('SuccessAlert', 'alert');
return back();
}
I am developing a project and i would like to have a notification system especially for the "first days of live" - to receive an email everytime an exception is thrown .
I've read that article and implemented what it says. My problem is that the function report() in App\Exception\Handler.php doesn't trigger if an exception is thrown in one of my Controllers.
My problem :
I have an AdminController and a function which inserts some data in Database. The queries are inside a try/catch :
try {
// commands
} catch (\Exception $e){
Log::channel('admin_submissions')->info("Could not save submission. " . $e->getMessage());
return Redirect::back()->withErrors(['Could not save submission. Try again!']);
}
For testing purposes i've inserted inside the try{} an error to be thrown like :
try {
$error = \Illuminate\Validation\ValidationException::withMessages([
'field_name_1' => ['Validation Message #1'],
'field_name_2' => ['Validation Message #2'],
]);
throw $error;
// commands
But the dd("trigger") function (look below) is not triggered.
How could i make it so on every exception (everywhere) an email will be sent?
App\Exceptions\Handler.php
I have modified the report function just to check if the exception actually goes through that function :
public function report(Exception $exception)
{
dd("trigger");
if ($this->shouldReport($exception)) {
app('sneaker')->captureException($exception);
}
parent::report($exception);
}
You must firstly know which kind of exception your application thrown, may be the Exception that is thrown are in the $dontReport if so, you must firstly remove all exception from that table on you will get report for that Exception
Good day,
I modified this file.
vendor/laravel/framework/src/Illuminate/Foundation/Bootstrap/HandleExceptions.php
This is the function I modified.
public function handleError($level, $message, $file = '', $line = 0, $context = [])
I made it send me an email. The email has the $message and the $file and the $line.
This is what the email said.
MESSAGE FILE vendor/laravel/framework/src/Illuminate/Filesystem/Filesystem.php LINE 126
The message is blank, but it's running into an error in a file. I open the file.
public function delete($paths)
{
$paths = is_array($paths) ? $paths : func_get_args();
$success = true;
foreach ($paths as $path) {
try {
if (! #unlink($path)) { # the error is here
$success = false;
}
} catch (ErrorException $e) {
$success = false;
}
}
return $success;
}
I am confused. Why was the error not caught by the catch block? What type of error would not give a message to the error handler of laravel? Is there something I can do to get more information regarding the error?
This is our version of Laravel.
Laravel Framework version 5.1.45 (LTS)
First of all the question you have asked is misleading. What you are trying to do is add a custom error handler in laravel which you can achieve by following this URL .
The error you are facing might be caused due to file permission error.
And since there is the # sign before the function call unlink() any errors which are caused by it are suppressed.
Remove the # sign and if the file doesn't exists or if there is an error it would throw it and then catch block would catch it.
Before app starts I need to check a few things and if the go wrong load error page. All is done in App:before filter except the return as it just doesn’t work. Is somehow possible to stop the app from before filter?
I would try by throwing an exception. You could define something like a BootstrapException. Then you throw it if something goes wrong in App::before():
App::before(function ($request) {
// do checks
if (/* checks failed */) {
throw new BootstrapException;
}
});
After that, you can listen to that exception (e.g.) in start/global.php where there is already an App::error() call:
App::error(function (BootstrapException $exception, $code) {
return Response::view('errorpage', [], $code);
});
I'm not sure about this, but put your App::error() before the catch all App::error(function (Exception $exception, $code) { ... }); that is defined in start/global.php, because every exception is derived from Exception so your BootstrapException would be caught by that error handling call and not by your specific error handling code.
In my laravel app, say I have a bit of code as follows, as an example
function convert_amount($amount, $currency, $date)
{
if (strlen($currency) <> 3)
{
// Exception thrown
} else {
// convert $amount from $currency on $date
}
return $amount;
}
Here, I am simply converting a number from a currency to base. I perform a simple check to see if the currency string passed is 3 character to ensure it's a ISO currency code (EUR, GBP, USD etc). If not I want to throw an exception but not result in the app falling to an error page like is often the case with Laravel's error handler.
Instead, I'd like to continue processing the page but log the exception and perhaps display an error in a flash message.
Is there a listener I can define for Laravel that achieves this? Do I need to define a new exception type NonFatelException perhaps that does the logic.
Edit
Essentially, I guess I could register a new exception handler like so:
class NonFatalException extends Exception {}
App::error(function(NonFatalException $e)
{
// Log the exception
Log::error($e);
// Push it into a debug warning in the session that can be displayed in the view
Session::push('debug_warnings', $e->getMessage());
});
Then somewhere in my app:
throw new NonFatalException('Currency is the wrong format. The amount was not converted');
The trouble with this is that the default exception handlers will then be called resulting in an error page rather than the page that was going to be reached.
I could return a value in my handler to avoid the default but I believe that would result in that return value alone being shown and the rest of my scripts would not be run.
You are on a right path. Why not use try...catch tho?
Your helper method will be:
function convert_amount($amount, $currency, $date)
{
if (strlen($currency) <> 3)
{
throw new NonFatalException('Currency is the wrong format. The amount was not converted');
} else {
// convert $amount from $currency on $date
}
return $amount;
}
And whenever you'll use it, put it in a try...catch:
try {
convert_amount($amount, $currency, $date);
} catch (NonFatalException $e) {
// Log the exception
Log::error($e);
// Push it into a debug warning in the session that can be displayed in the view
Session::push('debug_warnings', $e->getMessage());
}
This way, your app will never stopp and you have the error message in Session.