laravel get error trace of 500 response - laravel

I have a middleware that automatically logs all incoming requests. It also sends me an email when the status is 500. However, $response->getContent() only returns "Server Error" when debugging mode is disabled.
Is there some way to get more detailed information about the cause, like
$exception->getMessage() vs. $exception->getTraceAsString()
are available for the Exception class
current code:
if($response->getStatusCode() == 500) {
ErrorHandler::sendEmail('Incoming Request Error 500', $response->getContent());
}

You can interrogate the exception in the ExceptionHandler class before reporting and rendering take place: Source Line 37.
For example:
public function report(Exception $exception)
{
logger($exception->getMessage());
parent::report($exception);
}

Related

Pass request to exception

Since I started logging exceptions on a production site I'm noticing a lot of them, especially 404s, more than I would expect for a site with barely any traffic, and I'd like to get to the bottom of whether they're genuine users or just bots. To help with this, I want to capture the URL that the user was trying to visit before being redirected to the 404 route, so I can keep track of which non-existent routes are being mistakenly hit. I think I'm correct in assuming this URL should be available in the request, and that I just need to store the request and pass it through to the exception.
What's the best way to do this in Laravel 8 onwards?
Catch 404 exceptions in Handler(App\Exceptions\Handler).
If you see Rendering Exceptions
By default, the Laravel exception handler will convert exceptions into
an HTTP response for you. However, you are free to register a custom
rendering closure for exceptions of a given type. You may accomplish
this via the renderable method of your exception handler.
The closure passed to the renderable method should return an instance
of Illuminate\Http\Response, which may be generated via the response
helper. Laravel will deduce what type of exception the closure renders
by examining the type-hint of the closure:
so in the register method,call renderable
public function register()
{
$this->renderable(function (NotFoundHttpException $e, $request) {
Log::alert("404",[
"fullUrl"=>$request->fullUrl(),
"path"=>$request->path(),
"message" =>$e->getMessage()
]);
return response()->view('errors.404', [], $e->getStatusCode());
});
}
Also, don't forget to import and use
Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
EDIT: just a few hours of using this solution in production, with the benefit of the newly-added request URLs, has confirmed for me just how many of these HTTP 4xx errors are junk - mostly automated bots and maybe a few script kiddies trying common routes. For this reason I've added some logic to ignore 404 and 405 errors, and may still add others that mostly contribute noise to the logfile.
This was harder than it should have been, but this is the solution I'm currently using to log the request with all exceptions. It's probably not the cleanest way to do it, but it works perfectly for my needs. Thanks to John Lobo's answer for pointing me in the right direction.
It works by inspecting each instance of the Exception class and using PHP's instanceof to check whether it's a HTTP exception or not. If it is, it gets logged with the request URL and returns a view with a status code. If it's a generic non-HTTP exception, it gets logged with the request URL and returns another view with no status code (or you can keep the default exception behaviour by removing the return block, which renders a blank screen in production).
public function register()
{
$this->renderable(function (Exception $exception, $request) {
$url = $request->fullUrl();
if ($exception instanceof HttpException) {
$status = $exception->getStatusCode();
// Do not log HTTP 404 and 405s errors for reasons that will
// become apparent after a few hours of logging 404s and 405s
if(($status !== 404) && ($status !== 405)) {
Log::warning("Error $status occurred when trying to visit $url. Received the following message: " . $exception->getMessage());
}
return response()->view("errors.error", [
"exception" => $exception,
"status" => $status
],
$status
);
} else {
$status = $exception->getCode();
Log::warning("Exception $status occurred when trying to visit $url. Received the following message: " . $exception->getMessage());
return response()->view("errors.exception", [
"exception" => $exception,
"status" => $status
]);
}
});
// Optionally suppress all Laravel's default logging for exceptions, so only your own logs go to the logfile
$this->reportable(function (Exception $e) {
})->stop();
}

Laravel logs doesn't report some things

I have an error, like the error below:
Symfony\Component\HttpKernel\Exception\MethodNotAllowedHttpException
The GET method is not supported for this route. Supported methods: PUT.
I know that's a error from routes, but I can't see anything in laravel.log.
Here I saw that Laravel doesn't report some things, but my Exceptions/Handler.php file is like this:
protected $dontReport = [
//
];
How do I report everything in laravel.log?
Laravel 6
All exceptions are handled by the App\Exceptions\Handler class. Use the report method to log exceptions.
public function report(Throwable $exception)
{
Log::error($exception); // add this line here
parent::report($exception);
}
See more from docs here

phpuint Illuminate\Auth\Access\AuthorizationException: This action is unauthorized

