1

I am working with the Laravel 9 application. I have created a custom Exception. I want to report the General Exception to the sentry and this custom Exception to another vendor like Papertrail.

The Handler.php is not calling the reportable closure function when the application throws a custom exception i.e ServiceException.

By the way, the Laravel documentation for errors is also not understandable

Handler.php

<?php

namespace App\Exceptions;

use Illuminate\Foundation\Exceptions\Handler as ExceptionHandler;
use Inertia\Inertia;
use Throwable;
use Exception;
use App\Exceptions\ServiceException;

class Handler extends ExceptionHandler
{
    /**
     * A list of the exception types that are not reported.
     *
     * @var array
     */
    protected $dontReport = [
        // ServiceException::class
    ];

    /**
     * A list of the inputs that are never flashed for validation exceptions.
     *
     * @var array
     */
    protected $dontFlash = [
        'current_password',
        'password',
        'password_confirmation',
    ];

    /**
     * Register the exception handling callbacks for the application.
     *
     * @return void
     */
    public function register()
    {
        $this->reportable(function (Throwable $e) {
            // this block is calling for Exception but not for custom Exception like ServiceException.
            if ($this->shouldReport($e) && app()->bound('sentry')) {
                app('sentry')->captureException($e);
            }
        });

        $this->reportable(function (ServiceException $e) {
            // this block is not calling when I throw ServiceException.
            echo "Send this ServiceException to papertrail app."; // this line is never called.
            die;
        });
    }

    public function render($request, Throwable $e)
    {
        $response = parent::render($request, $e);
        if ($request->isJson()) {
            //prevent error for local and staging.
            if (! app()->environment(['local', 'staging']) && in_array($response->status(), [500, 503, 404, 403])) {
                \Log::error('API Error Handler', [$response->getOriginalContent()]);
                $message = trans('message.error_description_500');
                if ($response->status() == 404) {
                    $message = trans('message.data_not_found');
                } elseif ($response->status() == 403) {
                    $message = trans('message.you_are_not_allowed_perform');
                } elseif ($response->status() == 503) {
                    $message = trans('message.error_description_503');
                }
                return response()->json([
                    'message' => $message,
                ], $response->status());
            }
        }
        return $response;
    }
}

ServiceException.php

<?php

namespace App\Exceptions;

use Exception;

class ServiceException extends Exception
{
    /**
     * Report the exception.
     *
     * @return void
     */
    public function report()
    {
        //
    }

    /**
     * Render the exception into an HTTP response.
     *
     * @param  \Illuminate\Http\Request  $request
     * @return \Illuminate\Http\Response
     */
    public function render($request, $exception)
    {
        if (! $request->ajax()) {
            // view('error_handler', compact('exception'));
        }

        return response()->json([
            'code' => $exception->getCode(),
            'status' => 'error',
            'message' => $exception->getMessage(),
            'data' => 'sample data',
        ]);
    }
}

AnyController.php

public function anyFunction() {
    // throw new Exception('Unhandled Exception.'); // It will call the $this->reportable(function (Throwable $e) { block.
    // throw new ServiceException('Unhandled ServiceException.'); // It will call the $this->reportable(function (Throwable $e) { block.

    try {
        $this->service->aFunctionThatThrowException(); // this function will throw ServiceException.
    } catch (Exception $e) {
        Log::error('controller_fun_error', ['error' => $e->getMessage()]);
        report($e);
        return $this->failResponse();
    }
}

AnyService.php

public function aFunctionThatThrowException() {
    try {
        throw new ServiceException('Throwing ServiceException...)');
    } catch (ServiceException | Exception $e) {
        Log::error('service_fun_error', ['error' => $e->getMessage()]);
        throw $e;
    }
}

2 Answers 2

1

I'm not intirely sure, but I don't think the error will be reported if you put a try catch around it yourself.

Sign up to request clarification or add additional context in comments.

2 Comments

No no, even if you have try catch block, you can call the report($e) function. and the exception will call the handler.php closure. but it is working only for Exception, not for a custom exception.
Your answer could be improved with additional supporting information. Please edit to add further details, such as citations or documentation, so that others can confirm that your answer is correct. You can find more information on how to write good answers in the help center.
0

Create a new helper functions file eg: app/Helpers/Common.php and create a service provider HelperServiceProvider in app/Providers/HelperServiceProvider.php use the code below :

<?php

namespace App\Providers;

use Illuminate\Support\ServiceProvider;

class AppServiceProvider extends ServiceProvider
{
    /**
     * Register any application services.
     *
     * @return void
     */
    public function register()
    {
        //
    }

    /**
     * Bootstrap any application services.
     *
     * @return void
     */
    public function boot()
    {
        require_once __DIR__ . '/../Helpers/Common.php';
    }
}

Now copy the below mentioned code to the app/Helpers/Common.php file :

if (! function_exists('throwResponse')) {
    function throwResponse($message='Exceptions',$data=[],$statusCode=500){
        if(request()->wantsJson()) {
            if ((gettype($message) !== 'string') && ($message instanceof \Exception)) {
                if($message->getMessage()){
                    $data      = (!empty($message->getTrace()))   ? $message->getTrace()   : [];
                    $message   = (!empty($message->getMessage())) ? $message->getMessage() : "Something went wrong";
                    $data      = $data?:[$message];
                    $statusCode = 500;
                }else{
                    throw new \Illuminate\Http\Exceptions\HttpResponseException($message->getResponse());
                }
            }
            $errStatus = (in_array($statusCode,[200,201])) ? false : true;

            $response = ['code'=>(int)$statusCode,
                         'error'=>$errStatus,
                         'message'=>$message];

            if(!empty($data)){
                $response['data'] = $data;
            }
            if($statusCode == 200 && $data == "empty"){
                $response['data'] = [];
            }           
            throw new \Illuminate\Http\Exceptions\HttpResponseException(response()->json($response,$statusCode));
        } else{
            if(is_object($message)){
                throw $message;
            }else{
                return $message;
            }
        }
    }
    
}

Now you can easily call the throwResponse function from anywhere in the project : for normal response

throwResponse('Message for the response',['user_id'=>1],200);

{
  "code":200,
  "error":false,
  "message":"Message for the response",
  "data":{
       "user_id":1
   }
}

or

throwResponse('unauthorized',null,401);

{
  "code":401,
  "error":true,
  "message":"unauthroized",
}

or for exceptions simply pass the exception $e

throwResponse($e);

{
  "code":500,
  "error":true,
  "message":"exception message",
  "data":{
       exception data
    }
}

The response will be

for example :

   public function exampleFunction(Request $request){
     try{
         
         if(empty($request->userId)){
            throwResponse('Validation error',null,401);
         }
          
         $userData = User::find($request->userId);
         if(!empty($userData)){
             throwResponse('User data',$userData,401);
         }else{
             throwResponse('Invalid user id',null,422);
         }                          
     }catch(\Exception $e){
         throwResponse($e);
    }
  }

Change responses according to your need

1 Comment

I don't know why you create another helper file. It didn't solve my issue. My question was very simple, why the Hanlder.php closure is not called when I throw a custom exception?

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.