Skip to content

Commit

Permalink
Merge pull request #167 from ArturMoczulski/github-38-truncate-payload
Browse files Browse the repository at this point in the history
GitHub Issue #38: truncate payload
  • Loading branch information
rokob authored May 16, 2017
2 parents e2b111c + 4721b15 commit 26d611a
Show file tree
Hide file tree
Showing 13 changed files with 483 additions and 2 deletions.
46 changes: 46 additions & 0 deletions src/DataBuilder.php
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,15 @@

class DataBuilder implements DataBuilderInterface
{
const MAX_PAYLOAD_SIZE = 524288; // 512 * 1024

protected static $truncationStrategies = array(
"Rollbar\Truncation\RawStrategy",
"Rollbar\Truncation\FramesStrategy",
"Rollbar\Truncation\StringsStrategy",
"Rollbar\Truncation\MinBodyStrategy"
);

protected static $defaults;

protected $environment;
Expand Down Expand Up @@ -932,4 +941,41 @@ protected static function uuid4()
mt_rand(0, 0xffff)
);
}

/**
* Applies truncation strategies in order to keep the payload size under
* configured limit.
*
* @param array $payload
* @param string $strategy
*
* @return array
*/
public function truncate(array $payload)
{

foreach (static::$truncationStrategies as $strategy) {
if (!$this->needsTruncating($payload)) {
break;
}

$strategy = new $strategy($this);

$payload = $strategy->execute($payload);
}

return $payload;
}

/**
* Check if the payload is too big to be sent
*
* @param array $payload
*
* @return boolean
*/
public function needsTruncating(array $payload)
{
return strlen(json_encode($payload)) > self::MAX_PAYLOAD_SIZE;
}
}
14 changes: 12 additions & 2 deletions src/RollbarLogger.php
Original file line number Diff line number Diff line change
Expand Up @@ -45,8 +45,9 @@ public function log($level, $toLog, array $context = array())
if ($this->config->checkIgnored($payload, $accessToken, $toLog, $isUncaught)) {
$response = new Response(0, "Ignored");
} else {
$scrubbed = $this->scrub($payload);
$response = $this->config->send($scrubbed, $accessToken);
$toSend = $this->scrub($payload);
$toSend = $this->truncate($toSend);
$response = $this->config->send($toSend, $accessToken);
}

$this->handleResponse($payload, $response);
Expand Down Expand Up @@ -80,4 +81,13 @@ protected function scrub(Payload $payload)
$serialized['data'] = $this->config->getDataBuilder()->scrub($serialized['data']);
return $serialized;
}

/**
* @param array $payload
* @return array
*/
protected function truncate(array $payload)
{
return $this->config->getDataBuilder()->truncate($payload);
}
}
16 changes: 16 additions & 0 deletions src/Truncation/AbstractStrategy.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
<?php namespace Rollbar\Truncation;

class AbstractStrategy implements IStrategy
{
protected $dataBuilder;

public function __construct($dataBuilder)
{
$this->dataBuilder = $dataBuilder;
}

public function execute(array $payload)
{
return $payload;
}
}
32 changes: 32 additions & 0 deletions src/Truncation/FramesStrategy.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
<?php namespace Rollbar\Truncation;

class FramesStrategy extends AbstractStrategy
{

const FRAMES_OPTIMIZATION_RANGE = 75;

public function execute(array $payload)
{
$frames = array();

if (isset($payload['data']['body']['trace_chain']['frames'])) {
$frames = $payload['data']['body']['trace_chain']['frames'];
} elseif (isset($payload['data']['body']['trace']['frames'])) {
$frames = $payload['data']['body']['trace']['frames'];
}

return $this->selectFrames($frames);
}

public function selectFrames($frames, $range = self::FRAMES_OPTIMIZATION_RANGE)
{
if (count($frames) <= $range * 2) {
return $frames;
}

return array_merge(
array_splice($frames, 0, $range),
array_splice($frames, -$range, $range)
);
}
}
11 changes: 11 additions & 0 deletions src/Truncation/IStrategy.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
<?php namespace Rollbar\Truncation;

interface IStrategy
{

/**
* @param array $payload
* @return array
*/
public function execute(array $payload);
}
45 changes: 45 additions & 0 deletions src/Truncation/MinBodyStrategy.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
<?php namespace Rollbar\Truncation;

