Skip to content

Commit 60febb5

Browse files
authored
Merge pull request #35 from cego/lejo/add-truncation-of-body-and-fix-exception
Lejo/add truncation of body and fix exception
2 parents 0c4a9f3 + 96107f7 commit 60febb5

File tree

3 files changed

+131
-3
lines changed

3 files changed

+131
-3
lines changed

publishable/config/request-log.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@
1414

1515
'enabled' => env('REQUEST_LOG_ENABLED', false),
1616

17+
'truncateBodyLength' => env('REQUEST_LOG_TRUNCATE_BODY_LENGTH', 10000), // Truncate the length of body of request or response to maximum this size. Set to -1 to disable.
18+
1719
/*
1820
| Set of headers and query parameters to redact from the log
1921
*/

src/RequestLog/Middleware/LogRequest.php

Lines changed: 22 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,23 @@ protected function routeIsBlacklisted($request)
7575
return false;
7676
}
7777

78+
/**
79+
* Truncate a string to a given length
80+
*
81+
* @param string $string
82+
* @param int $length
83+
*
84+
* @return string
85+
*/
86+
protected function truncate(string $string, int $length): string
87+
{
88+
if($length <= 0) {
89+
return $string;
90+
}
91+
92+
return mb_substr($string, 0, $length);
93+
}
94+
7895
/**
7996
* @param Request $request
8097
* @param Response $response
@@ -93,6 +110,8 @@ private function logRequest(Request $request, Response $response): void
93110
$responseHeaders = $response->headers->all();
94111
unset($responseHeaders['set-cookie']);
95112

113+
$truncateBodyLength = config('request-log.truncateBodyLength');
114+
96115
(new RequestLog(
97116
method: $request->method(),
98117
url: $request->url(),
@@ -101,12 +120,12 @@ private function logRequest(Request $request, Response $response): void
101120
queryString: SecurityUtility::getQueryWithMaskingApplied($request),
102121
requestHeaders: SecurityUtility::getHeadersWithMaskingApplied($request),
103122
requestCookies: SecurityUtility::getCookiesWithMaskingApplied($this->requestCookies, $request),
104-
requestBody: SecurityUtility::getBodyWithMaskingApplied($request) ?: '{}',
123+
requestBody: $this->truncate(SecurityUtility::getBodyWithMaskingApplied($request) ?: '{}', $truncateBodyLength),
105124
status: $response->getStatusCode(),
106125
responseHeaders: $responseHeaders,
107126
responseCookies: SecurityUtility::getResponseCookiesWithMaskingApplied($response->headers->getCookies(), $request),
108-
responseBody: $response->getContent() ?: '{}',
109-
responseException: $response->exception,
127+
responseBody: $this->truncate($response->getContent() ?: '{}', $truncateBodyLength),
128+
responseException: $response->exception ?? null,
110129
executionTimeNs: $executionTimeNs
111130
))->log(Log::getLogger());
112131

tests/Unit/LogRequestTest.php

Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,10 @@
33
namespace Tests\Unit;
44

55
use Cego\RequestLog\Services\RequestLogOptionsService;
6+
use Illuminate\Http\Request;
67
use Illuminate\Support\Facades\Log;
78
use Monolog\Logger;
9+
use Symfony\Component\HttpFoundation\Response;
810
use Tests\TestCase;
911
use Cego\RequestLog\Models\RequestLog;
1012
use Illuminate\Support\Facades\Config;
@@ -219,4 +221,109 @@ public function it_masks_response_cookies(): void
219221
// Act
220222
$this->post('/test', [], $headers);
221223
}
224+
225+
/** @test */
226+
public function it_doesnt_crash_if_exception_on_response_doesnt_exist(): void
227+
{
228+
$loggerMock = $this->createMock(Logger::class);
229+
Log::partialMock()->shouldReceive('getLogger')->once()->withAnyArgs()->andReturn($loggerMock);
230+
Log::partialMock()->shouldNotReceive('error');
231+
232+
233+
$middleware = new LogRequest();
234+
235+
$request = new Request();
236+
237+
$response = new Response();
238+
239+
$middleware->terminate($request, $response);
240+
}
241+
242+
/** @test */
243+
public function it_truncates_very_long_json_bodies(): void
244+
{
245+
// Set config request-log.truncateBodyLength to 100
246+
Config::set('request-log.truncateBodyLength', 100);
247+
248+
$loggerMock = $this->createMock(Logger::class);
249+
Log::partialMock()->shouldReceive('getLogger')->once()->withAnyArgs()->andReturn($loggerMock);
250+
251+
$loggerMock->expects($this->once())->method('debug')->with($this->stringStartsWith('Timing for'))->willReturnCallback(function ($message, $context) {
252+
$this->assertEquals(100, strlen($context['http']['request']['body']['content']));
253+
$this->assertEquals(100, strlen($context['http']['response']['body']['content']));
254+
});
255+
256+
257+
$middleware = new LogRequest();
258+
259+
260+
$request = new Request();
261+
// Set request body to a very long json string
262+
$request->initialize([], [], [], [], [], [], json_encode(range(0, 10000)));
263+
264+
$response = new Response();
265+
// Set response body to a very long json string
266+
$response->setContent(json_encode(range(0, 10000)));
267+
268+
$middleware->terminate($request, $response);
269+
}
270+
271+
/** @test */
272+
public function it_doesnt_truncate_very_long_json_bodies_if_disabled(): void
273+
{
274+
// Set config request-log.truncateBodyLength to 100
275+
Config::set('request-log.truncateBodyLength', -1);
276+
277+
$loggerMock = $this->createMock(Logger::class);
278+
Log::partialMock()->shouldReceive('getLogger')->once()->withAnyArgs()->andReturn($loggerMock);
279+
280+
$loggerMock->expects($this->once())->method('debug')->with($this->stringStartsWith('Timing for'))->willReturnCallback(function ($message, $context) {
281+
$this->assertEquals(48897, strlen($context['http']['request']['body']['content']));
282+
$this->assertEquals(48897, strlen($context['http']['response']['body']['content']));
283+
});
284+
285+
286+
$middleware = new LogRequest();
287+
288+
289+
$request = new Request();
290+
// Set request body to a very long json string
291+
$request->initialize([], [], [], [], [], [], json_encode(range(0, 10000)));
292+
293+
$response = new Response();
294+
// Set response body to a very long json string
295+
$response->setContent(json_encode(range(0, 10000)));
296+
297+
$middleware->terminate($request, $response);
298+
}
299+
300+
/** @test */
301+
public function it_doesnt_truncate_bodies_shorter_than_truncate_limit(): void
302+
{
303+
// Set config request-log.truncateBodyLength to 100
304+
Config::set('request-log.truncateBodyLength', 100);
305+
306+
$loggerMock = $this->createMock(Logger::class);
307+
Log::partialMock()->shouldReceive('getLogger')->once()->withAnyArgs()->andReturn($loggerMock);
308+
309+
$loggerMock->expects($this->once())->method('debug')->with($this->stringStartsWith('Timing for'))->willReturnCallback(function ($message, $context) {
310+
$this->assertEquals(3, strlen($context['http']['request']['body']['content']));
311+
$this->assertEquals(3, strlen($context['http']['response']['body']['content']));
312+
});
313+
314+
315+
$middleware = new LogRequest();
316+
317+
318+
$request = new Request();
319+
// Set request body to a very long json string
320+
$request->initialize([], [], [], [], [], [], "hej");
321+
322+
$response = new Response();
323+
// Set response body to a very long json string
324+
$response->setContent("hej");
325+
326+
$middleware->terminate($request, $response);
327+
}
328+
222329
}

0 commit comments

Comments
 (0)