Compare commits

..

No commits in common. "master" and "0.0.3" have entirely different histories.

15 changed files with 7441 additions and 301 deletions

1
.gitignore vendored
View file

@ -1,3 +1,2 @@
workspace.code-workspace workspace.code-workspace
vendor vendor
.vscode

22
.vscode/launch.json vendored Normal file
View file

@ -0,0 +1,22 @@
{
// Utilisez IntelliSense pour en savoir plus sur les attributs possibles.
// Pointez pour afficher la description des attributs existants.
// Pour plus d'informations, visitez : https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"name": "Listen for XDebug",
"type": "php",
"request": "launch",
"port": 9000
},
{
"name": "Launch currently open script",
"type": "php",
"request": "launch",
"program": "${file}",
"cwd": "${fileDirname}",
"port": 9000
}
]
}

View file

@ -2,37 +2,8 @@
All notable changes to `ExceptionHandler` will be documented in this file. All notable changes to `ExceptionHandler` will be documented in this file.
# version 2.0.0
- Changed enhance getAnonymizedMessage (for DB queries)
- Changed Support only for laravel 10
- Added env element EXCEPTION_MAIL_LIST
# version 0.1.1 ## version 0.0.2
- Changed better parsing for email recipients
- Changed if no recipients are available, a warning will be emitted, also native Laravel report method will be called instead.
- Changed if mail report fails, native reporting method will be called.
# version 0.1.0
- Add a a method to manually send exception by email (plus the same to call statically).
# version 0.0.9
- Multiline exception message in mail.
# version 0.0.8
- Make sure sending mail error is always correctly handled (write in logs)
# version 0.0.7
- Added support for laravel 9
# version 0.0.6
- Added class name to email message
# version 0.0.5
- Fixed Handle cases where exception came from a function instead of a class
# version 0.0.4
- Fixed Try to fix undefined index file error
## version 0.0.3
- Fixed getAnonymizedStackTrace change param type to Throwable - Fixed getAnonymizedStackTrace change param type to Throwable
- Fixed remove finally on reportExceptionByEmail to let exceptions bubble - Fixed remove finally on reportExceptionByEmail to let exceptions bubble

View file

@ -17,11 +17,13 @@
"Policies" "Policies"
], ],
"require": { "require": {
"illuminate/support": "~10", "illuminate/support": "~7|~8",
"laravel/framework": "~10" "laravel/framework": "~7|~8"
}, },
"require-dev": { "require-dev": {
"slevomat/coding-standard": "^8.10" "phpunit/phpunit": "~9.0",
"squizlabs/php_codesniffer": "^3.5",
"orchestra/testbench": "^6.13"
}, },
"autoload": { "autoload": {
"psr-4": { "psr-4": {
@ -39,10 +41,5 @@
"Kwaadpepper\\ExceptionHandler\\ExceptionHandlerServiceProvider" "Kwaadpepper\\ExceptionHandler\\ExceptionHandlerServiceProvider"
] ]
} }
},
"config": {
"allow-plugins": {
"dealerdirect/phpcodesniffer-composer-installer": true
}
} }
} }

7242
composer.lock generated Normal file

File diff suppressed because it is too large Load diff

View file

@ -11,5 +11,5 @@ return [
* Put a developper secured mailbox in here * Put a developper secured mailbox in here
* so you can receive email if things go mad * so you can receive email if things go mad
*/ */
'contactsList' => collect(explode(',', env('EXCEPTION_MAIL_LIST')))->all(), 'contactsList' => []
]; ];

175
phpcs.xml
View file

