Skip to content

Commit

Permalink
github-171: exception sampling and tests; moved error sampling to a s…
Browse files Browse the repository at this point in the history
…eparate function as well
  • Loading branch information
ArturMoczulski committed Jun 28, 2017
1 parent 3f3d638 commit a3e0988
Show file tree
Hide file tree
Showing 4 changed files with 200 additions and 17 deletions.
20 changes: 20 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -404,6 +404,13 @@ Default: `'production'`
Default: empty array, meaning all errors are reported.
</dd>

<dt>exception_sample_rates
</dt>
<dd>Associative array mapping exception classes to sample rates. Sample rates are ratio out of 1, e.g. 0 is "never report", 1 is "always report", and 0.1 is "report 10% of the time". Sampling is done on a per-exception basis. It also respects class inheritance meaning if Exception is at 1.0 then ExceptionSublcass is also at 1.0, unless explicitly configured otherwise. If ExceptionSubclass is set to 0.5, but Exception is at 1.0 then Exception and all its' subclasses run at 1.0, except for ExceptionSubclass and its' subclasses which run at 0.5. Names of exception classes should NOT be followed with additional `\` for global namespace, i.e. Rollbar\SampleException and NOT \Rollbar\SampleException.

Default: empty array, meaning all exceptions are reported.
</dd>

<dt>fluent_host</dt>
<dd>Either an `IPv4`, `IPv6`, or a `unix socket`.

Expand Down Expand Up @@ -579,6 +586,19 @@ $config['error_sample_rates'] = array(
?>
```

Example use of exception_sample_rates:

```php
<?php
$config['exception_sample_rates'] = array(
// Exception omitted, so defaults to 1

// SometimesException set at 0.1 so only reported 10% of the time
'SometimesException' => 0.1,
);
?>
```

Example use of person_fn:

