Skip to content

Commit

Permalink
Fixed "malformed UTF-8 characters" issue (#1120)
Browse files Browse the repository at this point in the history
* Workaround potential "malformed UTF-8 characters" exception

* Fixed unit tests
  • Loading branch information
SergeyKleyman authored Jan 15, 2024
1 parent 10be2bd commit 7a3f179
Show file tree
Hide file tree
Showing 10 changed files with 35 additions and 87 deletions.
16 changes: 8 additions & 8 deletions agent/php/ElasticApm/Impl/Error.php
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ class Error implements SerializableDataInterface, LoggableInterface
*
* @link https://github.com/elastic/apm-server/blob/7.0/docs/spec/errors/error.json#L6
*/
public $timestamp;
private $timestamp;

/**
* @var string
Expand All @@ -71,7 +71,7 @@ class Error implements SerializableDataInterface, LoggableInterface
*
* @link https://github.com/elastic/apm-server/blob/7.0/docs/spec/errors/error.json#L14
*/
public $traceId = null;
private $traceId = null;

/**
* @var ?string
Expand All @@ -83,7 +83,7 @@ class Error implements SerializableDataInterface, LoggableInterface
*
* @link https://github.com/elastic/apm-server/blob/7.0/docs/spec/errors/error.json#L19
*/
public $transactionId = null;
private $transactionId = null;

/**
* @var ?string
Expand All @@ -95,7 +95,7 @@ class Error implements SerializableDataInterface, LoggableInterface
*
* @link https://github.com/elastic/apm-server/blob/7.0/docs/spec/errors/error.json#L24
*/
public $parentId = null;
private $parentId = null;

/**
* @var ?ErrorTransactionData
Expand All @@ -104,14 +104,14 @@ class Error implements SerializableDataInterface, LoggableInterface
*
* @link https://github.com/elastic/apm-server/blob/7.0/docs/spec/errors/error.json#L29
*/
public $transaction = null;
private $transaction = null;

/**
* @var ?TransactionContext
*
* @link https://github.com/elastic/apm-server/blob/7.0/docs/spec/errors/error.json#L44
*/
public $context = null;
private $context = null;

/**
* @var ?string
Expand All @@ -122,7 +122,7 @@ class Error implements SerializableDataInterface, LoggableInterface
*
* @link https://github.com/elastic/apm-server/blob/v7.0.0/docs/spec/errors/error.json#L47
*/
public $culprit = null;
private $culprit = null;

/**
* @var ?ErrorExceptionData
Expand All @@ -131,7 +131,7 @@ class Error implements SerializableDataInterface, LoggableInterface
*
* @link https://github.com/elastic/apm-server/blob/7.0/docs/spec/errors/error.json#L29
*/
public $exception = null;
private $exception = null;

public static function build(Tracer $tracer, ErrorExceptionData $errorExceptionData, ?Transaction $transaction, ?Span $span): Error
{
Expand Down
12 changes: 6 additions & 6 deletions agent/php/ElasticApm/Impl/Span.php
Original file line number Diff line number Diff line change
Expand Up @@ -41,22 +41,22 @@
final class Span extends ExecutionSegment implements SpanInterface, SpanToSendInterface
{
/** @var string */
public $parentId;
private $parentId;

/** @var string */
public $transactionId;
private $transactionId;

/** @var ?string */
public $action = null;
private $action = null;

/** @var ?string */
public $subtype = null;
private $subtype = null;

/** @var null|StackTraceFrame[] */
public $stackTrace = null;
private $stackTrace = null;

/** @var ?SpanContext */
public $context = null;
private $context = null;

/** @var Logger */
private $logger;
Expand Down
10 changes: 5 additions & 5 deletions agent/php/ElasticApm/Impl/Transaction.php
Original file line number Diff line number Diff line change
Expand Up @@ -49,22 +49,22 @@
final class Transaction extends ExecutionSegment implements TransactionInterface
{
/** @var ?string */
public $parentId = null;
private $parentId = null;

/** @var int */
public $startedSpansCount = 0;

/** @var int */
public $droppedSpansCount = 0;
private $droppedSpansCount = 0;

/** @var ?string */
public $result = null;
private $result = null;

/** @var bool */
public $isSampled;
private $isSampled;

/** @var ?TransactionContext */
public $context = null;
private $context = null;

/** @var Tracer */
private $tracer;
Expand Down
2 changes: 1 addition & 1 deletion agent/php/ElasticApm/Impl/Util/JsonUtil.php
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ final class JsonUtil
*/
public static function encode($data, bool $prettyPrint = false): string
{
$options = 0;
$options = JSON_INVALID_UTF8_SUBSTITUTE;
$options |= $prettyPrint ? JSON_PRETTY_PRINT : 0;
$encodedData = json_encode($data, $options);
if ($encodedData === false) {
Expand Down
15 changes: 15 additions & 0 deletions tests/ElasticApmTests/UnitTests/PublicApiTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -585,4 +585,19 @@ public function testNumericStringForLabelKey(): void
self::assertSame('label_value_for_key_123', self::getLabel($reportedTx, '123'));
self::assertSame('custom_value_for_key_456', self::getTransactionContextCustom($reportedTx, '456'));
}

private const INVALID_UTF8_CHARACTER = "\xb0";
private const UNICODE_REPLACEMENT_CHARACTER_JSON_ENCODED = '\ufffd';

public function testMalformedUtf8(): void
{
// Act
$tx = $this->tracer->beginTransaction('test_TX_name_[' . self::INVALID_UTF8_CHARACTER . ']', 'test_TX_type');
$tx->end();

// Assert
$reportedTx = $this->mockEventSink->singleTransaction();
$this->assertSame('test_TX_name_[' . json_decode('"' . self::UNICODE_REPLACEMENT_CHARACTER_JSON_ENCODED . '"') . ']', $reportedTx->name);
$this->assertSame('test_TX_type', $reportedTx->type);
}
}
5 changes: 0 additions & 5 deletions tests/ElasticApmTests/UnitTests/Util/MockEventSink.php
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,6 @@ private function consumeMetadata(Metadata $original): void
$deserialized = $this->validateAndDeserializeMetadata($serialized);

self::assertValidMetadata($deserialized);
TestCaseBase::assertEqualsEx($original, $deserialized);
$this->dataFromAgent->metadatas[] = $deserialized;
}

Expand All @@ -116,7 +115,6 @@ private function consumeTransaction(Transaction $original): void
$serialized = SerializationUtil::serializeAsJson($original);
$deserialized = $this->validateAndDeserializeTransaction($serialized);
$deserialized->assertValid();
$deserialized->assertEquals($original);

TestCaseBase::assertNull($this->dataFromAgent->executionSegmentByIdOrNull($deserialized->id));
ArrayUtilForTests::addUnique($deserialized->id, $deserialized, /* ref */ $this->dataFromAgent->idToTransaction);
Expand All @@ -135,7 +133,6 @@ private function consumeSpan(SpanToSendInterface $original): void
$deserialized = $this->validateAndDeserializeSpan($serialized);
$dbgCtx->add(['deserialized' => $deserialized]);
$deserialized->assertValid();
$deserialized->assertEquals($original);

TestCaseBase::assertNull($this->dataFromAgent->executionSegmentByIdOrNull($deserialized->id));
ArrayUtilForTests::addUnique($deserialized->id, $deserialized, /* ref */ $this->dataFromAgent->idToSpan);
Expand All @@ -148,7 +145,6 @@ private function consumeError(Error $original): void
$serialized = SerializationUtil::serializeAsJson($original);
$deserialized = $this->validateAndDeserializeError($serialized);
$deserialized->assertValid();
$deserialized->assertEquals($original);

ArrayUtilForTests::addUnique($deserialized->id, $deserialized, /* ref */ $this->dataFromAgent->idToError);
}
Expand All @@ -161,7 +157,6 @@ private function consumeMetricSet(MetricSet $original): void
$serialized = SerializationUtil::serializeAsJson($original);
$deserialized = $this->validateAndDeserializeMetricSet($serialized);
MetricSetValidator::assertValid($deserialized);
TestCaseBase::assertEqualsEx($original, $deserialized);

$this->dataFromAgent->metricSets[] = $deserialized;
}
Expand Down
44 changes: 0 additions & 44 deletions tests/ElasticApmTests/Util/AssertValidTrait.php
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,6 @@

use Elastic\Apm\Impl\Constants;
use Elastic\Apm\Impl\StackTraceFrame;
use Elastic\Apm\Impl\Util\DbgUtil;
use Elastic\Apm\Impl\Util\IdValidationUtil;
use Elastic\Apm\Impl\Util\TextUtil;

Expand Down Expand Up @@ -293,47 +292,4 @@ public static function assertValidCount($count, int $minValue = 0): int
TestCaseBase::assertGreaterThanOrEqual($minValue, $count);
return $count;
}

