├── README.md ├── composer.json └── src ├── ErrorNotifierServiceProvider.php ├── Exceptions └── EmailNotFound.php ├── Helper.php ├── Http ├── Controllers │ └── ErrorNotifierController.php ├── Requests │ └── ErrorNotifierRequest.php └── Services │ └── ErrorNotifierService.php ├── Jobs └── SlackNotificationJob.php ├── Notifications └── NotifierNotification.php ├── config └── notifier.php ├── resources └── views │ ├── 500.blade.php │ └── emails │ └── index.blade.php └── routes └── web.php /README.md: -------------------------------------------------------------------------------- 1 | 2 | # Error Notification For Laravel 3 | 4 | This package sends you email and slack notification whenever 500 internal server errors happens in your application. 5 | This package only works for laravel 8, 9 and 10 6 | *This package works for both API and view based laravel project* 7 | 8 | 9 |  10 | 11 | 12 | ## What the package sends as a notification 13 | 14 | - Last route the user visited 15 | - If they are authenticated or not 16 | - User ID (If user is authenticated) 17 | - User email (If user is authenticated) 18 | - User error description message (After form is submitted, for a non view based App) 19 | - File Error (The actual file the error was associated with) 20 | - Line Number (The line number the error happened) 21 | - Trace error (The list of files associated with the error; *before and after form is submitted*) 22 | 23 | 24 | ## How to install 25 | 26 | ``` 27 | composer require jgodstime/laravel-error-notifier 28 | 29 | ``` 30 | 31 | In your */app/Exceptions/Handler.php* file, in the register method, add `\ErrorNotifier\Notify\Helper::getError($e);` this must be inside reportable callback 32 | 33 | ```php 34 | public function register() 35 | { 36 | $this->reportable(function (Throwable $e) { 37 | \ErrorNotifier\Notify\Helper::getError($e); 38 | }); 39 | } 40 | 41 | ``` 42 | 43 | ## Disable Instant Notification 44 | 45 | By default this package sends notification immediately the error occured i.e without waiting for users description of the error, you can disable this by adding `NOTIFIER_INSTANT=false` in your .env file 46 | 47 | ### Publishing the vendor files 48 | 49 | By default, Laravel displays the 500 error page in your */views/errors/500.blade.php* file when a 500 error occurs in your application 50 | 51 | This package places the 500.blade.php file in the */views/errors/* folder after you publish the vendor file 52 | 53 | If you already have a 500.blade.php file in your */views/errors* folder and you want to use this package; you should remove or move it to another folder. 54 | 55 | ### Publish the vendor files if your app is not API 56 | 57 | ``` 58 | php artisan vendor:publish --provider="ErrorNotifier\Notify\ErrorNotifierServiceProvider" 59 | ``` 60 | 61 | *Now the vendor files are published in their respective paths* 62 | 63 | ## Let's Test 64 | 65 | ### Setup your email driver 66 | > To setup your email driver, you can use [mailtrap](https://mailtrap.io/) for test purpose. 67 | 68 | ``` 69 | MAIL_DRIVER=smtp 70 | MAIL_HOST=sandbox.smtp.mailtrap.io 71 | MAIL_PORT=587 72 | MAIL_USERNAME=528a733... 73 | MAIL_PASSWORD=73c29... 74 | MAIL_ENCRYPTION=tls 75 | MAIL_FROM_ADDRESS=mygoogle@gmail.com 76 | MAIL_FROM_NAME="${APP_NAME}" 77 | ``` 78 | ### Disable notification to email 79 | > To disable notification to email, set `NOTIFIER_EMAIL=` in your .env file 80 | 81 | 82 | ### Add your slack webhook (Optional) 83 | > To setup your [slack](https://api.slack.com/messaging/webhooks) webhook url 84 | 85 | ``` 86 | LOG_SLACK_WEBHOOK_URL=https://hooks.slack.com/services/T.... 87 | 88 | ``` 89 | 90 | ### We are good 😊 91 | To test this, simply add `$array['key1'] = 'john'; $data = $array['key2'];` in one of your routes, then hit the route In your browser. 92 | 93 | ```php 94 | Route::get('/convert/file', function(){ 95 | $array['key1'] = 'john'; 96 | $data = $array['key2']; 97 | }); 98 | ``` 99 | 100 | Notice that we are trying to access an array with key2 that doesn’t exist, this will throw an error and the package will send the error as notification to your email as well as the trace. 101 | 102 | Also, you must turn `APP_DEBUG=false` and `LOG_LEVEL=debug` in your .env file 103 | 104 | **You should see this** 105 | 106 |  107 | 108 | If you don’t have `NOTIFIER_INSTANT=false` in your .env file, the package sends instant notification to the setup email and slack channel 109 | 110 | Also, after user submits the form, the package sends another notification with user error description message. 111 | 112 | ### Modify Page Design 113 | You can modify the style for the page to suit your taste, 114 | > **Note:** Be careful not to change the **form route** and **hidden inputs**. 115 | 116 | A notification email is sent to the email specified in the laravel environment variable (`MAIL_FROM_ADDRESS`) in your *.env* file 117 | 118 | You can change this by adding `NOTIFIER_EMAIL="hello@example.com"` in your .env file. 119 | 120 | 121 | ### Send Email to Multiple Recipients 122 | 123 | You can send notification to multiple recipients by adding multiple emails as a string in comma separated format without space 124 | 125 | ``` 126 | NOTIFIER_EMAIL="hello1@example.com,hello2@example.com" 127 | ``` 128 | 129 | ### Change Redirect Page URL 130 | 131 | You can change the page user is taken to after submitting the form, by default it goes to the home page (/) 132 | 133 | NOTIFIER_REDIRECT_URL='/thank-you' 134 | 135 | 136 | ## Exciting upgrade coming. Stay tuned! 137 | 138 | 139 | 140 | ## How can I thank you? 141 | 142 | Why not star the github repo? I'd love the attention! Why not share the link for this repository on Twitter, LinkedIn or HackerNews? Spread the word! 143 | 144 | Don't forget to [follow me on twitter](https://twitter.com/johngodstime)! 145 | 146 | Thanks! Godstime John. 147 | 148 | 149 | 150 | 151 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "jgodstime/laravel-error-notifier", 3 | "description": "Error notification for laravel", 4 | "type": "library", 5 | "authors": [ 6 | { 7 | "name": "Godstime john" 8 | } 9 | ], 10 | "minimum-stability": "dev", 11 | "require": { 12 | 13 | }, 14 | 15 | "autoload": { 16 | "psr-4": { 17 | "ErrorNotifier\\Notify\\": "src/" 18 | } 19 | }, 20 | "extra": { 21 | "laravel": { 22 | "providers": [ 23 | "ErrorNotifier\\Notify\\ErrorNotifierServiceProvider" 24 | ] 25 | } 26 | } 27 | 28 | } 29 | -------------------------------------------------------------------------------- /src/ErrorNotifierServiceProvider.php: -------------------------------------------------------------------------------- 1 | loadRoutesFrom(__DIR__.'/routes/web.php'); 27 | $this->loadViewsFrom(__DIR__.'/resources/views', 'notifier'); 28 | $this->mergeConfigFrom(__DIR__.'/config/notifier.php', 'notifier'); 29 | 30 | $this->publishes([ 31 | __DIR__.'/config/notifier.php' => config_path('notifier.php'), 32 | __DIR__.'/resources/views' => resource_path('views/errors'), 33 | ]); 34 | 35 | 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/Exceptions/EmailNotFound.php: -------------------------------------------------------------------------------- 1 | sendInstantNotification($e); 12 | } 13 | } 14 | 15 | 16 | } 17 | 18 | -------------------------------------------------------------------------------- /src/Http/Controllers/ErrorNotifierController.php: -------------------------------------------------------------------------------- 1 | errorNotifierService = $errorNotifierService; 15 | } 16 | 17 | 18 | public function index($statusCode = 500) 19 | { 20 | return view("notifier::{$statusCode}"); 21 | } 22 | 23 | 24 | public function sendNotification(ErrorNotifierRequest $request) 25 | { 26 | return $this->errorNotifierService->sendNotification($request->validated()); 27 | } 28 | 29 | } 30 | -------------------------------------------------------------------------------- /src/Http/Requests/ErrorNotifierRequest.php: -------------------------------------------------------------------------------- 1 | 'nullable', 29 | 'is_authenticated' => 'nullable', 30 | 'id' => 'nullable', 31 | 'email' => 'nullable', 32 | 'status_code' => 'nullable', 33 | 'message' => 'required|string', 34 | 'notifier_message' => 'nullable|string', 35 | 'notifier_data' => 'nullable|string', 36 | 'notifier_line' => 'nullable|string', 37 | 'notifier_file' => 'nullable|string', 38 | ]; 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/Http/Services/ErrorNotifierService.php: -------------------------------------------------------------------------------- 1 | $e->getFile(), 22 | 'line' => $e->getLine(), 23 | 'code' => $e->getCode(), 24 | ] 25 | ])->concat(collect($e->getTrace())->take(3))->toArray(); 26 | 27 | 28 | $data['is_authenticated'] = auth()->check() ? true : false; 29 | $data['id'] = auth()->check() ? auth()->id() : "N/A"; 30 | $data['email'] = auth()->check() ? @auth()->user()->email : "N/A"; 31 | 32 | $data['status_code'] = $e->getCode(); 33 | $data['access_url'] = url()->current(); 34 | $data['message'] = $e->getMessage(); 35 | 36 | $data['line'] = $e->getLine(); 37 | $data['file'] = $e->getFile(); 38 | $data['trace'] = json_encode($errorLogs); 39 | } 40 | 41 | if(config('notifier.channels.mail.address')){ 42 | $emails = explode(',', config('notifier.channels.mail.address')); 43 | $this->sendToEmail($emails, $data); 44 | } 45 | 46 | 47 | if(config('notifier.channels.slack.url')){ 48 | $this->sendToSlack($e->getMessage(), $data); 49 | } 50 | 51 | session()->put('error_notifier_package_message_123', $e->getMessage()); 52 | session()->put('error_notifier_package_data_123', json_encode($data)); 53 | session()->put('error_notifier_package_file_123', $e->getFile()); 54 | session()->put('error_notifier_package_line_123', $e->getLine()); 55 | 56 | } 57 | 58 | 59 | public function sendToSlack($message, array $data) 60 | { 61 | if(config('notifier.should_queue')){ 62 | SlackNotificationJob::dispatch($data); 63 | }else{ 64 | SlackNotificationJob::dispatchSync($data); 65 | } 66 | } 67 | 68 | 69 | public function sendToEmail(array $emails, array $data) 70 | { 71 | Notification::route('mail', $emails)->notify(new NotifierNotification($data)); 72 | } 73 | 74 | 75 | public function sendNotification(array $data) 76 | { 77 | 78 | $data['id'] = auth()->check() ? auth()->id() : "N/A"; 79 | $data['email'] = auth()->check() ? @auth()->user()->email : "N/A"; 80 | $data['trace'] = $data['notifier_data']; 81 | $data['line'] = $data['notifier_line']; 82 | $data['file'] = $data['notifier_file']; 83 | $data['message'] = $data['message']. '...'.$data['notifier_message']; 84 | 85 | if(config('notifier.channels.mail.address')){ 86 | $emails = explode(',', config('notifier.channels.mail.address')); 87 | $this->sendToEmail($emails, $data); 88 | } 89 | 90 | if(config('notifier.channels.slack.url')){ 91 | unset($data['notifier_message']); 92 | unset($data['notifier_data']); 93 | unset($data['notifier_line']); 94 | unset($data['notifier_file']); 95 | $message = $data['message']; 96 | $this->sendToSlack($message, $data); 97 | } 98 | 99 | session()->forget('error_notifier_package_message_123'); 100 | session()->forget('error_notifier_package_data_123'); 101 | session()->forget('error_notifier_package_line_123'); 102 | session()->forget('error_notifier_package_file_123'); 103 | 104 | return redirect(config('notifier.redirect_url'))->with('success', 'Thank you for your response, our tech team is on it'); 105 | } 106 | 107 | 108 | } 109 | -------------------------------------------------------------------------------- /src/Jobs/SlackNotificationJob.php: -------------------------------------------------------------------------------- 1 | data = $data; 27 | } 28 | 29 | /** 30 | * Execute the job. 31 | * 32 | * @return void 33 | */ 34 | public function handle() 35 | { 36 | // $payload = [ 37 | 38 | $data = $this->data; 39 | // dd($data); 40 | $isAuthenticated = $data['is_authenticated'] ? "True" : "False"; 41 | 42 | $payload = [ 43 | "attachments" => [ 44 | [ 45 | 46 | "color" => "#c71616", 47 | "blocks" => [ 48 | [ 49 | "type" => "context", 50 | "elements" => [ 51 | [ 52 | "type" => "mrkdwn", 53 | "text" => "@channel :zap:" 54 | ] 55 | ] 56 | ], 57 | 58 | [ 59 | "type" => "section", 60 | "text" => [ 61 | "type" => "plain_text", 62 | "text" =>"Error Message: {$data['message']}" 63 | ] 64 | ], 65 | [ 66 | "type" => "divider" 67 | ], 68 | [ 69 | "type" => "section", 70 | "fields" => [ 71 | [ 72 | "type" => "mrkdwn", 73 | "text" => "*File:* {$data['file']}" 74 | ], 75 | [ 76 | "type" => "mrkdwn", 77 | "text" => "*Line:* {$data['line']}" 78 | ] 79 | ] 80 | ], 81 | [ 82 | "type" => "section", 83 | "fields" => [ 84 | [ 85 | "type" => "mrkdwn", 86 | "text" => "*Access Url:* {$data['access_url']}" 87 | ], 88 | [ 89 | "type" => "mrkdwn", 90 | "text" => "*Is Authenticated:* {$isAuthenticated}" 91 | ] 92 | 93 | ] 94 | ], 95 | [ 96 | "type" => "section", 97 | "fields" => [ 98 | [ 99 | "type" => "mrkdwn", 100 | "text" => "*ID:* ".$data['id'] 101 | ], 102 | [ 103 | "type" => "mrkdwn", 104 | "text" => "*Email:* ".$data['email'] 105 | ] 106 | 107 | ] 108 | ], 109 | [ 110 | "type" => "divider" 111 | ], 112 | [ 113 | "type" => "section", 114 | "text" => [ 115 | "type" => "plain_text", 116 | "text" => "Trace: {$data['trace']} " 117 | ] 118 | ], 119 | 120 | ] 121 | ] 122 | ] 123 | ]; 124 | 125 | $response = Http::post(config('notifier.channels.slack.url'), $payload); 126 | $response->json(); 127 | 128 | } 129 | } 130 | -------------------------------------------------------------------------------- /src/Notifications/NotifierNotification.php: -------------------------------------------------------------------------------- 1 | data = $data; 21 | } 22 | 23 | /** 24 | * Get the notification's delivery channels. 25 | * 26 | * @param mixed $notifiable 27 | * @return array 28 | */ 29 | public function via($notifiable) 30 | { 31 | return ['mail']; 32 | } 33 | 34 | /** 35 | * Get the mail representation of the notification. 36 | * 37 | * @param mixed $notifiable 38 | * @return \Illuminate\Notifications\Messages\MailMessage 39 | */ 40 | public function toMail($notifiable) 41 | { 42 | return (new MailMessage)->view( 43 | 'notifier::emails.index', ['data' => $this->data] 44 | )->subject(ucfirst(config('notifier.name')).' Notifier'); 45 | } 46 | 47 | /** 48 | * Get the array representation of the notification. 49 | * 50 | * @param mixed $notifiable 51 | * @return array 52 | */ 53 | public function toArray($notifiable) 54 | { 55 | return [ 56 | // 57 | ]; 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /src/config/notifier.php: -------------------------------------------------------------------------------- 1 | env('NOTIFIER_REDIRECT_URL', '/'), 6 | 'instant' => env('NOTIFIER_INSTANT', true), 7 | 'name' => env('NOTIFIER_FROM_NAME', config('app.name')), 8 | 'should_queue' => env('NOTIFIER_SHOULD_QUEUE', true), 9 | 10 | 'channels' => [ 11 | 12 | 'slack' => [ 13 | 'url' => env('LOG_SLACK_WEBHOOK_URL'), 14 | ], 15 | 16 | 'mail' => [ 17 | 'address' => env('NOTIFIER_EMAIL', env('MAIL_FROM_ADDRESS', 'hello@example.com')), 18 | ], 19 | 20 | ], 21 | 22 | 23 | ]; 24 | -------------------------------------------------------------------------------- /src/resources/views/500.blade.php: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 |
5 | 6 | 7 | 8 |Our team has been notified. If you'd like to help, tell us what happened below. 116 |
117 | 118 | 140 | 141 |
431 |
|
505 |