```php
Expand Down
117 changes: 100 additions & 17 deletions src/Config.php
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ class Config
*/
private $checkIgnore;
private $error_sample_rates = array();
private $exception_sample_rates = array();
private $mt_randmax;

private $included_errno = ROLLBAR_INCLUDED_ERRNO_BITMASK;
Expand All @@ -77,6 +78,10 @@ public function __construct(array $configArray)
if (isset($configArray['error_sample_rates'])) {
$this->error_sample_rates = $configArray['error_sample_rates'];
}

if (isset($configArray['exception_sample_rates'])) {
$this->exception_sample_rates = $configArray['exception_sample_rates'];
}

$levels = array(E_WARNING, E_NOTICE, E_USER_ERROR, E_USER_WARNING,
E_USER_NOTICE, E_STRICT, E_RECOVERABLE_ERROR);
Expand Down Expand Up @@ -407,6 +412,7 @@ public function checkIgnored($payload, $accessToken, $toLog, $isUncaught)
if ($this->shouldSuppress()) {
return true;
}

if (isset($this->checkIgnore)) {
try {
if (call_user_func($this->checkIgnore, $isUncaught, $toLog, $payload)) {
Expand All @@ -417,38 +423,115 @@ public function checkIgnored($payload, $accessToken, $toLog, $isUncaught)
$this->checkIgnore = null;
}
}

if ($this->levelTooLow($payload)) {
return true;
}

if (!is_null($this->filter)) {
return $this->filter->shouldSend($payload, $accessToken);
}

if ($toLog instanceof ErrorWrapper) {
$errno = $toLog->errorLevel;

return $this->shouldIgnoreError($toLog);

}

if ($toLog instanceof \Exception) {

return $this->shouldIgnoreException($toLog);

}

return false;
}

/**
* Check if the error should be ignored due to `included_errno` config,
* `use_error_reporting` config or `error_sample_rates` config.
*
* @param \Rollbar\ErrorWrapper $toLog
*
* @return bool
*/
protected function shouldIgnoreError(ErrorWrapper $toLog)
{
$errno = $toLog->errorLevel;

if ($this->included_errno != -1 && ($errno & $this->included_errno) != $errno) {
// ignore
return true;
}
if ($this->included_errno != -1 && ($errno & $this->included_errno) != $errno) {
// ignore
return true;
}

if ($this->use_error_reporting && ($errno & error_reporting()) != $errno) {
// ignore due to error_reporting level
return true;
}
if ($this->use_error_reporting && ($errno & error_reporting()) != $errno) {
// ignore due to error_reporting level
return true;
}

if (isset($this->error_sample_rates[$errno])) {
// get a float in the range [0, 1)
// mt_rand() is inclusive, so add 1 to mt_randmax
$float_rand = mt_rand() / ($this->mt_randmax + 1);
if ($float_rand > $this->error_sample_rates[$errno]) {
// skip
return true;
}
if (isset($this->error_sample_rates[$errno])) {
// get a float in the range [0, 1)
// mt_rand() is inclusive, so add 1 to mt_randmax
$float_rand = mt_rand() / ($this->mt_randmax + 1);
if ($float_rand > $this->error_sample_rates[$errno]) {
// skip
return true;
}
}

return false;
}

/**
* Check if the exception should be ignored due to configured exception
* sample rates.
*
* @param \Exception $toLog
*
* @return bool
*/
protected function shouldIgnoreException(\Exception $toLog)
{
// get a float in the range [0, 1)
// mt_rand() is inclusive, so add 1 to mt_randmax
$floatRand = mt_rand() / ($this->mt_randmax + 1);
if ($floatRand > $this->exceptionSampleRate($toLog)) {
// skip
return true;
}

return false;
}

/**
* Calculate what's the chance of logging this exception according to
* exception sampling.
*
* @param \Exception $toLog
*
* @return float
*/
public function exceptionSampleRate(\Exception $toLog)
{
$sampleRate = 1.0;

$exceptionClasses = array();

$class = get_class($toLog);
while ($class) {
$exceptionClasses []= $class;
$class = get_parent_class($class);
}
$exceptionClasses = array_reverse($exceptionClasses);

foreach ($exceptionClasses as $exceptionClass) {
if (isset($this->exception_sample_rates["$exceptionClass"])) {
$sampleRate = $this->exception_sample_rates["$exceptionClass"];
}
}

return $sampleRate;
}

/**
* @param Payload $payload
Expand Down
60 changes: 60 additions & 0 deletions tests/ConfigTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,18 @@
use Rollbar\Payload\Payload;
use Rollbar\RollbarLogger;

class MidExceptionSampleRate extends \Exception {}

class SilentExceptionSampleRate extends MidExceptionSampleRate {}

class FiftyFityExceptionSampleRate extends MidExceptionSampleRate {}

class FiftyFityChildExceptionSampleRate extends FiftyFityExceptionSampleRate {}

class QuarterExceptionSampleRate extends FiftyFityExceptionSampleRate {}

class VerboseExceptionSampleRate extends MidExceptionSampleRate {}

class ConfigTest extends \PHPUnit_Framework_TestCase
{
private $error;
Expand Down Expand Up @@ -438,4 +450,52 @@ public function useErrorReportingProvider()
)
);
}

/**
* @dataProvider providerExceptionSampleRate
*/
public function testExceptionSampleRate($exception, $expected)
{
$config = new Config(array(
"access_token" => "ad865e76e7fb496fab096ac07b1dbabb",
"environment" => "testing-php",
"exception_sample_rates" => array(
get_class($exception) => $expected
)
));

$sampleRate = $config->exceptionSampleRate($exception);

$this->assertEquals($expected, $sampleRate);
}

public function providerExceptionSampleRate()
{
return array(
array(
new \Exception,
1.0
),
array(
new SilentExceptionSampleRate,
0.0
),
array(
new FiftyFityExceptionSampleRate,
0.5
),
array(
new FiftyFityChildExceptionSampleRate,
0.5
),
array(
new QuarterExceptionSampleRate,
0.25
),
array(
new VerboseExceptionSampleRate,
1.0
),
);
}
}
20 changes: 20 additions & 0 deletions tests/RollbarLoggerTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
use Rollbar\Payload\Level;
use Rollbar\Payload\Payload;

class SilentExceptionSampleRate extends \Exception {}

class RollbarLoggerTest extends \PHPUnit_Framework_TestCase
{

Expand Down Expand Up @@ -66,6 +68,24 @@ public function testErrorSampleRates()
$this->assertEquals(0, $response->getStatus());
}

public function testExceptionSampleRates()
{
$l = new RollbarLogger(array(
"access_token" => "ad865e76e7fb496fab096ac07b1dbabb",
"environment" => "testing-php",
"exception_sample_rates" => array(
'Rollbar\SilentExceptionSampleRate' => 0.0
)
));
$response = $l->log(Level::ERROR, new SilentExceptionSampleRate);

$this->assertEquals(0, $response->getStatus());

$response = $l->log(Level::ERROR, new \Exception);

$this->assertEquals(200, $response->getStatus());
}

public function testIncludedErrNo()
{
$l = new RollbarLogger(array(
Expand Down

0 comments on commit a3e0988

Please sign in to comment.