/**
* @param mixed $original
* @param mixed $dto
*/
public static function assertEqualOriginalAndDto($original, $dto): void
{
AssertMessageStack::newScope(/* out */ $dbgCtx, AssertMessageStack::funcArgs());
$dbgCtx->add(['original type' => DbgUtil::getType($original), 'dto type' => DbgUtil::getType($dto)]);

if (is_object($dto)) {
TestCaseBase::assertIsObject($original);
$originalPropNameToVal = get_object_vars($original);
$dbgCtx->add(['originalPropNameToVal' => $originalPropNameToVal]);
$dbgCtx->pushSubScope();
foreach (get_object_vars($dto) as $dtoPropName => $dtoPropVal) {
$dbgCtx->clearCurrentSubScope(['dtoPropName' => $dtoPropName, 'dtoPropVal' => $dtoPropVal]);
if ($dtoPropVal !== null) {
TestCaseBase::assertArrayHasKey($dtoPropName, $originalPropNameToVal);
}
if (array_key_exists($dtoPropName, $originalPropNameToVal)) {
self::assertEqualOriginalAndDto($originalPropNameToVal[$dtoPropName], $dtoPropVal);
}
}
$dbgCtx->popSubScope();
return;
}

if (is_array($dto)) {
TestCaseBase::assertIsArray($original);
TestCaseBase::assertSameCount($original, $dto);
$dbgCtx->pushSubScope();
foreach ($dto as $dtoArrayKey => $dtoArrayVal) {
$dbgCtx->clearCurrentSubScope(['dtoArrayKey' => $dtoArrayKey, 'dtoArrayVal' => $dtoArrayVal]);
TestCaseBase::assertArrayHasKey($dtoArrayKey, $original);
self::assertEqualOriginalAndDto($original[$dtoArrayKey], $dtoArrayVal);
}
$dbgCtx->popSubScope();
return;
}

TestCaseBase::assertSame($original, $dto);
}
}
6 changes: 0 additions & 6 deletions tests/ElasticApmTests/Util/ErrorDto.php
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,6 @@
namespace ElasticApmTests\Util;

