Skip to content

Commit 83cdaab

Browse files
committed
Fix: Body serialization should respect max_nesting_depth config
- Add maxDepth parameter to Body constructor - Update Body::serialize() to use maxDepth when calling serializeForRollbar - Update DataBuilder to pass maxNestingDepth config to Body constructor - Add test to verify deep nesting is properly truncated This fixes memory exhaustion issues when logging errors with complex nested data structures (like Drupal form arrays) by respecting the configured max_nesting_depth limit during Body serialization. Previously, Body::serialize() always used unlimited depth (-1) via serializeForRollbarInternal(), ignoring the max_nesting_depth config. Fixes memory issues similar to those described in issue reports about Rollbar consuming excessive memory with deeply nested context data.
1 parent 9c28cd7 commit 83cdaab

File tree

3 files changed

+51
-3
lines changed

3 files changed

+51
-3
lines changed

src/DataBuilder.php

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,8 @@ class DataBuilder implements DataBuilderInterface
5959
protected $captureEmail;
6060
protected $captureUsername;
6161

62+
protected $maxNestingDepth;
63+
6264
/**
6365
* @var Utilities
6466
*/
@@ -99,6 +101,7 @@ public function __construct(array $config)
99101
$this->setCustom($config);
100102
$this->setFingerprint($config);
101103
$this->setTitle($config);
104+
$this->setMaxNestingDepth($config);
102105
$this->setNotifier($config);
103106
$this->setBaseException($config);
104107
$this->setIncludeCodeContext($config);
@@ -129,6 +132,12 @@ protected function setCaptureUsername($config)
129132
$fromConfig = $config['capture_username'] ?? null;
130133
$this->captureUsername = self::$defaults->captureUsername($fromConfig);
131134
}
135+
136+
protected function setMaxNestingDepth($config)
137+
{
138+
$fromConfig = $config['max_nesting_depth'] ?? null;
139+
$this->maxNestingDepth = self::$defaults->maxNestingDepth($fromConfig);
140+
}
132141

133142
protected function setEnvironment($config)
134143
{
@@ -411,7 +420,7 @@ protected function getBody(Throwable|string|Stringable $toLog, array $context):
411420
} else {
412421
$content = $this->getMessage($toLog);
413422
}
414-
return new Body($content, $context, $this->getTelemetry());
423+
return new Body($content, $context, $this->getTelemetry(), $this->maxNestingDepth);
415424
}
416425

417426
public function getErrorTrace(ErrorWrapper $error)

src/Payload/Body.php

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,8 @@ class Body implements SerializerInterface
2323
public function __construct(
2424
private ContentInterface $value,
2525
private array $extra = [],
26-
private ?array $telemetry = null
26+
private ?array $telemetry = null,
27+
private int $maxDepth = -1
2728
) {
2829
}
2930

@@ -122,6 +123,11 @@ public function serialize()
122123
$result['telemetry'] = $this->telemetry;
123124
}
124125

125-
return $this->utilities()->serializeForRollbarInternal($result, array('extra'));
126+
if ($this->maxDepth === -1) {
127+
return $this->utilities()->serializeForRollbarInternal($result, array('extra'));
128+
} else {
129+
$objectHashes = array();
130+
return $this->utilities()->serializeForRollbar($result, array('extra'), $objectHashes, $this->maxDepth);
131+
}
126132
}
127133
}

tests/BodyTest.php

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,4 +46,37 @@ public function testSerialize(): void
4646
$encoded
4747
);
4848
}
49+
50+
public function testSerializeWithMaxNestingDepth(): void
51+
{
52+
$value = m::mock(ContentInterface::class)
53+
->shouldReceive("serialize")
54+
->andReturn("{CONTENT}")
55+
->shouldReceive("getKey")
56+
->andReturn("content_interface")
57+
->mock();
58+
59+
// Create deeply nested array that would cause memory issues
60+
$deepArray = array('level1' => array('level2' => array('level3' => array('level4' => 'deep_value'))));
61+
62+
// Test without depth limit - should serialize completely
63+
$bodyNoLimit = new Body($value, array('deep' => $deepArray), null, -1);
64+
$resultNoLimit = $bodyNoLimit->serialize();
65+
66+
// Test with depth limit - should truncate deep nesting
67+
$bodyWithLimit = new Body($value, array('deep' => $deepArray), null, 2);
68+
$resultWithLimit = $bodyWithLimit->serialize();
69+
70+
// Verify basic structure exists
71+
$this->assertArrayHasKey('extra', $resultNoLimit);
72+
$this->assertArrayHasKey('extra', $resultWithLimit);
73+
74+
// Without limit should have all nested levels
75+
$this->assertEquals('deep_value', $resultNoLimit['extra']['deep']['level1']['level2']['level3']['level4']);
76+
77+
// With limit should truncate the 'deep' array due to depth constraint
78+
// At depth 2: root -> extra -> deep (gets truncated to empty array)
79+
$this->assertArrayHasKey('deep', $resultWithLimit['extra']);
80+
$this->assertEmpty($resultWithLimit['extra']['deep']); // Truncated due to depth limit
81+
}
4982
}

0 commit comments

Comments
 (0)