Skip to content

Integration with the Laravel Exception Handler #25

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,19 @@ foreach ($results->all() as $result) {
$html = $healthz->html();
```

**Enable integration with the Laravel Exception Handler**

It allows to report Healthz failure and warning exceptions to the Laravel log exceptions or send them to an external service like Flare, Bugsnag or Sentry.

This feature is disabled by default, here how to enable it for Healthz failure and warning exceptions:
```php
$healthz->setReportFailure(true);
$healthz->setReportWarning(true);
```
NOTE: The [`report()`](https://laravel.com/docs/8.x/errors#the-report-helper) Laravel helper must be present, otherwise this feature does nothing

For more informations see the Laravel Docs: https://laravel.com/docs/8.x/errors

----------------------------------------------------------------------------

## Check configuration
Expand Down
83 changes: 83 additions & 0 deletions src/Healthz.php
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,16 @@ class Healthz
Stack::push as stackPush;
}

/**
* @var bool
*/
protected $reportFailure = false;

/**
* @var bool
*/
protected $reportWarning = false;

/**
* @param HealthCheck[] $healthChecks
*/
Expand Down Expand Up @@ -56,6 +66,18 @@ public function run(): ResultStack
} catch (Exception $e) {
$check->setStatus($e->getMessage());
$resultCode = $e instanceof HealthWarningException ? HealthResult::RESULT_WARNING : HealthResult::RESULT_FAILURE;

if ($resultCode === HealthResult::RESULT_FAILURE &&
$this->shouldReportFailure()
) {
$this->reportException($e);
}

if ($resultCode === HealthResult::RESULT_WARNING &&
$this->shouldReportWarning()
) {
$this->reportException($e);
}
}

$results[] = new HealthResult($resultCode, $check);
Expand Down Expand Up @@ -86,4 +108,65 @@ public function html(ResultStack $results=null): string

return $twig->render('healthz', ['results' => $results->all()]);
}

/**
* @return bool
*/
public function shouldReportFailure()
{
return $this->reportFailure;
}

/**
* If set to true, report a HealthFailureException to the Laravel Exception Handler
*
* @param bool $value
*
* @return $this
*/
public function setReportFailure(bool $value)
{
$this->reportFailure = $value;

return $this;
}

/**
* @return bool
*/
public function shouldReportWarning()
{
return $this->reportWarning;
}

/**
* If set to true, report a HealthWarningException to the Laravel Exception Handler
*
* @param bool $value
*
* @return $this
*/
public function setReportWarning(bool $value)
{
$this->reportWarning = $value;

return $this;
}

/**
* Report an exception to the Laravel Exception Handler
*
* @param \Throwable $exception
* @return void
*/
protected function reportException($e)
{
if (function_exists('report')) {
try {
report($e);
} catch (\Throwable $e) {
// silent failed
}
}
}
}
42 changes: 40 additions & 2 deletions tests/HealthzTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -57,12 +57,50 @@ public function run_health_checks_and_return_result_stack()
$this->check1->shouldReceive('run');

# warning
$this->check2->shouldReceive('run')->andThrow(new HealthWarningException('warning'));
$warningException = new HealthWarningException('warning');
$this->check2->shouldReceive('run')->andThrow($warningException);
$this->check2->shouldReceive('setStatus')->with('warning')->once();
self::$functions->shouldNotReceive('report')->with($warningException);

# failure
$this->check3->shouldReceive('run')->andThrow(new Exception('failure'));
$failureException = new Exception('failure');
$this->check3->shouldReceive('run')->andThrow($failureException);
$this->check3->shouldReceive('setStatus')->with('failure')->once();
self::$functions->shouldNotReceive('report')->with($failureException);

$result = $this->healthz->run();
$this->assertInstanceOf(ResultStack::class, $result);
$this->assertTrue($result->all()[0]->passed());
$this->assertTrue($result->all()[1]->warned());
$this->assertTrue($result->all()[2]->failed());
}

/** @test */
public function enable_laravel_exception_handler_then_run_health_checks_and_return_result_stack()
{
$this->healthz = Mockery::spy($this->healthz);
$this->healthz->push($this->check3);

# success
$this->check1->shouldReceive('run');

# warning
$warningException = new HealthWarningException('warning');
$this->check2->shouldReceive('run')->andThrow($warningException);
$this->check2->shouldReceive('setStatus')->with('warning')->once();
self::$functions->shouldReceive('report')->with($warningException)->once();

# failure
$failureException = new Exception('failure');
$this->check3->shouldReceive('run')->andThrow($failureException);
$this->check3->shouldReceive('setStatus')->with('failure')->once();
self::$functions->shouldReceive('report')->with($failureException)->once();

$this->healthz->setReportWarning(true);
$this->healthz->shouldHaveReceived('setReportWarning')->with(true)->once();

$this->healthz->setReportFailure(true);
$this->healthz->shouldHaveReceived('setReportFailure')->with(true)->once();

$result = $this->healthz->run();
$this->assertInstanceOf(ResultStack::class, $result);
Expand Down
20 changes: 20 additions & 0 deletions tests/TestCase.php
Original file line number Diff line number Diff line change
@@ -1,8 +1,28 @@
<?php

/**
* Report an exception.
*
* @param \Throwable|string $exception
* @return void
*/
function report($exception)
{
// Override the Laravel report() function for testing purpose!
TestCase::$functions->report($exception);
}

class TestCase extends \PHPUnit\Framework\TestCase
{

public static $functions;

public function setUp(): void
{
parent::setUp();
self::$functions = Mockery::mock();
}

public function tearDown(): void
{
Mockery::close();
Expand Down