I have index method in controller like this
public function index()
{
$this->authorize('index', Contact::class);
....
}
and index method in ContactPolicy
public function index()
{
return Auth::user()->can('view_Contact');
}
and test method like this
/** #test */
public function a_user_without_permission_can_not_see_contacts()
{
$this->login(['no_permission']);
$this->get('/contacts')
->assertStatus(403);
}
When I run my test show me this error
1) Tests\Feature\AccountTest::a_user_without_permission_can_not_see_contacts
Illuminate\Auth\Access\AuthorizationException: This action is unauthorized.
Note1:
When I change My controller to this it work correctly and show me green
public function index()
{
if(Auth::user()->can('view_Contact')){
........
}else
return response()->view('403',['msg' => 'you_not_have_not_permission'])->setStatusCode(403) ;
Note2:
In login method in test class I send user permissions parameters and it work correctly.
the authorize method throws an exception if the user is not authorized. you need to use #expectesException or similar methods to signal your test to expect an exception.
Or, you can add a condition to your Handler class to catch this kind of exception and return a 403 response
edit: more explanation
when you call authorize method and the user does not have permission, the program throws an exception.
when a user is calling the api, the exception is translated to a response with 403 status and shown to the user, but when you are calling the api from within the program itself, the exception is thrown and the program halts, hence there would be no JSON response whatsoever.
To handle this situation, you have two options:
1- if you decide not to change your program, then instead of expecting a response with a specific status, you should tell your test to expect an AuthorizationException thrown. you can read how to do this hear
2- you can change your code so that instead of delegating the task of handling the exception to Laravel, you do it yourself in the Handler class and prepare a JSON response with 403 status and then your test should be run correctly.

prevent Lumen from showing default 500 Internal Server Error on API responses

I have a Route defined as:
$app->post('api/v1/Subject','SubjectController#createSubject');
And in the Controller I have the following code:
public function createSubject(Request $request){
$Subject = Subject::create($request->all());
return response()->json($Subject);
}
Now, when someone sends incorrect data, it triggers a Query Exception - "SQLSTATE[23000]: Integrity constraint violation:" which is understood.
However, what I want is: I want do not want Lumen to send its own default Error Page in API Response. I want to capture this error event and send my own customized response. How can I do that?
Since I could not find a solution, I tried to add my own view at: /resources/views/errors/500.blade.php but Lumen is even ignoring this template. Please help. Ideally, I would want to capture this error event and send my own customized response.
EDIT:
Lumen was reporting two exceptions - PDOException and QueryException at the 500 error response. So, to get a custom error message, I put the following code in side function render() in app\Exceptions\Handler:
public function render($request, Exception $e)
{
if($e instanceof PDOException){
return response('It is my Custom response for PDOException that caused 500 error response.');
}
if($e instanceof QueryException){
return response('It is my Custom response for QueryException that cuased 500 error response.');
}
return parent::render($request, $e);
}

Design 500 error page only for production

I'm looking for a modern (Laravel 5.4) way to display custom 500 error page only for HTTP (non ajax/fetch) response. I read some threads but each response looks like a trick or is outdated. There is probably something to modify in \App\Exceptions\Handler, but I did not find the "right way".
Is there a simple way to display a specific page on fatal error (uncatched, returning 500) in Laravel 5.4?
In other words, when I have a syntax error on one of my controller, it displays "Whoops something went wrong" with some HTML and 500 error code. I would like to display something else, with the same rules as default behavior (ideally only for HTML browser, not for ajax/fetch, etc.).
EDIT: only in production environment.
Laravel makes it easy to display custom error pages for various HTTP status codes. For example, if you wish to customize the error page for 404 HTTP status codes, create a resources/views/errors/404.blade.php. This file will be served on all 404 errors generated by your application. The views within this directory should be named to match the HTTP status code they correspond to. The HttpException instance raised by the abort function will be passed to the view as an $exception variable.
https://laravel.com/docs/5.4/errors#custom-http-error-pages
From the selected "best answer" of this thread: https://laracasts.com/discuss/channels/general-discussion/custom-error-page-er500
You could modify \App\Exceptions\Handler::render():
public function render($request, Exception $exception)
{
if (config('app.debug') && !$this->isHttpException($exception)) {
$exception = new \Symfony\Component\HttpKernel\Exception\HttpException(500);
}
return parent::render($request, $exception);
}
Your exception will be reported in the logs as usually, but woops page will be replaced by your 500.blade.php view.
Sometimes you have to catch the specific exception in order to render the error view. in Laravel 5.4 you can do this by editing the report() method in the App\Exceptions\Handler class
public function report(Exception $exception)
{
if ($exception instanceof CustomException) {
// here you can log the error and return the view, redirect, etc...
}
return parent::report($exception);
}

Resources