use Elastic\Apm\Impl\Constants;
use Elastic\Apm\Impl\Error;
use ElasticApmTests\Util\Deserialization\DeserializationUtil;

class ErrorDto
Expand Down Expand Up @@ -152,9 +151,4 @@ public static function assertValidId($errorId): string
{
return self::assertValidIdEx($errorId, Constants::ERROR_ID_SIZE_IN_BYTES);
}

public function assertEquals(Error $original): void
{
self::assertEqualOriginalAndDto($original, $this);
}
}
6 changes: 0 additions & 6 deletions tests/ElasticApmTests/Util/SpanDto.php
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@

namespace ElasticApmTests\Util;

use Elastic\Apm\Impl\SpanToSendInterface;
use Elastic\Apm\Impl\StackTraceFrame;
use ElasticApmTests\Util\Deserialization\DeserializationUtil;
use ElasticApmTests\Util\Deserialization\StacktraceDeserializer;
Expand Down Expand Up @@ -132,11 +131,6 @@ public function assertMatches(SpanExpectations $expectations): void
SpanContextExpectations::assertNullableMatches($expectations->context, $this->context);
}

public function assertEquals(SpanToSendInterface $original): void
{
self::assertEqualOriginalAndDto($original, $this);
}

public function assertService(?string $targetType, ?string $targetName, string $destinationName, string $destinationResource, string $destinationType): void
{
TestCaseBase::assertNotNull($this->context);
Expand Down
6 changes: 0 additions & 6 deletions tests/ElasticApmTests/Util/TransactionDto.php
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@

namespace ElasticApmTests\Util;

use Elastic\Apm\Impl\Transaction;
use ElasticApmTests\Util\Deserialization\DeserializationUtil;

class TransactionDto extends ExecutionSegmentDto
Expand Down Expand Up @@ -142,9 +141,4 @@ public function assertMatches(TransactionExpectations $expectations): void
$this->context->assertValid();
}
}

public function assertEquals(Transaction $original): void
{
self::assertEqualOriginalAndDto($original, $this);
}
}

0 comments on commit 7a3f179

Please sign in to comment.