Skip to content
Merged
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
37 changes: 34 additions & 3 deletions src/Diagnostics/PanelLogger.php
Original file line number Diff line number Diff line change
Expand Up @@ -201,7 +201,7 @@ private function logQuery(
'uri' => $request->getUri()->getPath(),
'requestBody' =>
\Tracy\Dumper::toHtml(
\Nette\Utils\Json::decode($contents, \JSON_OBJECT_AS_ARRAY),
$this->decodeBody($contents),
[
\Tracy\Dumper::DEPTH => 30,
],
Expand All @@ -218,10 +218,9 @@ private function logQuery(
if ($contents === '') {
$contents = '{}';
}
$decoded = \Nette\Utils\Json::decode($contents, \JSON_OBJECT_AS_ARRAY);
$query['responseBody'] =
\Tracy\Dumper::toHtml(
$decoded,
$this->decodeBody($contents),
[
\Tracy\Dumper::DEPTH => 30,
],
Expand All @@ -236,4 +235,36 @@ private function logQuery(
}


/**
* Bulk API bodies are NDJSON (one JSON document per line), not a single JSON document.
* Diagnostics must never break the request itself, so anything undecodable is dumped
* as the raw string instead of throwing.
*/
private function decodeBody(
string $contents,
): mixed
{
try {
return \Nette\Utils\Json::decode($contents, \JSON_OBJECT_AS_ARRAY);
} catch (\Nette\Utils\JsonException) {
// Not a single JSON document, try NDJSON below.
}

$documents = [];
foreach (\explode("\n", $contents) as $line) {
if (\trim($line) === '') {
continue;
}

try {
$documents[] = \Nette\Utils\Json::decode($line, \JSON_OBJECT_AS_ARRAY);
} catch (\Nette\Utils\JsonException) {
return $contents;
}
}

return $documents;
}


}
113 changes: 113 additions & 0 deletions tests/SpameriTests/Elastic/Diagnostics/PanelLoggerTest.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
<?php declare(strict_types = 1);

namespace SpameriTests\Elastic\Diagnostics;

require_once __DIR__ . '/../../../bootstrap-unit.php';

/**
* @testCase
*/
class PanelLoggerTest extends \Tester\TestCase
{

private \Spameri\Elastic\Diagnostics\PanelLogger $panelLogger;


protected function setUp(): void
{
$this->panelLogger = new \Spameri\Elastic\Diagnostics\PanelLogger(
new \Psr\Log\NullLogger(),
);
}


public function testLogsSingleJsonDocumentRequest(): void
{
$this->panelLogger->info('Request: POST /index/_search', [
'request' => new \GuzzleHttp\Psr7\Request(
'POST',
'http://localhost:9200/index/_search',
[],
'{"query": {"match_all": {}}}',
),
]);

$queries = $this->panelLogger->getQueries();

\Tester\Assert::count(1, $queries);
\Tester\Assert::same('/index/_search', $queries[0]['uri']);
\Tester\Assert::contains('match_all', $queries[0]['requestBody']);
}


public function testLogsNdjsonBulkRequestWithoutThrowing(): void
{
$ndjsonBody = '{"index": {"_index": "index", "_id": "1"}}' . "\n"
. '{"name": "First document"}' . "\n"
. '{"index": {"_index": "index", "_id": "2"}}' . "\n"
. '{"name": "Second document"}' . "\n";

$this->panelLogger->info('Request: POST /_bulk', [
'request' => new \GuzzleHttp\Psr7\Request(
'POST',
'http://localhost:9200/_bulk',
[],
$ndjsonBody,
),
]);

$queries = $this->panelLogger->getQueries();

\Tester\Assert::count(1, $queries);
\Tester\Assert::same('/_bulk', $queries[0]['uri']);
\Tester\Assert::contains('First document', $queries[0]['requestBody']);
\Tester\Assert::contains('Second document', $queries[0]['requestBody']);
}


public function testLogsUndecodableBodyAsRawString(): void
{
$this->panelLogger->info('Request: POST /index/_doc', [
'request' => new \GuzzleHttp\Psr7\Request(
'POST',
'http://localhost:9200/index/_doc',
[],
'not json at all',
),
]);

$queries = $this->panelLogger->getQueries();

\Tester\Assert::count(1, $queries);
\Tester\Assert::contains('not json at all', $queries[0]['requestBody']);
}


public function testPairsResponseWithRequest(): void
{
$this->panelLogger->info('Request: POST /index/_search', [
'request' => new \GuzzleHttp\Psr7\Request(
'POST',
'http://localhost:9200/index/_search',
[],
'{"query": {"match_all": {}}}',
),
]);
$this->panelLogger->info('Response (retry 0): 200', [
'response' => new \GuzzleHttp\Psr7\Response(
200,
[],
'{"took": 1, "hits": {"total": {"value": 0}}}',
),
]);

$queries = $this->panelLogger->getQueries();

\Tester\Assert::count(1, $queries);
\Tester\Assert::contains('hits', $queries[0]['responseBody']);
\Tester\Assert::true(isset($queries[0]['duration']));
}

}

(new PanelLoggerTest())->run();
Loading