class MinBodyStrategy extends AbstractStrategy
{

const EXCEPTION_MESSAGE_LIMIT = 256;
const EXCEPTION_FRAMES_RANGE = 1;

public function execute(array $payload)
{

$traceData = null;

if (isset($payload['data']['body']['trace'])) {
$traceData = &$payload['data']['body']['trace'];
} elseif (isset($payload['data']['body']['trace_chain'])) {
$traceData = &$payload['data']['body']['trace_chain'];
}

/**
* Delete exception description
*/
unset($traceData['exception']['description']);

/**
* Truncate exception message
*/
$traceData['exception']['message'] = substr(
$traceData['exception']['message'],
0,
static::EXCEPTION_MESSAGE_LIMIT
);

/**
* Limit trace frames
*/
$framesStrategy = new FramesStrategy($this->dataBuilder);
$traceData['frames'] = $framesStrategy->selectFrames(
$traceData['frames'],
static::EXCEPTION_FRAMES_RANGE
);

return $payload;
}
}
9 changes: 9 additions & 0 deletions src/Truncation/RawStrategy.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<?php namespace Rollbar\Truncation;

class RawStrategy extends AbstractStrategy
{
public function execute(array $payload)
{
return $payload;
}
}
28 changes: 28 additions & 0 deletions src/Truncation/StringsStrategy.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
<?php namespace Rollbar\Truncation;

class StringsStrategy extends AbstractStrategy
{

public static function getThresholds()
{
return array(1024, 512, 256);
}

public function execute(array $payload)
{
foreach (static::getThresholds() as $threshold) {
if (!$this->dataBuilder->needsTruncating($payload)) {
break;
}

array_walk_recursive($payload, function (&$value) use ($threshold) {

if (is_string($value) && strlen($value) > $threshold) {
$value = substr($value, 0, $threshold);
}
});
}

return $payload;
}
}
32 changes: 32 additions & 0 deletions tests/DataBuilderTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

use Rollbar\Payload\Level;
use Rollbar\TestHelpers\MockPhpStream;
use Rollbar\Truncation\FramesStrategy;

class DataBuilderTest extends \PHPUnit_Framework_TestCase
{
Expand Down Expand Up @@ -911,4 +912,35 @@ public function testSetRequestBody()

stream_wrapper_restore("php");
}

/**
* @dataProvider truncateProvider
*/
public function testTruncate($data)
{
$result = $this->dataBuilder->truncate($data);

$size = strlen(json_encode($result));

$this->assertTrue(
$size <= DataBuilder::MAX_PAYLOAD_SIZE,
"Truncation failed. Payload size exceeds MAX_PAYLOAD_SIZE."
);
}

public function truncateProvider()
{

$stringsTest = new Truncation\StringsStrategyTest();
$framesTest = new Truncation\FramesStrategyTest();
$minBodyTest = new Truncation\MinBodyStrategyTest();

$data = array_merge(
$stringsTest->executeProvider(),
$framesTest->executeProvider(),
$minBodyTest->executeProvider()
);

return $data;
}
}
82 changes: 82 additions & 0 deletions tests/Truncation/FramesStrategyTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
<?php

namespace Rollbar\Truncation;

use Rollbar\DataBuilder;

class FramesStrategyTest extends \PHPUnit_Framework_TestCase
{
/**
* @dataProvider executeProvider
*/
public function testExecute($data, $expected)
{
$dataBuilder = new DataBuilder(array(
'accessToken' => 'abcd1234efef5678abcd1234567890be',
'environment' => 'tests'
));

$strategy = new FramesStrategy($dataBuilder);
$result = $strategy->execute($data);

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

public function executeProvider()
{
$data = array(
'nothing to truncate using trace key' => array(
array('data' => array('body' =>
array('trace' => array('frames' => range(1, 6)))
)),
range(1, 6)
),
'nothing to truncate using trace_chain key' => array(
array('data' => array('body' =>
array('trace_chain' => array('frames' => range(1, 6)))
)),
range(1, 6)
),
'truncate middle using trace key' => array(
array('data' => array('body' =>
array(
'trace' => array(
'frames' => range(
1,
FramesStrategy::FRAMES_OPTIMIZATION_RANGE * 2 + 1
)
)
)
)),
array_merge(
range(1, FramesStrategy::FRAMES_OPTIMIZATION_RANGE),
range(
FramesStrategy::FRAMES_OPTIMIZATION_RANGE + 2,
FramesStrategy::FRAMES_OPTIMIZATION_RANGE * 2 + 1
)
)
),
'truncate middle using trace_chain key' => array(
array('data' => array('body' =>
array(
'trace_chain' => array(
'frames' => range(
1,
FramesStrategy::FRAMES_OPTIMIZATION_RANGE * 2 + 1
)
)
)
)),
array_merge(
range(1, FramesStrategy::FRAMES_OPTIMIZATION_RANGE),
range(
FramesStrategy::FRAMES_OPTIMIZATION_RANGE + 2,
FramesStrategy::FRAMES_OPTIMIZATION_RANGE * 2 + 1
)
)
)
);

return $data;
}
}
Loading

0 comments on commit 26d611a

Please sign in to comment.