@ -1,86 +1,74 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<ruleset name="LDC Laravel" <ruleset name="RealWorld" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="vendor/squizlabs/php_codesniffer/phpcs.xsd">
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="vendor/squizlabs/php_codesniffer/phpcs.xsd"> <arg name="colors"/>
<description>Created with the PHP Coding Standard Generator. http://edorian.github.com/php-coding-standard-generator/</description> <arg name="extensions" value="php"/>
<arg name="colors" />
<arg name="extensions" value="php" />
<file>src/</file> <file>src/</file>
<rule ref="PSR1" /> <file>tests/</file>
<rule ref="PSR12" /> <description>Created with the PHP Coding Standard Generator. http://edorian.github.com/php-coding-standard-generator/
</description>
<rule ref="Generic.Classes.DuplicateClassName"/> <rule ref="Generic.CodeAnalysis.EmptyStatement" />
<rule ref="Generic.CodeAnalysis.EmptyStatement"/> <rule ref="Generic.CodeAnalysis.ForLoopShouldBeWhileLoop" />
<rule ref="Generic.CodeAnalysis.ForLoopShouldBeWhileLoop"/> <rule ref="Generic.CodeAnalysis.ForLoopWithTestFunctionCall" />
<rule ref="Generic.CodeAnalysis.ForLoopWithTestFunctionCall"/> <rule ref="Generic.CodeAnalysis.JumbledIncrementer" />
<rule ref="Generic.CodeAnalysis.JumbledIncrementer"/> <rule ref="Generic.CodeAnalysis.UnconditionalIfStatement" />
<rule ref="Generic.CodeAnalysis.UnconditionalIfStatement"/> <rule ref="Generic.CodeAnalysis.UnnecessaryFinalModifier" />
<rule ref="Generic.CodeAnalysis.UnnecessaryFinalModifier"/> <rule ref="Generic.CodeAnalysis.UnusedFunctionParameter" />
<rule ref="Generic.CodeAnalysis.UnusedFunctionParameter"/> <rule ref="Generic.CodeAnalysis.UselessOverridingMethod" />
<rule ref="Generic.CodeAnalysis.UselessOverridingMethod"/> <rule ref="Generic.Commenting.Todo" />
<rule ref="Generic.Commenting.DocComment.SpacingBeforeTags"/> <rule ref="Generic.Commenting.Fixme" />
<rule ref="Generic.Commenting.Todo"/> <rule ref="Generic.ControlStructures.InlineControlStructure" />
<rule ref="Generic.Commenting.Fixme"/> <rule ref="Generic.Files.ByteOrderMark" />
<rule ref="Generic.ControlStructures.InlineControlStructure"/> <rule ref="Generic.Files.LineEndings" />
<rule ref="Generic.Files.ByteOrderMark"/> <rule ref="Generic.Files.LineLength">
<rule ref="Generic.Files.LineEndings"/>
<rule ref="Generic.Files.LineLength"/>
<rule ref="Generic.Formatting.DisallowMultipleStatements"/>
<rule ref="Generic.Formatting.MultipleStatementAlignment"/>
<rule ref="Generic.Formatting.NoSpaceAfterCast"/>
<rule ref="Generic.Functions.CallTimePassByReference"/>
<rule ref="Generic.Functions.FunctionCallArgumentSpacing"/>
<rule ref="Generic.Metrics.CyclomaticComplexity"/>
<rule ref="Generic.Metrics.NestingLevel"/>
<rule ref="Generic.NamingConventions.ConstructorName"/>
<rule ref="Generic.NamingConventions.UpperCaseConstantName"/>
<rule ref="Generic.NamingConventions.CamelCapsFunctionName"/>
<rule ref="Generic.PHP.DeprecatedFunctions"/>
<rule ref="Generic.PHP.DisallowShortOpenTag"/>
<rule ref="Generic.PHP.ForbiddenFunctions"/>
<rule ref="Generic.PHP.NoSilencedErrors"/>
<rule ref="Generic.Strings.UnnecessaryStringConcat"/>
<rule ref="Generic.WhiteSpace.DisallowTabIndent"/>
<rule ref="Generic.WhiteSpace.ScopeIndent"/>
<rule ref="MySource.PHP.EvalObjectFactory"/>
<rule ref="MySource.PHP.GetRequestData"/>
<rule ref="MySource.PHP.AjaxNullComparison"/>
<rule ref="PEAR.Classes.ClassDeclaration"/>
<rule ref="PEAR.Commenting.InlineComment"/>
<rule ref="PSR1.Classes.ClassDeclaration"/>
<rule ref="PSR1.Files.SideEffects"/>
<rule ref="PSR2.Classes.ClassDeclaration"/>
<rule ref="PSR2.Classes.PropertyDeclaration"/>
<rule ref="PSR2.ControlStructures.ElseIfDeclaration"/>
<rule ref="PSR2.ControlStructures.SwitchDeclaration"/>
<rule ref="PSR2.Files.EndFileNewline"/>
<rule ref="PSR2.Methods.MethodDeclaration"/>
<rule ref="PSR2.Namespaces.NamespaceDeclaration"/>
<rule ref="PSR2.Namespaces.UseDeclaration"/>
<rule ref="Squiz.PHP.CommentedOutCode"/>
<rule ref="Squiz.PHP.DisallowMultipleAssignments">
<exclude name="Squiz.PHP.DisallowMultipleAssignments.FoundInControlStructure" />
</rule>
<rule ref="Squiz.PHP.DisallowSizeFunctionsInLoops"/>
<rule ref="Squiz.PHP.DiscouragedFunctions">
<properties> <properties>
<property name="error" value="true"/> <property name="lineLimit" value="120"/>
<property name="absoluteLineLimit" value="0"/>
</properties> </properties>
</rule> </rule>
<rule ref="Squiz.PHP.Eval"/> <rule ref="Generic.Formatting.DisallowMultipleStatements" />
<rule ref="Squiz.PHP.GlobalKeyword"/> <rule ref="Generic.Formatting.NoSpaceAfterCast" />
<rule ref="Squiz.PHP.Heredoc"/> <rule ref="Generic.Functions.CallTimePassByReference" />
<rule ref="Squiz.PHP.InnerFunctions"/> <rule ref="Generic.Functions.FunctionCallArgumentSpacing" />
<rule ref="Squiz.PHP.LowercasePHPFunctions"/> <rule ref="Generic.Metrics.CyclomaticComplexity" />
<rule ref="Squiz.PHP.NonExecutableCode"/> <rule ref="Generic.Metrics.NestingLevel" />
<rule ref="Squiz.Scope.MemberVarScope"/> <rule ref="Generic.NamingConventions.ConstructorName" />
<rule ref="Squiz.Scope.MethodScope"/> <rule ref="Generic.NamingConventions.UpperCaseConstantName" />
<rule ref="Squiz.Scope.StaticThisUsage"/> <rule ref="Generic.NamingConventions.CamelCapsFunctionName" />
<rule ref="Squiz.WhiteSpace.LanguageConstructSpacing"/> <rule ref="Generic.PHP.DeprecatedFunctions" />
<rule ref="Squiz.WhiteSpace.LogicalOperatorSpacing"/> <rule ref="Generic.PHP.DisallowShortOpenTag" />
<rule ref="Squiz.WhiteSpace.MemberVarSpacing"> <rule ref="Generic.PHP.ForbiddenFunctions" />
<rule ref="Generic.PHP.LowerCaseConstant" />
<rule ref="Generic.PHP.NoSilencedErrors" />
<rule ref="Generic.Strings.UnnecessaryStringConcat" />
<rule ref="Generic.WhiteSpace.DisallowTabIndent" />
<rule ref="Generic.WhiteSpace.ScopeIndent" />
<rule ref="MySource.PHP.EvalObjectFactory" />
<rule ref="MySource.PHP.GetRequestData" />
<rule ref="MySource.PHP.AjaxNullComparison" />
<rule ref="PEAR.Classes.ClassDeclaration" />
<rule ref="PEAR.Commenting.InlineComment" />
<rule ref="PSR1.Classes.ClassDeclaration" />
<rule ref="PSR1.Files.SideEffects" />
<rule ref="Squiz.PHP.CommentedOutCode" />
<rule ref="Squiz.PHP.DisallowSizeFunctionsInLoops" />
<rule ref="Squiz.PHP.DiscouragedFunctions">
<properties> <properties>
<property name="spacingBeforeFirst " value="false"/> <property name="error" value="true" />
</properties>
</rule>
<rule ref="Squiz.PHP.Eval" />
<rule ref="Squiz.PHP.GlobalKeyword" />
<rule ref="Squiz.PHP.Heredoc" />
<rule ref="Squiz.PHP.InnerFunctions" />
<rule ref="Squiz.PHP.LowercasePHPFunctions" />
<rule ref="Squiz.PHP.NonExecutableCode" />
<rule ref="Squiz.Scope.MemberVarScope" />
<rule ref="Squiz.Scope.MethodScope" />
<rule ref="Squiz.Scope.StaticThisUsage" />
<rule ref="Squiz.WhiteSpace.MemberVarSpacing" />
<rule ref="Squiz.WhiteSpace.ObjectOperatorSpacing">
<properties>
<property name="ignoreNewlines" value="true"/>
</properties> </properties>
</rule> </rule>
<rule ref="Squiz.WhiteSpace.OperatorSpacing"> <rule ref="Squiz.WhiteSpace.OperatorSpacing">
@ -88,39 +76,12 @@
<property name="ignoreNewlines" value="true"/> <property name="ignoreNewlines" value="true"/>
</properties> </properties>
</rule> </rule>
<rule ref="Squiz.WhiteSpace.ObjectOperatorSpacing"> <rule ref="Squiz.WhiteSpace.ScopeKeywordSpacing" />
<properties> <rule ref="Squiz.WhiteSpace.SemicolonSpacing" />
<property name="ignoreNewlines" value="true"/>
</properties>
</rule>
<rule ref="Squiz.WhiteSpace.ScopeKeywordSpacing"/>
<rule ref="Squiz.WhiteSpace.SemicolonSpacing"/>
<rule ref="Squiz.Commenting.BlockComment">
<exclude name="Squiz.Commenting.BlockComment.WrongStart" />
</rule>
<rule ref="Squiz.Commenting.ClassComment">
<exclude name="Squiz.Commenting.ClassComment.Missing" />
<exclude name="Squiz.Commenting.ClassComment.TagNotAllowed" />
</rule>
<rule ref="Squiz.Commenting.DocCommentAlignment"/>
<rule ref="Squiz.Commenting.EmptyCatchComment"/>
<rule ref="Squiz.Commenting.FunctionComment">
<exclude name="Squiz.Commenting.FunctionComment.MissingParamComment" />
</rule>
<rule ref="Squiz.Commenting.FunctionCommentThrowTag"/>
<rule ref="Squiz.Commenting.InlineComment">
<exclude name="Squiz.Commenting.InlineComment.DocBlock" />
</rule>
<rule ref="Squiz.Commenting.LongConditionClosingComment">
<exclude name="Squiz.Commenting.LongConditionClosingComment.SpacingBefore" />
</rule>
<rule ref="Squiz.Commenting.PostStatementComment"/>
<rule ref="Squiz.Commenting.VariableComment"/>
<rule ref="SlevomatCodingStandard.Namespaces.UnusedUses.UnusedUse" />
<!-- For Laravel migrations --> <!-- For Laravel migrations -->
<rule ref="PSR1.Classes.ClassDeclaration.MissingNamespace"> <rule ref="PSR1.Classes.ClassDeclaration.MissingNamespace">
<exclude-pattern>*/database/migrations/*</exclude-pattern> <exclude-pattern>*/database/migrations/*</exclude-pattern>
</rule> </rule>
</ruleset> </ruleset>

View file

@ -2,6 +2,8 @@
[![Latest Version on Packagist][ico-version]][link-packagist] [![Latest Version on Packagist][ico-version]][link-packagist]
[![Total Downloads][ico-downloads]][link-downloads] [![Total Downloads][ico-downloads]][link-downloads]
[![Build Status][ico-travis]][link-travis]
[![StyleCI][ico-styleci]][link-styleci]
Handles exceptions and send email when it does happen Handles exceptions and send email when it does happen
It does send you a mail with anonymized debug data It does send you a mail with anonymized debug data
@ -16,26 +18,23 @@ $ composer require kwaadpepper/exception-handler
## Usage ## Usage
- `php artisan handler:install` 1 - php artisan handler:install
- Change configuration in `config/exception-handler.php` if needed. 2 - Change configuration in config/exception-handler.php
- Put at least your team email in the above file in `contactsList` array or using `.env` put a list in `EXCEPTION_MAIL_LIST`. You can add emails separated with `,` like `admin1@email.com,admin2@email.com` 3 - put at least your team email in the above file in `contactsList` array
You can uninstall it by using `php artisan handler:remove` or just revert changes in Exception/Handler.php You can uninstall it by using php artisan handler:remove or juste revert changes in Exception/Handler.php
## Change log ## Change log
Please see the [changelog](changelog.md) for more information on what has changed recently. Please see the [changelog](changelog.md) for more information on what has changed recently.
# Todo
- Add other transmission possibilities (other than mail)
## Security ## Security
If you discover any security related issues, please email `github@jeremydev.ovh` instead of using the issue tracker. If you discover any security related issues, please email github@jeremydev.ovh instead of using the issue tracker.
## Credits ## Credits
- [Jérémy Munsch][link-author] - [Jérémy Munsch][link-author]
- [All Contributors][link-contributors]
## License ## License

View file

@ -5,7 +5,6 @@
]) }} ]) }}
{{ __('check_trace') }} {{ __('check_trace') }}
{{ $ename }}
{{ $emessage }} {{ $emessage }}
{{ __('uri') }} : {{ $uri }} {{ __('uri') }} : {{ $uri }}
{{ __('date') }} : {{ date('Y-m-d H:i:s') }} {{ __('date') }} : {{ date('Y-m-d H:i:s') }}

View file

@ -11,8 +11,7 @@
<h1>{{ __('error_occured_in_app', ['appName' => $appName]) }}</h1> <h1>{{ __('error_occured_in_app', ['appName' => $appName]) }}</h1>
<p>{{ __('check_trace') }}</p> <p>{{ __('check_trace') }}</p>
<h2>{{ $ename }}</h2> <h2>{{ $emessage }}</h2>
<h3>{!! nl2br(strip_tags(e($emessage))) !!}</h3>
<p>{{ __('uri') }} : {{ $uri }}</p> <p>{{ __('uri') }} : {{ $uri }}</p>
<p>{{ __('date') }} : {{ date('Y-m-d H:i:s') }}</p> <p>{{ __('date') }} : {{ date('Y-m-d H:i:s') }}</p>
<p>{{ __('code') }} : {{ $code }}</p> <p>{{ __('code') }} : {{ $code }}</p>

View file

@ -7,6 +7,7 @@ use Illuminate\Support\Facades\File;
class InstallHandler extends Command class InstallHandler extends Command
{ {
/** /**
* The name and signature of the console command. * The name and signature of the console command.
* *
@ -34,8 +35,8 @@ class InstallHandler extends Command
exit; exit;
} }
$handlerFile = File::get($handlerPath); $handlerFile = File::get($handlerPath);
$find = 'use Illuminate\Foundation\Exceptions\Handler as ExceptionHandler;'; $find = 'use Illuminate\Foundation\Exceptions\Handler as ExceptionHandler;';
$replace = 'use Kwaadpepper\ExceptionHandler\Exceptions\ExceptionHandler;'; $replace = 'use Kwaadpepper\ExceptionHandler\Exceptions\ExceptionHandler;';
if (\strpos($handlerFile, $replace) !== false) { if (\strpos($handlerFile, $replace) !== false) {
$this->error('Exception handler seems to already be installed'); $this->error('Exception handler seems to already be installed');
exit; exit;
@ -48,8 +49,8 @@ class InstallHandler extends Command
File::put($handlerPath, $handlerFile); File::put($handlerPath, $handlerFile);
$this->info("Replaced $handlerPath"); $this->info("Replaced $handlerPath");
$source = __DIR__ . '/../../../config/exception-handler.php'; $source = __DIR__ . '/../../../config/exception-handler.php';
$dest = \config_path('exception-handler.php'); $dest = \config_path('exception-handler.php');
$destExist = File::exists($dest); $destExist = File::exists($dest);
if ( if (
!$destExist or !$destExist or

View file

@ -7,6 +7,7 @@ use Illuminate\Support\Facades\File;
class RemoveHandler extends Command class RemoveHandler extends Command
{ {
/** /**
* The name and signature of the console command. * The name and signature of the console command.
* *
@ -34,8 +35,8 @@ class RemoveHandler extends Command
exit; exit;
} }
$handlerFile = File::get($handlerPath); $handlerFile = File::get($handlerPath);
$find = 'use Kwaadpepper\ExceptionHandler\Exceptions\ExceptionHandler;'; $find = 'use Kwaadpepper\ExceptionHandler\Exceptions\ExceptionHandler;';
$replace = 'use Illuminate\Foundation\Exceptions\Handler as ExceptionHandler;'; $replace = 'use Illuminate\Foundation\Exceptions\Handler as ExceptionHandler;';
if (\strpos($handlerFile, $replace) !== false) { if (\strpos($handlerFile, $replace) !== false) {
$this->error('Exception handler seems to already be removed'); $this->error('Exception handler seems to already be removed');
exit; exit;
@ -47,7 +48,7 @@ class RemoveHandler extends Command
$handlerFile = \str_replace($find, $replace, $handlerFile); $handlerFile = \str_replace($find, $replace, $handlerFile);
File::put($handlerPath, $handlerFile); File::put($handlerPath, $handlerFile);
$source = \config_path('exception-handler.php'); $source = \config_path('exception-handler.php');
$sourceExist = File::exists($source); $sourceExist = File::exists($source);
if ( if (
$sourceExist and $sourceExist and

View file

@ -6,6 +6,7 @@ use Illuminate\Support\ServiceProvider;
class ExceptionHandlerServiceProvider extends ServiceProvider class ExceptionHandlerServiceProvider extends ServiceProvider
{ {
protected $commands = [ protected $commands = [
'Kwaadpepper\ExceptionHandler\Console\Commands\InstallHandler', 'Kwaadpepper\ExceptionHandler\Console\Commands\InstallHandler',
'Kwaadpepper\ExceptionHandler\Console\Commands\RemoveHandler' 'Kwaadpepper\ExceptionHandler\Console\Commands\RemoveHandler'
@ -29,7 +30,7 @@ class ExceptionHandlerServiceProvider extends ServiceProvider
public function register() public function register()
{ {
$this->mergeConfigFrom( $this->mergeConfigFrom(
__DIR__ . '/../config/exception-handler.php', sprintf('%s/../config/exception-handler.php', __DIR__),
'exception-handler' 'exception-handler'
); );
$this->commands($this->commands); $this->commands($this->commands);

View file

@ -2,115 +2,92 @@
namespace Kwaadpepper\ExceptionHandler\Exceptions; namespace Kwaadpepper\ExceptionHandler\Exceptions;
use Closure;
use Exception;
use Illuminate\Contracts\Container\Container; use Illuminate\Contracts\Container\Container;
use Illuminate\Foundation\Exceptions\Handler; use Illuminate\Foundation\Exceptions\Handler;
use Illuminate\Support\Facades\Log; use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Mail; use Illuminate\Support\Facades\Mail;
use Throwable;
class ExceptionHandler extends Handler class ExceptionHandler extends Handler
{ {
/** /**
* Create a new exception handler instance. * Create a new exception handler instance.
* *
* @param \Illuminate\Contracts\Container\Container $container * @param \Illuminate\Contracts\Container\Container $container
* @return void * @return void
*/ */
public function __construct(Container $container) public function __construct(Container $container)
{ {
parent::__construct($container); parent::__construct($container);
$this->reportable($this->reportExceptionByEmail()); if (\strpos(app()->version(), '8') === 0) {
$this->reportable($this->reportExceptionByEmail());
}
} }
/** /**
* Report or log an exception. * Report or log an exception.
* *
* @param \Throwable $e * @param \Throwable $exception
* @return void * @return void
*
* @throws \Exception
*/ */
public function report(\Throwable $e) public function report(Throwable $e)
{ {
if (get_class($e) == RedirectTo::class) { if (get_class($e) == RedirectTo::class) {
abort(redirect($e->url)); abort(redirect($e->url));
} }
if (
\strpos(app()->version(), '8') !== 0 and
!$this->reportExceptionByEmail()($e)
) {
return false;
}
parent::report($e); parent::report($e);
} }
/**
* Manually send exception by email
*
* @param \Throwable $e
* @return void
*/
public static function sendExceptionByEmailStatic(\Throwable $e)
{
app(self::class)->sendExceptionByEmail($e);
}
/**
* Manually send exception by email
*
* @param \Throwable $e
* @return void
*/
public function sendExceptionByEmail(\Throwable $e)
{
$this->reportExceptionByEmail()($e);
}
/** /**
* Report sending mail closure * Report sending mail closure
* *
* @return \Closure * @return Closure
*/ */
private function reportExceptionByEmail(): \Closure private function reportExceptionByEmail(): Closure
{ {
return function (\Throwable $e) { return function (Throwable $e) {
if ($this->shouldntReport($e)) { $dest = config('exception-handler.contactsList', config('mail.from.address'));
return false;
}
// * Ignore NoContactException.
if ($e instanceof NoContactException) {
Log::critical($e->getMessage());
return false;
}
$dest = \collect(config('exception-handler.contactsList', config('mail.from.address')))->filter();
try { try {
if (!config('exception-handler.enableMaiLog')) { if ($dest and config('exception-handler.enableMaiLog')) {
return false; if ($this->shouldntReport($e)) {
return false;
}
Mail::send([
'html' => 'exception-handler::emails.exception',
'text' => 'exception-handler::emails.exception-plain'
], [
'appName' => config('app.name'),
'code' => $e->getCode(),
'line' => $e->getLine(),
'emessage' => $this->getAnonymizedMessage($e->getMessage()),
'file' => str_replace(\realpath(\sprintf('%s/../', \app_path())), '', $e->getFile()),
'stackTrace' => $this->getAnonymizedStackTrace($e),
'uri' => app()->runningInConsole() ?
'CONSOLE' : (
(request() and request()->fullUrl()) ?
request()->fullUrl() : 'none')
], function ($m) use ($dest) {
/** @var \Illuminate\Mail\Message $m */
$m->to(
$dest,
sprintf('%s Exception Handler', config('app.name'))
)->subject('Need help !, an error occured in your application, please check Logs ASAP');
});
} }
if (!$dest->count()) { } catch (Exception $e) {
throw new NoContactException( Log::critical('Handler could not send Exception email', ['exception' => $e]);
'No contact in `EXCEPTION_MAIL_LIST` nor `exception-handler.contactsList` config', }
0,
$e
);
}
Mail::send([
'html' => 'exception-handler::emails.exception',
'text' => 'exception-handler::emails.exception-plain'
], [
'appName' => config('app.name'),
'code' => $e->getCode(),
'line' => $e->getLine(),
'ename' => \get_class($e),
'emessage' => self::getAnonymizedMessage($e->getMessage()),
'file' => str_replace(\realpath(\sprintf('%s/../', \app_path())), '', $e->getFile()),
'stackTrace' => self::getAnonymizedStackTrace($e),
'uri' => app()->runningInConsole() ?
'CONSOLE' : (
(request() and request()->fullUrl()) ?
request()->fullUrl() : 'none')
], function ($m) use ($dest) {
/** @var \Illuminate\Mail\Message $m */
$m->to(
$dest->all(),
sprintf('%s Exception Handler', config('app.name'))
)->subject('Need help !, an error occured in your application, please check Logs ASAP');
});
} catch (\Error $reportE) {
parent::report($e);
Log::critical('Handler could not send Exception email', ['exception' => $reportE]);
}//end try
}; };
} }
@ -120,81 +97,59 @@ class ExceptionHandler extends Handler
* @param string $message * @param string $message
* @return string * @return string
*/ */
private static function getAnonymizedMessage(string $message): string private function getAnonymizedMessage(string $message): string
{ {
$message = \preg_replace('/^(.*)( \(SQL:.*\))$/', '$1', $message) ?? $message; $message = \preg_replace('/^(.*)( \(SQL:.*\))$/', '$1', $message) ?? $message;
if ($sqlPos = \strpos($message, 'to use near')) {
$message = \mb_strcut($message, 0, $sqlPos);
}
return $message; return $message;
} }
/** /**
* Anonymize stack trace * Anonymize stack trace
* *
* @param \Throwable $e * @param \Exception $e
* @return string * @return string
*/ */
private static function getAnonymizedStackTrace(\Throwable $e): string private function getAnonymizedStackTrace(Throwable $e): string
{ {
$projectPath = \realpath(\sprintf('%s/../', \app_path())); $projectPath = \realpath(\sprintf('%s/../', \app_path()));
$traceStack = $e->getTrace(); $traceStack = $e->getTrace();
$traceStr = ''; $traceStr = '';
$maxStackStage = 0; $maxStackStage = 0;
foreach ($traceStack as $stackStage => $stack) { foreach ($traceStack as $stackStage => $stack) {
$maxStackStage = $stackStage; $maxStackStage = $stackStage;
$traceStr .= \sprintf( $traceStr .= \sprintf(
'#%s %s(%d): ', '#%s %s(%d): ',
$stackStage, $stackStage,
str_replace($projectPath, '', $stack['file'] ?? ''), str_replace($projectPath, '', $stack['file']),
$stack['line'] ?? '' $stack['line']
); );
if (isset($stack['class'])) { $traceStr .= "{$stack['class']}->{$stack['function']}(";
$traceStr .= "{$stack['class']}->{$stack['function']}("; $traceArgs = '';
if (isset($stack['args'])) { if (isset($stack['args'])) {
$traceStr .= self::handleArgs($stack); $argsCount = count($stack['args']);
for ($i = 0; $i < $argsCount; $i++) {
$type = \gettype($stack['args'][$i]);
switch ($type) {
case 'object':
$traceArgs .= sprintf('Object(%s)', \get_class($stack['args'][$i]));
break;
case 'array':
$traceArgs .= sprintf('Array(%d)', \count($stack['args'][$i]));
break;
case 'string':
$traceArgs .= sprintf('String(%d)', \strlen($stack['args'][$i]));
break;
default:
$traceArgs .= \gettype($stack['args'][$i]);
}
$traceArgs .= ($i !== ($argsCount - 1) ? ',' : '');
} }
$traceStr .= ")\n"; $traceStr .= $traceArgs;
} elseif (isset($stack['function'])) {
$traceStr .= "{$stack['function']}(";
if (isset($stack['args'])) {
$traceStr .= self::handleArgs($stack);
}
$traceStr .= ")\n";
} }
} //end foreach $traceStr .= ")\n";
}
$maxStackStage++; $maxStackStage++;
$traceStr .= "#$maxStackStage {main}"; $traceStr .= "#$maxStackStage {main}";
return $traceStr; return $traceStr;
} }
/**
* Handle Args
*
* @param array $stack
* @return string
*/
private static function handleArgs(array $stack): string
{
$traceArgs = '';
$argsCount = count($stack['args']);
for ($i = 0; $i < $argsCount; $i++) {
$type = \gettype($stack['args'][$i]);
switch ($type) {
case 'object':
$traceArgs .= sprintf('Object(%s)', \get_class($stack['args'][$i]));
break;
case 'array':
$traceArgs .= sprintf('Array(%d)', \count($stack['args'][$i]));
break;
case 'string':
$traceArgs .= sprintf('String(%d)', \strlen($stack['args'][$i]));
break;
default:
$traceArgs .= \gettype($stack['args'][$i]);
}
$traceArgs .= ($i !== ($argsCount - 1) ? ',' : '');
}
return $traceArgs;
}
} }

View file

@ -1,7 +0,0 @@
<?php
namespace Kwaadpepper\ExceptionHandler\Exceptions;
class NoContactException extends \RuntimeException
{
}