I am trying to figure out how I can send errors to my email in Laravel 5. I haven\'t had much luck finding any good resources.
There used to be good packages like: http
You can use the Exception Handler for this. Place your mail code in the report function and it will email you the error every time one occurs.
I expanded on West55's answer, to include some request info in the email. Useful to know, especially when you want to try and duplicate the error yourself. It's wrapped in a try/catch just to make sure we don't throw another exception while getting request info.
public function report(Exception $e)
{
if ($e instanceof Exception){
$debugSetting = \Config::get('app.debug');
// email err if debug off (in prod env)
if (!$debugSetting){
\Config::set('app.debug', true);
if (ExceptionHandler::isHttpException($e)) {
$content = ExceptionHandler::toIlluminateResponse(ExceptionHandler::renderHttpException($e), $e);
} else {
$content = ExceptionHandler::toIlluminateResponse(ExceptionHandler::convertExceptionToResponse($e), $e);
}
\Config::set('app.debug', $debugSetting);
$lc2 = (isset($content->original)) ? $content->original : $e->getMessage();
// add request info to err msg
$lc1= '';
try{
$request= request();
$lc1=
"<div>" .
"-- Request --<br>" .
"<br>Method: " . $request->getMethod() .
"<br>Uri: " . $request->getUri() .
"<br>Ip: " . $request->getClientIp() .
"<br>Referer: " . $request->server('HTTP_REFERER') .
"<br>Is secure: " . $request->isSecure() .
"<br>Is ajax: " . $request->ajax() .
"<br>User agent: " . $request->server('HTTP_USER_AGENT') .
"<br>Content:<br>" . nl2br(htmlentities($request->getContent())) .
"</div>";
}catch (Exception $e2){}
if (strpos($lc2, '</body>') !== false){
$lc2= str_replace('</body>', $lc1 . '</body>', $lc2);
}else{
$lc2.= $lc1;
}
$la2['content']= $lc2; //put into array for blade
$ln1= Mail::send('emails.exception2', $la2, function($m){
$m->to('support@mydomain.com')
->subject('Fatal Error');
});
}
}
parent::report($e);
}
You can do this by catching all the errors in the App\Exceptions\Handler::report()
. So in you App/Exceptions/Handler.php
add a report
function if its not already there.
/**
* Report or log an exception.
*
* This is a great spot to send exceptions to Sentry, Bugsnag, etc.
*
* @param \Exception $e
* @return void
*/
public function report(\Exception $e)
{
if ($e instanceof \Exception) {
// emails.exception is the template of your email
// it will have access to the $error that we are passing below
Mail::send('emails.exception', ['error' => $e->getMessage()], function ($m) {
$m->to('your email', 'your name')->subject('your email subject');
});
}
return parent::report($e);
}
If you need more info, refer to laravel documentation form mailer and errors.
The other answers seem quite right. We have done this a while ago and found one big issue with this: If the mail command fails, it can result in a infinite loop of throwing an error and trying to send the according email which will result in a failure again... This will fill up the log quite quickly and kill your server.
Bear this in mind and don't send an email in that case.
Side Note: I decided to put this in the answer because it is relevant to all answers and should not get hidden in one comment.
EDIT: I found a 3rd party log system built for Laravel
www.understand.io
I played around with this and went through the Laravel core files and came up with something similar to what you see on an error page.
You just need to create a view file that echos out $content for the email
public function report(\Exception $e)
{
if ($e instanceof \Exception) {
$debugSetting = Config::get('app.debug');
Config::set('app.debug', true);
if (ExceptionHandler::isHttpException($e)) {
$content = ExceptionHandler::toIlluminateResponse(ExceptionHandler::renderHttpException($e), $e);
} else {
$content = ExceptionHandler::toIlluminateResponse(ExceptionHandler::convertExceptionToResponse($e), $e);
}
Config::set('app.debug', $debugSetting);
$data['content'] = (!isset($content->original)) ? $e->getMessage() : $content->original;
Mail::queue('errors.emails.error', $data, function ($m) {
$m->to('email@email.com', 'Server Message')->subject('Error');
});
}
return parent::report($e);
}
Here you have a solution for Laravel 6.x with an html error description, trace, and all vars if somebody wants to increase, there should be a render also for the mysql queries that can be added to this html body.
Important! dont forget to put your Debug config in false, you can do that in the env file setting:
APP_DEBUG=false
Or you also can changing it in the /config/app.php file
/*
|--------------------------------------------------------------------------
| Application Debug Mode
|--------------------------------------------------------------------------
|
| When your application is in debug mode, detailed error messages with
| stack traces will be shown on every error that occurs within your
| application. If disabled, a simple generic error page is shown.
|
*/
//'debug' => env('APP_DEBUG', false),
//to
'debug' => false, //now not lookin for the envfile value
Otherway all the users are going to see a DEBUG SCREEN with a lot of private information.
The solution is to extend the Handler in /app/Exceptions/Handler.php
and replace the function Render for the next code.
public function render($request, Exception $exception)
{
$html = $this->renderExceptionWithSymfony($exception, true);
$array = $this->convertExceptionToArray($exception);
$html .= $this->renderVars($_SERVER, "Server Vars");
$html .= $this->renderVars($_REQUEST, "Requests Vars");
$html .= $this->renderVars($_COOKIE, "Cookies");
\Mail::send('emails.error', ['html'=>$html], function ($m) {
$m->to('developer@mail.com', 'Server Message')->subject('WEB ERROR');
});
return parent::render($request, $exception);
}
And also add the new function renderVars next to the new render. This functions is going to convert the Sever, request an cookies arrays to a legible
public function renderVars($vars, $titulo)
{
$html = '<table class="trace-details">
<thead class="trace-head">
<tr>
<th><h3 class="trace-class"><span class="exception_title">'.$titulo.'</span></h3></th>
</tr>
</thead>
<tbody>';
forEach($vars as $key=>$value)
{
$html .= '<tr>
<td>
<span class="block trace-file-path">
<span title="'.$key.'"><strong>'.$key.'</strong></span>
</span>
</td>
<td>
<span class="block trace-file-path">';
if(!is_array($value))
{
$html .= '<span title="'.$value.'"><strong>'.$value.'</strong></span>';
}
else
{
$html .= '<span title="ARR | '.implode(', ', $value).'">ARR | <strong>'.implode(', ', $value).'</strong></span>';
}
$html .= '</span>
</td>';
$html .= '</tr>';
}
$html .= '</tbody>
</table>';
return $html;
}
And for the last thing you need to create a blade view in the folder /resources/views/emails/error.blade.php this blade is in this case only printing the html error formatted from render function. The mail function send array param and convert for the view.
In this case the code for the blade error template (/resources/views/emails/error.blade.php) is only:
<?=$html?>
That print the formated html received.
Next to that if you want to customize the error frontend screen you need to create a new blade template in the folder (/resources/views/errors/<error_code>.blade.php) like: 500.blade.php ----> for Internal Server Errors 404.blade.php ----> for not found errors. If you wan't to do that, laravel is going to show the default error template.
I hope that is helpfull for other people.
best regards, Reb duvid.